Class: Bitcoin::Protocol::Block

Inherits:
Object
  • Object
show all
Defined in:
lib/bitcoin/protocol/block.rb

Constant Summary collapse

BLOCK_VERSION_DEFAULT =
(1 << 0)
BLOCK_VERSION_AUXPOW =
(1 << 8)
BLOCK_VERSION_CHAIN_START =
(1 << 16)
BLOCK_VERSION_CHAIN_END =
(1 << 30)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(data = nil) ⇒ Block

create block from raw binary data



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

def initialize(data=nil)
  @tx = []
  parse_data_from_io(data) if data
end

Instance Attribute Details

#aux_powObject

AuxPow linking the block to a merge-mined chain



43
44
45
# File 'lib/bitcoin/protocol/block.rb', line 43

def aux_pow
  @aux_pow
end

#bitsObject

difficulty target bits



31
32
33
# File 'lib/bitcoin/protocol/block.rb', line 31

def bits
  @bits
end

#hashObject

block hash



14
15
16
# File 'lib/bitcoin/protocol/block.rb', line 14

def hash
  @hash
end

#mrkl_rootObject

merkle root



25
26
27
# File 'lib/bitcoin/protocol/block.rb', line 25

def mrkl_root
  @mrkl_root
end

#nonceObject

nonce (number counted when searching for block hash matching target)



34
35
36
# File 'lib/bitcoin/protocol/block.rb', line 34

def nonce
  @nonce
end

#payloadObject

raw protocol payload



40
41
42
# File 'lib/bitcoin/protocol/block.rb', line 40

def payload
  @payload
end

#prev_block_hashObject Also known as: prev_block

previous block hash



17
18
19
# File 'lib/bitcoin/protocol/block.rb', line 17

def prev_block_hash
  @prev_block_hash
end

#timeObject

block generation time



28
29
30
# File 'lib/bitcoin/protocol/block.rb', line 28

def time
  @time
end

#txObject Also known as: transactions

transactions (Array of Tx)



22
23
24
# File 'lib/bitcoin/protocol/block.rb', line 22

def tx
  @tx
end

#verObject

version (usually 1)



37
38
39
# File 'lib/bitcoin/protocol/block.rb', line 37

def ver
  @ver
end

Class Method Details

.binary_from_hash(h) ⇒ Object

convert ruby hash to raw binary



213
# File 'lib/bitcoin/protocol/block.rb', line 213

def self.binary_from_hash(h); from_hash(h).to_payload; end

.binary_from_json(json_string) ⇒ Object

convert json representation to raw binary



219
# File 'lib/bitcoin/protocol/block.rb', line 219

def self.binary_from_json(json_string); from_json(json_string).to_payload; end

.from_file(path) ⇒ Object

read binary block from a file



234
# File 'lib/bitcoin/protocol/block.rb', line 234

def self.from_file(path); new( Bitcoin::Protocol.read_binary_file(path) ); end

.from_hash(h, do_raise = true) ⇒ Object

