seekia/internal/cryptocurrency/ethereumAddress/ethereumAddress.go

204 lines
5.1 KiB
Go
Raw Normal View History

// 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
}