mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-21 06:04:33 +00:00
* feat: pseudo-generic extra payloads in `params.ChainConfig` and `params.Rules` * feat: `params.ExtraPayloadGetter` for end-user type safety * refactor: payloads only available through `params.ExtraPayloadGetter` * chore: make `libevm/examples/extraparams` a `params` testable example * doc: `libevm/pseudo` package comments and improved readability * doc: `params.*Extra*` comments and improved readability * doc: `params.ExtraPayloadGetter` comments and improved readability * doc: `params/config.libevm_test.go` comments and improved readability * refactor: simplify `params.ChainConfig.UnmarshalJSON()` * refactor: abstract new/nil-pointer creation into `pseudo.Constructor`s * feat: precompile override via `params.Extras` hooks * doc: flesh out `PrecompileOverride()` in example * doc: complete commentary and improve readability * refactor: `ChainConfig.Hooks()` + `Rules` equivalent * chore: rename precompiles test file in keeping with geth equivalent * feat: stateful precompiles + allowlist hooks The allowlist hooks are included in this commit because they allow for the same functionality as stateful precompiles in `ava-labs/coreth` and `ava-labs/subnet-evm`. * fix: `StateTransition.canExecuteTransaction()` used `msg.From` instead of `To` * test: `params.RulesHooks.CanCreateContract` integration * test: `params.RulesHooks.CanExecuteTransaction` integration * test: `vm.NewStatefulPrecompile()` integration * refactor: simplify test of `CanCreateContract` * refactor: abstract generation of random `Address`/`Hash` values * doc: full documentation + readability refactoring/renaming * fix: remove circular dependency in tests
178 lines
5.1 KiB
Go
178 lines
5.1 KiB
Go
package vm_test
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/core/vm"
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
"github.com/ethereum/go-ethereum/libevm"
|
|
"github.com/ethereum/go-ethereum/libevm/ethtest"
|
|
"github.com/ethereum/go-ethereum/libevm/hookstest"
|
|
"github.com/ethereum/go-ethereum/params"
|
|
"github.com/holiman/uint256"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/exp/rand"
|
|
)
|
|
|
|
type precompileStub struct {
|
|
requiredGas uint64
|
|
returnData []byte
|
|
}
|
|
|
|
func (s *precompileStub) RequiredGas([]byte) uint64 { return s.requiredGas }
|
|
func (s *precompileStub) Run([]byte) ([]byte, error) { return s.returnData, nil }
|
|
|
|
func TestPrecompileOverride(t *testing.T) {
|
|
type test struct {
|
|
name string
|
|
addr common.Address
|
|
requiredGas uint64
|
|
stubData []byte
|
|
}
|
|
|
|
const gasLimit = uint64(1e7)
|
|
|
|
tests := []test{
|
|
{
|
|
name: "arbitrary values",
|
|
addr: common.Address{'p', 'r', 'e', 'c', 'o', 'm', 'p', 'i', 'l', 'e'},
|
|
requiredGas: 314159,
|
|
stubData: []byte("the return data"),
|
|
},
|
|
}
|
|
|
|
rng := rand.New(rand.NewSource(42))
|
|
for _, addr := range vm.PrecompiledAddressesCancun {
|
|
tests = append(tests, test{
|
|
name: fmt.Sprintf("existing precompile %v", addr),
|
|
addr: addr,
|
|
requiredGas: rng.Uint64n(gasLimit),
|
|
stubData: addr[:],
|
|
})
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
hooks := &hookstest.Stub{
|
|
PrecompileOverrides: map[common.Address]libevm.PrecompiledContract{
|
|
tt.addr: &precompileStub{
|
|
requiredGas: tt.requiredGas,
|
|
returnData: tt.stubData,
|
|
},
|
|
},
|
|
}
|
|
hooks.RegisterForRules(t)
|
|
|
|
t.Run(fmt.Sprintf("%T.Call([overridden precompile address = %v])", &vm.EVM{}, tt.addr), func(t *testing.T) {
|
|
_, evm := ethtest.NewZeroEVM(t)
|
|
gotData, gotGasLeft, err := evm.Call(vm.AccountRef{}, tt.addr, nil, gasLimit, uint256.NewInt(0))
|
|
require.NoError(t, err)
|
|
assert.Equal(t, tt.stubData, gotData, "contract's return data")
|
|
assert.Equal(t, gasLimit-tt.requiredGas, gotGasLeft, "gas left")
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewStatefulPrecompile(t *testing.T) {
|
|
rng := ethtest.NewPseudoRand(314159)
|
|
precompile := rng.Address()
|
|
slot := rng.Hash()
|
|
|
|
const gasLimit = 1e6
|
|
gasCost := rng.Uint64n(gasLimit)
|
|
|
|
makeOutput := func(caller, self common.Address, input []byte, stateVal common.Hash) []byte {
|
|
return []byte(fmt.Sprintf(
|
|
"Caller: %v Precompile: %v State: %v Input: %#x",
|
|
caller, self, stateVal, input,
|
|
))
|
|
}
|
|
hooks := &hookstest.Stub{
|
|
PrecompileOverrides: map[common.Address]libevm.PrecompiledContract{
|
|
precompile: vm.NewStatefulPrecompile(
|
|
func(state vm.StateDB, _ *params.Rules, caller, self common.Address, input []byte) ([]byte, error) {
|
|
return makeOutput(caller, self, input, state.GetState(precompile, slot)), nil
|
|
},
|
|
func(b []byte) uint64 {
|
|
return gasCost
|
|
},
|
|
),
|
|
},
|
|
}
|
|
hooks.RegisterForRules(t)
|
|
|
|
caller := rng.Address()
|
|
input := rng.Bytes(8)
|
|
value := rng.Hash()
|
|
|
|
state, evm := ethtest.NewZeroEVM(t)
|
|
state.SetState(precompile, slot, value)
|
|
wantReturnData := makeOutput(caller, precompile, input, value)
|
|
wantGasLeft := gasLimit - gasCost
|
|
|
|
gotReturnData, gotGasLeft, err := evm.Call(vm.AccountRef(caller), precompile, input, gasLimit, uint256.NewInt(0))
|
|
require.NoError(t, err)
|
|
assert.Equal(t, wantReturnData, gotReturnData)
|
|
assert.Equal(t, wantGasLeft, gotGasLeft)
|
|
}
|
|
|
|
func TestCanCreateContract(t *testing.T) {
|
|
rng := ethtest.NewPseudoRand(142857)
|
|
account := rng.Address()
|
|
slot := rng.Hash()
|
|
|
|
makeErr := func(cc *libevm.AddressContext, stateVal common.Hash) error {
|
|
return fmt.Errorf("Origin: %v Caller: %v Contract: %v State: %v", cc.Origin, cc.Caller, cc.Self, stateVal)
|
|
}
|
|
hooks := &hookstest.Stub{
|
|
CanCreateContractFn: func(cc *libevm.AddressContext, s libevm.StateReader) error {
|
|
return makeErr(cc, s.GetState(account, slot))
|
|
},
|
|
}
|
|
hooks.RegisterForRules(t)
|
|
|
|
origin := rng.Address()
|
|
caller := rng.Address()
|
|
value := rng.Hash()
|
|
code := rng.Bytes(8)
|
|
salt := rng.Hash()
|
|
|
|
create := crypto.CreateAddress(caller, 0)
|
|
create2 := crypto.CreateAddress2(caller, salt, crypto.Keccak256(code))
|
|
|
|
tests := []struct {
|
|
name string
|
|
create func(*vm.EVM) ([]byte, common.Address, uint64, error)
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "Create",
|
|
create: func(evm *vm.EVM) ([]byte, common.Address, uint64, error) {
|
|
return evm.Create(vm.AccountRef(caller), code, 1e6, uint256.NewInt(0))
|
|
},
|
|
wantErr: makeErr(&libevm.AddressContext{Origin: origin, Caller: caller, Self: create}, value),
|
|
},
|
|
{
|
|
name: "Create2",
|
|
create: func(evm *vm.EVM) ([]byte, common.Address, uint64, error) {
|
|
return evm.Create2(vm.AccountRef(caller), code, 1e6, uint256.NewInt(0), new(uint256.Int).SetBytes(salt[:]))
|
|
},
|
|
wantErr: makeErr(&libevm.AddressContext{Origin: origin, Caller: caller, Self: create2}, value),
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
state, evm := ethtest.NewZeroEVM(t)
|
|
state.SetState(account, slot, value)
|
|
evm.TxContext.Origin = origin
|
|
|
|
_, _, _, err := tt.create(evm)
|
|
require.EqualError(t, err, tt.wantErr.Error())
|
|
})
|
|
}
|
|
}
|