203 lines
5.1 KiB
Go
203 lines
5.1 KiB
Go
|
|
// ethereumAddress provides functions to derive Ethereum addresses for identity hashes, credit account keys, and memo hashes
|
|
|
|
package ethereumAddress
|
|
|
|
import "seekia/internal/cryptography/blake3"
|
|
import "seekia/internal/identity"
|
|
import "seekia/internal/encoding"
|
|
|
|
import "golang.org/x/crypto/sha3"
|
|
|
|
import "crypto/rand"
|
|
import "encoding/hex"
|
|
import "slices"
|
|
import "errors"
|
|
|
|
|
|
func GetIdentityScoreEthereumAddressFromIdentityHash(identityHash [16]byte)(string, error){
|
|
|
|
isValid, err := identity.VerifyIdentityHash(identityHash, true, "Moderator")
|
|
if (err != nil) { return "", err }
|
|
if (isValid == false){
|
|
identityHashHex := encoding.EncodeBytesToHexString(identityHash[:])
|
|
return "", errors.New("GetIdentityScoreEthereumAddressFromIdentityHash called with invalid identity hash: " + identityHashHex)
|
|
}
|
|
|
|
hashInputSuffix, err := encoding.DecodeBase32StringToBytes("ethereum")
|
|
if (err != nil) { return "", err }
|
|
|
|
hashInput := slices.Concat(identityHash[:], hashInputSuffix)
|
|
|
|
pseudoRandomBytes, err := blake3.GetBlake3HashAsBytes(20, hashInput)
|
|
if (err != nil) { return "", err }
|
|
|
|
resultAddress, err := getEthereumAddressFromBytes(pseudoRandomBytes)
|
|
if (err != nil) { return "", err }
|
|
|
|
return resultAddress, nil
|
|
}
|
|
|
|
func GetCreditAccountEthereumAddressFromAccountPublicKey(publicKey [32]byte)(string, error){
|
|
|
|
hashInputSuffix, err := encoding.DecodeBase32StringToBytes("ethereum")
|
|
if (err != nil) { return "", err }
|
|
|
|
hashInput := slices.Concat(publicKey[:], hashInputSuffix)
|
|
|
|
pseudoRandomBytes, err := blake3.GetBlake3HashAsBytes(20, hashInput)
|
|
if (err != nil) { return "", err }
|
|
|
|
resultAddress, err := getEthereumAddressFromBytes(pseudoRandomBytes)
|
|
if (err != nil) { return "", err }
|
|
|
|
return resultAddress, nil
|
|
}
|
|
|
|
func GetMemoEthereumAddressFromMemoHash(memoHash [32]byte)(string, error){
|
|
|
|
hashInputSuffix, err := encoding.DecodeBase32StringToBytes("memoethereumaddr")
|
|
if (err != nil) { return "", err }
|
|
|
|
hashInput := slices.Concat(memoHash[:], hashInputSuffix)
|
|
|
|
pseudoRandomBytes, err := blake3.GetBlake3HashAsBytes(20, hashInput)
|
|
if (err != nil) { return "", err }
|
|
|
|
resultAddress, err := getEthereumAddressFromBytes(pseudoRandomBytes)
|
|
if (err != nil) { return "", err }
|
|
|
|
return resultAddress, nil
|
|
}
|
|
|
|
|
|
func GetNewRandomEthereumAddress()(string, error){
|
|
|
|
randomBytes := make([]byte, 20)
|
|
_, err := rand.Read(randomBytes[:])
|
|
if (err != nil) { return "", err }
|
|
|
|
address, err := getEthereumAddressFromBytes(randomBytes)
|
|
if (err != nil) { return "", err }
|
|
|
|
return address, nil
|
|
}
|
|
|
|
// Below code is adopted from go-ethereum
|
|
// github.com/ethereum/go-ethereum/common/types.go
|
|
|
|
func getEthereumAddressFromBytes(inputBytes []byte) (string, error){
|
|
|
|
if (len(inputBytes) != 20) {
|
|
return "", errors.New("getEthereumAddressFromBytes called with invalid inputBytes: Invalid length.")
|
|
}
|
|
|
|
var addressArray [42]byte
|
|
copy(addressArray[:2], "0x")
|
|
|
|
hex.Encode(addressArray[2:], inputBytes)
|
|
|
|
addressLowercase := string(addressArray[:])
|
|
|
|
addressString, err := convertLowercaseEthereumAddressToChecksumAddress(addressLowercase)
|
|
if (err != nil){ return "", err }
|
|
|
|
return addressString, nil
|
|
}
|
|
|
|
func VerifyEthereumAddress(input string)bool{
|
|
|
|
if (len(input) != 42){
|
|
return false
|
|
}
|
|
if (input[0] != '0'){
|
|
return false
|
|
}
|
|
if (input[1] != 'x' && input[1] != 'X'){
|
|
return false
|
|
}
|
|
|
|
addressWithoutPrefix := input[2:]
|
|
|
|
addressWithoutPrefixCharacterBytes := []byte(addressWithoutPrefix)
|
|
|
|
// We verify all characters are hexadecimal
|
|
for _, char := range addressWithoutPrefixCharacterBytes{
|
|
|
|
if ('0' <= char && char <= '9'){
|
|
continue
|
|
}
|
|
if ('a' <= char && char <= 'f'){
|
|
continue
|
|
}
|
|
if ('A' <= char && char <= 'F'){
|
|
continue
|
|
}
|
|
return false
|
|
}
|
|
|
|
addressWithoutPrefixBytes, err := hex.DecodeString(addressWithoutPrefix)
|
|
if (err != nil){
|
|
return false
|
|
}
|
|
|
|
if (len(addressWithoutPrefixBytes) != 20){
|
|
return false
|
|
}
|
|
|
|
// Now we compute checksum and compare with input
|
|
|
|
var expectedAddressArray [42]byte
|
|
copy(expectedAddressArray[:2], "0x")
|
|
hex.Encode(expectedAddressArray[2:], addressWithoutPrefixBytes)
|
|
|
|
expectedAddressLowercase := string(expectedAddressArray[:])
|
|
|
|
expectedAddressString, _ := convertLowercaseEthereumAddressToChecksumAddress(expectedAddressLowercase)
|
|
|
|
if (input != expectedAddressString){
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// This function performs the checksum
|
|
// An ethereum address checksum is performed by changing some characters to uppercase
|
|
// Input:
|
|
// -[]byte: A hex encoded ethereum address with all lowercase characters
|
|
// Output:
|
|
// -string: Input address with checksum performed
|
|
// -error
|
|
func convertLowercaseEthereumAddressToChecksumAddress(inputAddress string)(string, error){
|
|
|
|
inputAddressBytes := []byte(inputAddress)
|
|
|
|
if (len(inputAddressBytes) != 42){
|
|
return "", errors.New("convertLowercaseEthereumAddressToChecksumAddress called with invalid address.")
|
|
}
|
|
|
|
sha := sha3.NewLegacyKeccak256()
|
|
sha.Write(inputAddressBytes[2:])
|
|
hash := sha.Sum(nil)
|
|
|
|
for i := 2; i < len(inputAddressBytes); i++ {
|
|
|
|
hashByte := hash[(i-2)/2]
|
|
if (i%2 == 0) {
|
|
hashByte = hashByte >> 4
|
|
} else {
|
|
hashByte &= 0xf
|
|
}
|
|
|
|
if (inputAddressBytes[i] > '9' && hashByte > 7) {
|
|
inputAddressBytes[i] -= 32
|
|
}
|
|
}
|
|
|
|
addressString := string(inputAddressBytes)
|
|
|
|
return addressString, nil
|
|
}
|
|
|
|
|