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