Search Apps Documentation Source Content File Folder Download Copy Actions Download

btchash_test.gno

7.64 Kb ยท 242 lines
  1package btchash
  2
  3import (
  4	"encoding/hex"
  5	"testing"
  6)
  7
  8// hexToBytes decodes a hex string into bytes, panicking on error.
  9// Used only in tests.
 10func hexToBytes(s string) []byte {
 11	b, err := hex.DecodeString(s)
 12	if err != nil {
 13		panic("invalid hex in test: " + err.Error())
 14	}
 15	return b
 16}
 17
 18// hexTo32 decodes a 64-char hex string into a [32]byte.
 19func hexTo32(s string) [32]byte {
 20	b := hexToBytes(s)
 21	if len(b) != 32 {
 22		panic("expected 32 bytes, got " + string(rune(len(b))))
 23	}
 24	var arr [32]byte
 25	copy(arr[:], b)
 26	return arr
 27}
 28
 29// ================================================================
 30// SHA-256d Tests
 31// ================================================================
 32
 33func TestSHA256d_Empty(t *testing.T) {
 34	// SHA-256d of empty input is a well-known constant:
 35	// SHA-256("") = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
 36	// SHA-256d("") = SHA-256(above) = 5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456
 37	result := SHA256d([]byte{})
 38	expected := hexTo32("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456")
 39	if result != expected {
 40		t.Errorf("SHA256d(empty): got %x, want %x", result, expected)
 41	}
 42}
 43
 44func TestSHA256d_Hello(t *testing.T) {
 45	// SHA-256("hello") = 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
 46	// SHA-256d("hello") = SHA-256(above) = 9595c9df90075148eb06860365df33584b75bff782a510c6cd4883a419833d50
 47	result := SHA256d([]byte("hello"))
 48	expected := hexTo32("9595c9df90075148eb06860365df33584b75bff782a510c6cd4883a419833d50")
 49	if result != expected {
 50		t.Errorf("SHA256d(hello): got %x, want %x", result, expected)
 51	}
 52}
 53
 54func TestSHA256d_BitcoinGenesisBlockHeader(t *testing.T) {
 55	// Bitcoin Genesis Block (Block 0) header โ€” 80 bytes
 56	// This is the canonical test vector for SHA-256d in the Bitcoin context.
 57	//
 58	// Header fields (all little-endian):
 59	//   Version:    01000000
 60	//   PrevHash:   0000000000000000000000000000000000000000000000000000000000000000
 61	//   MerkleRoot: 3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a
 62	//   Timestamp:  29ab5f49 (2009-01-03 18:15:05 UTC)
 63	//   Bits:       ffff001d
 64	//   Nonce:      1dac2b7c
 65	headerHex := "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c"
 66	headerBytes := hexToBytes(headerHex)
 67
 68	if len(headerBytes) != 80 {
 69		t.Fatalf("Genesis header should be 80 bytes, got %d", len(headerBytes))
 70	}
 71
 72	result := SHA256d(headerBytes)
 73
 74	// The hash in internal byte order:
 75	// 6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000
 76	//
 77	// In display order (reversed):
 78	// 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
 79	expectedInternal := hexTo32("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000")
 80	if result != expectedInternal {
 81		t.Errorf("Genesis block hash (internal): got %x, want %x", result, expectedInternal)
 82	}
 83
 84	// Verify display order via ReverseHash
 85	display := ReverseHash(result)
 86	expectedDisplay := hexTo32("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")
 87	if display != expectedDisplay {
 88		t.Errorf("Genesis block hash (display): got %x, want %x", display, expectedDisplay)
 89	}
 90}
 91
 92func TestSHA256dBytes(t *testing.T) {
 93	result := SHA256dBytes([]byte("hello"))
 94	if len(result) != 32 {
 95		t.Errorf("SHA256dBytes should return 32 bytes, got %d", len(result))
 96	}
 97	// Compare with array version
 98	expected := SHA256d([]byte("hello"))
 99	for i := 0; i < 32; i++ {
100		if result[i] != expected[i] {
101			t.Errorf("SHA256dBytes mismatch at byte %d", i)
102		}
103	}
104}
105
106// ================================================================
107// TaggedHash Tests (BIP 340/341)
108// ================================================================
109
110func TestTaggedHash_DomainSeparation(t *testing.T) {
111	// Same data with different tags MUST produce different hashes
112	data := []byte("test data")
113	h1 := TaggedHash("TapTweak", data)
114	h2 := TaggedHash("TapLeaf", data)
115	h3 := TaggedHash("BIP0340/challenge", data)
116
117	if h1 == h2 {
118		t.Error("TaggedHash: TapTweak and TapLeaf produced same hash โ€” domain separation broken")
119	}
120	if h1 == h3 {
121		t.Error("TaggedHash: TapTweak and BIP0340/challenge produced same hash")
122	}
123	if h2 == h3 {
124		t.Error("TaggedHash: TapLeaf and BIP0340/challenge produced same hash")
125	}
126}
127
128func TestTaggedHash_Determinism(t *testing.T) {
129	// Same tag + same data MUST always produce the same hash
130	data := []byte("deterministic test")
131	h1 := TaggedHash("TapTweak", data)
132	h2 := TaggedHash("TapTweak", data)
133	if h1 != h2 {
134		t.Error("TaggedHash is not deterministic")
135	}
136}
137
138func TestTaggedHash_EmptyData(t *testing.T) {
139	// TaggedHash with empty data should still work
140	// SHA-256("TapTweak") = known value
141	// Result = SHA-256(tagHash || tagHash)
142	result := TaggedHash("TapTweak", []byte{})
143	if result == [32]byte{} {
144		t.Error("TaggedHash(TapTweak, empty) should not be zero")
145	}
146}
147
148func TestTaggedHashBytes(t *testing.T) {
149	result := TaggedHashBytes("TapTweak", []byte("test"))
150	if len(result) != 32 {
151		t.Errorf("TaggedHashBytes should return 32 bytes, got %d", len(result))
152	}
153	// Compare with array version
154	expected := TaggedHash("TapTweak", []byte("test"))
155	for i := 0; i < 32; i++ {
156		if result[i] != expected[i] {
157			t.Errorf("TaggedHashBytes mismatch at byte %d", i)
158		}
159	}
160}
161
162// ================================================================
163// MerkleNodeHash Tests
164// ================================================================
165
166func TestMerkleNodeHash(t *testing.T) {
167	// MerkleNodeHash = SHA-256d(left || right)
168	left := hexTo32("0000000000000000000000000000000000000000000000000000000000000001")
169	right := hexTo32("0000000000000000000000000000000000000000000000000000000000000002")
170
171	result := MerkleNodeHash(left, right)
172
173	// Verify it equals SHA-256d of concatenation
174	var combined [64]byte
175	copy(combined[:32], left[:])
176	copy(combined[32:], right[:])
177	expected := SHA256d(combined[:])
178
179	if result != expected {
180		t.Errorf("MerkleNodeHash: got %x, want %x", result, expected)
181	}
182}
183
184func TestMerkleNodeHash_OrderMatters(t *testing.T) {
185	a := hexTo32("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
186	b := hexTo32("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
187
188	h1 := MerkleNodeHash(a, b)
189	h2 := MerkleNodeHash(b, a)
190
191	if h1 == h2 {
192		t.Error("MerkleNodeHash should be order-dependent")
193	}
194}
195
196// ================================================================
197// ReverseBytes / ReverseHash Tests
198// ================================================================
199
200func TestReverseBytes(t *testing.T) {
201	input := []byte{0x01, 0x02, 0x03, 0x04}
202	result := ReverseBytes(input)
203	expected := []byte{0x04, 0x03, 0x02, 0x01}
204
205	if len(result) != len(expected) {
206		t.Fatalf("ReverseBytes length mismatch")
207	}
208	for i := range result {
209		if result[i] != expected[i] {
210			t.Errorf("ReverseBytes mismatch at %d: got %x, want %x", i, result[i], expected[i])
211		}
212	}
213
214	// Verify original is not modified
215	if input[0] != 0x01 {
216		t.Error("ReverseBytes modified original slice")
217	}
218}
219
220func TestReverseBytes_Empty(t *testing.T) {
221	result := ReverseBytes([]byte{})
222	if len(result) != 0 {
223		t.Error("ReverseBytes of empty should be empty")
224	}
225}
226
227func TestReverseHash(t *testing.T) {
228	// Test with genesis block internal hash โ†’ display hash
229	internal := hexTo32("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000")
230	display := ReverseHash(internal)
231	expected := hexTo32("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")
232
233	if display != expected {
234		t.Errorf("ReverseHash: got %x, want %x", display, expected)
235	}
236
237	// Double reverse should return original
238	doubleReverse := ReverseHash(display)
239	if doubleReverse != internal {
240		t.Error("Double ReverseHash should return original")
241	}
242}