Class: Bitcoin::Protocol::Block

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

Direct Known Subclasses

Storage::Models::Block

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) ⇒ Block

create block from raw binary data



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

def initialize(data)
  @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



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

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

.binary_from_json(json_string) ⇒ Object

convert json representation to raw binary



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

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

.from_file(path) ⇒ Object

read binary block from a file



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

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)



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/bitcoin/protocol/block.rb', line 186

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? && !Bitcoin.freicoin?
      (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)



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

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

.from_json_file(path) ⇒ Object

read json block from a file



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

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.



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

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_workObject

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



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

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

#decimaltargetObject



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

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

#difficultyObject



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

def difficulty
  Bitcoin.block_difficulty(@bits)
end

#header_infoObject

get the block header info

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


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

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.



213
214
215
216
217
# File 'lib/bitcoin/protocol/block.rb', line 213

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



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

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 (@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_mrkl_rootObject



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

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

#sizeObject



141
142
143
# File 'lib/bitcoin/protocol/block.rb', line 141

def size
  payload.bytesize
end

#to_hashObject

convert to ruby hash (see also #from_hash)



128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/bitcoin/protocol/block.rb', line 128

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)



175
176
177
# File 'lib/bitcoin/protocol/block.rb', line 175

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)



181
182
183
# File 'lib/bitcoin/protocol/block.rb', line 181

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

#to_payloadObject

convert to raw binary format



118
119
120
121
122
123
124
125
# File 'lib/bitcoin/protocol/block.rb', line 118

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

#validator(store, prev_block = nil) ⇒ Object



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

def validator(store, prev_block = nil)
  @validator ||= Bitcoin::Validation::Block.new(self, store, prev_block)
end

#verify_mrkl_rootObject

verify mrkl tree



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

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