bech32_test.go raw
1 package bech32
2
3 import (
4 "encoding/hex"
5 "strings"
6 "testing"
7 )
8
9 // TestBIP173Vectors: the valid addresses from the BIP173 test vectors.
10 //
11 // The first is the canonical example: a 20-byte program of 0x751e... encoded for the "bc"
12 // prefix. If the checksum or the 5-bit regrouping were wrong, this would not match.
13 func TestBIP173Vectors(t *testing.T) {
14 valid := map[string]string{
15 "751e76e8199196d454941c45d1b3a323f1433bd6": "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4",
16 "1863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262": "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3",
17 }
18
19 for program, want := range valid {
20 raw, err := hex.DecodeString(program)
21
22 if err != nil {
23 t.Fatal(err)
24 }
25
26 got, err := EncodeSegwit("bc", 0, raw)
27
28 if err != nil {
29 t.Fatalf("%s: %v", program, err)
30 }
31
32 if got != want {
33 t.Fatalf("program %s encoded to %q, want %q", program, got, want)
34 }
35
36 hrp, version, decoded, err := DecodeSegwit(want)
37
38 if err != nil {
39 t.Fatalf("%s: %v", want, err)
40 }
41
42 if hrp != "bc" || version != 0 || hex.EncodeToString(decoded) != program {
43 t.Fatalf("%s decoded to %q/%d/%s", want, hrp, version, hex.EncodeToString(decoded))
44 }
45 }
46 }
47
48 // TestRegtestPrefix: the chain profile's prefix is part of the address, so the same
49 // program under bcrt is a different string.
50 func TestRegtestPrefix(t *testing.T) {
51 raw, err := hex.DecodeString("751e76e8199196d454941c45d1b3a323f1433bd6")
52
53 if err != nil {
54 t.Fatal(err)
55 }
56
57 mainnet, err := EncodeSegwit("bc", 0, raw)
58
59 if err != nil {
60 t.Fatal(err)
61 }
62
63 regtest, err := EncodeSegwit("bcrt", 0, raw)
64
65 if err != nil {
66 t.Fatal(err)
67 }
68
69 if mainnet == regtest {
70 t.Fatal("the same program encoded identically under two prefixes")
71 }
72
73 if !strings.HasPrefix(regtest, "bcrt1") {
74 t.Fatalf("the regtest address is %q", regtest)
75 }
76
77 // The program survives both, which is what makes the prefix a profile fact and not a
78 // second encoding.
79 for _, address := range []string{mainnet, regtest} {
80 hrp, _, decoded, err := DecodeSegwit(address)
81
82 if err != nil {
83 t.Fatalf("%s: %v", address, err)
84 }
85
86 if len(hrp) == 0 || hex.EncodeToString(decoded) != hex.EncodeToString(raw) {
87 t.Fatalf("%s decoded to %q/%s", address, hrp, hex.EncodeToString(decoded))
88 }
89 }
90 }
91
92 // TestRefusals: the shapes that must not be accepted, each for its own reason.
93 func TestRefusals(t *testing.T) {
94 raw, err := hex.DecodeString("751e76e8199196d454941c45d1b3a323f1433bd6")
95
96 if err != nil {
97 t.Fatal(err)
98 }
99
100 good, err := EncodeSegwit("bc", 0, raw)
101
102 if err != nil {
103 t.Fatal(err)
104 }
105
106 // One character changed is a different checksum.
107 broken := []byte(good)
108 broken[len(broken)-1] ^= 1
109
110 cases := map[string]string{
111 "a changed character": string(broken),
112 "mixed case": strings.ToUpper(good[:4]) + good[4:],
113 "no separator": "bcqw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4",
114 "too short": "bc1qqqqq",
115 "a bad character": good[:len(good)-1] + "b",
116 }
117
118 for name, address := range cases {
119 if _, _, _, err := DecodeSegwit(address); err == nil {
120 t.Errorf("%s was accepted: %q", name, address)
121 }
122 }
123
124 if _, err := EncodeSegwit("bc", 1, raw); err == nil {
125 t.Fatal("witness version 1 was encoded with the version 0 checksum")
126 }
127
128 if _, err := EncodeSegwit("bc", 0, raw[:19]); err == nil {
129 t.Fatal("a 19-byte program was encoded")
130 }
131
132 if _, err := EncodeSegwit("", 0, raw); err == nil {
133 t.Fatal("an empty prefix was encoded")
134 }
135
136 if _, err := EncodeSegwit("BC", 0, raw); err == nil {
137 t.Fatal("an uppercase prefix was encoded")
138 }
139 }
140
141 // TestRoundTrip: every program length a wallet uses survives encode and decode.
142 func TestRoundTrip(t *testing.T) {
143 for _, size := range []int{20, 32} {
144 program := make([]byte, size)
145
146 for i := range program {
147 program[i] = byte(i*7 + 1)
148 }
149
150 for _, hrp := range []string{"bc", "tb", "bcrt"} {
151 address, err := EncodeSegwit(hrp, 0, program)
152
153 if err != nil {
154 t.Fatalf("%s/%d: %v", hrp, size, err)
155 }
156
157 gotHRP, _, decoded, err := DecodeSegwit(address)
158
159 if err != nil {
160 t.Fatalf("%s: %v", address, err)
161 }
162
163 if gotHRP != hrp || hex.EncodeToString(decoded) != hex.EncodeToString(program) {
164 t.Fatalf("%s decoded to %q/%s", address, gotHRP,
165 hex.EncodeToString(decoded))
166 }
167 }
168 }
169 }
170