require 'spec_helper' require 'btc_wire_proto' # Fixtures data. Taken from: https://en.bitcoin.it/wiki/Protocol_specification include ::BtcWireProto describe Ipv6Address do it "should read IPv6 strings into IPv6 addresses" do ex_obj = IPAddress("fea1:fea1:fea1:fea1:fea1:fea1:fea1:fea1") ex = ex_obj.data ip = Ipv6Address::read(ex) ip.should == ex_obj ip.to_binary_s.should == ex 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 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 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") ip = Ipv6Address.new(ipv4.to_u32) ip.should == IPAddress("::ffff:127.0.0.1") ip = Ipv6Address.new(ipv6.to_u128) ip.should == ipv6 end it "should raise ArgumentError for unparseable objects" do lambda { Ipv6Address.new("bad IP") }.should raise_error(ArgumentError) end end 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 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 }) SRV = { :node_network => 1 } describe Message do context "Version message" do it "should have a Version payload" do m = Message::new(:command => 'version') m.payload.selection.should == "version" end it "should have a :version command symbol" do m = Message::new(:command => 'version') m.cmd_sym.should == :version end it "should parse binary data correctly" do m = Message::read(GOOD_VERSION_DATA) m.magic.should == BtcWireProto::NETWORKS[:main] m.network_name.should == :main m.command.should == "version\x00\x00\x00\x00\x00" m.payload_len.should == 85 m.has_parameter?(:checksum).should be_false 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 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 end end 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 }) context "Verack message" do it "should have no payload" do m = Message.new(:command => "verack") m.payload.selection.should == "null" end it "should have a :verack command symbol" do m = Message.new(:command => "verack") m.cmd_sym.should == :verack end it "should parse the binary data correctly" do 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 end end end end