Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ validate := validator.New(validator.WithRequiredStructEnabled())
| bcp47_language_tag | Language tag (BCP 47) |
| btc_addr | Bitcoin Address |
| btc_addr_bech32 | Bitcoin Bech32 Address (segwit) |
| trx_addr | Tron Address |
| eth_addr | Ethereum Address |
| credit_card | Credit Card Number |
| mongodb | MongoDB ObjectID |
| mongodb_connection_string | MongoDB Connection String |
Expand All @@ -180,7 +182,6 @@ validate := validator.New(validator.WithRequiredStructEnabled())
| e164 | e164 formatted phone number |
| ein | U.S. Employeer Identification Number |
| email | E-mail String
| eth_addr | Ethereum Address |
| hexadecimal | Hexadecimal String |
| hexcolor | Hexcolor String |
| hsl | HSL String |
Expand Down
42 changes: 42 additions & 0 deletions baked_in.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ var (
"eth_addr_checksum": isEthereumAddressChecksum,
"btc_addr": isBitcoinAddress,
"btc_addr_bech32": isBitcoinBech32Address,
"trx_addr": isTronAddress,
"uuid": isUUID,
"uuid3": isUUID3,
"uuid4": isUUID4,
Expand Down Expand Up @@ -874,6 +875,47 @@ func isBitcoinBech32Address(fl FieldLevel) bool {
return true
}

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

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

alphabet := []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")

decode := [25]byte{}

for _, n := range []byte(address) {
d := bytes.IndexByte(alphabet, n)

for i := 24; i >= 0; i-- {
d += 58 * int(decode[i])
decode[i] = byte(d % 256)
d /= 256
}
}

if decode[0] != 0x41 {
return false
}

h := sha256.New()
_, _ = h.Write(decode[:21])
d := h.Sum(nil)
h = sha256.New()
_, _ = h.Write(d)

validchecksum := [4]byte{}
computedchecksum := [4]byte{}

copy(computedchecksum[:], h.Sum(d[:0]))
copy(validchecksum[:], decode[21:])

return validchecksum == computedchecksum
}

// excludesRune is the validation function for validating that the field's value does not contain the rune specified within the param.
func excludesRune(fl FieldLevel) bool {
return !containsRune(fl)
Expand Down
8 changes: 8 additions & 0 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,14 @@ The format of the string is checked to ensure it matches the standard Ethereum a

Usage: eth_addr

# Tron Address

This validates that a string value contains a valid TRON address.
The address is decoded using Base58Check encoding and validated by verifying
the version byte and checksum.

Usage: trx_addr

# Contains

This validates that a string value contains the substring value.
Expand Down
2 changes: 2 additions & 0 deletions regexes.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ const (
ethAddressRegexString = `^0x[0-9a-fA-F]{40}$`
ethAddressUpperRegexString = `^0x[0-9A-F]{40}$`
ethAddressLowerRegexString = `^0x[0-9a-f]{40}$`
trxAddressRegexString = `^T[1-9A-HJ-NP-Za-km-z]{33}$`
uRLEncodedRegexString = `^(?:[^%]|%[0-9A-Fa-f]{2})*$`
hTMLEncodedRegexString = `&#[x]?([0-9a-fA-F]{2})|(&gt)|(&lt)|(&quot)|(&amp)+[;]?`
hTMLRegexString = `<[/]?([a-zA-Z]+).*?>`
Expand Down Expand Up @@ -151,6 +152,7 @@ var (
btcUpperAddressRegexBech32 = lazyRegexCompile(btcAddressUpperRegexStringBech32)
btcLowerAddressRegexBech32 = lazyRegexCompile(btcAddressLowerRegexStringBech32)
ethAddressRegex = lazyRegexCompile(ethAddressRegexString)
trxAddressRegex = lazyRegexCompile(trxAddressRegexString)
uRLEncodedRegex = lazyRegexCompile(uRLEncodedRegexString)
hTMLEncodedRegex = lazyRegexCompile(hTMLEncodedRegexString)
hTMLRegex = lazyRegexCompile(hTMLRegexString)
Expand Down
57 changes: 57 additions & 0 deletions validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6470,6 +6470,63 @@ func TestBitcoinBech32AddressValidation(t *testing.T) {
}
}

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

tests := []struct {
param string
expected bool
}{
{"", false},
{" ", false},
{"\n", false},
{"\t", false},
{"TRjE1H8dx", false},
{"T123", false},
{"TRjE1H8dxfmem1NZRdsmek9wo7huR4bdNzFkeodme5", false},
{"TRjE1H8dxfmem1NZRdsmek9wo7", false},
{"TRjE1H8dxfmem1NZRdsmek9wo7huR4bdN", false},
{"asjE1H8dxfmem1NZRdsmek9wo7huR4bdNz", false},
{"tRjE1H8dxfmem1NZRdsmek9wo7huR4bdNz", false},
{"1RjE1H8dxfmem1NZRdsmek9wo7huR4bdNz", false},
{"TRjE1H8dxfmem1NZRdsmek9wo7huR4bdN0", false},
{"TRjE1H8dxfmem1NZRdsmek9wo7huR4bdNO", false},
{"TRjE1H8dxfmem1NZRdsmek9wo7huR4bdNI", false},
{"TRjE1H8dxfmem1NZRdsmek9wo7huR4bdNl", false},
{" TQRyXh7Rzec4udkfmPc9izomNkrZBrKeFh", false},
{"TQRyXh7Rzec4udkfmPc9izomNkrZBrKeFh ", false},
{"\nTQRyXh7Rzec4udkfmPc9izomNkrZBrKeFh", false},
{"TQRyXh7Rzec4udkfmPc9izomNkrZBrKeF☺", false},
{"3ALJH9Y951VCGcVZYAdpA3KchoP9McEj1G", false},
{"12KYrjTdVGjFMtaxERSk3gphreJ5US8aUP", false},
{"12QeMLzSrB8XH8FvEzPMVoRxVAzTr5XM2y", false},
{"TQRyXh7Rzec4udkaVPc9izomNkrZBrKeFh", true},
{"TRjE1H8dxypKM1NZRdysbs9wo7huR4bdNz", true},
{"TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", true},
{"TDiikeBPMsHwcq5NSDvHgPiUVDwsEDxmDN", true},
{"TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", true},
}

for i, test := range tests {
errs := validate.Var(test.param, "trx_addr")

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

func TestNoStructLevelValidation(t *testing.T) {
type Inner struct {
Test string `validate:"len=5"`
Expand Down