This repository has been archived on 2024-04-07. You can view files and clone it, but cannot push or open issues or pull requests.
moneroutil/base58.go

104 lines
2.7 KiB
Go

package moneroutil
import (
"lukechampine.com/uint128"
)
const BASE58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
var base58Lookup = map[string]int{
"1": 0, "2": 1, "3": 2, "4": 3, "5": 4, "6": 5, "7": 6, "8": 7,
"9": 8, "A": 9, "B": 10, "C": 11, "D": 12, "E": 13, "F": 14, "G": 15,
"H": 16, "J": 17, "K": 18, "L": 19, "M": 20, "N": 21, "P": 22, "Q": 23,
"R": 24, "S": 25, "T": 26, "U": 27, "V": 28, "W": 29, "X": 30, "Y": 31,
"Z": 32, "a": 33, "b": 34, "c": 35, "d": 36, "e": 37, "f": 38, "g": 39,
"h": 40, "i": 41, "j": 42, "k": 43, "m": 44, "n": 45, "o": 46, "p": 47,
"q": 48, "r": 49, "s": 50, "t": 51, "u": 52, "v": 53, "w": 54, "x": 55,
"y": 56, "z": 57,
}
var base128 = uint128.From64(58)
func encodeChunk(raw []byte, padding int, buf []byte) []byte {
var data [16]byte
copy(data[16-len(raw):], raw)
remainder := uint128.FromBytesBE(data[:])
var current uint128.Uint128
for remainder.Cmp64(0) > 0 {
remainder, current = remainder.QuoRem(base128)
buf = append(buf, BASE58[current.Lo])
}
for len(buf) < padding {
buf = append(buf, '1')
}
for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 {
buf[i], buf[j] = buf[j], buf[i]
}
return buf
}
func decodeChunk(encoded string) (result []byte) {
var bigResult uint128.Uint128
currentMultiplier := uint128.From64(1)
for i := len(encoded) - 1; i >= 0; i-- {
bigResult = bigResult.Add(currentMultiplier.Mul64(uint64(base58Lookup[string(encoded[i])])))
currentMultiplier = currentMultiplier.Mul(base128)
}
result = make([]byte, 16)
bigResult.ReverseBytes().PutBytes(result)
switch len(encoded) {
case 0:
return result[8+8:]
case 2:
return result[8+7:]
case 3:
return result[8+6:]
case 5:
return result[8+5:]
case 6:
return result[8+4:]
case 7:
return result[8+3:]
case 9:
return result[8+2:]
case 10:
return result[8+1:]
case 11:
return result[8:]
default:
}
return nil
}
func EncodeMoneroBase58(data ...[]byte) string {
//preallocate common case
combined := make([]byte, 0, 96)
for _, item := range data {
combined = append(combined, item...)
}
result := make([]byte, 0, len(combined)*2)
buf := make([]byte, 0, len(combined)*2)
length := len(combined)
rounds := length / 8
for i := 0; i < rounds; i++ {
result = append(result, encodeChunk(combined[i*8:(i+1)*8], 11, buf[:0])...)
}
if length%8 > 0 {
result = append(result, encodeChunk(combined[rounds*8:], 7, buf[:0])...)
}
return string(result)
}
func DecodeMoneroBase58(data string) (result []byte) {
length := len(data)
rounds := length / 11
for i := 0; i < rounds; i++ {
result = append(result, decodeChunk(data[i*11:i*11+11])...)
}
if length%11 > 0 {
result = append(result, decodeChunk(data[rounds*11:])...)
}
return
}