Module: Bitcoin::Protocol

Defined in:
lib/bitcoin/protocol.rb,
lib/bitcoin/protocol/tx.rb,
lib/bitcoin/protocol/txin.rb,
lib/bitcoin/protocol/block.rb,
lib/bitcoin/protocol/txout.rb,
lib/bitcoin/protocol/parser.rb,
lib/bitcoin/protocol/reject.rb,
lib/bitcoin/protocol/address.rb,
lib/bitcoin/protocol/aux_pow.rb,
lib/bitcoin/protocol/handler.rb,
lib/bitcoin/protocol/version.rb,
lib/bitcoin/protocol/script_witness.rb,
lib/bitcoin/protocol/partial_merkle_tree.rb

Defined Under Namespace

Classes: Addr, AuxPow, Block, Handler, Parser, PartialMerkleTree, Reject, ScriptWitness, Tx, TxIn, TxOut, Version

Constant Summary collapse

MAX_INV_SZ =

bitcoin/src/main.h

50000
BIP0031_VERSION =

BIP 0031, pong message, is enabled for all versions AFTER this one

60000
Uniq =
rand(0xffffffffffffffff)
BINARY =
Encoding.find('ASCII-8BIT')
TypeLookup =
DEFAULT_STOP_HASH =
"00"*32

Class Method Summary collapse

Class Method Details

.getblocks_pkt(version, locator_hashes, stop_hash = DEFAULT_STOP_HASH) ⇒ Object



174
175
176
# File 'lib/bitcoin/protocol.rb', line 174

def self.getblocks_pkt(version, locator_hashes, stop_hash=DEFAULT_STOP_HASH)
  pkt "getblocks",  locator_payload(version, locator_hashes, stop_hash)
end

.getdata_pkt(type, hashes) ⇒ Object



151
152
153
154
155
# File 'lib/bitcoin/protocol.rb', line 151

def self.getdata_pkt(type, hashes)
  return if hashes.size > MAX_INV_SZ
  t = [ TypeLookup[type] ].pack("V")
  pkt("getdata", pack_var_int(hashes.size) + hashes.map{|hash| t + hash[0..32].reverse }.join)
end

.getheaders_pkt(version, locator_hashes, stop_hash = DEFAULT_STOP_HASH) ⇒ Object



178
179
180
# File 'lib/bitcoin/protocol.rb', line 178

def self.getheaders_pkt(version, locator_hashes, stop_hash=DEFAULT_STOP_HASH)
  pkt "getheaders", locator_payload(version, locator_hashes, stop_hash)
end

.headers_pkt(version, blocks) ⇒ Object



182
183
184
# File 'lib/bitcoin/protocol.rb', line 182

def self.headers_pkt(version, blocks)
  pkt "headers", [pack_var_int(blocks.size), blocks.map{|block| block.block_header}.join].join
end

.inv_pkt(type, hashes) ⇒ Object



157
158
159
160
161
# File 'lib/bitcoin/protocol.rb', line 157

def self.inv_pkt(type, hashes)
  return if hashes.size > MAX_INV_SZ
  t = [ TypeLookup[type] ].pack("V")
  pkt("inv", pack_var_int(hashes.size) + hashes.map{|hash| t + hash[0..32].reverse }.join)
end

.locator_payload(version, locator_hashes, stop_hash) ⇒ Object



165
166
167
168
169
170
171
172
# File 'lib/bitcoin/protocol.rb', line 165

def self.locator_payload(version, locator_hashes, stop_hash)
  [
    [version].pack("V"),
    pack_var_int(locator_hashes.size),
    locator_hashes.map{|l| l.htb_reverse }.join,
    stop_hash.htb_reverse
  ].join
end

.pack_boolean(b) ⇒ Object



105
106
107
# File 'lib/bitcoin/protocol.rb', line 105

def self.pack_boolean(b)
  (b == true) ? [0xFF].pack("C") : [0x00].pack("C")
end

.pack_var_int(i) ⇒ Object



53
54
55
56
57
58
59
60
# File 'lib/bitcoin/protocol.rb', line 53

def self.pack_var_int(i)
  if    i <  0xfd;                [      i].pack("C")
  elsif i <= 0xffff;              [0xfd, i].pack("Cv")
  elsif i <= 0xffffffff;          [0xfe, i].pack("CV")
  elsif i <= 0xffffffffffffffff;  [0xff, i].pack("CQ")
  else raise "int(#{i}) too large!"
  end
end

.pack_var_string(payload) ⇒ Object



72
73
74
# File 'lib/bitcoin/protocol.rb', line 72

def self.pack_var_string(payload)
  pack_var_int(payload.bytesize) + payload
end

.ping_pkt(nonce = rand(0xffffffff)) ⇒ Object



137
138
139
# File 'lib/bitcoin/protocol.rb', line 137

def self.ping_pkt(nonce = rand(0xffffffff))
  pkt("ping", [nonce].pack("Q"))
end

.pkt(command, payload) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
# File 'lib/bitcoin/protocol.rb', line 111

