2011-06-04 22:53:33 +01:00
|
|
|
require 'spec_helper'
|
|
|
|
require 'btc_wire_proto'
|
|
|
|
|
|
|
|
# Fixtures data. Taken from: https://en.bitcoin.it/wiki/Protocol_specification
|
|
|
|
|
|
|
|
include ::BtcWireProto
|
|
|
|
|
2011-06-10 23:01:00 +01:00
|
|
|
describe Ipv6Address do
|
|
|
|
|
2011-06-21 20:47:03 +01:00
|
|
|
it "should read IPv6 strings into IPv6 addresses" do
|
|
|
|
ex_obj = IPAddress("fea1:fea1:fea1:fea1:fea1:fea1:fea1:fea1")
|
|
|
|
ex = ex_obj.data
|
2011-06-10 23:01:00 +01:00
|
|
|
|
2011-06-21 20:47:03 +01:00
|
|
|
ip = Ipv6Address::read(ex)
|
|
|
|
ip.should == ex_obj
|
|
|
|
ip.to_binary_s.should == ex
|
2011-06-10 23:01:00 +01:00
|
|
|
|
2011-06-21 20:47:03 +01:00
|
|
|
ip = Ipv6Address.new(ex_obj)
|
|
|
|
ip.to_binary_s.should == ex
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should read IPv4 strings into IPv6-mapped addresses" do
|
|
|
|
ip = Ipv6Address.new("127.0.0.1")
|
|
|
|
ip.should == IPAddress("::ffff:127.0.0.1")
|
|
|
|
end
|
2011-06-10 23:01:00 +01:00
|
|
|
|
2011-06-21 20:47:03 +01:00
|
|
|
it "should read IPAddress objects into IPv6/mapped addresses" do
|
|
|
|
ip = Ipv6Address.new(IPAddress("127.0.0.1"))
|
|
|
|
ip.should == (ip_m = IPAddress("::ffff:127.0.0.1"))
|
|
|
|
ip.to_binary_s.should == ip_m.data
|
|
|
|
end
|
2011-06-10 23:01:00 +01:00
|
|
|
|
2011-06-21 20:47:03 +01:00
|
|
|
it "should read arbitrary objects with #to_u128 into IPv6/mapped addresses" do
|
|
|
|
o = Object.new
|
|
|
|
class << o ; def to_u128 ; 0xffffffffffff ; end ; end
|
|
|
|
ip = Ipv6Address.new(o)
|
|
|
|
ip.should == IPAddress("::ffff:ffff:ffff")
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should read Fixnums and Bignums into IPv6/mapped addresses" do
|
|
|
|
ipv4 = IPAddress("127.0.0.1") # Should still be mapped
|
|
|
|
ipv6 = IPAddress("fe80::1")
|
2011-06-10 23:01:00 +01:00
|
|
|
|
2011-06-21 20:47:03 +01:00
|
|
|
ip = Ipv6Address.new(ipv4.to_u32)
|
|
|
|
ip.should == IPAddress("::ffff:127.0.0.1")
|
|
|
|
|
|
|
|
ip = Ipv6Address.new(ipv6.to_u128)
|
|
|
|
ip.should == ipv6
|
|
|
|
end
|
2011-06-10 23:01:00 +01:00
|
|
|
|
2011-06-21 20:47:03 +01:00
|
|
|
it "should raise ArgumentError for unparseable objects" do
|
|
|
|
lambda { Ipv6Address.new("bad IP") }.should raise_error(ArgumentError)
|
2011-06-10 23:01:00 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-06-04 22:53:33 +01:00
|
|
|
describe ::BtcWireProto do
|
|
|
|
|
|
|
|
# Payload fragments
|
|
|
|
describe ServicesMask do
|
|
|
|
it "should have node_network set to false when its bit is 0" do
|
|
|
|
s = ServicesMask::read("\x00" * 8) # All 64 bits unset
|
|
|
|
s.node_network.should == 0
|
|
|
|
|
|
|
|
s = ServicesMask::read(binary(%w{01 00 00 00 00 00 00 00}))
|
|
|
|
s.node_network.should == 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe NetAddr do
|
|
|
|
it "should have all fields set to 0 when the input data is all zeroes" do
|
|
|
|
na = NetAddr::read("\x00" * 26)
|
|
|
|
|
|
|
|
na.services.node_network.should == 0
|
|
|
|
na.ip.to_u128.should == 0
|
|
|
|
na.port.should == 0
|
|
|
|
end
|
|
|
|
|
|
|
|
it "Should allow the Ip field to be set with Ruby native types" do
|
|
|
|
na = NetAddr::read("\x00" * 26)
|
|
|
|
mip = IPAddress("::ffff:0.0.0.1")
|
|
|
|
na.ip = mip
|
|
|
|
na.ip.to_u128.should == mip.to_u128
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should have the fields set appropriately when fed binary data" do
|
|
|
|
na = NetAddr::read(
|
|
|
|
binary(%w{
|
|
|
|
01 00 00 00 00 00 00 00 00 00 00 00 00
|
|
|
|
00 00 00 00 00 FF FF 0A 00 00 01 20 8D
|
|
|
|
})
|
|
|
|
)
|
|
|
|
|
|
|
|
na.services.node_network.should == 1
|
|
|
|
na.ip.to_s.should == "::ffff:10.0.0.1"
|
|
|
|
na.port.should == 8333
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
describe TimestampedNetAddr do
|
|
|
|
it "should leverage NetAddr" do
|
|
|
|
tna = TimestampedNetAddr::read("\x00" * 30)
|
|
|
|
tna.net_addr.class.should == BtcWireProto::NetAddr
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should have all fields set to 0 when the input data is all zeroes" do
|
|
|
|
tna = TimestampedNetAddr::read("\x00" * 30)
|
|
|
|
tna.timestamp.should == 0
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should have the fields set appropriately when fed binary data" do
|
|
|
|
tna = TimestampedNetAddr::read(
|
|
|
|
binary(%w{
|
|
|
|
E2 15 10 4D 01 00 00 00 00 00 00 00 00 00 00
|
|
|
|
00 00 00 00 00 00 00 FF FF 0A 00 00 01 20 8D
|
|
|
|
})
|
|
|
|
)
|
|
|
|
|
|
|
|
tna.timestamp.should == 1292899810
|
|
|
|
|
|
|
|
tna.net_addr.services.node_network.should == 1
|
|
|
|
tna.net_addr.ip.to_s.should == "::ffff:10.0.0.1"
|
|
|
|
tna.net_addr.port.should == 8333
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
describe VarInt do
|
|
|
|
it "should hold numbers < 0x00000000 in nine bytes" do
|
|
|
|
bin_minus_1 = "\xff" + "\x00\x00\x00\x00" + "\x01\x00\x00\x00"
|
|
|
|
a = VarInt::read(bin_minus_1)
|
|
|
|
a.should == -1
|
|
|
|
a.num_bytes.should == 9
|
|
|
|
a.to_binary_s.should == bin_minus_1
|
|
|
|
|
|
|
|
a = VarInt::read("\xff" * 9)
|
|
|
|
a.should == -(2**64 - 1)
|
|
|
|
a.num_bytes.should == 9
|
|
|
|
a.to_binary_s.should == "\xff" * 9
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should hold numbers >= 0x00000000 and < 0x000000fd in one byte" do
|
|
|
|
a = VarInt::read("\x00")
|
|
|
|
a.should == 0x00
|
|
|
|
a.num_bytes.should == 1
|
|
|
|
a.to_binary_s.should == "\x00"
|
|
|
|
|
|
|
|
a = VarInt::read("\xFC")
|
|
|
|
a.should == 0xFC
|
|
|
|
a.num_bytes.should == 1
|
|
|
|
a.to_binary_s.should == "\xFC"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should hold numbers >= 0x000000fd and < 0x00010000 in three bytes" do
|
|
|
|
a = VarInt::read("\xFD\xFD\x00")
|
|
|
|
a.should == 0xFD
|
|
|
|
a.num_bytes.should == 3
|
|
|
|
a.to_binary_s.should == "\xFD\xFD\x00"
|
|
|
|
|
|
|
|
a = VarInt::read("\xFD\xFF\xFF")
|
|
|
|
a.should == 0xFFFF
|
|
|
|
a.num_bytes.should == 3
|
|
|
|
a.to_binary_s.should == "\xFD\xFF\xFF"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should hold numbers >= 0x00010000 and < 0xffffffff in five bytes" do
|
|
|
|
a = VarInt::read("\xFE\x00\x00\x01\x00")
|
|
|
|
a.should == 0x10000
|
|
|
|
a.num_bytes.should == 5
|
|
|
|
a.to_binary_s.should == "\xFE\x00\x00\x01\x00"
|
|
|
|
|
|
|
|
a = VarInt::read("\xFE\xFF\xFF\xFF\xFF")
|
|
|
|
a.should == 0xFFFFFFFF
|
|
|
|
a.num_bytes.should == 5
|
|
|
|
a.to_binary_s.should == "\xFE\xFF\xFF\xFF\xFF"
|
|
|
|
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe VarStr do
|
|
|
|
it "should store string length in a var_int" do
|
|
|
|
a = VarStr::read("\x04abcd")
|
|
|
|
a.should == "abcd"
|
|
|
|
a.num_bytes.should == 5
|
|
|
|
a.to_binary_s.should == "\x04abcd"
|
|
|
|
|
|
|
|
a = VarStr::read("\xFD\xFF\xFF" + "A" * 0xFFFF)
|
|
|
|
a.should == "A" * 0xFFFF
|
|
|
|
a.num_bytes.should == 0xFFFF + 3
|
|
|
|
a.to_binary_s.should == "\xFD\xFF\xFF" + "A" * 0xFFFF
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe InventoryVector do
|
|
|
|
end
|
|
|
|
|
|
|
|
describe Sha256 do
|
|
|
|
end
|
|
|
|
|
|
|
|
describe TransactionIn do
|
|
|
|
end
|
|
|
|
|
|
|
|
describe TransactionOut do
|
|
|
|
end
|
|
|
|
|
|
|
|
describe BlockHdr do
|
|
|
|
end
|
|
|
|
|
|
|
|
# Payloads
|
|
|
|
|
|
|
|
describe Version do
|
|
|
|
it "should interpret binary data correctly" do
|
|
|
|
ver = Version::read(binary(%w{
|
|
|
|
9C 7C 00 00 01 00 00 00 00 00 00 00 E6 15 10 4D 00 00 00 00
|
|
|
|
01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF
|
|
|
|
0A 00 00 01 DA F6 01 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
|
|
00 00 00 00 FF FF 0A 00 00 02 20 8D DD 9D 20 2C 3A B4 57 13
|
|
|
|
00 55 81 01 00
|
|
|
|
}))
|
|
|
|
|
|
|
|
ver.version.should == 31900
|
|
|
|
ver.services.node_network.should == 1
|
|
|
|
ver.timestamp.should == 1292899814
|
|
|
|
ver.addr_me.class.should == NetAddr
|
|
|
|
ver.addr_you.class.should == NetAddr
|
|
|
|
ver.nonce.should == 0x1357B43A2C209DDD
|
|
|
|
ver.sub_version.should == ""
|
|
|
|
ver.start_height.should == 98645
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should exclude some fields by version" do
|
|
|
|
v = Version::read([1].pack("V") + "\x00" * 42)
|
|
|
|
v.num_bytes.should == 46
|
|
|
|
v = Version::read([106].pack("V") + "\x00" * 77)
|
|
|
|
v.num_bytes.should == 81
|
|
|
|
v = Version::read([209].pack("V") + "\x00" * 81)
|
|
|
|
v.num_bytes.should == 85
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe AddrPre31402 do
|
|
|
|
end
|
|
|
|
|
|
|
|
describe AddrFrom31402 do
|
|
|
|
end
|
|
|
|
|
|
|
|
describe Inventory do
|
|
|
|
end
|
|
|
|
|
|
|
|
describe BlockSpec do
|
|
|
|
end
|
|
|
|
|
|
|
|
describe Transaction do
|
|
|
|
end
|
|
|
|
|
|
|
|
describe Block do
|
|
|
|
end
|
|
|
|
|
|
|
|
describe Headers do
|
|
|
|
end
|
|
|
|
|
|
|
|
describe CheckOrder do
|
|
|
|
end
|
|
|
|
|
|
|
|
describe SubmitOrder do
|
|
|
|
end
|
|
|
|
|
|
|
|
describe Reply do
|
|
|
|
end
|
|
|
|
|
|
|
|
describe Alert do
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
# Messages
|
2011-06-21 20:47:03 +01:00
|
|
|
GOOD_VERSION_DATA = binary(%w{
|
|
|
|
F9 BE B4 D9 76 65 72 73 69 6F 6E 00 00 00 00 00 55 00 00
|
|
|
|
00 9C 7C 00 00 01 00 00 00 00 00 00 00 E6 15 10 4D 00 00
|
|
|
|
00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
|
|
00 FF FF 0A 00 00 01 DA F6 01 00 00 00 00 00 00 00 00 00
|
|
|
|
00 00 00 00 00 00 00 00 FF FF 0A 00 00 02 20 8D DD 9D 20
|
|
|
|
2C 3A B4 57 13 00 55 81 01 00
|
|
|
|
})
|
2011-06-04 22:53:33 +01:00
|
|
|
|
2011-06-21 20:47:03 +01:00
|
|
|
SRV = { :node_network => 1 }
|
|
|
|
|
2011-06-04 22:53:33 +01:00
|
|
|
describe Message do
|
|
|
|
context "Version message" do
|
|
|
|
it "should have a Version payload" do
|
2011-06-21 20:47:03 +01:00
|
|
|
m = Message::new(:command => 'version')
|
2011-06-04 22:53:33 +01:00
|
|
|
m.payload.selection.should == "version"
|
|
|
|
end
|
|
|
|
|
2011-06-09 22:03:57 +01:00
|
|
|
it "should have a :version command symbol" do
|
2011-06-21 20:47:03 +01:00
|
|
|
m = Message::new(:command => 'version')
|
2011-06-09 22:03:57 +01:00
|
|
|
m.cmd_sym.should == :version
|
|
|
|
end
|
|
|
|
|
2011-06-04 22:53:33 +01:00
|
|
|
it "should parse binary data correctly" do
|
2011-06-21 20:47:03 +01:00
|
|
|
m = Message::read(GOOD_VERSION_DATA)
|
|
|
|
|
|
|
|
m.magic.should == BtcWireProto::NETWORKS[:main]
|
|
|
|
m.command.should == "version\x00\x00\x00\x00\x00"
|
|
|
|
m.payload_len.should == 85
|
|
|
|
m.has_parameter?(:checksum).should be_false
|
2011-06-04 22:53:33 +01:00
|
|
|
m.payload.version.should == 31900
|
|
|
|
m.payload.services.node_network.should == 1
|
|
|
|
m.payload.timestamp.should == 1292899814
|
|
|
|
m.payload.addr_me.class.should == NetAddr
|
|
|
|
m.payload.addr_you.class.should == NetAddr
|
|
|
|
m.payload.nonce.should == 0x1357B43A2C209DDD
|
|
|
|
m.payload.sub_version.should == ""
|
|
|
|
m.payload.start_height.should == 98645
|
2011-06-21 20:47:03 +01:00
|
|
|
m.to_binary_s.should == GOOD_VERSION_DATA
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should generate binary data correctly" do
|
|
|
|
m = Message::read(GOOD_VERSION_DATA)
|
|
|
|
ex = Message::new(
|
|
|
|
:magic => BtcWireProto::NETWORKS[:main], :command => 'version',
|
|
|
|
:payload => {
|
|
|
|
:version => 31900, :services => {:node_network => 1},
|
|
|
|
:timestamp => 1292899814, :nonce => 0x1357B43A2C209DDD,
|
|
|
|
:sub_version => "", :start_height => 98645,
|
|
|
|
:addr_me => { :services => SRV, :ip => "10.0.0.1", :port => 56054 },
|
|
|
|
:addr_you => { :services => SRV, :ip => "10.0.0.2", :port => 8333 }
|
|
|
|
}
|
|
|
|
)
|
|
|
|
m.should == ex
|
|
|
|
ex.to_binary_s.should == GOOD_VERSION_DATA
|
|
|
|
|
2011-06-04 22:53:33 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-06-21 20:47:03 +01:00
|
|
|
GOOD_VERACK_DATA = binary(%w{
|
|
|
|
F9 BE B4 D9 76 65 72 61 63 6B 00 00 00 00 00 00 00 00 00 00
|
|
|
|
})
|
|
|
|
|
2011-06-04 22:53:33 +01:00
|
|
|
context "Verack message" do
|
2011-06-21 20:47:03 +01:00
|
|
|
|
2011-06-04 22:53:33 +01:00
|
|
|
it "should have no payload" do
|
2011-06-21 20:47:03 +01:00
|
|
|
m = Message.new(:command => "verack")
|
2011-06-04 22:53:33 +01:00
|
|
|
m.payload.selection.should == "null"
|
|
|
|
end
|
2011-06-09 22:03:57 +01:00
|
|
|
|
|
|
|
it "should have a :verack command symbol" do
|
2011-06-21 20:47:03 +01:00
|
|
|
m = Message.new(:command => "verack")
|
2011-06-09 22:03:57 +01:00
|
|
|
m.cmd_sym.should == :verack
|
|
|
|
end
|
2011-06-04 22:53:33 +01:00
|
|
|
|
|
|
|
it "should parse the binary data correctly" do
|
2011-06-21 20:47:03 +01:00
|
|
|
m = Message::read(GOOD_VERACK_DATA)
|
|
|
|
m.magic.should == BtcWireProto::NETWORKS[:main]
|
|
|
|
m.command.should == "verack\x00\x00\x00\x00\x00\x00"
|
|
|
|
m.payload_len.should == 0
|
|
|
|
m.has_parameter?(:chcksum).should be_false
|
|
|
|
m.to_binary_s.should == GOOD_VERACK_DATA
|
2011-06-04 22:53:33 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|