blockheader.go raw

   1  package btcwire
   2  
   3  import (
   4  	"encoding/binary"
   5  	"fmt"
   6  	"io"
   7  	"math/big"
   8  
   9  	"git.smesh.lol/nostrino/pkg/crypto/chainhash"
  10  )
  11  
  12  // BlockHeaderSize is the serialized size of a block header in bytes.
  13  const BlockHeaderSize = 80
  14  
  15  // targetMaxBits is the number of bits a proof-of-work target may occupy.
  16  const targetMaxBits = 256
  17  
  18  // BlockHeader is the 80-byte Bitcoin block header. It commits to the previous
  19  // block, the merkle root of its transactions, and the proof-of-work solution.
  20  type BlockHeader struct {
  21  	// Version is the block version.
  22  	Version int32
  23  
  24  	// PrevBlock is the hash of the previous block.
  25  	PrevBlock chainhash.Hash
  26  
  27  	// MerkleRoot is the root of the block's transaction merkle tree.
  28  	MerkleRoot chainhash.Hash
  29  
  30  	// Timestamp is the block's time in seconds since the Unix epoch.
  31  	Timestamp uint32
  32  
  33  	// Bits is the compact encoding of the proof-of-work target.
  34  	Bits uint32
  35  
  36  	// Nonce is the proof-of-work nonce.
  37  	Nonce uint32
  38  }
  39  
  40  // Serialize returns the 80-byte wire encoding of the header.
  41  func (h *BlockHeader) Serialize() [BlockHeaderSize]byte {
  42  	var b [BlockHeaderSize]byte
  43  	binary.LittleEndian.PutUint32(b[0:4], uint32(h.Version))
  44  	copy(b[4:36], h.PrevBlock[:])
  45  	copy(b[36:68], h.MerkleRoot[:])
  46  	binary.LittleEndian.PutUint32(b[68:72], h.Timestamp)
  47  	binary.LittleEndian.PutUint32(b[72:76], h.Bits)
  48  	binary.LittleEndian.PutUint32(b[76:80], h.Nonce)
  49  	return b
  50  }
  51  
  52  // Deserialize replaces h with the header decoded from the first 80 bytes of b.
  53  // It returns an error if b is shorter than BlockHeaderSize.
  54  func (h *BlockHeader) Deserialize(b []byte) error {
  55  	if len(b) < BlockHeaderSize {
  56  		return fmt.Errorf(
  57  			"btcwire: block header needs %d bytes, got %d: %w",
  58  			BlockHeaderSize, len(b), io.ErrUnexpectedEOF,
  59  		)
  60  	}
  61  	h.Version = int32(binary.LittleEndian.Uint32(b[0:4]))
  62  	copy(h.PrevBlock[:], b[4:36])
  63  	copy(h.MerkleRoot[:], b[36:68])
  64  	h.Timestamp = binary.LittleEndian.Uint32(b[68:72])
  65  	h.Bits = binary.LittleEndian.Uint32(b[72:76])
  66  	h.Nonce = binary.LittleEndian.Uint32(b[76:80])
  67  	return nil
  68  }
  69  
  70  // BlockHash returns the double-SHA256 hash of the serialized header, in
  71  // internal byte order.
  72  func (h *BlockHeader) BlockHash() chainhash.Hash {
  73  	b := h.Serialize()
  74  	return chainhash.DoubleHashH(b[:])
  75  }
  76  
  77  // Target returns the proof-of-work target encoded by the header's Bits field.
  78  // Negative and out-of-range compact encodings are rejected.
  79  func (h *BlockHeader) Target() (*big.Int, error) {
  80  	target, err := CompactToBig(h.Bits)
  81  	if err != nil {
  82  		return nil, fmt.Errorf("btcwire: target for bits 0x%08x: %w", h.Bits, err)
  83  	}
  84  	return target, nil
  85  }
  86  
  87  // CheckProofOfWork reports whether the header hash satisfies the target
  88  // encoded in its Bits field. It returns false for negative, zero, or
  89  // out-of-range targets.
  90  func (h *BlockHeader) CheckProofOfWork() bool {
  91  	target, err := h.Target()
  92  	if err != nil || target.Sign() <= 0 {
  93  		return false
  94  	}
  95  	hash := h.BlockHash()
  96  	return HashToBig(&hash).Cmp(target) <= 0
  97  }
  98  
  99  // CompactToBig converts a compact target (the encoding used in a block
 100  // header's Bits field) to a big integer. It rejects negative encodings and
 101  // targets wider than 256 bits.
 102  func CompactToBig(compact uint32) (*big.Int, error) {
 103  	mantissa := compact & 0x007fffff
 104  	negative := compact&0x00800000 != 0
 105  	exponent := uint(compact >> 24)
 106  	if negative {
 107  		return nil, fmt.Errorf("%w: compact 0x%08x", ErrTargetNegative, compact)
 108  	}
 109  	target := new(big.Int).SetUint64(uint64(mantissa))
 110  	if exponent <= 3 {
 111  		target.Rsh(target, 8*(3-exponent))
 112  		return target, nil
 113  	}
 114  	shift := 8 * (exponent - 3)
 115  	if target.Sign() != 0 && target.BitLen()+int(shift) > targetMaxBits {
 116  		return nil, fmt.Errorf("%w: compact 0x%08x", ErrTargetOverflow, compact)
 117  	}
 118  	target.Lsh(target, shift)
 119  	return target, nil
 120  }
 121  
 122  // HashToBig interprets h as a little-endian 256-bit unsigned integer, the form
 123  // in which block hashes are compared against the proof-of-work target. Hashes
 124  // are stored in internal byte order, so this reverses the bytes before
 125  // interpreting them.
 126  func HashToBig(h *chainhash.Hash) *big.Int {
 127  	var reversed [chainhash.HashSize]byte
 128  	for i := 0; i < chainhash.HashSize; i++ {
 129  		reversed[i] = h[chainhash.HashSize-1-i]
 130  	}
 131  	return new(big.Int).SetBytes(reversed[:])
 132  }
 133