github.com/deroproject/derosuite@v2.1.6-1.0.20200307070847-0f2e589c7a2b+incompatible/address/base58.go (about) 1 // Copyright 2017-2018 DERO Project. All rights reserved. 2 // Use of this source code in any form is governed by RESEARCH license. 3 // license can be found in the LICENSE file. 4 // GPG: 0F39 E425 8C65 3947 702A 8234 08B2 0360 A03A 9DE8 5 // 6 // 7 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 8 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 9 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 10 // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 11 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 12 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 13 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 14 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 15 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 17 package address 18 19 import "strings" 20 import "math/big" 21 22 // all characters in the base58 23 const BASE58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" 24 25 var base58Lookup = map[string]int{ 26 "1": 0, "2": 1, "3": 2, "4": 3, "5": 4, "6": 5, "7": 6, "8": 7, 27 "9": 8, "A": 9, "B": 10, "C": 11, "D": 12, "E": 13, "F": 14, "G": 15, 28 "H": 16, "J": 17, "K": 18, "L": 19, "M": 20, "N": 21, "P": 22, "Q": 23, 29 "R": 24, "S": 25, "T": 26, "U": 27, "V": 28, "W": 29, "X": 30, "Y": 31, 30 "Z": 32, "a": 33, "b": 34, "c": 35, "d": 36, "e": 37, "f": 38, "g": 39, 31 "h": 40, "i": 41, "j": 42, "k": 43, "m": 44, "n": 45, "o": 46, "p": 47, 32 "q": 48, "r": 49, "s": 50, "t": 51, "u": 52, "v": 53, "w": 54, "x": 55, 33 "y": 56, "z": 57, 34 } 35 var bigBase = big.NewInt(58) 36 37 // chunk are max 8 bytes long refer https://cryptonote.org/cns/cns007.txt for more documentation 38 var bytes_to_base58_length_mapping = []int{ 39 0, // 0 bytes of input, 0 byte of base58 output 40 2, // 1 byte of input, 2 bytes of base58 output 41 3, // 2 byte of input, 3 bytes of base58 output 42 5, // 3 byte of input, 5 bytes of base58 output 43 6, // 4 byte of input, 6 bytes of base58 output 44 7, // 5 byte of input, 7 bytes of base58 output 45 9, // 6 byte of input, 9 bytes of base58 output 46 10, // 7 byte of input, 10 bytes of base58 output 47 11, // 8 byte of input, 11 bytes of base58 output 48 } 49 50 // encode 8 byte chunk with necessary padding 51 func encodeChunk(raw []byte) (result string) { 52 remainder := new(big.Int) 53 remainder.SetBytes(raw) 54 bigZero := new(big.Int) 55 for remainder.Cmp(bigZero) > 0 { 56 current := new(big.Int) 57 remainder.DivMod(remainder, bigBase, current) 58 result = string(BASE58[current.Int64()]) + result 59 } 60 61 for i := range bytes_to_base58_length_mapping { 62 if i == len(raw) { 63 if len(result) < bytes_to_base58_length_mapping[i] { 64 result = strings.Repeat("1", (bytes_to_base58_length_mapping[i]-len(result))) + result 65 } 66 return result 67 } 68 } 69 70 return // we never reach here, if inputs are well-formed <= 8 bytes 71 } 72 73 // decode max 11 char base58 to 8 byte chunk as necessary 74 // proper error handling is not being done 75 func decodeChunk(encoded string) (result []byte) { 76 bigResult := big.NewInt(0) 77 currentMultiplier := big.NewInt(1) 78 tmp := new(big.Int) 79 for i := len(encoded) - 1; i >= 0; i-- { 80 // make sure decoded character is a base58 char , otherwise return 81 if strings.IndexAny(BASE58, string(encoded[i])) < 0 { 82 return 83 } 84 tmp.SetInt64(int64(base58Lookup[string(encoded[i])])) 85 tmp.Mul(currentMultiplier, tmp) 86 bigResult.Add(bigResult, tmp) 87 currentMultiplier.Mul(currentMultiplier, bigBase) 88 } 89 90 for i := range bytes_to_base58_length_mapping { 91 if bytes_to_base58_length_mapping[i] == len(encoded) { 92 result = append([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0}, bigResult.Bytes()...) 93 return result[len(result)-i:] // return necessary bytes, initial zero appended as per mapping 94 } 95 } 96 97 return // we never reach here, if inputs are well-formed <= 11 chars 98 } 99 100 // split into 8 byte chunks, process and merge back result 101 func EncodeDeroBase58(data ...[]byte) (result string) { 102 var combined []byte 103 for _, item := range data { 104 combined = append(combined, item...) 105 } 106 107 fullblocks := len(combined) / 8 108 for i := 0; i < fullblocks; i++ { // process any chunks in 8 byte form 109 result += encodeChunk(combined[i*8 : (i+1)*8]) 110 } 111 if len(combined)%8 > 0 { // process last partial block 112 result += encodeChunk(combined[fullblocks*8:]) 113 } 114 return 115 } 116 117 // split into 11 char chunks, process and merge back result 118 func DecodeDeroBase58(data string) (result []byte) { 119 fullblocks := len(data) / 11 120 for i := 0; i < fullblocks; i++ { // process partial block 121 result = append(result, decodeChunk(data[i*11:(i+1)*11])...) 122 } 123 if len(data)%11 > 0 { // process last partial block 124 result = append(result, decodeChunk(data[fullblocks*11:])...) 125 } 126 return 127 }