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,
lib/bitcoin/protocol/script_witness.rb

Defined Under Namespace

Classes: Addr, Alert, 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



171
172
173
# File 'lib/bitcoin/protocol.rb', line 171

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



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

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



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

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



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

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



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

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



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

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



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

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

.pack_var_int(i) ⇒ Object



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

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



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

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

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



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

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

.pkt(command, payload) ⇒ Object



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

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



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

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

.read_binary_file(path) ⇒ Object



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

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

.unpack_boolean(payload) ⇒ Object



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

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.



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

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>



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

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



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

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



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

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>



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

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



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

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

.verack_pktObject



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

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



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

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