def self.pkt(command, payload)
  cmd      = command.ljust(12, "\x00")[0...12]
  length   = [payload.bytesize].pack("V")
  checksum = Digest::SHA256.digest(Digest::SHA256.digest(payload))[0...4]
  pkt      = "".force_encoding(BINARY)
  pkt << Bitcoin.network[:magic_head].force_encoding(BINARY)
  pkt << cmd.force_encoding(BINARY)
  pkt << length
  pkt << checksum
  pkt << payload.dup.force_encoding(BINARY)
end

.pong_pkt(nonce) ⇒ Object



141
142
143
# File 'lib/bitcoin/protocol.rb', line 141

def self.pong_pkt(nonce)
  pkt("pong", [nonce].pack("Q"))
end

.read_binary_file(path) ⇒ Object



186
187
188
# File 'lib/bitcoin/protocol.rb', line 186

def self.read_binary_file(path)
  File.open(path, 'rb'){|f| f.read }
end

.unpack_boolean(payload) ⇒ Object



100
101
102
103
# File 'lib/bitcoin/protocol.rb', line 100

def self.unpack_boolean(payload)
  bdata, payload = payload.unpack("Ca*")
  [ (bdata == 0 ? false : true), payload ]
end

.unpack_var_int(payload) ⇒ Object

var_int refers to en.bitcoin.it/wiki/Protocol_specification#Variable_length_integer and is what Satoshi called “CompactSize” BitcoinQT has later added even more compact format called CVarInt to use in its local block storage. CVarInt is not implemented here.



34
35
36
37
38
39
40
41
# File 'lib/bitcoin/protocol.rb', line 34

def self.unpack_var_int(payload)
  case payload.unpack("C")[0] # TODO add test cases
  when 0xfd; payload.unpack("xva*")
  when 0xfe; payload.unpack("xVa*")
  when 0xff; payload.unpack("xQa*") # TODO add little-endian version of Q
  else;      payload.unpack("Ca*")
  end
end

.unpack_var_int_array(payload) ⇒ Object

unpacks set<int>



88
89
90
91
92
93
94
95
96
97
98
# File 'lib/bitcoin/protocol.rb', line 88

def self.unpack_var_int_array(payload) # unpacks set<int>
  buf = StringIO.new(payload)
  size =  unpack_var_int_from_io(buf)
  return [nil, buf.read] if size == 0
  ints = []
  size.times{
    break if buf.eof?
    ints << unpack_var_int_from_io(buf)
  }
  [ints, buf.read]
end

.unpack_var_int_from_io(io) ⇒ Object



43
44
45
46
47
48
49
50
51
# File 'lib/bitcoin/protocol.rb', line 43

def self.unpack_var_int_from_io(io)
  uchar = io.read(1).unpack("C")[0]
  case uchar
  when 0xfd; io.read(2).unpack("v")[0]
  when 0xfe; io.read(4).unpack("V")[0]
  when 0xff; io.read(8).unpack("Q")[0]
  else;      uchar
  end
end

.unpack_var_string(payload) ⇒ Object



62
63
64
65
# File 'lib/bitcoin/protocol.rb', line 62

def self.unpack_var_string(payload)
  size, payload = unpack_var_int(payload)
  size > 0 ? (_, payload = payload.unpack("a#{size}a*")) : [nil, payload]
end

.unpack_var_string_array(payload) ⇒ Object

unpacks set<string>



76
77
78
79
80
81
82
83
84
85
86
# File 'lib/bitcoin/protocol.rb', line 76

def self.unpack_var_string_array(payload) # unpacks set<string>
  buf = StringIO.new(payload)
  size = unpack_var_int_from_io(buf)
  return [nil, buf.read] if size == 0
  strings = []
  size.times{
    break if buf.eof?
    strings << unpack_var_string_from_io(buf)
  }
  [strings, buf.read]
end

.unpack_var_string_from_io(buf) ⇒ Object



67
68
69
70
# File 'lib/bitcoin/protocol.rb', line 67

def self.unpack_var_string_from_io(buf)
  size = unpack_var_int_from_io(buf)
  size > 0 ? buf.read(size) : nil
end

.verack_pktObject



145
146
147
# File 'lib/bitcoin/protocol.rb', line 145

def self.verack_pkt
  pkt("verack", "")
end

.version_pkt(from_id, from = nil, to = nil, last_block = nil, time = nil, user_agent = nil, version = nil) ⇒ Object



123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/bitcoin/protocol.rb', line 123

def self.version_pkt(from_id, from=nil, to=nil, last_block=nil, time=nil, user_agent=nil, version=nil)
  opts = if from_id.is_a?(Hash)
    from_id
  else
    STDERR.puts "Bitcoin::Protocol.version_pkt - API deprecated. please change it soon.."
    {
      :nonce => from_id, :from => from, :to => to, :last_block => last_block,
      :time => time, :user_agent => user_agent, :version => version
    }
  end
  version = Protocol::Version.new(opts)
  version.to_pkt
end