├── .gitignore ├── .mailmap ├── LICENSE ├── README.md ├── alphabet.go ├── base58.go ├── base58 ├── DEPRECATED.md ├── alphabet.go ├── base58.go ├── base58_2_test.go └── base58_test.go ├── base58_2_test.go ├── base58_test.go ├── doc.go ├── examples ├── cli │ └── cli.go └── wif │ └── main.go ├── go.mod ├── go.sum └── trivial.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | # This file allows re-mapping author names/emails 2 | # while maintaining the integrity of the repository 3 | # 4 | # Spec: https://www.kernel.org/pub/software/scm/git/docs/git-shortlog.html#_mapping_authors 5 | # 6 | 7 | Peter Rabbitson 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Denis Subbotin 4 | Copyright (c) 2017 Nika Jones 5 | Copyright (c) 2017 Philip Schlump 6 | Copyright (c) 2020 Peter 'ribasushi' Rabbitson 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fast Implementation of Base58 encoding 2 | [![GoDoc](https://godoc.org/github.com/mr-tron/base58?status.svg)](https://godoc.org/github.com/mr-tron/base58) [![Go Report Card](https://goreportcard.com/badge/github.com/mr-tron/base58)](https://goreportcard.com/report/github.com/mr-tron/base58) 3 | [![Used By](https://sourcegraph.com/github.com/mr-tron/base58/-/badge.svg)](https://sourcegraph.com/github.com/mr-tron/base58?badge) 4 | 5 | Fast implementation of base58 encoding in Go. 6 | 7 | Base algorithm is adapted from https://github.com/trezor/trezor-crypto/blob/master/base58.c 8 | 9 | ## Benchmark 10 | - Trivial - encoding based on big.Int (most libraries use such an implementation) 11 | - Fast - optimized algorithm provided by this module 12 | 13 | ``` 14 | BenchmarkTrivialBase58Encoding-4 123063 9568 ns/op 15 | BenchmarkFastBase58Encoding-4 690040 1598 ns/op 16 | 17 | BenchmarkTrivialBase58Decoding-4 275216 4301 ns/op 18 | BenchmarkFastBase58Decoding-4 1812105 658 ns/op 19 | ``` 20 | Encoding - **faster by 6 times** 21 | 22 | Decoding - **faster by 6 times** 23 | 24 | ## Usage example 25 | 26 | ```go 27 | 28 | package main 29 | 30 | import ( 31 | "fmt" 32 | "github.com/mr-tron/base58" 33 | ) 34 | 35 | func main() { 36 | 37 | encoded := "1QCaxc8hutpdZ62iKZsn1TCG3nh7uPZojq" 38 | num, err := base58.Decode(encoded) 39 | if err != nil { 40 | fmt.Printf("Demo %v, got error %s\n", encoded, err) 41 | } 42 | chk := base58.Encode(num) 43 | if encoded == string(chk) { 44 | fmt.Printf ( "Successfully decoded then re-encoded %s\n", encoded ) 45 | } 46 | } 47 | 48 | ``` 49 | -------------------------------------------------------------------------------- /alphabet.go: -------------------------------------------------------------------------------- 1 | package base58 2 | 3 | // Alphabet is a a b58 alphabet. 4 | type Alphabet struct { 5 | decode [128]int8 6 | encode [58]byte 7 | } 8 | 9 | // NewAlphabet creates a new alphabet from the passed string. 10 | // 11 | // It panics if the passed string is not 58 bytes long, isn't valid ASCII, 12 | // or does not contain 58 distinct characters. 13 | func NewAlphabet(s string) *Alphabet { 14 | if len(s) != 58 { 15 | panic("base58 alphabets must be 58 bytes long") 16 | } 17 | ret := new(Alphabet) 18 | copy(ret.encode[:], s) 19 | for i := range ret.decode { 20 | ret.decode[i] = -1 21 | } 22 | 23 | distinct := 0 24 | for i, b := range ret.encode { 25 | if ret.decode[b] == -1 { 26 | distinct++ 27 | } 28 | ret.decode[b] = int8(i) 29 | } 30 | 31 | if distinct != 58 { 32 | panic("provided alphabet does not consist of 58 distinct characters") 33 | } 34 | 35 | return ret 36 | } 37 | 38 | // BTCAlphabet is the bitcoin base58 alphabet. 39 | var BTCAlphabet = NewAlphabet("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") 40 | 41 | // FlickrAlphabet is the flickr base58 alphabet. 42 | var FlickrAlphabet = NewAlphabet("123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ") 43 | -------------------------------------------------------------------------------- /base58.go: -------------------------------------------------------------------------------- 1 | package base58 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Encode encodes the passed bytes into a base58 encoded string. 8 | func Encode(bin []byte) string { 9 | return FastBase58EncodingAlphabet(bin, BTCAlphabet) 10 | } 11 | 12 | // EncodeAlphabet encodes the passed bytes into a base58 encoded string with the 13 | // passed alphabet. 14 | func EncodeAlphabet(bin []byte, alphabet *Alphabet) string { 15 | return FastBase58EncodingAlphabet(bin, alphabet) 16 | } 17 | 18 | // FastBase58Encoding encodes the passed bytes into a base58 encoded string. 19 | func FastBase58Encoding(bin []byte) string { 20 | return FastBase58EncodingAlphabet(bin, BTCAlphabet) 21 | } 22 | 23 | // FastBase58EncodingAlphabet encodes the passed bytes into a base58 encoded 24 | // string with the passed alphabet. 25 | func FastBase58EncodingAlphabet(bin []byte, alphabet *Alphabet) string { 26 | 27 | size := len(bin) 28 | 29 | zcount := 0 30 | for zcount < size && bin[zcount] == 0 { 31 | zcount++ 32 | } 33 | 34 | // It is crucial to make this as short as possible, especially for 35 | // the usual case of bitcoin addrs 36 | size = zcount + 37 | // This is an integer simplification of 38 | // ceil(log(256)/log(58)) 39 | (size-zcount)*555/406 + 1 40 | 41 | out := make([]byte, size) 42 | 43 | var i, high int 44 | var carry uint32 45 | 46 | high = size - 1 47 | for _, b := range bin { 48 | i = size - 1 49 | for carry = uint32(b); i > high || carry != 0; i-- { 50 | carry = carry + 256*uint32(out[i]) 51 | out[i] = byte(carry % 58) 52 | carry /= 58 53 | } 54 | high = i 55 | } 56 | 57 | // Determine the additional "zero-gap" in the buffer (aside from zcount) 58 | for i = zcount; i < size && out[i] == 0; i++ { 59 | } 60 | 61 | // Now encode the values with actual alphabet in-place 62 | val := out[i-zcount:] 63 | size = len(val) 64 | for i = 0; i < size; i++ { 65 | out[i] = alphabet.encode[val[i]] 66 | } 67 | 68 | return string(out[:size]) 69 | } 70 | 71 | // Decode decodes the base58 encoded bytes. 72 | func Decode(str string) ([]byte, error) { 73 | return FastBase58DecodingAlphabet(str, BTCAlphabet) 74 | } 75 | 76 | // DecodeAlphabet decodes the base58 encoded bytes using the given b58 alphabet. 77 | func DecodeAlphabet(str string, alphabet *Alphabet) ([]byte, error) { 78 | return FastBase58DecodingAlphabet(str, alphabet) 79 | } 80 | 81 | // FastBase58Decoding decodes the base58 encoded bytes. 82 | func FastBase58Decoding(str string) ([]byte, error) { 83 | return FastBase58DecodingAlphabet(str, BTCAlphabet) 84 | } 85 | 86 | // FastBase58DecodingAlphabet decodes the base58 encoded bytes using the given 87 | // b58 alphabet. 88 | func FastBase58DecodingAlphabet(str string, alphabet *Alphabet) ([]byte, error) { 89 | if len(str) == 0 { 90 | return nil, fmt.Errorf("zero length string") 91 | } 92 | 93 | zero := alphabet.encode[0] 94 | b58sz := len(str) 95 | 96 | var zcount int 97 | for i := 0; i < b58sz && str[i] == zero; i++ { 98 | zcount++ 99 | } 100 | 101 | var t, c uint64 102 | 103 | // the 32bit algo stretches the result up to 2 times 104 | binu := make([]byte, 2*((b58sz*406/555)+1)) 105 | outi := make([]uint32, (b58sz+3)/4) 106 | 107 | for _, r := range str { 108 | if r > 127 { 109 | return nil, fmt.Errorf("high-bit set on invalid digit") 110 | } 111 | if alphabet.decode[r] == -1 { 112 | return nil, fmt.Errorf("invalid base58 digit (%q)", r) 113 | } 114 | 115 | c = uint64(alphabet.decode[r]) 116 | 117 | for j := len(outi) - 1; j >= 0; j-- { 118 | t = uint64(outi[j])*58 + c 119 | c = t >> 32 120 | outi[j] = uint32(t & 0xffffffff) 121 | } 122 | } 123 | 124 | // initial mask depends on b58sz, on further loops it always starts at 24 bits 125 | mask := (uint(b58sz%4) * 8) 126 | if mask == 0 { 127 | mask = 32 128 | } 129 | mask -= 8 130 | 131 | outLen := 0 132 | for j := 0; j < len(outi); j++ { 133 | for mask < 32 { // loop relies on uint overflow 134 | binu[outLen] = byte(outi[j] >> mask) 135 | mask -= 8 136 | outLen++ 137 | } 138 | mask = 24 139 | } 140 | 141 | // find the most significant byte post-decode, if any 142 | for msb := zcount; msb < len(binu); msb++ { 143 | if binu[msb] > 0 { 144 | return binu[msb-zcount : outLen], nil 145 | } 146 | } 147 | 148 | // it's all zeroes 149 | return binu[:outLen], nil 150 | } 151 | -------------------------------------------------------------------------------- /base58/DEPRECATED.md: -------------------------------------------------------------------------------- 1 | Files from this directory was copied to level up directory 2 | ========================================================== 3 | 4 | Now all development will be on top level -------------------------------------------------------------------------------- /base58/alphabet.go: -------------------------------------------------------------------------------- 1 | package base58 2 | 3 | // Alphabet is a a b58 alphabet. 4 | type Alphabet struct { 5 | decode [128]int8 6 | encode [58]byte 7 | } 8 | 9 | // NewAlphabet creates a new alphabet from the passed string. 10 | // 11 | // It panics if the passed string is not 58 bytes long or isn't valid ASCII. 12 | func NewAlphabet(s string) *Alphabet { 13 | if len(s) != 58 { 14 | panic("base58 alphabets must be 58 bytes long") 15 | } 16 | ret := new(Alphabet) 17 | copy(ret.encode[:], s) 18 | for i := range ret.decode { 19 | ret.decode[i] = -1 20 | } 21 | for i, b := range ret.encode { 22 | ret.decode[b] = int8(i) 23 | } 24 | return ret 25 | } 26 | 27 | // BTCAlphabet is the bitcoin base58 alphabet. 28 | var BTCAlphabet = NewAlphabet("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") 29 | 30 | // FlickrAlphabet is the flickr base58 alphabet. 31 | var FlickrAlphabet = NewAlphabet("123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ") 32 | -------------------------------------------------------------------------------- /base58/base58.go: -------------------------------------------------------------------------------- 1 | package base58 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | ) 7 | 8 | var ( 9 | bn0 = big.NewInt(0) 10 | bn58 = big.NewInt(58) 11 | ) 12 | 13 | // Encode encodes the passed bytes into a base58 encoded string. 14 | func Encode(bin []byte) string { 15 | return FastBase58Encoding(bin) 16 | } 17 | 18 | // EncodeAlphabet encodes the passed bytes into a base58 encoded string with the 19 | // passed alphabet. 20 | func EncodeAlphabet(bin []byte, alphabet *Alphabet) string { 21 | return FastBase58EncodingAlphabet(bin, alphabet) 22 | } 23 | 24 | // FastBase58Encoding encodes the passed bytes into a base58 encoded string. 25 | func FastBase58Encoding(bin []byte) string { 26 | return FastBase58EncodingAlphabet(bin, BTCAlphabet) 27 | } 28 | 29 | // FastBase58EncodingAlphabet encodes the passed bytes into a base58 encoded 30 | // string with the passed alphabet. 31 | func FastBase58EncodingAlphabet(bin []byte, alphabet *Alphabet) string { 32 | zero := alphabet.encode[0] 33 | 34 | binsz := len(bin) 35 | var i, j, zcount, high int 36 | var carry uint32 37 | 38 | for zcount < binsz && bin[zcount] == 0 { 39 | zcount++ 40 | } 41 | 42 | size := ((binsz-zcount)*138/100 + 1) 43 | 44 | // allocate one big buffer up front 45 | buf := make([]byte, size*2+zcount) 46 | 47 | // use the second half for the temporary buffer 48 | tmp := buf[size+zcount:] 49 | 50 | high = size - 1 51 | for i = zcount; i < binsz; i++ { 52 | j = size - 1 53 | for carry = uint32(bin[i]); j > high || carry != 0; j-- { 54 | carry = carry + 256*uint32(tmp[j]) 55 | tmp[j] = byte(carry % 58) 56 | carry /= 58 57 | } 58 | high = j 59 | } 60 | 61 | for j = 0; j < size && tmp[j] == 0; j++ { 62 | } 63 | 64 | // Use the first half for the result 65 | b58 := buf[:size-j+zcount] 66 | 67 | if zcount != 0 { 68 | for i = 0; i < zcount; i++ { 69 | b58[i] = zero 70 | } 71 | } 72 | 73 | for i = zcount; j < size; i++ { 74 | b58[i] = alphabet.encode[tmp[j]] 75 | j++ 76 | } 77 | 78 | return string(b58) 79 | } 80 | 81 | // TrivialBase58Encoding encodes the passed bytes into a base58 encoded string 82 | // (inefficiently). 83 | func TrivialBase58Encoding(a []byte) string { 84 | return TrivialBase58EncodingAlphabet(a, BTCAlphabet) 85 | } 86 | 87 | // TrivialBase58EncodingAlphabet encodes the passed bytes into a base58 encoded 88 | // string (inefficiently) with the passed alphabet. 89 | func TrivialBase58EncodingAlphabet(a []byte, alphabet *Alphabet) string { 90 | zero := alphabet.encode[0] 91 | idx := len(a)*138/100 + 1 92 | buf := make([]byte, idx) 93 | bn := new(big.Int).SetBytes(a) 94 | var mo *big.Int 95 | for bn.Cmp(bn0) != 0 { 96 | bn, mo = bn.DivMod(bn, bn58, new(big.Int)) 97 | idx-- 98 | buf[idx] = alphabet.encode[mo.Int64()] 99 | } 100 | for i := range a { 101 | if a[i] != 0 { 102 | break 103 | } 104 | idx-- 105 | buf[idx] = zero 106 | } 107 | return string(buf[idx:]) 108 | } 109 | 110 | // Decode decodes the base58 encoded bytes. 111 | func Decode(str string) ([]byte, error) { 112 | return FastBase58Decoding(str) 113 | } 114 | 115 | // DecodeAlphabet decodes the base58 encoded bytes using the given b58 alphabet. 116 | func DecodeAlphabet(str string, alphabet *Alphabet) ([]byte, error) { 117 | return FastBase58DecodingAlphabet(str, alphabet) 118 | } 119 | 120 | // FastBase58Decoding decodes the base58 encoded bytes. 121 | func FastBase58Decoding(str string) ([]byte, error) { 122 | return FastBase58DecodingAlphabet(str, BTCAlphabet) 123 | } 124 | 125 | // FastBase58DecodingAlphabet decodes the base58 encoded bytes using the given 126 | // b58 alphabet. 127 | func FastBase58DecodingAlphabet(str string, alphabet *Alphabet) ([]byte, error) { 128 | if len(str) == 0 { 129 | return nil, fmt.Errorf("zero length string") 130 | } 131 | 132 | var ( 133 | t uint64 134 | zmask, c uint32 135 | zcount int 136 | 137 | b58u = []rune(str) 138 | b58sz = len(b58u) 139 | 140 | outisz = (b58sz + 3) / 4 // check to see if we need to change this buffer size to optimize 141 | binu = make([]byte, (b58sz+3)*3) 142 | bytesleft = b58sz % 4 143 | 144 | zero = rune(alphabet.encode[0]) 145 | ) 146 | 147 | if bytesleft > 0 { 148 | zmask = (0xffffffff << uint32(bytesleft*8)) 149 | } else { 150 | bytesleft = 4 151 | } 152 | 153 | var outi = make([]uint32, outisz) 154 | 155 | for i := 0; i < b58sz && b58u[i] == zero; i++ { 156 | zcount++ 157 | } 158 | 159 | for _, r := range b58u { 160 | if r > 127 { 161 | return nil, fmt.Errorf("High-bit set on invalid digit") 162 | } 163 | if alphabet.decode[r] == -1 { 164 | return nil, fmt.Errorf("Invalid base58 digit (%q)", r) 165 | } 166 | 167 | c = uint32(alphabet.decode[r]) 168 | 169 | for j := (outisz - 1); j >= 0; j-- { 170 | t = uint64(outi[j])*58 + uint64(c) 171 | c = uint32(t>>32) & 0x3f 172 | outi[j] = uint32(t & 0xffffffff) 173 | } 174 | 175 | if c > 0 { 176 | return nil, fmt.Errorf("Output number too big (carry to the next int32)") 177 | } 178 | 179 | if outi[0]&zmask != 0 { 180 | return nil, fmt.Errorf("Output number too big (last int32 filled too far)") 181 | } 182 | } 183 | 184 | // the nested for-loop below is the same as the original code: 185 | // switch (bytesleft) { 186 | // case 3: 187 | // *(binu++) = (outi[0] & 0xff0000) >> 16; 188 | // //-fallthrough 189 | // case 2: 190 | // *(binu++) = (outi[0] & 0xff00) >> 8; 191 | // //-fallthrough 192 | // case 1: 193 | // *(binu++) = (outi[0] & 0xff); 194 | // ++j; 195 | // //-fallthrough 196 | // default: 197 | // break; 198 | // } 199 | // 200 | // for (; j < outisz; ++j) 201 | // { 202 | // *(binu++) = (outi[j] >> 0x18) & 0xff; 203 | // *(binu++) = (outi[j] >> 0x10) & 0xff; 204 | // *(binu++) = (outi[j] >> 8) & 0xff; 205 | // *(binu++) = (outi[j] >> 0) & 0xff; 206 | // } 207 | var j, cnt int 208 | for j, cnt = 0, 0; j < outisz; j++ { 209 | for mask := byte(bytesleft-1) * 8; mask <= 0x18; mask, cnt = mask-8, cnt+1 { 210 | binu[cnt] = byte(outi[j] >> mask) 211 | } 212 | if j == 0 { 213 | bytesleft = 4 // because it could be less than 4 the first time through 214 | } 215 | } 216 | 217 | for n, v := range binu { 218 | if v > 0 { 219 | start := n - zcount 220 | if start < 0 { 221 | start = 0 222 | } 223 | return binu[start:cnt], nil 224 | } 225 | } 226 | return binu[:cnt], nil 227 | } 228 | 229 | // TrivialBase58Decoding decodes the base58 encoded bytes (inefficiently). 230 | func TrivialBase58Decoding(str string) ([]byte, error) { 231 | return TrivialBase58DecodingAlphabet(str, BTCAlphabet) 232 | } 233 | 234 | // TrivialBase58DecodingAlphabet decodes the base58 encoded bytes 235 | // (inefficiently) using the given b58 alphabet. 236 | func TrivialBase58DecodingAlphabet(str string, alphabet *Alphabet) ([]byte, error) { 237 | zero := alphabet.encode[0] 238 | 239 | var zcount int 240 | for i := 0; i < len(str) && str[i] == zero; i++ { 241 | zcount++ 242 | } 243 | leading := make([]byte, zcount) 244 | 245 | var padChar rune = -1 246 | src := []byte(str) 247 | j := 0 248 | for ; j < len(src) && src[j] == byte(padChar); j++ { 249 | } 250 | 251 | n := new(big.Int) 252 | for i := range src[j:] { 253 | c := alphabet.decode[src[i]] 254 | if c == -1 { 255 | return nil, fmt.Errorf("illegal base58 data at input index: %d", i) 256 | } 257 | n.Mul(n, bn58) 258 | n.Add(n, big.NewInt(int64(c))) 259 | } 260 | return append(leading, n.Bytes()...), nil 261 | } 262 | -------------------------------------------------------------------------------- /base58/base58_2_test.go: -------------------------------------------------------------------------------- 1 | package base58 2 | 3 | import "testing" 4 | 5 | func TestBase58_test2(t *testing.T) { 6 | 7 | testAddr := []string{ 8 | "1QCaxc8hutpdZ62iKZsn1TCG3nh7uPZojq", 9 | "1DhRmSGnhPjUaVPAj48zgPV9e2oRhAQFUb", 10 | "17LN2oPYRYsXS9TdYdXCCDvF2FegshLDU2", 11 | "14h2bDLZSuvRFhUL45VjPHJcW667mmRAAn", 12 | } 13 | 14 | for ii, vv := range testAddr { 15 | // num := Base58Decode([]byte(vv)) 16 | // chk := Base58Encode(num) 17 | num, err := FastBase58Decoding(vv) 18 | if err != nil { 19 | t.Errorf("Test %d, expected success, got error %s\n", ii, err) 20 | } 21 | chk := FastBase58Encoding(num) 22 | if vv != string(chk) { 23 | t.Errorf("Test %d, expected=%s got=%s Address did base58 encode/decode correctly.", ii, vv, chk) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /base58/base58_test.go: -------------------------------------------------------------------------------- 1 | package base58 2 | 3 | import ( 4 | "crypto/rand" 5 | "encoding/hex" 6 | "testing" 7 | ) 8 | 9 | type testValues struct { 10 | dec []byte 11 | enc string 12 | } 13 | 14 | var n = 5000000 15 | var testPairs = make([]testValues, 0, n) 16 | 17 | func initTestPairs() { 18 | if len(testPairs) > 0 { 19 | return 20 | } 21 | // pre-make the test pairs, so it doesn't take up benchmark time... 22 | for i := 0; i < n; i++ { 23 | data := make([]byte, 32) 24 | rand.Read(data) 25 | testPairs = append(testPairs, testValues{dec: data, enc: FastBase58Encoding(data)}) 26 | } 27 | } 28 | 29 | func randAlphabet() *Alphabet { 30 | // Permutes [0, 127] and returns the first 58 elements. 31 | // Like (math/rand).Perm but using crypto/rand. 32 | var randomness [128]byte 33 | rand.Read(randomness[:]) 34 | 35 | var bts [128]byte 36 | for i, r := range randomness { 37 | j := int(r) % (i + 1) 38 | bts[i] = bts[j] 39 | bts[j] = byte(i) 40 | } 41 | return NewAlphabet(string(bts[:58])) 42 | } 43 | 44 | func TestFastEqTrivialEncodingAndDecoding(t *testing.T) { 45 | for k := 0; k < 10; k++ { 46 | testEncDecLoop(t, randAlphabet()) 47 | } 48 | testEncDecLoop(t, BTCAlphabet) 49 | testEncDecLoop(t, FlickrAlphabet) 50 | } 51 | 52 | func testEncDecLoop(t *testing.T, alph *Alphabet) { 53 | for j := 1; j < 256; j++ { 54 | var b = make([]byte, j) 55 | for i := 0; i < 100; i++ { 56 | rand.Read(b) 57 | fe := FastBase58EncodingAlphabet(b, alph) 58 | te := TrivialBase58EncodingAlphabet(b, alph) 59 | 60 | if fe != te { 61 | t.Errorf("encoding err: %#v", hex.EncodeToString(b)) 62 | } 63 | 64 | fd, ferr := FastBase58DecodingAlphabet(fe, alph) 65 | if ferr != nil { 66 | t.Errorf("fast error: %v", ferr) 67 | } 68 | td, terr := TrivialBase58DecodingAlphabet(te, alph) 69 | if terr != nil { 70 | t.Errorf("trivial error: %v", terr) 71 | } 72 | 73 | if hex.EncodeToString(b) != hex.EncodeToString(td) { 74 | t.Errorf("decoding err: %s != %s", hex.EncodeToString(b), hex.EncodeToString(td)) 75 | } 76 | if hex.EncodeToString(b) != hex.EncodeToString(fd) { 77 | t.Errorf("decoding err: %s != %s", hex.EncodeToString(b), hex.EncodeToString(fd)) 78 | } 79 | } 80 | } 81 | } 82 | 83 | func BenchmarkTrivialBase58Encoding(b *testing.B) { 84 | initTestPairs() 85 | b.ResetTimer() 86 | 87 | for i := 0; i < b.N; i++ { 88 | TrivialBase58Encoding([]byte(testPairs[i].dec)) 89 | } 90 | } 91 | 92 | func BenchmarkFastBase58Encoding(b *testing.B) { 93 | initTestPairs() 94 | b.ResetTimer() 95 | 96 | for i := 0; i < b.N; i++ { 97 | FastBase58Encoding(testPairs[i].dec) 98 | } 99 | } 100 | 101 | func BenchmarkTrivialBase58Decoding(b *testing.B) { 102 | initTestPairs() 103 | b.ResetTimer() 104 | 105 | for i := 0; i < b.N; i++ { 106 | TrivialBase58Decoding(testPairs[i].enc) 107 | } 108 | } 109 | 110 | func BenchmarkFastBase58Decoding(b *testing.B) { 111 | initTestPairs() 112 | b.ResetTimer() 113 | 114 | for i := 0; i < b.N; i++ { 115 | FastBase58Decoding(testPairs[i].enc) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /base58_2_test.go: -------------------------------------------------------------------------------- 1 | package base58 2 | 3 | import "testing" 4 | 5 | func TestBase58_test2(t *testing.T) { 6 | 7 | testAddr := []string{ 8 | "1QCaxc8hutpdZ62iKZsn1TCG3nh7uPZojq", 9 | "1DhRmSGnhPjUaVPAj48zgPV9e2oRhAQFUb", 10 | "17LN2oPYRYsXS9TdYdXCCDvF2FegshLDU2", 11 | "14h2bDLZSuvRFhUL45VjPHJcW667mmRAAn", 12 | } 13 | 14 | for ii, vv := range testAddr { 15 | // num := Base58Decode([]byte(vv)) 16 | // chk := Base58Encode(num) 17 | num, err := FastBase58Decoding(vv) 18 | if err != nil { 19 | t.Errorf("Test %d, expected success, got error %s\n", ii, err) 20 | } 21 | chk := FastBase58Encoding(num) 22 | if vv != string(chk) { 23 | t.Errorf("Test %d, expected=%s got=%s Address did base58 encode/decode correctly.", ii, vv, chk) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /base58_test.go: -------------------------------------------------------------------------------- 1 | package base58 2 | 3 | import ( 4 | "encoding/hex" 5 | "math/rand" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | type testValues struct { 11 | dec []byte 12 | enc string 13 | } 14 | 15 | var n = 5000000 16 | var testPairs = make([]testValues, 0, n) 17 | 18 | func init() { 19 | // If we do not seed the prng - it will default to a seed of (1) 20 | // https://golang.org/pkg/math/rand/#Seed 21 | rand.Seed(time.Now().UTC().UnixNano()) 22 | } 23 | 24 | func initTestPairs() { 25 | if len(testPairs) > 0 { 26 | return 27 | } 28 | // pre-make the test pairs, so it doesn't take up benchmark time... 29 | for i := 0; i < n; i++ { 30 | data := make([]byte, 32) 31 | rand.Read(data) 32 | testPairs = append(testPairs, testValues{dec: data, enc: FastBase58Encoding(data)}) 33 | } 34 | } 35 | 36 | func randAlphabet() *Alphabet { 37 | // Permutes [0, 127] and returns the first 58 elements. 38 | var randomness [128]byte 39 | rand.Read(randomness[:]) 40 | 41 | var bts [128]byte 42 | for i, r := range randomness { 43 | j := int(r) % (i + 1) 44 | bts[i] = bts[j] 45 | bts[j] = byte(i) 46 | } 47 | return NewAlphabet(string(bts[:58])) 48 | } 49 | 50 | var btcDigits = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" 51 | 52 | func TestInvalidAlphabetTooShort(t *testing.T) { 53 | defer func() { 54 | if r := recover(); r == nil { 55 | t.Errorf("Expected panic on alphabet being too short did not occur") 56 | } 57 | }() 58 | 59 | _ = NewAlphabet(btcDigits[1:]) 60 | } 61 | 62 | func TestInvalidAlphabetTooLong(t *testing.T) { 63 | defer func() { 64 | if r := recover(); r == nil { 65 | t.Errorf("Expected panic on alphabet being too long did not occur") 66 | } 67 | }() 68 | 69 | _ = NewAlphabet("0" + btcDigits) 70 | } 71 | 72 | func TestInvalidAlphabetNon127(t *testing.T) { 73 | defer func() { 74 | if r := recover(); r == nil { 75 | t.Errorf("Expected panic on alphabet containing non-ascii chars did not occur") 76 | } 77 | }() 78 | 79 | _ = NewAlphabet("\xFF" + btcDigits[1:]) 80 | } 81 | 82 | func TestInvalidAlphabetDup(t *testing.T) { 83 | defer func() { 84 | if r := recover(); r == nil { 85 | t.Errorf("Expected panic on alphabet containing duplicate chars did not occur") 86 | } 87 | }() 88 | 89 | _ = NewAlphabet("z" + btcDigits[1:]) 90 | } 91 | 92 | func TestFastEqTrivialEncodingAndDecoding(t *testing.T) { 93 | for k := 0; k < 10; k++ { 94 | testEncDecLoop(t, randAlphabet()) 95 | } 96 | testEncDecLoop(t, BTCAlphabet) 97 | testEncDecLoop(t, FlickrAlphabet) 98 | } 99 | 100 | func testEncDecLoop(t *testing.T, alph *Alphabet) { 101 | for j := 1; j < 256; j++ { 102 | var b = make([]byte, j) 103 | for i := 0; i < 100; i++ { 104 | rand.Read(b) 105 | fe := FastBase58EncodingAlphabet(b, alph) 106 | te := TrivialBase58EncodingAlphabet(b, alph) 107 | 108 | if fe != te { 109 | t.Errorf("encoding err: %#v", hex.EncodeToString(b)) 110 | } 111 | 112 | fd, ferr := FastBase58DecodingAlphabet(fe, alph) 113 | if ferr != nil { 114 | t.Errorf("fast error: %v", ferr) 115 | } 116 | td, terr := TrivialBase58DecodingAlphabet(te, alph) 117 | if terr != nil { 118 | t.Errorf("trivial error: %v", terr) 119 | } 120 | 121 | if hex.EncodeToString(b) != hex.EncodeToString(td) { 122 | t.Errorf("decoding err: %s != %s", hex.EncodeToString(b), hex.EncodeToString(td)) 123 | } 124 | if hex.EncodeToString(b) != hex.EncodeToString(fd) { 125 | t.Errorf("decoding err: %s != %s", hex.EncodeToString(b), hex.EncodeToString(fd)) 126 | } 127 | } 128 | } 129 | } 130 | 131 | func BenchmarkTrivialBase58Encoding(b *testing.B) { 132 | initTestPairs() 133 | b.ResetTimer() 134 | 135 | for i := 0; i < b.N; i++ { 136 | TrivialBase58Encoding([]byte(testPairs[i].dec)) 137 | } 138 | } 139 | 140 | func BenchmarkFastBase58Encoding(b *testing.B) { 141 | initTestPairs() 142 | b.ResetTimer() 143 | 144 | for i := 0; i < b.N; i++ { 145 | FastBase58Encoding(testPairs[i].dec) 146 | } 147 | } 148 | 149 | func BenchmarkTrivialBase58Decoding(b *testing.B) { 150 | initTestPairs() 151 | b.ResetTimer() 152 | 153 | for i := 0; i < b.N; i++ { 154 | TrivialBase58Decoding(testPairs[i].enc) 155 | } 156 | } 157 | 158 | func BenchmarkFastBase58Decoding(b *testing.B) { 159 | initTestPairs() 160 | b.ResetTimer() 161 | 162 | for i := 0; i < b.N; i++ { 163 | FastBase58Decoding(testPairs[i].enc) 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package base58 provides fast implementation of base58 encoding. 3 | 4 | Base58 Usage 5 | 6 | To decode a base58 string: 7 | 8 | encoded := "1QCaxc8hutpdZ62iKZsn1TCG3nh7uPZojq" 9 | buf, _ := base58.Decode(encoded) 10 | 11 | To encode the same data: 12 | 13 | encoded := base58.Encode(buf) 14 | 15 | With custom alphabet 16 | 17 | customAlphabet := base58.NewAlphabet("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") 18 | encoded := base58.EncodeAlphabet(buf, customAlphabet) 19 | 20 | */ 21 | package base58 22 | -------------------------------------------------------------------------------- /examples/cli/cli.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/mr-tron/base58" 8 | ) 9 | 10 | func main() { 11 | 12 | exampleBase58Encoded := []string{ 13 | "1QCaxc8hutpdZ62iKZsn1TCG3nh7uPZojq", 14 | "1DhRmSGnhPjUaVPAj48zgPV9e2oRhAQFUb", 15 | "17LN2oPYRYsXS9TdYdXCCDvF2FegshLDU2", 16 | "14h2bDLZSuvRFhUL45VjPHJcW667mmRAAn", 17 | } 18 | 19 | // If a base58 string is on the command line, then use that instead of the 4 exampels above. 20 | if len(os.Args) > 1 { 21 | exampleBase58Encoded = os.Args[1:] 22 | } 23 | 24 | for _, vv := range exampleBase58Encoded { 25 | num, err := base58.Decode(vv) 26 | if err != nil { 27 | fmt.Printf("Demo %s, got error %s\n", vv, err) 28 | continue 29 | } 30 | chk := base58.Encode(num) 31 | if vv == string(chk) { 32 | fmt.Printf("Successfully decoded then re-encoded %s\n", vv) 33 | } else { 34 | fmt.Printf("Failed on %s\n", vv) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/wif/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "crypto/sha256" 6 | "encoding/hex" 7 | "flag" 8 | "fmt" 9 | "io" 10 | "io/ioutil" 11 | "os" 12 | 13 | "github.com/mr-tron/base58" 14 | ) 15 | 16 | func checkSum(b []byte) []byte { 17 | sh1, sh2 := sha256.New(), sha256.New() 18 | sh1.Write(b) 19 | sh2.Write(sh1.Sum(nil)) 20 | return sh2.Sum(nil) 21 | } 22 | 23 | func main() { 24 | var ( 25 | err error 26 | bin []byte 27 | 28 | help = flag.Bool("h", false, "display this message") 29 | lnBreak = flag.Int("b", 76, "break encoded string into num character lines. Use 0 to disable line wrapping") 30 | input = flag.String("i", "-", `input file (use: "-" for stdin)`) 31 | output = flag.String("o", "-", `output file (use: "-" for stdout)`) 32 | decode = flag.Bool("d", false, `decode input`) 33 | check = flag.Bool("k", false, `use sha256 check`) 34 | useError = flag.Bool("e", false, `write error to stderr`) 35 | ) 36 | 37 | flag.Parse() 38 | 39 | if *help { 40 | flag.Usage() 41 | os.Exit(0) 42 | } 43 | 44 | fin, fout := os.Stdin, os.Stdout 45 | if *input != "-" { 46 | if fin, err = os.Open(*input); err != nil { 47 | fmt.Fprintln(os.Stderr, "input file err:", err) 48 | os.Exit(1) 49 | } 50 | } 51 | 52 | if *output != "-" { 53 | if fout, err = os.Create(*output); err != nil { 54 | fmt.Fprintln(os.Stderr, "output file err:", err) 55 | os.Exit(1) 56 | } 57 | } 58 | 59 | if bin, err = ioutil.ReadAll(fin); err != nil { 60 | fmt.Fprintln(os.Stderr, "read input err:", err) 61 | os.Exit(1) 62 | } 63 | 64 | if *decode { 65 | decoded, err := base58.Decode(string(bin)) 66 | if err != nil { 67 | fmt.Fprintln(os.Stderr, "decode input err:", err) 68 | os.Exit(1) 69 | } 70 | 71 | var checkResult bool 72 | if *check { 73 | chk := len(decoded) - 4 74 | decodedCk := decoded[chk:] 75 | decoded = decoded[:chk] 76 | sum := checkSum(decoded) 77 | checkResult = hex.EncodeToString(sum[:4]) == hex.EncodeToString(decodedCk) 78 | } 79 | 80 | _, err = io.Copy(fout, bytes.NewReader(decoded)) 81 | if err != nil { 82 | fmt.Fprintln(os.Stderr, err) 83 | os.Exit(1) 84 | } 85 | 86 | if *check && !checkResult { 87 | if *useError { 88 | fmt.Fprintf(os.Stderr, "%t\n", false) 89 | } 90 | os.Exit(3) 91 | } 92 | 93 | os.Exit(0) 94 | } 95 | 96 | if *check { 97 | sum := checkSum(bin) 98 | bin = append(bin, sum[:4]...) 99 | } 100 | 101 | encoded := base58.Encode(bin) 102 | 103 | if *lnBreak > 0 { 104 | lines := (len(encoded) / *lnBreak) + 1 105 | for i := 0; i < lines; i++ { 106 | start := i * *lnBreak 107 | end := start + *lnBreak 108 | if i == lines-1 { 109 | fmt.Fprintln(fout, encoded[start:]) 110 | return 111 | } 112 | fmt.Fprintln(fout, encoded[start:end]) 113 | } 114 | } 115 | fmt.Fprintln(fout, encoded) 116 | } 117 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mr-tron/base58 2 | 3 | go 1.11 4 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-tron/base58/7feefc872974aa6185828bf1c3eff2d048d9c0dc/go.sum -------------------------------------------------------------------------------- /trivial.go: -------------------------------------------------------------------------------- 1 | package base58 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | ) 7 | 8 | var ( 9 | bn0 = big.NewInt(0) 10 | bn58 = big.NewInt(58) 11 | ) 12 | 13 | // TrivialBase58Encoding encodes the passed bytes into a base58 encoded string 14 | // (inefficiently). 15 | func TrivialBase58Encoding(a []byte) string { 16 | return TrivialBase58EncodingAlphabet(a, BTCAlphabet) 17 | } 18 | 19 | // TrivialBase58EncodingAlphabet encodes the passed bytes into a base58 encoded 20 | // string (inefficiently) with the passed alphabet. 21 | func TrivialBase58EncodingAlphabet(a []byte, alphabet *Alphabet) string { 22 | zero := alphabet.encode[0] 23 | idx := len(a)*138/100 + 1 24 | buf := make([]byte, idx) 25 | bn := new(big.Int).SetBytes(a) 26 | var mo *big.Int 27 | for bn.Cmp(bn0) != 0 { 28 | bn, mo = bn.DivMod(bn, bn58, new(big.Int)) 29 | idx-- 30 | buf[idx] = alphabet.encode[mo.Int64()] 31 | } 32 | for i := range a { 33 | if a[i] != 0 { 34 | break 35 | } 36 | idx-- 37 | buf[idx] = zero 38 | } 39 | return string(buf[idx:]) 40 | } 41 | 42 | // TrivialBase58Decoding decodes the base58 encoded bytes (inefficiently). 43 | func TrivialBase58Decoding(str string) ([]byte, error) { 44 | return TrivialBase58DecodingAlphabet(str, BTCAlphabet) 45 | } 46 | 47 | // TrivialBase58DecodingAlphabet decodes the base58 encoded bytes 48 | // (inefficiently) using the given b58 alphabet. 49 | func TrivialBase58DecodingAlphabet(str string, alphabet *Alphabet) ([]byte, error) { 50 | zero := alphabet.encode[0] 51 | 52 | var zcount int 53 | for i := 0; i < len(str) && str[i] == zero; i++ { 54 | zcount++ 55 | } 56 | leading := make([]byte, zcount) 57 | 58 | var padChar rune = -1 59 | src := []byte(str) 60 | j := 0 61 | for ; j < len(src) && src[j] == byte(padChar); j++ { 62 | } 63 | 64 | n := new(big.Int) 65 | for i := range src[j:] { 66 | c := alphabet.decode[src[i]] 67 | if c == -1 { 68 | return nil, fmt.Errorf("illegal base58 data at input index: %d", i) 69 | } 70 | n.Mul(n, bn58) 71 | n.Add(n, big.NewInt(int64(c))) 72 | } 73 | return append(leading, n.Bytes()...), nil 74 | } 75 | --------------------------------------------------------------------------------