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  }