feat: add eth_addr_checksum validation, update eth_addr (#1080)

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/
pull/1081/head
Mohamed Sohail 1 year ago committed by GitHub
parent 8f07b03682
commit b95730fb11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      baked_in.go
  2. 2
      regexes.go
  3. 52
      validator_test.go

@ -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,
@ -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()

@ -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)

@ -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},
@ -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()

Loading…
Cancel
Save