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