parse ruby hash (see also #to_hash)



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/bitcoin/protocol/block.rb', line 195

def self.from_hash(h, do_raise=true)
  blk = new(nil)
  blk.instance_eval{
    @ver, @time, @bits, @nonce = h.values_at('ver', 'time', 'bits', 'nonce')
    @prev_block_hash, @mrkl_root = h.values_at('prev_block', 'mrkl_root').map{|i| i.htb_reverse }
    unless h['hash'] == recalc_block_hash
      raise "Block hash mismatch! Claimed: #{h['hash']}, Actual: #{@hash}" if do_raise
    end
    @aux_pow = AuxPow.from_hash(h['aux_pow'])  if h['aux_pow']
    h['tx'].each{|tx| @tx << Tx.from_hash(tx, do_raise) }
    if h['tx'].any?
      (raise "Block merkle root mismatch! Block: #{h['hash']}"  unless verify_mrkl_root) if do_raise
    end
  }
  blk
end

.from_json(json_string) ⇒ Object

parse json representation (see also #to_json)



216
# File 'lib/bitcoin/protocol/block.rb', line 216

def self.from_json(json_string); from_hash( JSON.load(json_string) ); end

.from_json_file(path) ⇒ Object

read json block from a file



237
# File 'lib/bitcoin/protocol/block.rb', line 237

def self.from_json_file(path); from_json( Bitcoin::Protocol.read_binary_file(path) ); end

Instance Method Details

#==(other) ⇒ Object

compare to another block



48
49
50
# File 'lib/bitcoin/protocol/block.rb', line 48

def ==(other)
  @hash == other.hash
end

#binary_hashObject



52
53
54
# File 'lib/bitcoin/protocol/block.rb', line 52

def binary_hash
  [@hash].pack("H*")
end

#bip34_block_height(height = nil) ⇒ Object

introduced in block version 2 by BIP_0034 blockchain height as seen by the block itself. do not trust this value, instead verify with chain storage.



169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/bitcoin/protocol/block.rb', line 169

def bip34_block_height(height=nil)
  return nil unless @ver >= 2
  if height # generate height binary
    buf = [height].pack("V").gsub(/\x00+$/,"")
    [buf.bytesize, buf].pack("Ca*")
  else
    coinbase = @tx.first.inputs.first.script_sig
    coinbase[1..coinbase[0].ord].ljust(4, "\x00").unpack("V").first
  end
rescue
  nil
end

#block_headerObject

block header binary output



229
230
231
# File 'lib/bitcoin/protocol/block.rb', line 229

def block_header
  [@ver, @prev_block_hash, @mrkl_root, @time, @bits, @nonce, Protocol.pack_var_int(0)].pack("Va32a32VVVa*")
end

#block_workObject

get the (statistical) amount of work that was needed to generate this block.



240
241
242
243
244
# File 'lib/bitcoin/protocol/block.rb', line 240

def block_work
  target = Bitcoin.decode_compact_bits(@bits).to_i(16)
  return 0 if target <= 0
  (2**256) / (target + 1)
end

#decimaltargetObject



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

def decimaltarget
  Bitcoin.decode_compact_bits(@bits).to_i(16)
end

#difficultyObject



162
163
164
# File 'lib/bitcoin/protocol/block.rb', line 162

def difficulty
  Bitcoin.block_difficulty(@bits)
end

#header_infoObject

get the block header info

<version>, <prev_block>, <merkle_root>, <time>, <bits>, <nonce>, <txcount>, <size>


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

def header_info
  [@ver, @prev_block_hash.reverse_hth, @mrkl_root.reverse_hth, Time.at(@time), @bits, @nonce, @tx.size, @payload.size]
end

#header_to_json(options = {:space => ''}) ⇒ Object

convert header to json representation.



222
223
224
225
226
# File 'lib/bitcoin/protocol/block.rb', line 222

def header_to_json(options = {:space => ''})
  h = to_hash
  %w[tx mrkl_tree].each{|k| h.delete(k) }
  JSON.pretty_generate( h, options )
end

#hextargetObject



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

def hextarget
  Bitcoin.decode_compact_bits(@bits)
end

#parse_data(data) ⇒ Object

parse raw binary data



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

def parse_data(data)
  buf = parse_data_from_io(data)
  buf.eof? ? true : buf.read
end

#parse_data_from_io(buf, header_only = false) ⇒ Object

parse raw binary data



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/bitcoin/protocol/block.rb', line 73

def parse_data_from_io(buf, header_only=false)
  buf = buf.is_a?(String) ? StringIO.new(buf) : buf
  @ver, @prev_block_hash, @mrkl_root, @time, @bits, @nonce = buf.read(80).unpack("Va32a32VVV")
  recalc_block_hash

  if Bitcoin.network[:auxpow_chain_id] != nil && (@ver & BLOCK_VERSION_AUXPOW) > 0
    @aux_pow = AuxPow.new(nil)
    @aux_pow.parse_data_from_io(buf)
  end

  return buf if buf.eof?

  tx_size = Protocol.unpack_var_int_from_io(buf)
  @tx_count = tx_size
  return buf if header_only

  tx_size.times{
    break if payload == true
    return buf if buf.eof?

    t = Tx.new(nil)
    payload = t.parse_data_from_io(buf)
    @tx << t
  }

  @payload = to_payload
  buf
end

#prev_block=(hash) ⇒ Object



19
# File 'lib/bitcoin/protocol/block.rb', line 19

def prev_block=(hash); @prev_block_hash = hash; end

#prev_block_hexObject



56
57
58
# File 'lib/bitcoin/protocol/block.rb', line 56

def prev_block_hex
  @prev_block_hex ||= @prev_block_hash.reverse.unpack("H*")[0]
end

#recalc_block_hashObject

recalculate the block hash



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

def recalc_block_hash
  @hash = Bitcoin.block_hash(@prev_block_hash.reverse_hth, @mrkl_root.reverse_hth, @time, @bits, @nonce, @ver)
end

#recalc_block_scrypt_hashObject



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

def recalc_block_scrypt_hash
  @scrypt_hash = Bitcoin.block_scrypt_hash(@prev_block_hash.reverse_hth, @mrkl_root.reverse_hth, @time, @bits, @nonce, @ver)
end

#recalc_mrkl_rootObject



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

def recalc_mrkl_root
  @mrkl_root = Bitcoin.hash_mrkl_tree( @tx.map(&:hash) ).last.htb_reverse
end

#sizeObject



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

def size
  payload.bytesize
end

#to_hash(options = {}) ⇒ Object

convert to ruby hash (see also #from_hash)



137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/bitcoin/protocol/block.rb', line 137

def to_hash(options = {})
  h = {
    'hash' => @hash, 'ver' => @ver,
    'prev_block' => @prev_block_hash.reverse_hth, 'mrkl_root' => @mrkl_root.reverse_hth,
    'time' => @time, 'bits' => @bits, 'nonce' => @nonce,
    'n_tx' => @tx.size, 'size' => (@payload||to_payload).bytesize,
    'tx' => @tx.map{|i| i.to_hash(options) },
    'mrkl_tree' => Bitcoin.hash_mrkl_tree( @tx.map{|i| i.hash } )
  }
  h['aux_pow'] = @aux_pow.to_hash  if @aux_pow
  h
end

#to_json(options = {:space => ''}, *a) ⇒ Object

convert to json representation as seen in the block explorer. (see also #from_json)



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

def to_json(options = {:space => ''}, *a)
  JSON.pretty_generate( to_hash(options), options )
end

#to_json_file(path) ⇒ Object

write json representation to a file (see also #to_json)



190
191
192
# File 'lib/bitcoin/protocol/block.rb', line 190

def to_json_file(path)
  File.open(path, 'wb'){|f| f.print to_json; }
end

#to_payloadObject

convert to raw binary format



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

def to_payload
  head = [@ver, @prev_block_hash, @mrkl_root, @time, @bits, @nonce].pack("Va32a32VVV")
  head << @aux_pow.to_payload  if @aux_pow
  return head if @tx.size == 0
  head << Protocol.pack_var_int(@tx.size)
  @tx.each{|tx| head << tx.to_payload }
  head
end

#verify_mrkl_rootObject

verify mrkl tree



116
117
118
# File 'lib/bitcoin/protocol/block.rb', line 116

def verify_mrkl_root
  @mrkl_root.reverse_hth == Bitcoin.hash_mrkl_tree( @tx.map(&:hash) ).last
end