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 }