# encoding: ascii-8bit module Bitcoin module Protocol # https://en.bitcoin.it/wiki/Protocol_documentation#block 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 = [] @payload = nil 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? 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 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 # 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 # [, , ,