mirror of
https://github.com/luxfi/evm.git
synced 2026-09-24 00:27:44 +00:00
462 lines
14 KiB
Go
462 lines
14 KiB
Go
// (c) 2023, Lux Industries, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package core
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
"github.com/luxfi/evm/commontype"
|
|
"github.com/luxfi/evm/core/types"
|
|
"github.com/luxfi/evm/params"
|
|
"github.com/luxfi/evm/precompile/precompileconfig"
|
|
"github.com/luxfi/geth/common"
|
|
"github.com/luxfi/node/v2/utils/set"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/mock/gomock"
|
|
)
|
|
|
|
type predicateCheckTest struct {
|
|
accessList types.AccessList
|
|
gas uint64
|
|
predicateContext *precompileconfig.PredicateContext
|
|
createPredicates func(t testing.TB) map[common.Address]precompileconfig.Predicater
|
|
expectedRes map[common.Address][]byte
|
|
expectedErr error
|
|
}
|
|
|
|
func TestCheckPredicate(t *testing.T) {
|
|
testErr := errors.New("test error")
|
|
addr1 := common.HexToAddress("0xaa")
|
|
addr2 := common.HexToAddress("0xbb")
|
|
addr3 := common.HexToAddress("0xcc")
|
|
addr4 := common.HexToAddress("0xdd")
|
|
predicateContext := &precompileconfig.PredicateContext{
|
|
ProposerVMBlockCtx: &commontype.BlockContext{
|
|
PChainHeight: 10,
|
|
},
|
|
}
|
|
for name, test := range map[string]predicateCheckTest{
|
|
"no predicates, no access list, no context passes": {
|
|
gas: 53000,
|
|
predicateContext: nil,
|
|
expectedRes: make(map[common.Address][]byte),
|
|
expectedErr: nil,
|
|
},
|
|
"no predicates, no access list, with context passes": {
|
|
gas: 53000,
|
|
predicateContext: predicateContext,
|
|
expectedRes: make(map[common.Address][]byte),
|
|
expectedErr: nil,
|
|
},
|
|
"no predicates, with access list, no context passes": {
|
|
gas: 57300,
|
|
predicateContext: nil,
|
|
accessList: types.AccessList([]types.AccessTuple{
|
|
{
|
|
Address: addr1,
|
|
StorageKeys: []common.Hash{
|
|
{1},
|
|
},
|
|
},
|
|
}),
|
|
expectedRes: make(map[common.Address][]byte),
|
|
expectedErr: nil,
|
|
},
|
|
"predicate, no access list, no context passes": {
|
|
gas: 53000,
|
|
predicateContext: nil,
|
|
createPredicates: func(t testing.TB) map[common.Address]precompileconfig.Predicater {
|
|
predicater := precompileconfig.NewMockPredicater(gomock.NewController(t))
|
|
return map[common.Address]precompileconfig.Predicater{
|
|
addr1: predicater,
|
|
}
|
|
},
|
|
expectedRes: make(map[common.Address][]byte),
|
|
expectedErr: nil,
|
|
},
|
|
"predicate, no access list, no block context passes": {
|
|
gas: 53000,
|
|
predicateContext: &precompileconfig.PredicateContext{
|
|
ProposerVMBlockCtx: nil,
|
|
},
|
|
createPredicates: func(t testing.TB) map[common.Address]precompileconfig.Predicater {
|
|
predicater := precompileconfig.NewMockPredicater(gomock.NewController(t))
|
|
return map[common.Address]precompileconfig.Predicater{
|
|
addr1: predicater,
|
|
}
|
|
},
|
|
expectedRes: make(map[common.Address][]byte),
|
|
expectedErr: nil,
|
|
},
|
|
"predicate named by access list, without context errors": {
|
|
gas: 53000,
|
|
predicateContext: nil,
|
|
createPredicates: func(t testing.TB) map[common.Address]precompileconfig.Predicater {
|
|
predicater := precompileconfig.NewMockPredicater(gomock.NewController(t))
|
|
arg := common.Hash{1}
|
|
predicater.EXPECT().PredicateGas(arg[:]).Return(uint64(0), nil).Times(1)
|
|
return map[common.Address]precompileconfig.Predicater{
|
|
addr1: predicater,
|
|
}
|
|
},
|
|
accessList: types.AccessList([]types.AccessTuple{
|
|
{
|
|
Address: addr1,
|
|
StorageKeys: []common.Hash{
|
|
{1},
|
|
},
|
|
},
|
|
}),
|
|
expectedErr: ErrMissingPredicateContext,
|
|
},
|
|
"predicate named by access list, without block context errors": {
|
|
gas: 53000,
|
|
predicateContext: &precompileconfig.PredicateContext{
|
|
ProposerVMBlockCtx: nil,
|
|
},
|
|
createPredicates: func(t testing.TB) map[common.Address]precompileconfig.Predicater {
|
|
predicater := precompileconfig.NewMockPredicater(gomock.NewController(t))
|
|
arg := common.Hash{1}
|
|
predicater.EXPECT().PredicateGas(arg[:]).Return(uint64(0), nil).Times(1)
|
|
return map[common.Address]precompileconfig.Predicater{
|
|
addr1: predicater,
|
|
}
|
|
},
|
|
accessList: types.AccessList([]types.AccessTuple{
|
|
{
|
|
Address: addr1,
|
|
StorageKeys: []common.Hash{
|
|
{1},
|
|
},
|
|
},
|
|
}),
|
|
expectedErr: ErrMissingPredicateContext,
|
|
},
|
|
"predicate named by access list returns non-empty": {
|
|
gas: 53000,
|
|
predicateContext: predicateContext,
|
|
createPredicates: func(t testing.TB) map[common.Address]precompileconfig.Predicater {
|
|
predicater := precompileconfig.NewMockPredicater(gomock.NewController(t))
|
|
arg := common.Hash{1}
|
|
predicater.EXPECT().PredicateGas(arg[:]).Return(uint64(0), nil).Times(2)
|
|
predicater.EXPECT().VerifyPredicate(gomock.Any(), arg[:]).Return(nil)
|
|
return map[common.Address]precompileconfig.Predicater{
|
|
addr1: predicater,
|
|
}
|
|
},
|
|
accessList: types.AccessList([]types.AccessTuple{
|
|
{
|
|
Address: addr1,
|
|
StorageKeys: []common.Hash{
|
|
{1},
|
|
},
|
|
},
|
|
}),
|
|
expectedRes: map[common.Address][]byte{
|
|
addr1: {}, // valid bytes
|
|
},
|
|
expectedErr: nil,
|
|
},
|
|
"predicate returns gas err": {
|
|
gas: 53000,
|
|
predicateContext: predicateContext,
|
|
createPredicates: func(t testing.TB) map[common.Address]precompileconfig.Predicater {
|
|
predicater := precompileconfig.NewMockPredicater(gomock.NewController(t))
|
|
arg := common.Hash{1}
|
|
predicater.EXPECT().PredicateGas(arg[:]).Return(uint64(0), testErr)
|
|
return map[common.Address]precompileconfig.Predicater{
|
|
addr1: predicater,
|
|
}
|
|
},
|
|
accessList: types.AccessList([]types.AccessTuple{
|
|
{
|
|
Address: addr1,
|
|
StorageKeys: []common.Hash{
|
|
{1},
|
|
},
|
|
},
|
|
}),
|
|
expectedErr: testErr,
|
|
},
|
|
"two predicates one named by access list returns non-empty": {
|
|
gas: 53000,
|
|
predicateContext: predicateContext,
|
|
createPredicates: func(t testing.TB) map[common.Address]precompileconfig.Predicater {
|
|
predicater := precompileconfig.NewMockPredicater(gomock.NewController(t))
|
|
arg := common.Hash{1}
|
|
predicater.EXPECT().PredicateGas(arg[:]).Return(uint64(0), nil).Times(2)
|
|
predicater.EXPECT().VerifyPredicate(gomock.Any(), arg[:]).Return(nil)
|
|
return map[common.Address]precompileconfig.Predicater{
|
|
addr1: predicater,
|
|
addr2: predicater,
|
|
}
|
|
},
|
|
accessList: types.AccessList([]types.AccessTuple{
|
|
{
|
|
Address: addr1,
|
|
StorageKeys: []common.Hash{
|
|
{1},
|
|
},
|
|
},
|
|
}),
|
|
expectedRes: map[common.Address][]byte{
|
|
addr1: {}, // valid bytes
|
|
},
|
|
expectedErr: nil,
|
|
},
|
|
"two predicates both named by access list returns non-empty": {
|
|
gas: 53000,
|
|
predicateContext: predicateContext,
|
|
createPredicates: func(t testing.TB) map[common.Address]precompileconfig.Predicater {
|
|
ctrl := gomock.NewController(t)
|
|
predicate1 := precompileconfig.NewMockPredicater(ctrl)
|
|
arg1 := common.Hash{1}
|
|
predicate1.EXPECT().PredicateGas(arg1[:]).Return(uint64(0), nil).Times(2)
|
|
predicate1.EXPECT().VerifyPredicate(gomock.Any(), arg1[:]).Return(nil)
|
|
predicate2 := precompileconfig.NewMockPredicater(ctrl)
|
|
arg2 := common.Hash{2}
|
|
predicate2.EXPECT().PredicateGas(arg2[:]).Return(uint64(0), nil).Times(2)
|
|
predicate2.EXPECT().VerifyPredicate(gomock.Any(), arg2[:]).Return(testErr)
|
|
return map[common.Address]precompileconfig.Predicater{
|
|
addr1: predicate1,
|
|
addr2: predicate2,
|
|
}
|
|
},
|
|
accessList: types.AccessList([]types.AccessTuple{
|
|
{
|
|
Address: addr1,
|
|
StorageKeys: []common.Hash{
|
|
{1},
|
|
},
|
|
},
|
|
{
|
|
Address: addr2,
|
|
StorageKeys: []common.Hash{
|
|
{2},
|
|
},
|
|
},
|
|
}),
|
|
expectedRes: map[common.Address][]byte{
|
|
addr1: {}, // valid bytes
|
|
addr2: {1}, // invalid bytes
|
|
},
|
|
expectedErr: nil,
|
|
},
|
|
"two predicates neither named by access list": {
|
|
gas: 61600,
|
|
predicateContext: predicateContext,
|
|
createPredicates: func(t testing.TB) map[common.Address]precompileconfig.Predicater {
|
|
predicater := precompileconfig.NewMockPredicater(gomock.NewController(t))
|
|
return map[common.Address]precompileconfig.Predicater{
|
|
addr1: predicater,
|
|
addr2: predicater,
|
|
}
|
|
},
|
|
accessList: types.AccessList([]types.AccessTuple{
|
|
{
|
|
Address: addr3,
|
|
StorageKeys: []common.Hash{
|
|
{1},
|
|
},
|
|
},
|
|
{
|
|
Address: addr4,
|
|
StorageKeys: []common.Hash{
|
|
{1},
|
|
},
|
|
},
|
|
}),
|
|
expectedRes: make(map[common.Address][]byte),
|
|
expectedErr: nil,
|
|
},
|
|
"insufficient gas": {
|
|
gas: 53000,
|
|
predicateContext: predicateContext,
|
|
createPredicates: func(t testing.TB) map[common.Address]precompileconfig.Predicater {
|
|
predicater := precompileconfig.NewMockPredicater(gomock.NewController(t))
|
|
arg := common.Hash{1}
|
|
predicater.EXPECT().PredicateGas(arg[:]).Return(uint64(1), nil)
|
|
return map[common.Address]precompileconfig.Predicater{
|
|
addr1: predicater,
|
|
}
|
|
},
|
|
accessList: types.AccessList([]types.AccessTuple{
|
|
{
|
|
Address: addr1,
|
|
StorageKeys: []common.Hash{
|
|
{1},
|
|
},
|
|
},
|
|
}),
|
|
expectedErr: ErrIntrinsicGas,
|
|
},
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
require := require.New(t)
|
|
// Create the rules from TestChainConfig and update the predicates based on the test params
|
|
rules := params.TestChainConfig.Rules(common.Big0, 0)
|
|
if test.createPredicates != nil {
|
|
for address, predicater := range test.createPredicates(t) {
|
|
rules := params.GetRulesExtra(rules)
|
|
rules.Predicaters[address] = predicater
|
|
}
|
|
}
|
|
|
|
// Specify only the access list, since this test should not depend on any other values
|
|
tx := types.NewTx(&types.DynamicFeeTx{
|
|
AccessList: test.accessList,
|
|
Gas: test.gas,
|
|
})
|
|
predicateRes, err := CheckPredicates(rules, test.predicateContext, tx)
|
|
require.ErrorIs(err, test.expectedErr)
|
|
if test.expectedErr != nil {
|
|
return
|
|
}
|
|
require.Equal(test.expectedRes, predicateRes)
|
|
intrinsicGas, err := IntrinsicGas(tx.Data(), tx.AccessList(), true, rules)
|
|
require.NoError(err)
|
|
require.Equal(tx.Gas(), intrinsicGas) // Require test specifies exact amount of gas consumed
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCheckPredicatesOutput(t *testing.T) {
|
|
testErr := errors.New("test error")
|
|
addr1 := common.HexToAddress("0xaa")
|
|
addr2 := common.HexToAddress("0xbb")
|
|
validHash := common.Hash{1}
|
|
invalidHash := common.Hash{2}
|
|
predicateContext := &precompileconfig.PredicateContext{
|
|
ProposerVMBlockCtx: &commontype.BlockContext{
|
|
PChainHeight: 10,
|
|
},
|
|
}
|
|
type testTuple struct {
|
|
address common.Address
|
|
isValidPredicate bool
|
|
}
|
|
type resultTest struct {
|
|
name string
|
|
expectedRes map[common.Address][]byte
|
|
testTuple []testTuple
|
|
}
|
|
tests := []resultTest{
|
|
{name: "no predicates", expectedRes: map[common.Address][]byte{}},
|
|
{
|
|
name: "one address one predicate",
|
|
testTuple: []testTuple{
|
|
{address: addr1, isValidPredicate: true},
|
|
},
|
|
expectedRes: map[common.Address][]byte{addr1: set.NewBits().Bytes()},
|
|
},
|
|
{
|
|
name: "one address one invalid predicate",
|
|
testTuple: []testTuple{
|
|
{address: addr1, isValidPredicate: false},
|
|
},
|
|
expectedRes: map[common.Address][]byte{addr1: set.NewBits(0).Bytes()},
|
|
},
|
|
{
|
|
name: "one address two invalid predicates",
|
|
testTuple: []testTuple{
|
|
{address: addr1, isValidPredicate: false},
|
|
{address: addr1, isValidPredicate: false},
|
|
},
|
|
expectedRes: map[common.Address][]byte{addr1: set.NewBits(0, 1).Bytes()},
|
|
},
|
|
{
|
|
name: "one address two mixed predicates",
|
|
testTuple: []testTuple{
|
|
{address: addr1, isValidPredicate: true},
|
|
{address: addr1, isValidPredicate: false},
|
|
},
|
|
expectedRes: map[common.Address][]byte{addr1: set.NewBits(1).Bytes()},
|
|
},
|
|
{
|
|
name: "one address mixed predicates",
|
|
testTuple: []testTuple{
|
|
{address: addr1, isValidPredicate: true},
|
|
{address: addr1, isValidPredicate: false},
|
|
{address: addr1, isValidPredicate: false},
|
|
{address: addr1, isValidPredicate: true},
|
|
},
|
|
expectedRes: map[common.Address][]byte{addr1: set.NewBits(1, 2).Bytes()},
|
|
},
|
|
{
|
|
name: "two addresses mixed predicates",
|
|
testTuple: []testTuple{
|
|
{address: addr1, isValidPredicate: true},
|
|
{address: addr2, isValidPredicate: false},
|
|
{address: addr1, isValidPredicate: false},
|
|
{address: addr1, isValidPredicate: false},
|
|
{address: addr2, isValidPredicate: true},
|
|
{address: addr2, isValidPredicate: true},
|
|
{address: addr2, isValidPredicate: false},
|
|
{address: addr2, isValidPredicate: true},
|
|
},
|
|
expectedRes: map[common.Address][]byte{addr1: set.NewBits(1, 2).Bytes(), addr2: set.NewBits(0, 3).Bytes()},
|
|
},
|
|
{
|
|
name: "two addresses all valid predicates",
|
|
testTuple: []testTuple{
|
|
{address: addr1, isValidPredicate: true},
|
|
{address: addr2, isValidPredicate: true},
|
|
{address: addr1, isValidPredicate: true},
|
|
{address: addr1, isValidPredicate: true},
|
|
},
|
|
expectedRes: map[common.Address][]byte{addr1: set.NewBits().Bytes(), addr2: set.NewBits().Bytes()},
|
|
},
|
|
{
|
|
name: "two addresses all invalid predicates",
|
|
testTuple: []testTuple{
|
|
{address: addr1, isValidPredicate: false},
|
|
{address: addr2, isValidPredicate: false},
|
|
{address: addr1, isValidPredicate: false},
|
|
{address: addr1, isValidPredicate: false},
|
|
},
|
|
expectedRes: map[common.Address][]byte{addr1: set.NewBits(0, 1, 2).Bytes(), addr2: set.NewBits(0).Bytes()},
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
require := require.New(t)
|
|
// Create the rules from TestChainConfig and update the predicates based on the test params
|
|
rules := params.TestChainConfig.Rules(common.Big0, 0)
|
|
predicater := precompileconfig.NewMockPredicater(gomock.NewController(t))
|
|
predicater.EXPECT().PredicateGas(gomock.Any()).Return(uint64(0), nil).Times(len(test.testTuple))
|
|
|
|
var txAccessList types.AccessList
|
|
for _, tuple := range test.testTuple {
|
|
var predicateHash common.Hash
|
|
if tuple.isValidPredicate {
|
|
predicateHash = validHash
|
|
predicater.EXPECT().VerifyPredicate(gomock.Any(), validHash[:]).Return(nil)
|
|
} else {
|
|
predicateHash = invalidHash
|
|
predicater.EXPECT().VerifyPredicate(gomock.Any(), invalidHash[:]).Return(testErr)
|
|
}
|
|
txAccessList = append(txAccessList, types.AccessTuple{
|
|
Address: tuple.address,
|
|
StorageKeys: []common.Hash{
|
|
predicateHash,
|
|
},
|
|
})
|
|
}
|
|
|
|
rulesExtra := params.GetRulesExtra(rules)
|
|
rulesExtra.Predicaters[addr1] = predicater
|
|
rulesExtra.Predicaters[addr2] = predicater
|
|
|
|
// Specify only the access list, since this test should not depend on any other values
|
|
tx := types.NewTx(&types.DynamicFeeTx{
|
|
AccessList: txAccessList,
|
|
Gas: 53000,
|
|
})
|
|
predicateRes, err := CheckPredicates(rules, predicateContext, tx)
|
|
require.NoError(err)
|
|
require.Equal(test.expectedRes, predicateRes)
|
|
})
|
|
}
|
|
}
|