Skip to content

Commit

Permalink
Validate order and uniqueness of enabled precompiles
Browse files Browse the repository at this point in the history
  • Loading branch information
evgeniy-scherbina committed May 1, 2024
1 parent 6e25d37 commit 1552ed6
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 0 deletions.
45 changes: 45 additions & 0 deletions x/evm/types/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package types

import (
"bytes"
"fmt"
"math/big"

Expand Down Expand Up @@ -203,6 +204,50 @@ func validateEnabledPrecompiles(enabledPrecompiles []string) error {
}
}

if err := checkIfSortedInBytesRepr(enabledPrecompiles); err != nil {
return fmt.Errorf("enabled precompiles are not sorted: %v", err)
}

if err := checkIfUniqueInBytesRepr(enabledPrecompiles); err != nil {
return fmt.Errorf("enabled precompiles are not unique: %v", err)
}

return nil
}

func checkIfSortedInBytesRepr(addrs []string) error {
n := len(addrs)
addrsInBytes := make([]common.Address, n)
for i, addr := range addrs {
addrsInBytes[i] = common.HexToAddress(addr)
}

for i := 0; i < n-1; i++ {
cmp := bytes.Compare(addrsInBytes[i].Bytes(), addrsInBytes[i+1].Bytes())
if cmp == 1 {
return fmt.Errorf("addresses are not sorted, %v > %v", addrsInBytes[i], addrsInBytes[i+1])
}
}

return nil
}

func checkIfUniqueInBytesRepr(hexAddrs []string) error {
n := len(hexAddrs)
addrs := make([]common.Address, n)
for i, hexAddr := range hexAddrs {
addrs[i] = common.HexToAddress(hexAddr)
}

exists := make(map[common.Address]struct{}, n)
for _, addr := range addrs {
if _, ok := exists[addr]; ok {
return fmt.Errorf("addr %v not unique", addr)
}

exists[addr] = struct{}{}
}

return nil
}

Expand Down
94 changes: 94 additions & 0 deletions x/evm/types/params_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,16 @@ func TestParamsValidatePriv(t *testing.T) {
require.NoError(t, validateEnabledPrecompiles(nil))
require.NoError(t, validateEnabledPrecompiles([]string{}))
require.NoError(t, validateEnabledPrecompiles([]string{validEthAddress}))

// check if sorted
addr1 := "0x1000000000000000000000000000000000000000"
addr2 := "0x2000000000000000000000000000000000000000"
require.NoError(t, validateEnabledPrecompiles([]string{addr1, addr2}))
require.Error(t, validateEnabledPrecompiles([]string{addr2, addr1}))

// check if unique
require.NoError(t, validateEnabledPrecompiles([]string{addr1, addr2}))
require.Error(t, validateEnabledPrecompiles([]string{addr1, addr1}))
}

func TestValidateChainConfig(t *testing.T) {
Expand Down Expand Up @@ -229,3 +239,87 @@ func TestCheckIfEnabledPrecompilesAreRegistered(t *testing.T) {
}
}
}

func TestCheckIfSortedInBytesRepr(t *testing.T) {
addr1 := "0x1000000000000000000000000000000000000000"
addr2 := "0x2000000000000000000000000000000000000000"

// NOTE: we sort in bytes representation, so proper order will be []string{mixedCaseAddr, upperCaseAddr},
// and it differs from lexicographically sorted strings
upperCaseAddr := "0xAB00000000000000000000000000000000000000"
mixedCaseAddr := "0xaA00000000000000000000000000000000000000"

testCases := []struct {
name string
addrs []string
sorted bool
}{
{
name: "test-case #1",
addrs: []string{addr1, addr2},
sorted: true,
},
{
name: "test-case #2",
addrs: []string{addr2, addr1},
sorted: false,
},
{
name: "test-case #3",
addrs: []string{mixedCaseAddr, upperCaseAddr},
sorted: true,
},
}

for _, tc := range testCases {
err := checkIfSortedInBytesRepr(tc.addrs)

if tc.sorted {
require.NoError(t, err, tc.name)
} else {
require.Error(t, err, tc.name)
}
}
}

func TestCheckIfUniqueInBytesRepr(t *testing.T) {
addr1 := "0x1000000000000000000000000000000000000000"
addr2 := "0x2000000000000000000000000000000000000000"

// NOTE: we check uniqueness in bytes representation, so lowerCaseAddr and mixedCaseAddr are the same,
// despite it differs in string representation
lowerCaseAddr := "0xab00000000000000000000000000000000000000"
mixedCaseAddr := "0xAb00000000000000000000000000000000000000"

testCases := []struct {
name string
addrs []string
unique bool
}{
{
name: "test-case #1",
addrs: []string{addr1, addr2},
unique: true,
},
{
name: "test-case #2",
addrs: []string{addr1, addr1},
unique: false,
},
{
name: "test-case #3",
addrs: []string{lowerCaseAddr, mixedCaseAddr},
unique: false,
},
}

for _, tc := range testCases {
err := checkIfUniqueInBytesRepr(tc.addrs)

if tc.unique {
require.NoError(t, err, tc.name)
} else {
require.Error(t, err, tc.name)
}
}
}

0 comments on commit 1552ed6

Please sign in to comment.