Module: Bitcoin::Protocol

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

Defined Under Namespace

Classes: Addr, Alert, AuxPow, Block, Handler, Parser, Tx, TxIn, TxOut, Version

Constant Summary

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 =
Hash[:tx, 1, :block, 2, nil, 0]
DEFAULT_STOP_HASH =
"00"*32

Class Method Summary collapse

Class Method Details

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



156
157
158
# File 'lib/bitcoin/protocol.rb', line 156

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



133
134
135
136
137
# File 'lib/bitcoin/protocol.rb', line 133

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



160
161
162
# File 'lib/bitcoin/protocol.rb', line 160

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



164
165
166
# File 'lib/bitcoin/protocol.rb', line 164

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



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

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



147
148
149
150
151
152
153
154
# File 'lib/bitcoin/protocol.rb', line 147

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



91
92
93
# File 'lib/bitcoin/protocol.rb', line 91

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

.pack_var_int(i) ⇒ Object



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

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



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

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

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



119
120
121
# File 'lib/bitcoin/protocol.rb', line 119

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

.pkt(command, payload) ⇒ Object



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

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



123
124
125
# File 'lib/bitcoin/protocol.rb', line 123

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

.read_binary_file(path) ⇒ Object



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

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

.unpack_boolean(payload) ⇒ Object



86
87
88
89
# File 'lib/bitcoin/protocol.rb', line 86

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.



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

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>



80
81
82
83
84
# File 'lib/bitcoin/protocol.rb', line 80

def self.unpack_var_int_array(payload) # unpacks set<int>
  size, payload = unpack_var_int(payload)
  return [nil, payload] if size == 0
  [(0...size).map{ i, payload = unpack_var_int(payload); i }, payload]
end

.unpack_var_int_from_io(io) ⇒ Object



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

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



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

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>



74
75
76
77
78
# File 'lib/bitcoin/protocol.rb', line 74

def self.unpack_var_string_array(payload) # unpacks set<string>
  size, payload = unpack_var_int(payload)
  return [nil, payload] if size == 0
  [(0...size).map{ s, payload = unpack_var_string(payload); s }, payload]
end

.unpack_var_string_from_io(buf) ⇒ Object



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

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

.verack_pktObject



127
128
129
# File 'lib/bitcoin/protocol.rb', line 127

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



105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/bitcoin/protocol.rb', line 105

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