193 lines
5.6 KiB
Go
193 lines
5.6 KiB
Go
package abispec
|
|
|
|
import (
|
|
"fmt"
|
|
"math/big"
|
|
"regexp"
|
|
"strings"
|
|
"unicode"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/status-im/status-go/eth-node/crypto"
|
|
)
|
|
|
|
var unicodeZeroPattern = regexp.MustCompile("^(?:\u0000)*")
|
|
var hexZeroPattern = regexp.MustCompile("^(?:00)*")
|
|
var hexStringPattern = regexp.MustCompile("(?i)^[0-9a-f]+$")
|
|
var hexPrefixPattern = regexp.MustCompile("(?i)^0x")
|
|
|
|
var addressBasicPattern = regexp.MustCompile("(?i)^(0x)?[0-9a-f]{40}$")
|
|
var addressLowerCasePattern = regexp.MustCompile("^(0x|0X)?[0-9a-f]{40}$")
|
|
var addressUpperCasePattern = regexp.MustCompile("^(0x|0X)?[0-9A-F]{40}$")
|
|
|
|
func HexToNumber(hex string) string {
|
|
num, success := big.NewInt(0).SetString(hex, 16)
|
|
if success {
|
|
return num.String()
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func NumberToHex(numString string) string {
|
|
num, success := big.NewInt(0).SetString(numString, 0)
|
|
if success {
|
|
return fmt.Sprintf("%x", num)
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func Sha3(str string) string {
|
|
bytes := crypto.Keccak256([]byte(str))
|
|
return common.Bytes2Hex(bytes)
|
|
}
|
|
|
|
func reverse(str string) string {
|
|
bytes := []byte(str)
|
|
var out []byte
|
|
for i := len(bytes) - 1; i >= 0; i-- {
|
|
out = append(out, bytes[i])
|
|
}
|
|
return string(out)
|
|
}
|
|
|
|
// remove \u0000 padding from either side
|
|
func removeUnicodeZeroPadding(str string) string {
|
|
found := unicodeZeroPattern.FindString(str)
|
|
str = strings.Replace(str, found, "", 1)
|
|
str = reverse(str)
|
|
found = unicodeZeroPattern.FindString(str)
|
|
str = strings.Replace(str, found, "", 1)
|
|
return reverse(str)
|
|
}
|
|
|
|
// remove 00 padding from either side
|
|
func removeHexZeroPadding(str string) string {
|
|
found := hexZeroPattern.FindString(str)
|
|
str = strings.Replace(str, found, "", 1)
|
|
str = reverse(str)
|
|
found = hexZeroPattern.FindString(str)
|
|
str = strings.Replace(str, found, "", 1)
|
|
return reverse(str)
|
|
}
|
|
|
|
// implementation referenced from https://github.com/ChainSafe/web3.js/blob/edcd215bf657a4bba62fabaafd08e6e70040976e/packages/web3-utils/src/utils.js#L165
|
|
func Utf8ToHex(str string) (string, error) {
|
|
str, err := Utf8encode(str)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
str = removeUnicodeZeroPadding(str)
|
|
|
|
var hex = ""
|
|
for _, r := range str {
|
|
n := fmt.Sprintf("%x", r)
|
|
if len(n) < 2 {
|
|
hex += "0" + n
|
|
} else {
|
|
hex += n
|
|
}
|
|
}
|
|
return "0x" + hex, nil
|
|
}
|
|
|
|
// implementation referenced from https://github.com/ChainSafe/web3.js/blob/edcd215bf657a4bba62fabaafd08e6e70040976e/packages/web3-utils/src/utils.js#L193
|
|
func HexToUtf8(hexString string) (string, error) {
|
|
hexString = removeHexPrefix(hexString)
|
|
if !hexStringPattern.MatchString(hexString) {
|
|
return "", fmt.Errorf("the parameter '%s' must be a valid HEX string", hexString)
|
|
}
|
|
if len(hexString)%2 != 0 {
|
|
return "", fmt.Errorf("the parameter '%s' must have a even number of digits", hexString)
|
|
}
|
|
|
|
hexString = removeHexZeroPadding(hexString)
|
|
|
|
n := big.NewInt(0)
|
|
var bytes []byte
|
|
for i := 0; i < len(hexString); i += 2 {
|
|
hex := hexString[i : i+2]
|
|
n, success := n.SetString(hex, 16)
|
|
if !success {
|
|
return "", fmt.Errorf("invalid hex value %s", hex)
|
|
}
|
|
r := rune(n.Int64())
|
|
bs := stringFromCharCode(r)
|
|
bytes = appendBytes(bytes, bs)
|
|
}
|
|
|
|
bytes, err := Utf8decode(string(bytes))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(bytes), nil
|
|
}
|
|
|
|
func removeHexPrefix(str string) string {
|
|
found := hexPrefixPattern.FindString(str)
|
|
return strings.Replace(str, found, "", 1)
|
|
}
|
|
|
|
// implementation referenced from https://github.com/ChainSafe/web3.js/blob/edcd215bf657a4bba62fabaafd08e6e70040976e/packages/web3-utils/src/utils.js#L107
|
|
func CheckAddressChecksum(address string) (bool, error) {
|
|
address = removeHexPrefix(address)
|
|
addressHash := Sha3(strings.ToLower(address))
|
|
|
|
n := big.NewInt(0)
|
|
for i := 0; i < 40; i++ {
|
|
// the nth letter should be uppercase if the nth digit of casemap is 1
|
|
n, success := n.SetString(addressHash[i:i+1], 16)
|
|
if !success {
|
|
return false, fmt.Errorf("failed to convert hex value '%s' to int", addressHash[i:i+1])
|
|
}
|
|
v := n.Int64()
|
|
|
|
if (v > 7 && uint8(unicode.ToUpper(rune(address[i]))) != address[i]) || (v <= 7 && uint8(unicode.ToLower(rune(address[i]))) != address[i]) {
|
|
return false, nil
|
|
}
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// implementation referenced from https://github.com/ChainSafe/web3.js/blob/edcd215bf657a4bba62fabaafd08e6e70040976e/packages/web3-utils/src/utils.js#L85
|
|
func IsAddress(address string) (bool, error) {
|
|
// check if it has the basic requirements of an address
|
|
if !addressBasicPattern.MatchString(address) {
|
|
return false, nil
|
|
} else if addressLowerCasePattern.MatchString(address) || addressUpperCasePattern.MatchString(address) {
|
|
return true, nil
|
|
} else {
|
|
return CheckAddressChecksum(address)
|
|
}
|
|
}
|
|
|
|
// implementation referenced from https://github.com/ChainSafe/web3.js/blob/2022b17d52d31ce95559d18d5530d18c83eb4d1c/packages/web3-utils/src/index.js#L283
|
|
func ToChecksumAddress(address string) (string, error) {
|
|
if strings.Trim(address, "") == "" {
|
|
return "", nil
|
|
}
|
|
if !addressBasicPattern.MatchString(address) {
|
|
return "", fmt.Errorf("given address '%s' is not a valid Ethereum address", address)
|
|
}
|
|
|
|
address = strings.ToLower(address)
|
|
address = removeHexPrefix(address)
|
|
addressHash := Sha3(address)
|
|
|
|
var checksumAddress = []rune("0x")
|
|
var n = big.NewInt(0)
|
|
for i := 0; i < len(address); i++ {
|
|
// If ith character is 9 to f then make it uppercase
|
|
n, success := n.SetString(addressHash[i:i+1], 16)
|
|
if !success {
|
|
return "", fmt.Errorf("failed to convert hex value '%s' to int", addressHash[i:i+1])
|
|
}
|
|
if n.Int64() > 7 {
|
|
upper := unicode.ToUpper(rune(address[i]))
|
|
checksumAddress = append(checksumAddress, upper)
|
|
} else {
|
|
checksumAddress = append(checksumAddress, rune(address[i]))
|
|
}
|
|
}
|
|
return string(checksumAddress), nil
|
|
}
|