-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathaddress_roundtrip_test.go
More file actions
157 lines (136 loc) · 5.08 KB
/
Copy pathaddress_roundtrip_test.go
File metadata and controls
157 lines (136 loc) · 5.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package main
import (
"bytes"
"testing"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/txscript"
)
// TestAddressRoundTripAllTypes verifies that address -> script -> address
// conversion works correctly for all address types. This is CRITICAL for
// block rewards - we cannot afford to lose funds due to address conversion bugs!
func TestAddressRoundTripAllTypes(t *testing.T) {
params := &chaincfg.MainNetParams
testCases := []struct {
name string
address string
}{
// Real mainnet addresses that should work
{"P2PKH_genesis", "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"},
{"P2PKH_hal", "1HLoD9E4SDFFPDiYfNYnkBLQ85Y51J3Zb1"}, // Hal Finney
{"P2WPKH_segwit", "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"},
{"P2WSH_segwit", "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3"},
{"P2TR_taproot_1", "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr"},
{"P2TR_taproot_2", "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0"},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Step 1: Verify btcd can decode it
btcdAddr, err := btcutil.DecodeAddress(tc.address, params)
if err != nil {
t.Skipf("btcd cannot decode: %v", err)
}
// Step 2: Get script from btcd
btcdScript, err := txscript.PayToAddrScript(btcdAddr)
if err != nil {
t.Fatalf("btcd PayToAddrScript failed: %v", err)
}
// Step 3: Address -> script with goPool
poolScript, err := scriptForAddress(tc.address, params)
if err != nil {
t.Fatalf("scriptForAddress failed: %v", err)
}
// Verify scripts match
if !bytes.Equal(btcdScript, poolScript) {
t.Fatalf("script mismatch:\nbtcd: %x\ngoPool: %x", btcdScript, poolScript)
}
// Step 4: Script -> address with goPool (THE CRITICAL TEST!)
resultAddr := scriptToAddress(poolScript, params)
if resultAddr == "" {
t.Fatalf("scriptToAddress returned empty string for script %x", poolScript)
}
// Step 5: Verify round-trip
if resultAddr != tc.address {
t.Errorf("ROUND-TRIP FAILURE (CRITICAL!):\noriginal: %s\nresult: %s\nscript: %x",
tc.address, resultAddr, poolScript)
} else {
t.Logf("✓ Round-trip OK: %s", tc.address)
}
// Step 6: Verify the result can be decoded by btcd
verifyAddr, err := btcutil.DecodeAddress(resultAddr, params)
if err != nil {
t.Errorf("btcd cannot decode result address %s: %v", resultAddr, err)
}
// Step 7: Verify the script from result address matches
verifyScript, err := txscript.PayToAddrScript(verifyAddr)
if err != nil {
t.Errorf("btcd PayToAddrScript failed for result: %v", err)
}
if !bytes.Equal(verifyScript, poolScript) {
t.Errorf("script from result address doesn't match:\noriginal: %x\nverify: %x",
poolScript, verifyScript)
}
})
}
}
// TestCoinbaseOutputAddressRecovery tests that we can correctly recover
// addresses from coinbase outputs. This simulates what would happen when
// we need to verify or display payout addresses from a mined block.
func TestCoinbaseOutputAddressRecovery(t *testing.T) {
params := &chaincfg.MainNetParams
testCases := []struct {
name string
poolAddr string
workerAddr string
}{
{
name: "taproot_both",
poolAddr: "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
workerAddr: "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0",
},
{
name: "taproot_segwit",
poolAddr: "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
workerAddr: "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4",
},
{
name: "segwit_taproot",
poolAddr: "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4",
workerAddr: "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
},
{
name: "legacy_taproot",
poolAddr: "1HLoD9E4SDFFPDiYfNYnkBLQ85Y51J3Zb1",
workerAddr: "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Generate scripts
poolScript, err := scriptForAddress(tc.poolAddr, params)
if err != nil {
t.Fatalf("failed to generate pool script: %v", err)
}
workerScript, err := scriptForAddress(tc.workerAddr, params)
if err != nil {
t.Fatalf("failed to generate worker script: %v", err)
}
// Simulate: we mined a block and need to recover addresses from outputs
recoveredPool := scriptToAddress(poolScript, params)
recoveredWorker := scriptToAddress(workerScript, params)
// CRITICAL: These must match exactly!
if recoveredPool != tc.poolAddr {
t.Errorf("CRITICAL: Pool address recovery failed!\nexpected: %s\ngot: %s",
tc.poolAddr, recoveredPool)
}
if recoveredWorker != tc.workerAddr {
t.Errorf("CRITICAL: Worker address recovery failed!\nexpected: %s\ngot: %s",
tc.workerAddr, recoveredWorker)
}
if recoveredPool == tc.poolAddr && recoveredWorker == tc.workerAddr {
t.Logf("✓ Pool: %s ← %x", recoveredPool, poolScript)
t.Logf("✓ Worker: %s ← %x", recoveredWorker, workerScript)
}
})
}
}