- Ethereum address
- An Ethereum address is the last 20 bytes of the Keccak256
hash of the uncompressed public key without the
0x04
prefix
ERC-55 Mixed-case checksum address encoding
- ERC-55 Ethereum address encoding
- The ERC-55 Ethereum address encoding is a backward-compatible, mixed-case encoding of case-insensitive Ethereum addresses that incorporates an error detecting checksum into the now case-sensitive Ethereum address by capitalizing certain hex letters of the Ethereum address. The Ethereum address encoding conveys the error detecting information without changing the semantic value of the address. The Ethereum address encoding is used to detect errors and protect against mistakes with the 99.986% accuracy
- Address derive
- The
AddressDerive
function derives an Ethereum address from a compressed or an uncompressed public key by taking the last 20 bytes of the Keccak256 hash of the uncompressed public key without the0x04
prefix. The address derive function converts a compressed public key into an uncompressed public key before applying the Keccak256 hash functionfunc AddressDerive(pub []byte) ([]byte, error) { switch { case len(pub) == 65 && pub[0] == 0x04: // Uncompressed public key case len(pub) == 33 && (pub[0] == 0x02 || pub[0] == 0x03): // Compressed public key pubx, puby := ecc.UnmarshalCompressed(ecc.P256k1(), pub) pub = NewPubKey(pubx, puby).Pub default: return nil, fmt.Errorf("address derive: invalid public key: %x", pub) } hash := crypto.Keccak256(pub[1:]) addr := hash[12:] return addr, nil }
- Address encode
- The
AddressEncode
function encodes an Ethereum address and incorporates the error detecting checksum into the address without changing the semantic value of the address. The address encode function- Convert the address into a lower case hex string
- Compute the keccak256 hash of the address hex string
- Convert the keccak256 hash into a lower case hex string
- For each character of the address hex string
- Capitalize each character of the address hex string if the hex value of the corresponding character of the hash hex string is greater than or equal to 8, leaving other characters of the address hex string unchanged
- Return the encoded mixed-case address with the incorporated checksum
func AddressEncode(addr []byte) string { hexAddr := fmt.Sprintf("%x", addr) hash := crypto.Keccak256([]byte(hexAddr)) hexHash := fmt.Sprintf("%x", hash) chAddr := strings.Split(hexAddr, "") chHash := strings.Split(hexHash, "") var encAddr strings.Builder for i := range chAddr { h, _ := strconv.ParseInt(chHash[i], 16, 8) a := chAddr[i] if h >= 8 { a = strings.ToUpper(a) } encAddr.WriteString(a) } return encAddr.String() }
- Address verify
- The
AddressVerify
function verifies that the encoded mixed-case Ethereum address does not have errors. The address verify function- Convert the address into a lower case hex string
- Compute the keccak256 hash of the address hex string
- Convert the keccak256 hash into a lower case hex string
- For each character of the address hex string
- Check that each character of the encoded address hex string is in the upper case if the hex value of the corresponding character of the hash hex string is greater than or equal to 8, and is in the lower case otherwise
- Return the invalid checksum error if at least one character of the encoded
address does not meet the above conditions, otherwise the encoded address is
valid
var reUpper = regexp.MustCompile(`[A-F0-9]`) var reLower = regexp.MustCompile(`[a-f0-9]`) func AddressVerify(hexAddr string) error { hash := crypto.Keccak256([]byte(strings.ToLower(hexAddr))) hexHash := fmt.Sprintf("%x", hash) chAddr := strings.Split(hexAddr, "") chHash := strings.Split(hexHash, "") for i := range hexAddr { h, _ := strconv.ParseInt(chHash[i], 16, 8) a := chAddr[i] if h >= 8 && !reUpper.MatchString(a) || h < 8 && !reLower.MatchString(a) { return fmt.Errorf("address verify: invalid checksum") } } return nil }
go build -o wallet; ./hdwallet/cli-test.nu
Show the help and usage instructions of the wallet address
command
./wallet address
# NAME:
# wallet address - Derive, encode and verify an Ethereum address (ERC-55)
# USAGE:
# wallet address [command [command options]]
# COMMANDS:
# derive Derive an Ethereum address from a secp256k1 public key
# stdin: a compressed or uncompressed secp256k1 public key in hex
# stdout: an Ethereum address in hex
# encode Encode an Ethereum address (ERC-55)
# stdin: an Ethereum address in hex
# stdout: an encoded case-sensitive Ethereum address string
# verify Verify an encoded case-sensitive Ethereum address (ERC-55)
# stdin: an encoded case-sensitive Ethereum address string
# stdout: true if the address is valid, false otherwise
# OPTIONS:
# --help, -h show help
Generate a random secp256k1 private key by keccak256 hashing a sequence of 32
bytes taken from the /dev/urandom
CSPRNG. Derive a secp256k1 public key from
the private key. Derive an Ethereum address from the derived public key. Note,
the that same address is derived from both uncompressed and compressed forms of
the public key
$env.PATH = $env.PATH | prepend ("." | path expand)
let prv = open /dev/urandom | first 32 | wallet keccak256
print $prv
# 838c2f329e8e98855bd648ca95e3939fc118a0f63b703fb443d0e1f0eaae33cb
let pub = $prv | wallet eckey derive | from yaml
print $pub
# ╭──────┬────────────────────────────────────────────────────────────────────────────────────╮
# │ prv │ 838c2f329e8e98855bd648ca95e3939fc118a0f63b703fb443d0e1f0eaae33cb │
# │ pub │ 04c694264d1933cb3d3b1a4073b3189452173d7f510312c5c86c9689574a6d25e81523533b578eb09d │
# │ │ 0b4e9414f53a3bd259843aeb22ea677025f51f8b90d8d05e │
# │ pubc │ 02c694264d1933cb3d3b1a4073b3189452173d7f510312c5c86c9689574a6d25e8 │
# ╰──────┴────────────────────────────────────────────────────────────────────────────────────╯
let addr = $pub.pub | wallet address derive
print $addr
# 445f86f47591cc2161e5efbb31b708e964cc8c6d
let addr2 = $pub.pubc | wallet address derive
print $addr2
# 445f86f47591cc2161e5efbb31b708e964cc8c6d
Generate a secp256k1 key pair. Derive an Ethereum address from the public key. Encode the address and incorporate the error detecting checksum. Note, the encoded address has some letters capitalized, while the initial address is all lower case. Verify the encoded address and confirm that the address is valid and does not have errors. Modify the encoded address in order to introduce errors. Verify the modified encoded address with errors and confirm that the validation returns the invalid checksum error
$env.PATH = $env.PATH | prepend ("." | path expand)
let key = wallet eckey generate | from yaml
print $key
# ╭──────┬────────────────────────────────────────────────────────────────────────────────────╮
# │ prv │ 840257eb47ab36bbd952b18f856eb399c57534d8eacda2765e89b799f214bde5 │
# │ pub │ 04e9eb5e40adab72f15ffe5b650498bc320252b92284c2522e3a30f5ed0bbe7da993b442e1a48e5840 │
# │ │ f59d9b72cf6df6b9fa3d2b45099388b503fbcfc2d77019ed │
# │ pubc │ 03e9eb5e40adab72f15ffe5b650498bc320252b92284c2522e3a30f5ed0bbe7da9 │
# ╰──────┴────────────────────────────────────────────────────────────────────────────────────╯
let addr = $key.pub | wallet address derive
print $addr
# 883477898a318f37fd7e4f19f9d3e47400f5bd5f
let encAddr = $addr | wallet address encode
print $encAddr
# 883477898A318F37Fd7E4F19f9D3e47400f5BD5F
$encAddr | wallet address verify
# true
$encAddr | str replace --regex '[a-f]' "A" | wallet address verify
# address verify: invalid checksum
# false