This commit is contained in:
Luxferre
2026-09-19 18:26:07 +03:00
parent 8ae332e74d
commit e9826b1ae2
28 changed files with 3417 additions and 1 deletions
+118
View File
@@ -0,0 +1,118 @@
// 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
}
+56
View File
@@ -0,0 +1,56 @@
package base58
import (
"bytes"
"encoding/hex"
"testing"
)
func TestRoundTrip(t *testing.T) {
cases := [][]byte{
{0x00},
{0x00, 0x00, 0x01},
bytes.Repeat([]byte{0xff}, 32),
[]byte("hello world"),
{},
}
for _, in := range cases {
enc := Encode(in)
got, err := Decode(enc)
if err != nil {
t.Fatalf("Decode(%q): %v", enc, err)
}
if !bytes.Equal(got, in) {
t.Fatalf("round trip mismatch: in=%x enc=%q got=%x", in, enc, got)
}
}
}
func TestCheckEncodeKnownAddress(t *testing.T) {
raw, err := hex.DecodeString("4143d75ee7cdcd7f2fcd4fc3b4eb574b6bdc2a3fcb")
if err != nil {
t.Fatalf("decode raw: %v", err)
}
if got := CheckEncode(raw); got != "TG9vH8c2jBWq2wSnksrq5mVyq2wjhG4A1n" {
t.Fatalf("CheckEncode = %s", got)
}
payload, err := CheckDecode("TG9vH8c2jBWq2wSnksrq5mVyq2wjhG4A1n")
if err != nil {
t.Fatalf("CheckDecode: %v", err)
}
if !bytes.Equal(payload, raw) {
t.Fatalf("CheckDecode = %x, want %x", payload, raw)
}
}
func TestCheckDecodeRejectsBadChecksum(t *testing.T) {
if _, err := CheckDecode("TG9vH8c2jBWq2wSnksrq5mVyq2wjhG4A1m"); err == nil {
t.Fatalf("expected checksum error")
}
}
func TestDecodeRejectsInvalidByte(t *testing.T) {
if _, err := Decode("0OIl"); err == nil {
t.Fatalf("expected invalid byte error")
}
}