119 lines
2.7 KiB
Go
119 lines
2.7 KiB
Go
// Package base58 implements Bitcoin-style base58 and base58check encoding on
|
|||
|
|
// top of math/big and crypto/sha256. It is the address encoding used by TRON.
|
||
|
|
package base58
|
||
|
|
|
||
|
|
import (
|
||
|
|
"crypto/sha256"
|
||
|
|
"errors"
|
||
|
|
"math/big"
|
||
|
|
"strings"
|
||
|
|
)
|
||
|
|
|
||
|
|
const alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
||
|
|
|
||
|
|
// ErrInvalidByte is returned when a byte is not part of the base58 alphabet.
|
||
|
|
var ErrInvalidByte = errors.New("base58: invalid byte")
|
||
|
|
|
||
|
|
// ErrChecksum indicates a base58check payload with a wrong checksum.
|
||
|
|
var ErrChecksum = errors.New("base58: checksum mismatch")
|
||
|
|
|
||
|
|
var decodeMap = buildDecodeMap()
|
||
|
|
|
||
|
|
func buildDecodeMap() [256]int16 {
|
||
|
|
var m [256]int16
|
||
|
|
for i := range m {
|
||
|
|
m[i] = -1
|
||
|
|
}
|
||
|
|
for i := 0; i < len(alphabet); i++ {
|
||
|
|
m[alphabet[i]] = int16(i)
|
||
|
|
}
|
||
|
|
return m
|
||
|
|
}
|
||
|
|
|
||
|
|
// Encode converts a byte slice to a base58 string.
|
||
|
|
func Encode(b []byte) string {
|
||
|
|
x := new(big.Int).SetBytes(b)
|
||
|
|
base := big.NewInt(58)
|
||
|
|
mod := new(big.Int)
|
||
|
|
out := make([]byte, 0, len(b)*137/100+1)
|
||
|
|
for x.Sign() > 0 {
|
||
|
|
x.DivMod(x, base, mod)
|
||
|
|
out = append(out, alphabet[mod.Int64()])
|
||
|
|
}
|
||
|
|
for _, c := range b {
|
||
|
|
if c != 0 {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
out = append(out, alphabet[0])
|
||
|
|
}
|
||
|
|
for i, j := 0, len(out)-1; i < j; i, j = i+1, j-1 {
|
||
|
|
out[i], out[j] = out[j], out[i]
|
||
|
|
}
|
||
|
|
return string(out)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Decode converts a base58 string to a byte slice.
|
||
|
|
func Decode(s string) ([]byte, error) {
|
||
|
|
x := big.NewInt(0)
|
||
|
|
base := big.NewInt(58)
|
||
|
|
for i := 0; i < len(s); i++ {
|
||
|
|
d := decodeMap[s[i]]
|
||
|
|
if d < 0 {
|
||
|
|
return nil, ErrInvalidByte
|
||
|
|
}
|
||
|
|
x.Mul(x, base)
|
||
|
|
x.Add(x, big.NewInt(int64(d)))
|
||
|
|
}
|
||
|
|
was := x.Bytes()
|
||
|
|
pad := 0
|
||
|
|
for pad < len(s) && s[pad] == alphabet[0] {
|
||
|
|
pad++
|
||
|
|
}
|
||
|
|
out := make([]byte, pad+len(was))
|
||
|
|
copy(out[pad:], was)
|
||
|
|
return out, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func checksum(payload []byte) []byte {
|
||
|
|
h1 := sha256.Sum256(payload)
|
||
|
|
h2 := sha256.Sum256(h1[:])
|
||
|
|
return h2[:4]
|
||
|
|
}
|
||
|
|
|
||
|
|
// CheckEncode returns the base58check encoding of payload.
|
||
|
|
func CheckEncode(payload []byte) string {
|
||
|
|
buf := make([]byte, 0, len(payload)+4)
|
||
|
|
buf = append(buf, payload...)
|
||
|
|
buf = append(buf, checksum(payload)...)
|
||
|
|
return Encode(buf)
|
||
|
|
}
|
||
|
|
|
||
|
|
// CheckDecode decodes a base58check string, verifying its checksum.
|
||
|
|
func CheckDecode(s string) ([]byte, error) {
|
||
|
|
raw, err := Decode(s)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
if len(raw) < 5 {
|
||
|
|
return nil, ErrChecksum
|
||
|
|
}
|
||
|
|
payload := raw[:len(raw)-4]
|
||
|
|
want := checksum(payload)
|
||
|
|
got := raw[len(raw)-4:]
|
||
|
|
for i := 0; i < 4; i++ {
|
||
|
|
if want[i] != got[i] {
|
||
|
|
return nil, ErrChecksum
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return payload, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// IsValid reports whether s is a well-formed base58check string.
|
||
|
|
func IsValid(s string) bool {
|
||
|
|
if len(s) == 0 || strings.IndexByte(s, ' ') >= 0 {
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
_, err := CheckDecode(s)
|
||
|
|
return err == nil
|
||
|
|
}
|