diff --git a/baked_in.go b/baked_in.go index df790d8..55bd4c4 100644 --- a/baked_in.go +++ b/baked_in.go @@ -11,6 +11,8 @@ import ( "sync" "time" "unicode/utf8" + "crypto/sha256" + "bytes" ) // Func accepts a FieldLevel interface for all validation needs. The return @@ -398,9 +400,51 @@ func isEthereumAddress(fl FieldLevel) bool { // IsBitcoinAddress is the validation function for validating if the field's value is a valid btc address, currently only based on the format func isBitcoinAddress(fl FieldLevel) bool { - field := fl.Field() + address := fl.Field().String() + + if btcAddressRegex.MatchString(address) { + + alphabet := []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") + + decode := [25]byte{} + + for _, n := range []byte(address) { + d := bytes.IndexByte(alphabet, n) + + if(d == -1){ + return false + } + + for i := 24; i >= 0; i-- { + d += 58 * int(decode[i]) + decode[i] = byte(d % 256) + d /= 256 + } + } + + + if decode[0] != 0 { + return false + } + + h := sha256.New() + h.Write(decode[:21]) + d := h.Sum([]byte{}) + h = sha256.New() + h.Write(d) + + validchecksum := [4]byte{} + computedchecksum := [4]byte{} + + copy(computedchecksum[:], h.Sum(d[:0])) + copy(validchecksum[:], decode[21:]) + + println(address, "::", validchecksum[:], computedchecksum[:]) + + return validchecksum == computedchecksum + } - return btcAddressRegex.MatchString(field.String()) || btcAddressRegexBech32.MatchString(field.String()) + return btcAddressRegexBech32.MatchString(address) } // ExcludesRune is the validation function for validating that the field's value does not contain the rune specified within the param. diff --git a/validator_test.go b/validator_test.go index d8be987..c16d426 100644 --- a/validator_test.go +++ b/validator_test.go @@ -4446,10 +4446,11 @@ func TestBitcoinAddressValidation(t *testing.T){ }{ {"", false}, {"0x02F9AE5f22EA3fA88F05780B30385bEC", false}, - {"123f681646d4a755815f9cb19e1acc8565a0c2ac", false}, - {"1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2", true}, - {"3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy", true}, - {"bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq", true}, + {"1A1zP1ePQGefi2DMPTifTL5SLmv7DivfNa", false}, + {"1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2", true}, // valid p2pkh address + {"3P14159f73E4gFr7JterCCQh9QjiTjiZrG", true}, // valid p2sh address + {"bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq", true}, // valid bech32 address + } @@ -4460,15 +4461,15 @@ func TestBitcoinAddressValidation(t *testing.T){ if test.expected { if !IsEqual(errs, nil) { - t.Fatalf("Index: %d btc_addr failed Error: %s", i, errs) + t.Fatalf("Index: %d btc_addr failed with Error: %s", i, errs) } } else { if IsEqual(errs, nil) { - t.Fatalf("Index: %d btc_addr failed Error: %s", i, errs) + t.Fatalf("Index: %d btc_addr failed with Error: %s", i, errs) } else { val := getError(errs, "", "") if val.Tag() != "btc_addr" { - t.Fatalf("Index: %d Latitude failed Error: %s", i, errs) + t.Fatalf("Index: %d Latitude failed with Error: %s", i, errs) } } }