1
1
package c32
2
2
3
3
import (
4
- "encoding/base32 "
4
+ "crypto/sha256 "
5
5
"errors"
6
6
"fmt"
7
7
"math/big"
@@ -12,9 +12,35 @@ import (
12
12
13
13
var crockfordAlphabet = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
14
14
15
- func C32Encode (input []byte ) string {
16
- encoder := base32 .NewEncoding (crockfordAlphabet ).WithPadding (base32 .NoPadding )
17
- return encoder .EncodeToString (input )
15
+ func C32Encode (data []byte ) string {
16
+ // Convert bytes to big.Int
17
+ bi := new (big.Int ).SetBytes (data )
18
+
19
+ // Convert big.Int to base32 string
20
+ var encoded strings.Builder
21
+ for bi .Cmp (big .NewInt (0 )) > 0 {
22
+ mod := new (big.Int )
23
+ bi .DivMod (bi , big .NewInt (32 ), mod )
24
+ encoded .WriteByte (crockfordAlphabet [mod .Int64 ()])
25
+ }
26
+
27
+ // Reverse the string
28
+ encodedStr := reverseString (encoded .String ())
29
+
30
+ // Handle leading zeros
31
+ leadingZeros := 0
32
+ for _ , b := range data {
33
+ if b == 0 {
34
+ leadingZeros ++
35
+ } else {
36
+ break
37
+ }
38
+ }
39
+ for i := 0 ; i < leadingZeros ; i ++ {
40
+ encodedStr = "0" + encodedStr
41
+ }
42
+
43
+ return encodedStr
18
44
}
19
45
20
46
func C32Decode (input string ) ([]byte , error ) {
@@ -64,51 +90,96 @@ func DecodeC32Address(address string) (version byte, hash160 [20]byte, err error
64
90
return version , hash160 , nil
65
91
}
66
92
67
- func SerializeAddress ( address string ) ([]byte , error ) {
68
- if len (address ) != 1 + 20 * 2 { // 'S' + version + 40 hex chars
69
- return nil , fmt . Errorf ( "invalid address length: %d" , len ( address ) )
93
+ func DecodeWithChecksum ( c32addr string ) (byte , []byte , error ) {
94
+ if len (c32addr ) < 1 {
95
+ return 0 , nil , errors . New ( " address too short" )
70
96
}
71
-
72
- var version byte
73
- switch address [0 ] {
74
- case 'S' :
75
- version = byte (stacks .AddressVersionMainnetSingleSig )
76
- case 'T' :
77
- version = byte (stacks .AddressVersionTestnetSingleSig )
78
- default :
79
- return nil , fmt .Errorf ("invalid address version: %c" , address [0 ])
97
+ if c32addr [0 ] != 'S' {
98
+ return 0 , nil , errors .New ("address must start with 'S'" )
80
99
}
81
100
82
- hashBytes , err := C32Decode (address [1 :])
101
+ c32str := c32addr [1 :]
102
+
103
+ data , err := C32Decode (c32str )
83
104
if err != nil {
84
- return nil , fmt .Errorf ("invalid address hash: %v" , err )
105
+ return 0 , nil , err
106
+ }
107
+
108
+ if len (data ) != 1 + stacks .AddressHashLength + 4 {
109
+ return 0 , nil , fmt .Errorf ("invalid decoded length: expected %d, got %d" , 1 + stacks .AddressHashLength + 4 , len (data ))
110
+ }
111
+
112
+ version := data [0 ]
113
+ payload := data [1 : 1 + stacks .AddressHashLength ]
114
+ checksum := data [1 + stacks .AddressHashLength :]
115
+
116
+ // Recompute checksum
117
+ versionedData := append ([]byte {version }, payload ... )
118
+ computedChecksum := sha256 .Sum256 (versionedData )
119
+ computedChecksum = sha256 .Sum256 (computedChecksum [:])
120
+ computedChecksum = sha256 .Sum256 (computedChecksum [:])
121
+ computedChecksumBytes := computedChecksum [:4 ]
122
+
123
+ // Compare checksums
124
+ for i := 0 ; i < 4 ; i ++ {
125
+ if checksum [i ] != computedChecksumBytes [i ] {
126
+ return 0 , nil , errors .New ("checksum mismatch" )
127
+ }
85
128
}
86
129
87
- result := make ([]byte , 1 + len (hashBytes ))
88
- result [0 ] = version
89
- copy (result [1 :], hashBytes )
130
+ return version , payload , nil
131
+ }
132
+
133
+ func EncodeWithChecksum (version byte , data []byte ) (string , error ) {
134
+ if len (data ) != stacks .AddressHashLength {
135
+ return "" , errors .New ("data must be 20 bytes for P2PKH" )
136
+ }
137
+
138
+ // Version byte + data
139
+ versionedData := append ([]byte {version }, data ... )
140
+
141
+ // Compute checksum: double SHA256, first 4 bytes
142
+ checksum := sha256 .Sum256 (versionedData )
143
+ checksum = sha256 .Sum256 (checksum [:])
144
+ checksumBytes := checksum [:4 ]
90
145
91
- return result , nil
146
+ // Append checksum
147
+ fullData := append (data , checksumBytes ... )
148
+
149
+ // Encode to c32
150
+ c32str := C32Encode (fullData )
151
+
152
+ // Add prefix 'S'
153
+ return "S" + string (crockfordAlphabet [version ]) + c32str , nil
154
+ }
155
+
156
+ func SerializeAddress (version stacks.AddressVersion , hash160 []byte ) (string , error ) {
157
+ return EncodeWithChecksum (byte (version ), hash160 )
92
158
}
93
159
94
- func DeserializeAddress (data []byte ) (string , int , error ) {
95
- if len (data ) < 1 + stacks .AddressHashLength {
96
- return "" , 0 , errors .New ("insufficient data for address" )
160
+ func DeserializeAddress (address string ) (stacks.AddressVersion , []byte , error ) {
161
+ version , payload , err := DecodeWithChecksum (address )
162
+ if err != nil {
163
+ return 0 , nil , err
97
164
}
98
165
99
- version := stacks .AddressVersion (data [0 ])
100
- var prefix string
166
+ var addrVersion stacks.AddressVersion
101
167
switch version {
102
- case stacks .AddressVersionMainnetSingleSig :
103
- prefix = "S"
104
- case stacks .AddressVersionTestnetSingleSig :
105
- prefix = "T"
168
+ case byte ( stacks .AddressVersionMainnetSingleSig ) :
169
+ addrVersion = stacks . AddressVersionMainnetSingleSig
170
+ case byte ( stacks .AddressVersionTestnetSingleSig ) :
171
+ addrVersion = stacks . AddressVersionTestnetSingleSig
106
172
default :
107
- return "" , 0 , fmt .Errorf ("invalid address version: %d" , version )
173
+ return 0 , nil , fmt .Errorf ("unknown address version: %d" , version )
108
174
}
109
175
110
- c32hash := C32Encode ( data [ 1 : 1 + stacks . AddressHashLength + 5 ])
111
- address := fmt . Sprintf ( "%s%s" , prefix , c32hash )
176
+ return addrVersion , payload , nil
177
+ }
112
178
113
- return address , 1 + stacks .AddressHashLength + 5 , nil
179
+ func reverseString (s string ) string {
180
+ runes := []rune (s )
181
+ for i , j := 0 , len (runes )- 1 ; i < j ; i , j = i + 1 , j - 1 {
182
+ runes [i ], runes [j ] = runes [j ], runes [i ]
183
+ }
184
+ return string (runes )
114
185
}
0 commit comments