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



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

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

#partial_merkle_treeObject (readonly)

Returns the value of attribute partial_merkle_tree.



45
46
47
# File 'lib/bitcoin/protocol/block.rb', line 45

def partial_merkle_tree
  @partial_merkle_tree
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



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

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

.binary_from_json(json_string) ⇒ Object

convert json representation to raw binary



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

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

.from_file(path) ⇒ Object

read binary block from a file



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

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)



231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/bitcoin/protocol/block.rb', line 231

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)



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

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

.from_json_file(path) ⇒ Object

read json block from a file



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

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

Instance Method Details

#==(other) ⇒ Object

compare to another block



50
51
52
# File 'lib/bitcoin/protocol/block.rb', line 50

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

#binary_hashObject



54
55
56
# File 'lib/bitcoin/protocol/block.rb', line 54

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.



205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/bitcoin/protocol/block.rb', line 205

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



265
266
267
# File 'lib/bitcoin/protocol/block.rb', line 265

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.



276
277
278
279
280
# File 'lib/bitcoin/protocol/block.rb', line 276

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

#decimaltargetObject



194
195
196
# File 'lib/bitcoin/protocol/block.rb', line 194

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

#difficultyObject



198
199
200
# File 'lib/bitcoin/protocol/block.rb', line 198

def difficulty
  Bitcoin.block_difficulty(@bits)
end

#header_infoObject

get the block header info

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


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

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.



258
259
260
261
262
# File 'lib/bitcoin/protocol/block.rb', line 258

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



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

def hextarget
  Bitcoin.decode_compact_bits(@bits)
end

#parse_data(data) ⇒ Object

parse raw binary data



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

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



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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/bitcoin/protocol/block.rb', line 75

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?

  if header_only == :filtered
    @tx_count = buf.read(4).unpack("V")[0]

    nhashes = Protocol.unpack_var_int_from_io(buf)
    hashes = []
    nhashes.times do
      hashes << buf.read(256 / 8)
    end

    nflags = Protocol.unpack_var_int_from_io(buf)
    flags = buf.read(nflags)

    @partial_merkle_tree = PartialMerkleTree.new(@tx_count, hashes, flags)
    @partial_merkle_tree.set_value

    return buf
  end

  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



58
59
60
# File 'lib/bitcoin/protocol/block.rb', line 58

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

#recalc_block_hashObject

recalculate the block hash



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

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



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

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



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

def recalc_mrkl_root
  @mrkl_root = if partial_merkle_tree
    partial_merkle_tree.root.value.htb_reverse
  else
    Bitcoin.hash_mrkl_tree( @tx.map(&:hash) ).last.htb_reverse
  end
end

#sizeObject



186
187
188
# File 'lib/bitcoin/protocol/block.rb', line 186

def size
  payload.bytesize
end

#to_hash(options = {}) ⇒ Object

convert to ruby hash (see also #from_hash)



173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/bitcoin/protocol/block.rb', line 173

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)



220
221
222
# File 'lib/bitcoin/protocol/block.rb', line 220

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)



226
227
228
# File 'lib/bitcoin/protocol/block.rb', line 226

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

#to_payloadObject

convert to raw binary format



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

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

#tx_hashesObject



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

def tx_hashes
  if partial_merkle_tree
    partial_merkle_tree.tx_hashes
  else
    @tx.map(&:hash)
  end
end

#verify_mrkl_rootObject

verify mrkl tree



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

def verify_mrkl_root
  if partial_merkle_tree
    partial_merkle_tree.valid_tree?(@mrkl_root.reverse_hth)
  else
    @mrkl_root.reverse_hth == Bitcoin.hash_mrkl_tree( @tx.map(&:hash) ).last
  end
end