Module: Bitcoin::Protocol

Defined in:
lib/bitcoin/protocol.rb,
lib/bitcoin/protocol/tx.rb,
lib/bitcoin/protocol/txin.rb,
lib/bitcoin/protocol/alert.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

Defined Under Namespace

Classes: Addr, Alert, AuxPow, Block, Handler, Parser, Reject, 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



169
170
171
# File 'lib/bitcoin/protocol.rb', line 169

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



146
147
148
149
150
# File 'lib/bitcoin/protocol.rb', line 146

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



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

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



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

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



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

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



160
161
162
163
164
165
166
167
# File 'lib/bitcoin/protocol.rb', line 160

def self.locator_payload(version, locator_hashes, stop_hash)
  payload = [
    [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



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

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

.pack_var_int(i) ⇒ Object



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

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



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

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

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



132
133
134
# File 'lib/bitcoin/protocol.rb', line 132

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

.pkt(command, payload) ⇒ Object



110
111
112
113
114
115
116
# File 'lib/bitcoin/protocol.rb', line 110

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) << cmd.force_encoding(BINARY) << length << checksum << payload.force_encoding(BINARY)
end

.pong_pkt(nonce) ⇒ Object



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

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

.read_binary_file(path) ⇒ Object



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

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

.unpack_boolean(payload) ⇒ Object



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

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.



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

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>



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

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



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

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



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

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

.unpack_var_string_array(payload) ⇒ Object

unpacks set<string>



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

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



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

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

.verack_pktObject



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

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



118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/bitcoin/protocol.rb', line 118

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