Skip to content

Commit

Permalink
feat: add eth_addr_checksum validation, update eth_addr (#1080)
Browse files Browse the repository at this point in the history
rationale:

- ethereum addresses are derived from a public key (the last 20 bytes). The network accepts any case insensitive address as long as it is a 40 char alphanumeric (When the last 20 bytes are converted to hex). It is 0x prefixed so that it becomes 42 in length, hence the current eth_addr implementation is modified to reflect this.
- Checksum-ing is optional on ethereum and is a way of typo checking the address hence this is introduced as a new validation i.e. eth_addr_checksum.

refs:

- https://github.com/ethereum/go-ethereum/blob/master/crypto/crypto.go#L275
- https://goethereumbook.org/en/wallet-generate/
- https://goethereumbook.org/en/address-check/
  • Loading branch information
kamikazechaser committed Mar 19, 2023
1 parent 8f07b03 commit b95730f
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 8 deletions.
13 changes: 8 additions & 5 deletions baked_in.go
Expand Up @@ -140,6 +140,7 @@ var (
"isbn10": isISBN10,
"isbn13": isISBN13,
"eth_addr": isEthereumAddress,
"eth_addr_checksum": isEthereumAddressChecksum,
"btc_addr": isBitcoinAddress,
"btc_addr_bech32": isBitcoinBech32Address,
"uuid": isUUID,
Expand Down Expand Up @@ -613,14 +614,16 @@ func isISBN10(fl FieldLevel) bool {
func isEthereumAddress(fl FieldLevel) bool {
address := fl.Field().String()

return ethAddressRegex.MatchString(address)
}

// isEthereumAddressChecksum is the validation function for validating if the field's value is a valid checksumed Ethereum address.
func isEthereumAddressChecksum(fl FieldLevel) bool {
address := fl.Field().String()

if !ethAddressRegex.MatchString(address) {
return false
}

if ethAddressRegexUpper.MatchString(address) || ethAddressRegexLower.MatchString(address) {
return true
}

// Checksum validation. Reference: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md
address = address[2:] // Skip "0x" prefix.
h := sha3.NewLegacyKeccak256()
Expand Down
2 changes: 0 additions & 2 deletions regexes.go
Expand Up @@ -118,8 +118,6 @@ var (
btcUpperAddressRegexBech32 = regexp.MustCompile(btcAddressUpperRegexStringBech32)
btcLowerAddressRegexBech32 = regexp.MustCompile(btcAddressLowerRegexStringBech32)
ethAddressRegex = regexp.MustCompile(ethAddressRegexString)
ethAddressRegexUpper = regexp.MustCompile(ethAddressUpperRegexString)
ethAddressRegexLower = regexp.MustCompile(ethAddressLowerRegexString)
uRLEncodedRegex = regexp.MustCompile(uRLEncodedRegexString)
hTMLEncodedRegex = regexp.MustCompile(hTMLEncodedRegexString)
hTMLRegex = regexp.MustCompile(hTMLRegexString)
Expand Down
52 changes: 51 additions & 1 deletion validator_test.go
Expand Up @@ -5672,7 +5672,7 @@ func TestEthereumAddressValidation(t *testing.T) {
{"0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", true},
{"0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB", true},
{"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", true},
{"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDB", false}, // Invalid checksum.
{"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDB", true}, // Invalid checksum, but valid address.

// Other.
{"", false},
Expand Down Expand Up @@ -5703,6 +5703,56 @@ func TestEthereumAddressValidation(t *testing.T) {
}
}

func TestEthereumAddressChecksumValidation(t *testing.T) {
validate := New()

tests := []struct {
param string
expected bool
}{
// All caps.
{"0x52908400098527886E0F7030069857D2E4169EE7", true},
{"0x8617E340B3D01FA5F11F306F4090FD50E238070D", true},

// All lower.
{"0x27b1fdb04752bbc536007a920d24acb045561c26", true},
{"0x123f681646d4a755815f9cb19e1acc8565a0c2ac", false},

// Mixed case: runs checksum validation.
{"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", true},
{"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDB", false}, // Invalid checksum.
{"0x000000000000000000000000000000000000dead", false}, // Invalid checksum.
{"0x000000000000000000000000000000000000dEaD", true}, // Valid checksum.

// Other.
{"", false},
{"D1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", false}, // Missing "0x" prefix.
{"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDbc", false}, // More than 40 hex digits.
{"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aD", false}, // Less than 40 hex digits.
{"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDw", false}, // Invalid hex digit "w".
}

for i, test := range tests {

errs := validate.Var(test.param, "eth_addr_checksum")

if test.expected {
if !IsEqual(errs, nil) {
t.Fatalf("Index: %d eth_addr_checksum failed Error: %s", i, errs)
}
} else {
if IsEqual(errs, nil) {
t.Fatalf("Index: %d eth_addr_checksum failed Error: %s", i, errs)
} else {
val := getError(errs, "", "")
if val.Tag() != "eth_addr_checksum" {
t.Fatalf("Index: %d Latitude failed Error: %s", i, errs)
}
}
}
}
}

func TestBitcoinAddressValidation(t *testing.T) {
validate := New()

Expand Down

0 comments on commit b95730f

Please sign in to comment.