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}