# encoding: ascii-8bit module Bitcoin module Protocol class Block BLOCK_VERSION_DEFAULT = (1 << 0) BLOCK_VERSION_AUXPOW = (1 << 8) BLOCK_VERSION_CHAIN_START = (1 << 16) BLOCK_VERSION_CHAIN_END = (1 << 30) # block hash attr_accessor :hash # previous block hash attr_accessor :prev_block_hash alias :prev_block :prev_block_hash def prev_block=(hash); @prev_block_hash = hash; end # transactions (Array of Tx) attr_accessor :tx # merkle root attr_accessor :mrkl_root # block generation time attr_accessor :time # difficulty target bits attr_accessor :bits # nonce (number counted when searching for block hash matching target) attr_accessor :nonce # version (usually 1) attr_accessor :ver # raw protocol payload attr_accessor :payload # AuxPow linking the block to a merge-mined chain attr_accessor :aux_pow attr_reader :partial_merkle_tree alias :transactions :tx # compare to another block def ==(other) @hash == other.hash end def binary_hash [@hash].pack("H*") end def prev_block_hex @prev_block_hex ||= @prev_block_hash.reverse.unpack("H*")[0] end # create block from raw binary +data+ def initialize(data=nil) @tx = [] parse_data_from_io(data) if data end # parse raw binary data def parse_data(data) buf = parse_data_from_io(data) buf.eof? ? true : buf.read end # parse raw binary data 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 # recalculate the block hash def recalc_block_hash @hash = Bitcoin.block_hash(@prev_block_hash.reverse_hth, @mrkl_root.reverse_hth, @time, @bits, @nonce, @ver) end 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 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 # verify mrkl tree 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 def tx_hashes if partial_merkle_tree partial_merkle_tree.tx_hashes else @tx.map(&:hash) end end # get the block header info # [, , ,