Class: Bitcoin::Protocol::Block
- Inherits:
-
Object
- Object
- Bitcoin::Protocol::Block
- Defined in:
- lib/bitcoin/protocol/block.rb
Overview
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
-
#aux_pow ⇒ Object
AuxPow linking the block to a merge-mined chain.
-
#bits ⇒ Object
difficulty target bits.
-
#hash ⇒ Object
block hash.
-
#mrkl_root ⇒ Object
merkle root.
-
#nonce ⇒ Object
nonce (number counted when searching for block hash matching target).
-
#partial_merkle_tree ⇒ Object
readonly
Returns the value of attribute partial_merkle_tree.
-
#payload ⇒ Object
raw protocol payload.
-
#prev_block_hash ⇒ Object
(also: #prev_block)
previous block hash.
-
#time ⇒ Object
block generation time.
-
#tx ⇒ Object
(also: #transactions)
transactions (Array of Tx).
-
#ver ⇒ Object
version (usually 1).
Class Method Summary collapse
-
.binary_from_hash(h) ⇒ Object
convert ruby hash to raw binary.
-
.binary_from_json(json_string) ⇒ Object
convert json representation to raw binary.
-
.from_file(path) ⇒ Object
read binary block from a file.
-
.from_hash(h, do_raise = true) ⇒ Object
parse ruby hash (see also #to_hash).
-
.from_json(json_string) ⇒ Object
parse json representation (see also #to_json).
-
.from_json_file(path) ⇒ Object
read json block from a file.
Instance Method Summary collapse
-
#==(other) ⇒ Object
compare to another block.
- #binary_hash ⇒ Object
-
#bip34_block_height(height = nil) ⇒ Object
introduced in block version 2 by BIP_0034 blockchain height as seen by the block itself.
-
#block_header ⇒ Object
block header binary output.
-
#block_work ⇒ Object
get the (statistical) amount of work that was needed to generate this block.
- #decimaltarget ⇒ Object
- #difficulty ⇒ Object
-
#header_info ⇒ Object
get the block header info [<version>, <prev_block>, <merkle_root>, <time>, <bits>, <nonce>, <txcount>, <size>].
-
#header_to_json(options = { space: '' }) ⇒ Object
convert header to json representation.
- #hextarget ⇒ Object
-
#initialize(data = nil) ⇒ Block
constructor
create block from raw binary
data
. -
#parse_data(data) ⇒ Object
parse raw binary data.
-
#parse_data_from_io(buf, header_only = false) ⇒ Object
parse raw binary data.
- #parse_filtered_header(buf) ⇒ Object
- #prev_block=(hash) ⇒ Object
- #prev_block_hex ⇒ Object
-
#recalc_block_hash ⇒ Object
recalculate the block hash.
- #recalc_block_scrypt_hash ⇒ Object
- #recalc_mrkl_root ⇒ Object
- #size ⇒ Object
-
#to_hash(options = {}) ⇒ Object
convert to ruby hash (see also #from_hash).
-
#to_json(options = { space: '' }, *_) ⇒ Object
convert to json representation as seen in the block explorer.
-
#to_json_file(path) ⇒ Object
write json representation to a file (see also #to_json).
-
#to_payload ⇒ Object
convert to raw binary format.
- #tx_hashes ⇒ Object
-
#verify_mrkl_root ⇒ Object
verify mrkl tree.
Constructor Details
#initialize(data = nil) ⇒ Block
create block from raw binary data
64 65 66 67 68 |
# File 'lib/bitcoin/protocol/block.rb', line 64 def initialize(data = nil) @tx = [] @payload = nil parse_data_from_io(data) if data end |
Instance Attribute Details
#aux_pow ⇒ Object
AuxPow linking the block to a merge-mined chain
44 45 46 |
# File 'lib/bitcoin/protocol/block.rb', line 44 def aux_pow @aux_pow end |
#bits ⇒ Object
difficulty target bits
32 33 34 |
# File 'lib/bitcoin/protocol/block.rb', line 32 def bits @bits end |
#hash ⇒ Object
block hash
13 14 15 |
# File 'lib/bitcoin/protocol/block.rb', line 13 def hash @hash end |
#mrkl_root ⇒ Object
merkle root
26 27 28 |
# File 'lib/bitcoin/protocol/block.rb', line 26 def mrkl_root @mrkl_root end |
#nonce ⇒ Object
nonce (number counted when searching for block hash matching target)
35 36 37 |
# File 'lib/bitcoin/protocol/block.rb', line 35 def nonce @nonce end |
#partial_merkle_tree ⇒ Object (readonly)
Returns the value of attribute partial_merkle_tree.
46 47 48 |
# File 'lib/bitcoin/protocol/block.rb', line 46 def partial_merkle_tree @partial_merkle_tree end |
#payload ⇒ Object
raw protocol payload
41 42 43 |
# File 'lib/bitcoin/protocol/block.rb', line 41 def payload @payload end |
#prev_block_hash ⇒ Object Also known as: prev_block
previous block hash
16 17 18 |
# File 'lib/bitcoin/protocol/block.rb', line 16 def prev_block_hash @prev_block_hash end |
#time ⇒ Object
block generation time
29 30 31 |
# File 'lib/bitcoin/protocol/block.rb', line 29 def time @time end |
#tx ⇒ Object Also known as: transactions
transactions (Array of Tx)
23 24 25 |
# File 'lib/bitcoin/protocol/block.rb', line 23 def tx @tx end |
#ver ⇒ Object
version (usually 1)
38 39 40 |
# File 'lib/bitcoin/protocol/block.rb', line 38 def ver @ver end |
Class Method Details
.binary_from_hash(h) ⇒ Object
convert ruby hash to raw binary
265 266 267 |
# File 'lib/bitcoin/protocol/block.rb', line 265 def self.binary_from_hash(h) from_hash(h).to_payload end |
.binary_from_json(json_string) ⇒ Object
convert json representation to raw binary
275 276 277 |
# File 'lib/bitcoin/protocol/block.rb', line 275 def self.binary_from_json(json_string) from_json(json_string).to_payload end |
.from_file(path) ⇒ Object
read binary block from a file
294 295 296 |
# File 'lib/bitcoin/protocol/block.rb', line 294 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)
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
# File 'lib/bitcoin/protocol/block.rb', line 244 def self.from_hash(h, do_raise = true) blk = new(nil) blk.instance_eval do @ver, @time, @bits, @nonce = h.values_at('ver', 'time', 'bits', 'nonce') @prev_block_hash, @mrkl_root = h.values_at('prev_block', 'mrkl_root').map(&:htb_reverse) if do_raise && h['hash'] != recalc_block_hash raise "Block hash mismatch! Claimed: #{h['hash']}, Actual: #{@hash}" 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? && do_raise && !verify_mrkl_root raise "Block merkle root mismatch! Block: #{h['hash']}" end end blk end |
.from_json(json_string) ⇒ Object
parse json representation (see also #to_json)
270 271 272 |
# File 'lib/bitcoin/protocol/block.rb', line 270 def self.from_json(json_string) from_hash(JSON.parse(json_string)) end |
.from_json_file(path) ⇒ Object
read json block from a file
299 300 301 |
# File 'lib/bitcoin/protocol/block.rb', line 299 def self.from_json_file(path) from_json(Bitcoin::Protocol.read_binary_file(path)) end |
Instance Method Details
#==(other) ⇒ Object
compare to another block
51 52 53 |
# File 'lib/bitcoin/protocol/block.rb', line 51 def ==(other) @hash == other.hash end |
#binary_hash ⇒ Object
55 56 57 |
# File 'lib/bitcoin/protocol/block.rb', line 55 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.
218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/bitcoin/protocol/block.rb', line 218 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 StandardError nil end |
#block_header ⇒ Object
block header binary output
287 288 289 290 291 |
# File 'lib/bitcoin/protocol/block.rb', line 287 def block_header [ @ver, @prev_block_hash, @mrkl_root, @time, @bits, @nonce, Protocol.pack_var_int(0) ].pack('Va32a32VVVa*') end |
#block_work ⇒ Object
get the (statistical) amount of work that was needed to generate this block.
304 305 306 307 308 |
# File 'lib/bitcoin/protocol/block.rb', line 304 def block_work target = Bitcoin.decode_compact_bits(@bits).to_i(16) return 0 if target <= 0 (2**256) / (target + 1) end |
#decimaltarget ⇒ Object
207 208 209 |
# File 'lib/bitcoin/protocol/block.rb', line 207 def decimaltarget Bitcoin.decode_compact_bits(@bits).to_i(16) end |
#difficulty ⇒ Object
211 212 213 |
# File 'lib/bitcoin/protocol/block.rb', line 211 def difficulty Bitcoin.block_difficulty(@bits) end |
#header_info ⇒ Object
get the block header info
- <version>, <prev_block>, <merkle_root>, <time>, <bits>, <nonce>, <txcount>, <size>
166 167 168 169 170 171 |
# File 'lib/bitcoin/protocol/block.rb', line 166 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.
280 281 282 283 284 |
# File 'lib/bitcoin/protocol/block.rb', line 280 def header_to_json( = { space: '' }) h = to_hash %w[tx mrkl_tree].each { |k| h.delete(k) } JSON.pretty_generate(h, ) end |
#hextarget ⇒ Object
203 204 205 |
# File 'lib/bitcoin/protocol/block.rb', line 203 def hextarget Bitcoin.decode_compact_bits(@bits) end |
#parse_data(data) ⇒ Object
parse raw binary data
71 72 73 74 |
# File 'lib/bitcoin/protocol/block.rb', line 71 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
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 |
# File 'lib/bitcoin/protocol/block.rb', line 77 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? return parse_filtered_header(buf) if header_only == :filtered tx_size = Protocol.unpack_var_int_from_io(buf) @tx_count = tx_size return buf if header_only tx_size.times do break if payload == true return buf if buf.eof? t = Tx.new(nil) t.parse_data_from_io(buf) @tx << t end @payload = to_payload buf end |
#parse_filtered_header(buf) ⇒ Object
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/bitcoin/protocol/block.rb', line 108 def parse_filtered_header(buf) @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.assign_value buf end |
#prev_block=(hash) ⇒ Object
18 19 20 |
# File 'lib/bitcoin/protocol/block.rb', line 18 def prev_block=(hash) @prev_block_hash = hash end |
#prev_block_hex ⇒ Object
59 60 61 |
# File 'lib/bitcoin/protocol/block.rb', line 59 def prev_block_hex @prev_block_hex ||= @prev_block_hash.reverse.unpack('H*')[0] end |
#recalc_block_hash ⇒ Object
recalculate the block hash
127 128 129 130 131 |
# File 'lib/bitcoin/protocol/block.rb', line 127 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_hash ⇒ Object
133 134 135 136 137 |
# File 'lib/bitcoin/protocol/block.rb', line 133 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_root ⇒ Object
139 140 141 142 143 144 145 |
# File 'lib/bitcoin/protocol/block.rb', line 139 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 |
#size ⇒ Object
199 200 201 |
# File 'lib/bitcoin/protocol/block.rb', line 199 def size payload.bytesize end |
#to_hash(options = {}) ⇒ Object
convert to ruby hash (see also #from_hash)
186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/bitcoin/protocol/block.rb', line 186 def to_hash( = {}) 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() }, 'mrkl_tree' => Bitcoin.hash_mrkl_tree(@tx.map(&:hash)) } h['aux_pow'] = @aux_pow.to_hash if @aux_pow h end |
#to_json(options = { space: '' }, *_) ⇒ Object
convert to json representation as seen in the block explorer. (see also #from_json)
233 234 235 |
# File 'lib/bitcoin/protocol/block.rb', line 233 def to_json( = { space: '' }, *_) JSON.pretty_generate(to_hash(), ) end |
#to_json_file(path) ⇒ Object
write json representation to a file (see also #to_json)
239 240 241 |
# File 'lib/bitcoin/protocol/block.rb', line 239 def to_json_file(path) File.open(path, 'wb') { |f| f.print to_json; } end |
#to_payload ⇒ Object
convert to raw binary format
174 175 176 177 178 179 180 181 182 183 |
# File 'lib/bitcoin/protocol/block.rb', line 174 def to_payload @aux_pow ||= nil head = [@ver, @prev_block_hash, @mrkl_root, @time, @bits, @nonce].pack('Va32a32VVV') head << @aux_pow.to_payload if @aux_pow return head if @tx.empty? head << Protocol.pack_var_int(@tx.size) @tx.each { |tx| head << tx.to_payload } head end |
#tx_hashes ⇒ Object
156 157 158 159 160 161 162 |
# File 'lib/bitcoin/protocol/block.rb', line 156 def tx_hashes if partial_merkle_tree partial_merkle_tree.tx_hashes else @tx.map(&:hash) end end |
#verify_mrkl_root ⇒ Object
verify mrkl tree
148 149 150 151 152 153 154 |
# File 'lib/bitcoin/protocol/block.rb', line 148 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 |