Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}