mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-07 13:34:27 +00:00
feat!: RulesHooks.CanCreateContract() accepts and returns gas (#28)
* refactor!: `RulesHooks.CanCreateContract()` via `defer` `TestCanCreateContract` is unchanged and merely moved to the appropriate file. * feat!: `RulesHooks.CanCreateContract()` accepts and returns gas * chore: move `TestCanCreateContract` to original file Simplifies code review * chore: pacify linter * refactor!: revert to non-deferred call (not at start)
This commit is contained in:
parent
58f38836c0
commit
38eaaab96c
5 changed files with 39 additions and 21 deletions
|
|
@ -296,12 +296,15 @@ func TestCanCreateContract(t *testing.T) {
|
||||||
account := rng.Address()
|
account := rng.Address()
|
||||||
slot := rng.Hash()
|
slot := rng.Hash()
|
||||||
|
|
||||||
|
const gasLimit uint64 = 1e6
|
||||||
|
gasUsage := rng.Uint64n(gasLimit)
|
||||||
|
|
||||||
makeErr := func(cc *libevm.AddressContext, stateVal common.Hash) error {
|
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)
|
return fmt.Errorf("Origin: %v Caller: %v Contract: %v State: %v", cc.Origin, cc.Caller, cc.Self, stateVal)
|
||||||
}
|
}
|
||||||
hooks := &hookstest.Stub{
|
hooks := &hookstest.Stub{
|
||||||
CanCreateContractFn: func(cc *libevm.AddressContext, s libevm.StateReader) error {
|
CanCreateContractFn: func(cc *libevm.AddressContext, gas uint64, s libevm.StateReader) (uint64, error) {
|
||||||
return makeErr(cc, s.GetState(account, slot))
|
return gas - gasUsage, makeErr(cc, s.GetState(account, slot))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
hooks.Register(t)
|
hooks.Register(t)
|
||||||
|
|
@ -309,7 +312,7 @@ func TestCanCreateContract(t *testing.T) {
|
||||||
origin := rng.Address()
|
origin := rng.Address()
|
||||||
caller := rng.Address()
|
caller := rng.Address()
|
||||||
value := rng.Hash()
|
value := rng.Hash()
|
||||||
code := rng.Bytes(8)
|
code := []byte{byte(vm.STOP)}
|
||||||
salt := rng.Hash()
|
salt := rng.Hash()
|
||||||
|
|
||||||
create := crypto.CreateAddress(caller, 0)
|
create := crypto.CreateAddress(caller, 0)
|
||||||
|
|
@ -323,14 +326,14 @@ func TestCanCreateContract(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "Create",
|
name: "Create",
|
||||||
create: func(evm *vm.EVM) ([]byte, common.Address, uint64, error) {
|
create: func(evm *vm.EVM) ([]byte, common.Address, uint64, error) {
|
||||||
return evm.Create(vm.AccountRef(caller), code, 1e6, uint256.NewInt(0))
|
return evm.Create(vm.AccountRef(caller), code, gasLimit, uint256.NewInt(0))
|
||||||
},
|
},
|
||||||
wantErr: makeErr(&libevm.AddressContext{Origin: origin, Caller: caller, Self: create}, value),
|
wantErr: makeErr(&libevm.AddressContext{Origin: origin, Caller: caller, Self: create}, value),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Create2",
|
name: "Create2",
|
||||||
create: func(evm *vm.EVM) ([]byte, common.Address, uint64, error) {
|
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[:]))
|
return evm.Create2(vm.AccountRef(caller), code, gasLimit, uint256.NewInt(0), new(uint256.Int).SetBytes(salt[:]))
|
||||||
},
|
},
|
||||||
wantErr: makeErr(&libevm.AddressContext{Origin: origin, Caller: caller, Self: create2}, value),
|
wantErr: makeErr(&libevm.AddressContext{Origin: origin, Caller: caller, Self: create2}, value),
|
||||||
},
|
},
|
||||||
|
|
@ -342,8 +345,10 @@ func TestCanCreateContract(t *testing.T) {
|
||||||
state.SetState(account, slot, value)
|
state.SetState(account, slot, value)
|
||||||
evm.TxContext.Origin = origin
|
evm.TxContext.Origin = origin
|
||||||
|
|
||||||
_, _, _, err := tt.create(evm)
|
_, _, gasRemaining, err := tt.create(evm)
|
||||||
require.EqualError(t, err, tt.wantErr.Error())
|
require.EqualError(t, err, tt.wantErr.Error())
|
||||||
|
// require prints uint64s in hex
|
||||||
|
require.Equal(t, int(gasLimit-gasUsage), int(gasRemaining), "gas remaining") //nolint:gosec // G115 won't overflow as <= 1e6
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -428,10 +428,6 @@ func (c *codeAndHash) Hash() common.Hash {
|
||||||
|
|
||||||
// create creates a new contract using code as deployment code.
|
// create creates a new contract using code as deployment code.
|
||||||
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *uint256.Int, address common.Address, typ OpCode) ([]byte, common.Address, uint64, error) {
|
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *uint256.Int, address common.Address, typ OpCode) ([]byte, common.Address, uint64, error) {
|
||||||
cc := &libevm.AddressContext{Origin: evm.Origin, Caller: caller.Address(), Self: address}
|
|
||||||
if err := evm.chainRules.Hooks().CanCreateContract(cc, evm.StateDB); err != nil {
|
|
||||||
return nil, common.Address{}, gas, err
|
|
||||||
}
|
|
||||||
// Depth check execution. Fail if we're trying to execute above the
|
// Depth check execution. Fail if we're trying to execute above the
|
||||||
// limit.
|
// limit.
|
||||||
if evm.depth > int(params.CallCreateDepth) {
|
if evm.depth > int(params.CallCreateDepth) {
|
||||||
|
|
@ -455,6 +451,19 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||||
if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) {
|
if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) {
|
||||||
return nil, common.Address{}, 0, ErrContractAddressCollision
|
return nil, common.Address{}, 0, ErrContractAddressCollision
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//libevm:start
|
||||||
|
//
|
||||||
|
// This check MUST be placed after the caller's nonce is incremented but
|
||||||
|
// before all other state-modifying behaviour, even if changes may be
|
||||||
|
// reverted to the snapshot.
|
||||||
|
addrs := &libevm.AddressContext{Origin: evm.Origin, Caller: caller.Address(), Self: address}
|
||||||
|
gas, err := evm.chainRules.Hooks().CanCreateContract(addrs, gas, evm.StateDB)
|
||||||
|
if err != nil {
|
||||||
|
return nil, common.Address{}, gas, err
|
||||||
|
}
|
||||||
|
//libevm:end
|
||||||
|
|
||||||
// Create a new account on the state
|
// Create a new account on the state
|
||||||
snapshot := evm.StateDB.Snapshot()
|
snapshot := evm.StateDB.Snapshot()
|
||||||
evm.StateDB.CreateAccount(address)
|
evm.StateDB.CreateAccount(address)
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ func Register[C params.ChainConfigHooks, R params.RulesHooks](tb testing.TB, ext
|
||||||
type Stub struct {
|
type Stub struct {
|
||||||
PrecompileOverrides map[common.Address]libevm.PrecompiledContract
|
PrecompileOverrides map[common.Address]libevm.PrecompiledContract
|
||||||
CanExecuteTransactionFn func(common.Address, *common.Address, libevm.StateReader) error
|
CanExecuteTransactionFn func(common.Address, *common.Address, libevm.StateReader) error
|
||||||
CanCreateContractFn func(*libevm.AddressContext, libevm.StateReader) error
|
CanCreateContractFn func(*libevm.AddressContext, uint64, libevm.StateReader) (uint64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register is a convenience wrapper for registering s as both the
|
// Register is a convenience wrapper for registering s as both the
|
||||||
|
|
@ -63,11 +63,11 @@ func (s Stub) CanExecuteTransaction(from common.Address, to *common.Address, sr
|
||||||
|
|
||||||
// CanCreateContract proxies arguments to the s.CanCreateContractFn function if
|
// CanCreateContract proxies arguments to the s.CanCreateContractFn function if
|
||||||
// non-nil, otherwise it acts as a noop.
|
// non-nil, otherwise it acts as a noop.
|
||||||
func (s Stub) CanCreateContract(cc *libevm.AddressContext, sr libevm.StateReader) error {
|
func (s Stub) CanCreateContract(cc *libevm.AddressContext, gas uint64, sr libevm.StateReader) (uint64, error) {
|
||||||
if f := s.CanCreateContractFn; f != nil {
|
if f := s.CanCreateContractFn; f != nil {
|
||||||
return f(cc, sr)
|
return f(cc, gas, sr)
|
||||||
}
|
}
|
||||||
return nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ interface {
|
var _ interface {
|
||||||
|
|
|
||||||
|
|
@ -105,11 +105,12 @@ func (r RulesExtra) PrecompileOverride(addr common.Address) (_ libevm.Precompile
|
||||||
// CanCreateContract implements the required [params.RuleHooks] method. Access
|
// CanCreateContract implements the required [params.RuleHooks] method. Access
|
||||||
// to state allows it to be configured on-chain however this is an optional
|
// to state allows it to be configured on-chain however this is an optional
|
||||||
// implementation detail.
|
// implementation detail.
|
||||||
func (r RulesExtra) CanCreateContract(*libevm.AddressContext, libevm.StateReader) error {
|
func (r RulesExtra) CanCreateContract(_ *libevm.AddressContext, gas uint64, _ libevm.StateReader) (uint64, error) {
|
||||||
if time.Unix(int64(r.timestamp), 0).UTC().Day() != int(time.Tuesday) { //nolint:gosec // G115 timestamp won't overflow int64 for millions of years so this is someone else's problem
|
if time.Unix(int64(r.timestamp), 0).UTC().Day() != int(time.Tuesday) { //nolint:gosec // G115 timestamp won't overflow int64 for millions of years so this is someone else's problem
|
||||||
return errors.New("uh oh")
|
// Consumes all remaining gas.
|
||||||
|
return 0, errors.New("uh oh")
|
||||||
}
|
}
|
||||||
return nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// This example demonstrates how the rest of this file would be used from a
|
// This example demonstrates how the rest of this file would be used from a
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,9 @@ type RulesHooks interface {
|
||||||
// RulesAllowlistHooks are a subset of [RulesHooks] that gate actions, signalled
|
// RulesAllowlistHooks are a subset of [RulesHooks] that gate actions, signalled
|
||||||
// by returning a nil (allowed) or non-nil (blocked) error.
|
// by returning a nil (allowed) or non-nil (blocked) error.
|
||||||
type RulesAllowlistHooks interface {
|
type RulesAllowlistHooks interface {
|
||||||
CanCreateContract(*libevm.AddressContext, libevm.StateReader) error
|
// CanCreateContract is called after the deployer's nonce is incremented but
|
||||||
|
// before all other state-modifying actions.
|
||||||
|
CanCreateContract(_ *libevm.AddressContext, gas uint64, _ libevm.StateReader) (gasRemaining uint64, _ error)
|
||||||
CanExecuteTransaction(from common.Address, to *common.Address, _ libevm.StateReader) error
|
CanExecuteTransaction(from common.Address, to *common.Address, _ libevm.StateReader) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,9 +73,10 @@ func (NOOPHooks) CanExecuteTransaction(_ common.Address, _ *common.Address, _ li
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanCreateContract allows all (otherwise valid) contract deployment.
|
// CanCreateContract allows all (otherwise valid) contract deployment, not
|
||||||
func (NOOPHooks) CanCreateContract(*libevm.AddressContext, libevm.StateReader) error {
|
// consuming any more gas.
|
||||||
return nil
|
func (NOOPHooks) CanCreateContract(_ *libevm.AddressContext, gas uint64, _ libevm.StateReader) (uint64, error) {
|
||||||
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrecompileOverride instructs the EVM interpreter to use the default
|
// PrecompileOverride instructs the EVM interpreter to use the default
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue