package base58 import ( "encoding/binary" ) const base58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" const reverseBase58 = // 0 1 2 3 4 5 6 7 8 9 a b c d e f */ /* 00 */ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + /* 10 */ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + /* 20 */ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + /* 30 */ "\xff\x00\x01\x02\x03\x04\x05\x06\x07\x08\xff\xff\xff\xff\xff\xff" + /* 40 */ "\xff\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\xff\x11\x12\x13\x14\x15\xff" + /* 50 */ "\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\xff\xff\xff\xff\xff" + /* 60 */ "\xff\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\xff\x2c\x2d\x2e" + /* 70 */ "\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\xff\xff\xff\xff\xff" + /* 80 */ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + /* 90 */ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + /* a0 */ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + /* b0 */ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + /* c0 */ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + /* d0 */ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + /* e0 */ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + /* f0 */ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" func encodeChunk(raw []byte, buf []byte) []byte { intToDecode := binary.BigEndian.Uint64(raw[:]) for intToDecode > 0 { buf = append(buf, base58[intToDecode%58]) intToDecode /= 58 } for len(buf) < 11 { 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 encodeChunkTail(raw []byte, buf []byte) []byte { var data [8]byte copy(data[8-len(raw):], raw) intToDecode := binary.BigEndian.Uint64(data[:]) for intToDecode > 0 { buf = append(buf, base58[intToDecode%58]) intToDecode /= 58 } for len(buf) < 7 { 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(buf []byte, encoded []byte) []byte { var intResult uint64 currentMultiplier := uint64(1) for i := len(encoded) - 1; i >= 0; i-- { intResult += currentMultiplier * uint64(reverseBase58[encoded[i]]) // this can overflow, but only on the last iteration when i == 0 when all data is valid currentMultiplier *= 58 } var result [8]byte binary.BigEndian.PutUint64(result[:], intResult) switch len(encoded) { case 0: return append(buf, result[8:]...) case 2: return append(buf, result[7:]...) case 3: return append(buf, result[6:]...) case 5: return append(buf, result[5:]...) case 6: return append(buf, result[4:]...) case 7: return append(buf, result[3:]...) case 9: return append(buf, result[2:]...) case 10: return append(buf, result[1:]...) case 11: return append(buf, result[:]...) default: } return nil } func EncodeMoneroBase58(data ...[]byte) []byte { return EncodeMoneroBase58PreAllocated(make([]byte, 0, func() (result int) { for _, v := range data { result += len(v) } return }()), data...) } func EncodeMoneroBase58PreAllocated(buf []byte, data ...[]byte) []byte { //preallocate common case combined := make([]byte, 0, 96) for _, item := range data { combined = append(combined, item...) } result := buf tmpBuf := 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], tmpBuf[:0])...) } if length%8 > 0 { result = append(result, encodeChunkTail(combined[rounds*8:], tmpBuf[:0])...) } return result } func DecodeMoneroBase58(data []byte) (result []byte) { //common case return DecodeMoneroBase58PreAllocated(make([]byte, 0, 69), data) } func DecodeMoneroBase58PreAllocated(buf []byte, data []byte) (result []byte) { result = buf length := len(data) rounds := length / 11 for i := 0; i < rounds; i++ { result = decodeChunk(result, data[i*11:i*11+11]) } if length%11 > 0 { result = decodeChunk(result, data[rounds*11:]) } return }