218 lines
6.8 KiB
Go
218 lines
6.8 KiB
Go
// Package bech32 reference implementation for Bech32 and segwit addresses.
|
|
// Copyright (c) 2017 Takatoshi Nakagawa
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
package address
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
var charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
|
|
|
|
var generator = []int{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3}
|
|
|
|
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
|
|
}
|
|
|
|
func hrpExpand(hrp string) []int {
|
|
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
|
|
}
|
|
|
|
func verifyChecksum(hrp string, data []int) bool {
|
|
return polymod(append(hrpExpand(hrp), data...)) == 1
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// Encode encodes hrp(human-readable part) and data(32bit data array), returns Bech32 / or error
|
|
// if hrp is uppercase, return uppercase Bech32
|
|
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
|
|
}
|
|
|
|
// Decode decodes bechString(Bech32) returns hrp(human-readable part) and data(32bit data array) / or error
|
|
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)
|
|
}
|
|
}
|
|
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
|
|
}
|
|
|
|
func convertbits(data []int, frombits, tobits uint, pad bool) ([]int, error) {
|
|
acc := 0
|
|
bits := uint(0)
|
|
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
|
|
}
|
|
|
|
// SegwitAddrDecode decodes hrp(human-readable part) Segwit Address(string), returns version(int) and data(bytes array) / or error
|
|
func SegwitAddrDecode(hrp, addr string) (int, []int, error) {
|
|
dechrp, data, err := Decode(addr)
|
|
if err != nil {
|
|
return -1, nil, err
|
|
}
|
|
if dechrp != hrp {
|
|
return -1, nil, fmt.Errorf("invalid human-readable part : %s != %s", hrp, dechrp)
|
|
}
|
|
if len(data) < 1 {
|
|
return -1, nil, fmt.Errorf("invalid decode data length : %d", len(data))
|
|
}
|
|
if data[0] > 16 {
|
|
return -1, nil, fmt.Errorf("invalid witness version : %d", data[0])
|
|
}
|
|
res, err := convertbits(data[1:], 5, 8, false)
|
|
if err != nil {
|
|
return -1, nil, err
|
|
}
|
|
if len(res) < 2 || len(res) > 40 {
|
|
return -1, nil, fmt.Errorf("invalid convertbits length : %d", len(res))
|
|
}
|
|
if data[0] == 0 && len(res) != 20 && len(res) != 32 {
|
|
return -1, nil, fmt.Errorf("invalid program length for witness version 0 (per BIP141) : %d", len(res))
|
|
}
|
|
return data[0], res, nil
|
|
}
|
|
|
|
// SegwitAddrEncode encodes hrp(human-readable part) , version(int) and data(bytes array), returns Segwit Address / or error
|
|
func SegwitAddrEncode(hrp string, version int, program []int) (string, error) {
|
|
if version < 0 || version > 16 {
|
|
return "", fmt.Errorf("invalid witness version : %d", version)
|
|
}
|
|
if len(program) < 2 || len(program) > 40 {
|
|
return "", fmt.Errorf("invalid program length : %d", len(program))
|
|
}
|
|
if version == 0 && len(program) != 20 && len(program) != 32 {
|
|
return "", fmt.Errorf("invalid program length for witness version 0 (per BIP141) : %d", len(program))
|
|
}
|
|
data, err := convertbits(program, 8, 5, true)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
ret, err := Encode(hrp, append([]int{version}, data...))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return ret, nil
|
|
}
|