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



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

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



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

def aux_pow
  @aux_pow
end

#bitsObject

difficulty target bits



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

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



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

def mrkl_root
  @mrkl_root
end

#nonceObject

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



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

def nonce
  @nonce
end

#payloadObject

raw protocol payload



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

def payload
  @payload
end

#prev_blockObject

previous block hash



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

def prev_block
  @prev_block
end

#timeObject

block generation time



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

def time
  @time
end

#txObject Also known as: transactions

transactions (Array of Tx)



20
21
22
# File 'lib/bitcoin/protocol/block.rb', line 20

def tx
  @tx
end

#verObject

version (usually 1)



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

def ver
  @ver
end

Class Method Details

.binary_from_hash(h) ⇒ Object

convert ruby hash to raw binary



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

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

.binary_from_json(json_string) ⇒ Object

convert json representation to raw binary



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

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

.from_file(path) ⇒ Object

read binary block from a file



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

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)



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/bitcoin/protocol/block.rb', line 190

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, @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) }
    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)



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

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

.from_json_file(path) ⇒ Object

read json block from a file



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

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

Instance Method Details

#==(other) ⇒ Object

compare to another block



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

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

#binary_hashObject



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

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.



164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/bitcoin/protocol/block.rb', line 164

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



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

def block_header
  [@ver, @prev_block, @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.



235
236
237
238
239
# File 'lib/bitcoin/protocol/block.rb', line 235

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

#decimaltargetObject



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

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

#difficultyObject



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

def difficulty
  Bitcoin.block_difficulty(@bits)
end

#header_infoObject

get the block header info

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


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

def header_info
  [@ver, @prev_block.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.



217
218
219
220
221
# File 'lib/bitcoin/protocol/block.rb', line 217

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



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

def hextarget
  Bitcoin.decode_compact_bits(@bits)
end

#parse_data(data) ⇒ Object

parse raw binary data



65
66
67
68
# File 'lib/bitcoin/protocol/block.rb', line 65

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



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

def parse_data_from_io(buf, header_only=false)
  buf = buf.is_a?(String) ? StringIO.new(buf) : buf
  @ver, @prev_block, @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
    t = Tx.new(nil)
    payload = t.parse_data_from_io(buf)
    @tx << t
  }

  @payload = to_payload
  buf
end

#prev_block_hexObject



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

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

#recalc_block_hashObject

recalculate the block hash



98
99
100
# File 'lib/bitcoin/protocol/block.rb', line 98

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

#recalc_block_scrypt_hashObject



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

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

#recalc_mrkl_rootObject



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

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

#sizeObject



145
146
147
# File 'lib/bitcoin/protocol/block.rb', line 145

def size
  payload.bytesize
end

#to_hashObject

convert to ruby hash (see also #from_hash)



132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/bitcoin/protocol/block.rb', line 132

def to_hash
  h = {
    'hash' => @hash, 'ver' => @ver,
    'prev_block' => @prev_block.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{|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)



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

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

#to_json_file(path) ⇒ Object

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



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

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

#to_payloadObject

convert to raw binary format



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

def to_payload
  head = [@ver, @prev_block, @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



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

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