-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathbech32.go
146 lines (135 loc) · 3.74 KB
/
bech32.go
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
package chia_client
import (
"bytes"
"fmt"
"strings"
)
var charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
func Encode(hrp string, data []int) (string, error) {
if (len(hrp) + len(data) + 7) > 90 {
return "", fmt.Errorf("too long : hrp length=%d, data length=%d", len(hrp), len(data))
}
if len(hrp) < 1 {
return "", fmt.Errorf("invalid hrp : hrp=%v", hrp)
}
for p, c := range hrp {
if c < 33 || c > 126 {
return "", fmt.Errorf("invalid character human-readable part : hrp[%d]=%d", p, c)
}
}
if strings.ToUpper(hrp) != hrp && strings.ToLower(hrp) != hrp {
return "", fmt.Errorf("mix case : hrp=%v", hrp)
}
lower := strings.ToLower(hrp) == hrp
hrp = strings.ToLower(hrp)
combined := append(data, createChecksum(hrp, data)...)
var ret bytes.Buffer
ret.WriteString(hrp)
ret.WriteString("1")
for idx, p := range combined {
if p < 0 || p >= len(charset) {
return "", fmt.Errorf("invalid data : data[%d]=%d", idx, p)
}
ret.WriteByte(charset[p])
}
if lower {
return ret.String(), nil
}
return strings.ToUpper(ret.String()), nil
}
func Decode(bechString string) (string, []int, error) {
if len(bechString) > 90 {
return "", nil, fmt.Errorf("too long : len=%d", len(bechString))
}
if strings.ToLower(bechString) != bechString && strings.ToUpper(bechString) != bechString {
return "", nil, fmt.Errorf("mixed case")
}
bechString = strings.ToLower(bechString)
pos := strings.LastIndex(bechString, "1")
if pos < 1 || pos+7 > len(bechString) {
return "", nil, fmt.Errorf("separator '1' at invalid position : pos=%d , len=%d", pos, len(bechString))
}
hrp := bechString[0:pos]
for p, c := range hrp {
if c < 33 || c > 126 {
return "", nil, fmt.Errorf("invalid character human-readable part : bechString[%d]=%d", p, c)
}
}
var data []int
for p := pos + 1; p < len(bechString); p++ {
d := strings.Index(charset, fmt.Sprintf("%c", bechString[p]))
if d == -1 {
return "", nil, fmt.Errorf("invalid character data part : bechString[%d]=%d", p, bechString[p])
}
data = append(data, d)
}
if !verifyChecksum(hrp, data) {
return "", nil, fmt.Errorf("invalid checksum")
}
return hrp, data[:len(data)-6], nil
}
const M = 0x2BC830A3
func ConvertBits(data []int, frombits, tobits uint, pad bool) ([]int, error) {
acc := 0
bits := uint(0)
var ret []int
maxv := (1 << tobits) - 1
for idx, value := range data {
if value < 0 || (value>>frombits) != 0 {
return nil, fmt.Errorf("invalid data range : data[%d]=%d (frombits=%d)", idx, value, frombits)
}
acc = (acc << frombits) | value
bits += frombits
for bits >= tobits {
bits -= tobits
ret = append(ret, (acc>>bits)&maxv)
}
}
if pad {
if bits > 0 {
ret = append(ret, (acc<<(tobits-bits))&maxv)
}
} else if bits >= frombits {
return nil, fmt.Errorf("illegal zero padding")
} else if ((acc << (tobits - bits)) & maxv) != 0 {
return nil, fmt.Errorf("non-zero padding")
}
return ret, nil
}
func createChecksum(hrp string, data []int) []int {
values := append(append(hrpExpand(hrp), data...), []int{0, 0, 0, 0, 0, 0}...)
mod := polymod(values) ^ 1
ret := make([]int, 6)
for p := 0; p < len(ret); p++ {
ret[p] = (mod >> uint(5*(5-p))) & 31
}
return ret
}
func verifyChecksum(hrp string, data []int) bool {
return polymod(append(hrpExpand(hrp), data...)) == M
}
func polymod(values []int) int {
chk := 1
for _, v := range values {
top := chk >> 25
chk = (chk&0x1ffffff)<<5 ^ v
for i := 0; i < 5; i++ {
if (top>>uint(i))&1 == 1 {
chk ^= generator[i]
}
}
}
return chk
}
var generator = []int{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3}
func hrpExpand(hrp string) []int {
var ret []int
for _, c := range hrp {
ret = append(ret, int(c>>5))
}
ret = append(ret, 0)
for _, c := range hrp {
ret = append(ret, int(c&31))
}
return ret
}