diff --git a/lib/btc_wire_proto.rb b/lib/btc_wire_proto.rb index 938c22f..2e71da5 100644 --- a/lib/btc_wire_proto.rb +++ b/lib/btc_wire_proto.rb @@ -47,23 +47,21 @@ module BtcWireProto "c5ecbcd68284" ## Components of payloads ## -end # Bitmask advertising various capabilities of the node. # @author Nick Thomas - class BtcWireProto::ServicesMask < BinData::Record + class ServicesMask < BinData::Record endian :little - bit62 :undefined_top + bit63 :undefined bit1 :node_network - bit1 :undefined_bottom end # Structure holding an IP address and port in a slightly unusual format. # This one is big-endian - everything else is little-endian. # # @author Nick Thomas - class BtcWireProto::NetAddr < BinData::Record + class NetAddr < BinData::Record endian :big services_mask :services uint128 :ip # IPv6 address. IPv4 addresses given as IPv6-mapped IPv4 @@ -72,7 +70,7 @@ end # Like a NetAddr but with a timestamp to boot. # @author Nick Thomas - class BtcWireProto::TimestampedNetAddr < BinData::Record + class TimestampedNetAddr < BinData::Record endian :little uint32 :timestamp @@ -81,7 +79,7 @@ end # Variable-length integer. This is slightly scary. # @author Nick Thomas - class BtcWireProto::VarInt < BinData::BasePrimitive + class VarInt < BinData::BasePrimitive def value_to_binary_string(val) val = val.to_i @@ -102,7 +100,6 @@ end "" end end - BinData::RegisteredClasses.register("var_int", BtcWireProto::VarInt) def read_and_return_value(io) return nil if io.length < 1 @@ -124,10 +121,12 @@ end end end + BinData::RegisteredClasses.register("var_int", VarInt) + # Variable-length pascal string with a variable-length int specifying the # length. I kid you not. # @author Nick Thomas - class BtcWireProto::VarStr < BinData::Primitive + class VarStr < BinData::Primitive endian :little var_int :len, :value => lambda { data.length } @@ -137,37 +136,42 @@ end def set(v) ; self.data = v ; end end - class BtcWireProto::InventoryVector < BinData::Record + class InventoryVector < BinData::Record endian :little uint32 :type # For values, see INV_VEC_TYPES - string :hash, :length => 32 + string :iv_hash, :length => 32 end # Simple class wrapping raw SHA256 data. Might have utility methods later. # @author Nick Thomas - class BtcWireProto::Sha256 < BinData::Record - uint256 :data # Raw SHA256 data + class Sha256 < BinData::Record + string :data, :length => 32 # Raw SHA256 data end - BtcWireProto::SHA256 = BtcWireProto::Sha256 + SHA256 = Sha256 + + # @author Nick Thomas + class TransactionIn < BinData::Record + endian :little + + sha256 :po_hash + uint32 :po_index - class BtcWireProto::TransactionIn < BinData::Record - struct :previous_output do - sha256 :hash - uint32 :index - end var_str :signature_script # Script for confirming transaction authorisation - uint32 :sequence # Version of this record. + uint32 :sequence # Version of this record. end - - class BtcWireProto::TranactionOut < BinData::Record - uint64 :value + + # @author Nick Thomas + class TransactionOut < BinData::Record + endian :little + + uint64 :txout_value var_str :pk_script # Script containing conditions to claim to transaction end # Header for a block. # @author Nick Thomas - class BtcWireProto::BlockHdr < BinData::Record + class BlockHdr < BinData::Record endian :little uint32 :version @@ -183,7 +187,7 @@ end # Payload for a version message # @author Nick Thomas - class BtcWireProto::Version < BinData::Record + class Version < BinData::Record endian :little uint32 :version @@ -199,41 +203,41 @@ end # Payload for an addr message in versions earlier than 31402. These are # used to get a list of peers to interact with. # @author Nick Thomas - class BtcWireProto::AddrPre31402 < BinData::Record + class AddrPre31402 < BinData::Record endian :little - var_int :count + var_int :addr_count array :addrs, :type => :net_addr, - :read_until => lambda { index == count - 1 } + :read_until => lambda { index == addr_count - 1 } end # Payload for an addr message in versions later than 31402. A timestamp was # added to the list of addresses, but otherwise it's the same as AddrPre31402 # @author Nick Thomas - class BtcWireProto::AddrFrom31402 < BinData::Record + class AddrFrom31402 < BinData::Record endian :little - var_int :count + var_int :addr_count array :timestamped_addrs, :type => :timestamped_net_addr, - :read_until => lambda { index == count - 1 } + :read_until => lambda { index == addr_count - 1 } end # Payload for a getdata or inv message. This lets the peer advertise the # various objects it has knowledge of. # @author Nick Thomas - class BtcWireProto::Inventory < BinData::Record + class Inventory < BinData::Record endian :little - var_int :count + var_int :iv_count array :items, :type => :inventory_vector, - :read_until => lambda { index == count - 1 } + :read_until => lambda { index == iv_count - 1 } end # Payload for a getblocks or getheaders message. Specifies a set of blocks # that the sender wants details of. # @author Nick thomas - class BtcWireProto::BlockSpec < BinData::Record + class BlockSpec < BinData::Record endian :little uint32 :version @@ -246,7 +250,7 @@ end # A transaction. This contains a number of transactions 'in', and 'out'. # @author Nick Thomas - class BtcWireProto::Transaction < BinData::Record + class Transaction < BinData::Record endian :little uint32 :version @@ -261,7 +265,7 @@ end # Details about a particular block. Returned in response to a block request # @author Nick Thomas - class BtcWireProto::Block < BinData::Record + class Block < BinData::Record endian :little block_hdr :header @@ -272,32 +276,32 @@ end # Headers payloads are returned in response to a getheaders request. # Limit of 2,000 entries per message. # @author Nick Thomas - class BtcWireProto::Headers < BinData::Record + class Headers < BinData::Record endian :little - var_int :count + var_int :hdr_count array :block_hdrs, :type => :block_hdr, - :read_until => lambda { index == count - 1 } + :read_until => lambda { index == hdr_count - 1 } end # For now, we don't support CheckOrder requests at all. Protocol documentation # is lacking! FIXME # @author Nick Thomas - class BtcWireProto::CheckOrder < BinData::Record + class CheckOrder < BinData::Record endian :little end - # We don't support SubmitOrder requests either. Receiving either of these will + # We don't support SubmitOrder replies either. Receiving either of these will # actually break the stream, since we don't even know how long they are. FIXME # @author Nick Thomas - class BtcWireProto::SubmitOrder < BinData::Record + class SubmitOrder < BinData::Record endian :little end # Used as a response to a CheckOrder request. # @author Nick Thomas - class BtcWireProto::Reply < BinData::Record + class Reply < BinData::Record endian :little uint32 :reply # See REPLYCODES for possible values @@ -307,7 +311,7 @@ end # sent it - if it's signed by a particular key, then we should apparently # show the message to the user and cease operation until further notice. Fun! # @author Nick Thomas - class BtcWireProto::Alert < BinData::Record + class Alert < BinData::Record endian :little var_str :message @@ -318,7 +322,7 @@ end # Found at the start of all Bitcoin messages. # @author Nick Thomas - class BtcWireProto::MessageHdr < BinData::Record + class MessageHdr < BinData::Record endian :little uint32 :magic string :command, :length => 12 @@ -334,9 +338,10 @@ end end end + # Everything on the wire is a Message. # @author Nick Thomas - class BtcWireProto::Message < BinData::Record + class Message < BinData::Record # @param[Fixnum,nil] version The protocol version. Setting this affects # the layout of various fields. @@ -348,8 +353,8 @@ end choice :payload, :selection => :payload_choice do version "version" - addr_pre_31402 "addr_pre_31402" - addr_from_31402 "addr_from_31402" + addr_pre31402 "addr_pre31402" + addr_from31402 "addr_from31402" inventory "inv" inventory "getdata" block_spec "getblocks" @@ -357,8 +362,8 @@ end transaction "tx" block "block" headers "headers" - checkorder "checkorder" - submitorder "submitorder" + check_order "checkorder" + submit_order "submitorder" alert "alert" end @@ -381,11 +386,11 @@ end return nil if %w|verack getaddr ping|.include?(header.command) # Payload has two forms, depending on protocol version. Ugh. - return (@version < 31402 ? "addr_pre_31402" : "addr_from_31402") if + return (@version < 31402 ? "addr_pre31402" : "addr_from31402") if header.command == "addr" end end -#end +end diff --git a/spec/btc_wire_proto_spec.rb b/spec/btc_wire_proto_spec.rb index 8666b6d..5b45f3f 100644 --- a/spec/btc_wire_proto_spec.rb +++ b/spec/btc_wire_proto_spec.rb @@ -5,13 +5,18 @@ require 'btc_wire_proto' SERVICE_MASK = "\x00\x00\x00\x01" # Sets NODE_NETWORK +include ::BtcWireProto + +describe ::BtcWireProto do -describe BtcWireProto do # Payload fragments describe ServicesMask do it "should have node_network set to false when its bit is 0" do - s = BtcWireProto::ServicesMask::read("\x00\x00\x00\x00") - s.node_network.should == false + s = ServicesMask::read("\x00" * 8) # All 64 bits unset + s.node_network.should == 0 + + s = ServicesMask::read("\x00" * 7 + "\x01") + s.node_network.should == 1 end end @@ -31,7 +36,7 @@ describe BtcWireProto do end describe TransactionOut do end - describe BlockHeader do + describe BlockHdr do end # Payloads