github.com/diadata-org/diadata@v1.4.593/pkg/dia/helpers/stackshelper/c32.go (about)

     1  package stackshelper
     2  
     3  import (
     4  	"crypto/sha256"
     5  	"encoding/hex"
     6  	"errors"
     7  	"fmt"
     8  	"regexp"
     9  	"strings"
    10  )
    11  
    12  const c32table = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
    13  const hextable = "0123456789abcdef"
    14  
    15  func c32address(version int, hash160hex string) (string, error) {
    16  	match, err := regexp.MatchString("^[0-9a-fA-F]{40}$", hash160hex)
    17  	if err != nil {
    18  		return "", err
    19  	}
    20  	if !match {
    21  		return "", errors.New("not a hash160 hex string")
    22  	}
    23  
    24  	c32str, err := c32checkEncode(version, hash160hex)
    25  	if err != nil {
    26  		return "", err
    27  	}
    28  	return "S" + c32str, nil
    29  }
    30  
    31  func c32checkEncode(version int, data string) (string, error) {
    32  	if version < 0 || version >= 32 {
    33  		return "", errors.New("invalid version (must be between 0 and 31)")
    34  	}
    35  
    36  	data = strings.ToLower(data)
    37  	if len(data)%2 != 0 {
    38  		data = "0" + data
    39  	}
    40  	versionHex := fmt.Sprintf("%02x", version)
    41  
    42  	checksum, err := c32checksum(versionHex + data)
    43  	if err != nil {
    44  		return "", err
    45  	}
    46  
    47  	c32str, err := c32encode(data + checksum)
    48  	if err != nil {
    49  		return "", err
    50  	}
    51  	return string(c32table[version]) + c32str, nil
    52  }
    53  
    54  func c32checksum(dataHex string) (string, error) {
    55  	bytes, err := hex.DecodeString(dataHex)
    56  	if err != nil {
    57  		return "", err
    58  	}
    59  
    60  	inner := sha256.Sum256(bytes)
    61  	checksum := sha256.Sum256(inner[:])
    62  	return hex.EncodeToString(checksum[0:4]), nil
    63  }
    64  
    65  func c32encode(inputHex string) (string, error) {
    66  	if len(inputHex)%2 != 0 {
    67  		inputHex = "0" + inputHex
    68  	}
    69  	inputHex = strings.ToLower(inputHex)
    70  
    71  	inputBytes, err := hex.DecodeString(inputHex)
    72  	if err != nil {
    73  		return "", err
    74  	}
    75  
    76  	res := make([]byte, 0)
    77  	carry := 0
    78  
    79  	for i := len(inputHex) - 1; i >= 0; i-- {
    80  		if carry < 4 {
    81  			currentCode := strings.IndexByte(hextable, inputHex[i]) >> carry
    82  			nextCode := 0
    83  			if i != 0 {
    84  				nextCode = strings.IndexByte(hextable, inputHex[i-1])
    85  			}
    86  
    87  			nextBits := 1 + carry
    88  			nextLowBits := nextCode % (1 << nextBits) << (5 - nextBits)
    89  			curC32Digit := c32table[currentCode+nextLowBits]
    90  
    91  			res = append([]byte{curC32Digit}, res...)
    92  			carry = nextBits
    93  		} else {
    94  			carry = 0
    95  		}
    96  	}
    97  
    98  	c32LeadingZeros := 0
    99  	for _, d := range res {
   100  		if d != '0' {
   101  			break
   102  		} else {
   103  			c32LeadingZeros++
   104  		}
   105  	}
   106  	res = res[c32LeadingZeros:]
   107  
   108  	numLeadingZeroBytesInHex := 0
   109  	for _, b := range inputBytes {
   110  		if b != 0 {
   111  			break
   112  		}
   113  		numLeadingZeroBytesInHex++
   114  	}
   115  
   116  	for i := 0; i < numLeadingZeroBytesInHex; i++ {
   117  		res = append([]byte{c32table[0]}, res...)
   118  	}
   119  	return string(res), nil
   120  }