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/alert.rb,
lib/bitcoin/protocol/parser.rb,
lib/bitcoin/protocol/version.rb,
lib/bitcoin/protocol/handler.rb,
lib/bitcoin/protocol/aux_pow.rb,
lib/bitcoin/protocol/address.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



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

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



124
125
126
127
128
# File 'lib/bitcoin/protocol.rb', line 124

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



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

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

.inv_pkt(type, hashes) ⇒ Object



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

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



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

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_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



110
111
112
# File 'lib/bitcoin/protocol.rb', line 110

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

.pkt(command, payload) ⇒ Object



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

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



114
115
116
# File 'lib/bitcoin/protocol.rb', line 114

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

.read_binary_file(path) ⇒ Object



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

def self.read_binary_file(path)
  File.open(path, 'rb'){|f| f.read }
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



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

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



96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/bitcoin/protocol.rb', line 96

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