btchash.gno
3.68 Kb · 106 lines
1// Package btchash provides Bitcoin-specific cryptographic hash primitives.
2//
3// Bitcoin uses several non-standard hash constructions:
4// - SHA-256d: Double SHA-256, used for block hashes, transaction IDs, Merkle trees
5// - HASH160: RIPEMD-160(SHA-256(data)), used for legacy addresses
6// - Tagged Hash: SHA-256(SHA-256(tag) || SHA-256(tag) || data), used by Taproot (BIP 340/341)
7//
8// All functions in this package are deterministic and side-effect free.
9// They operate on byte slices and return fixed-size arrays.
10//
11// References:
12// - Bitcoin whitepaper §7 (Merkle trees)
13// - BIP 340 §3 (Tagged hash specification)
14// - BIP 341 §5 (Taproot tagged hashes: TapTweak, TapLeaf, TapBranch, TapSighash)
15package btchash
16
17import (
18 "crypto/sha256"
19)
20
21// SHA256d computes double SHA-256: SHA-256(SHA-256(data)).
22// This is Bitcoin's standard hash function for:
23// - Block header hashes
24// - Transaction IDs (txid)
25// - Merkle tree internal nodes
26//
27// The double hash construction provides protection against length-extension attacks.
28func SHA256d(data []byte) [32]byte {
29 first := sha256.Sum256(data)
30 return sha256.Sum256(first[:])
31}
32
33// SHA256dBytes is a convenience wrapper around SHA256d that returns a byte slice.
34func SHA256dBytes(data []byte) []byte {
35 h := SHA256d(data)
36 return h[:]
37}
38
39// TaggedHash computes BIP 340 tagged hash:
40//
41// SHA-256(SHA-256(tag) || SHA-256(tag) || data)
42//
43// Tagged hashes ensure domain separation — the same data under different
44// tags produces completely different hashes. This prevents cross-protocol attacks.
45//
46// Used in Taproot (BIP 341) with tags:
47// - "TapTweak" — Internal key tweaking
48// - "TapLeaf" — Tapscript leaf hashing
49// - "TapBranch" — Tapscript branch hashing
50// - "TapSighash" — Signature hash computation
51// - "BIP0340/challenge" — Schnorr signature challenge
52// - "BIP0340/aux" — Schnorr signature auxiliary randomness
53// - "BIP0340/nonce" — Schnorr signature nonce derivation
54func TaggedHash(tag string, data []byte) [32]byte {
55 tagHash := sha256.Sum256([]byte(tag))
56
57 // Construct: SHA-256(tagHash || tagHash || data)
58 // Total input length: 32 + 32 + len(data) = 64 + len(data)
59 input := make([]byte, 0, 64+len(data))
60 input = append(input, tagHash[:]...)
61 input = append(input, tagHash[:]...)
62 input = append(input, data...)
63
64 return sha256.Sum256(input)
65}
66
67// TaggedHashBytes is a convenience wrapper around TaggedHash that returns a byte slice.
68func TaggedHashBytes(tag string, data []byte) []byte {
69 h := TaggedHash(tag, data)
70 return h[:]
71}
72
73// MerkleNodeHash computes the hash of a Merkle tree internal node.
74// For Bitcoin's Merkle tree: SHA-256d(left || right)
75// where left and right are 32-byte hashes of child nodes.
76func MerkleNodeHash(left, right [32]byte) [32]byte {
77 var combined [64]byte
78 copy(combined[:32], left[:])
79 copy(combined[32:], right[:])
80 return SHA256d(combined[:])
81}
82
83// ReverseBytes returns a copy of the input with bytes in reverse order.
84// Bitcoin displays transaction and block hashes in reverse byte order
85// compared to their internal representation (little-endian vs display order).
86func ReverseBytes(b []byte) []byte {
87 reversed := make([]byte, len(b))
88 for i, v := range b {
89 reversed[len(b)-1-i] = v
90 }
91 return reversed
92}
93
94// ReverseHash reverses a 32-byte hash for display purposes.
95// Bitcoin hashes are computed in internal byte order but displayed in reverse.
96// Example: Genesis block hash
97//
98// Internal: 6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000
99// Display: 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
100func ReverseHash(h [32]byte) [32]byte {
101 var reversed [32]byte
102 for i := 0; i < 32; i++ {
103 reversed[i] = h[31-i]
104 }
105 return reversed
106}