mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-20 05:41:35 +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()
|
||||
slot := rng.Hash()
|
||||
|
||||
const gasLimit uint64 = 1e6
|
||||
gasUsage := rng.Uint64n(gasLimit)
|
||||
|
||||
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))
|
||||
CanCreateContractFn: func(cc *libevm.AddressContext, gas uint64, s libevm.StateReader) (uint64, error) {
|
||||
return gas - gasUsage, makeErr(cc, s.GetState(account, slot))
|
||||
},
|
||||
}
|
||||
hooks.Register(t)
|
||||
|
|
@ -309,7 +312,7 @@ func TestCanCreateContract(t *testing.T) {
|
|||
origin := rng.Address()
|
||||
caller := rng.Address()
|
||||
value := rng.Hash()
|
||||
code := rng.Bytes(8)
|
||||
code := []byte{byte(vm.STOP)}
|
||||
salt := rng.Hash()
|
||||
|
||||
create := crypto.CreateAddress(caller, 0)
|
||||
|
|
@ -323,14 +326,14 @@ func TestCanCreateContract(t *testing.T) {
|
|||
{
|
||||
name: "Create",
|
||||
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),
|
||||
},
|
||||
{
|
||||
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[:]))
|
||||
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),
|
||||
},
|
||||
|
|
@ -342,8 +345,10 @@ func TestCanCreateContract(t *testing.T) {
|
|||
state.SetState(account, slot, value)
|
||||
evm.TxContext.Origin = origin
|
||||
|
||||
_, _, _, err := tt.create(evm)
|
||||
_, _, gasRemaining, err := tt.create(evm)
|
||||
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.
|
||||
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
|
||||
// limit.
|
||||
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) {
|
||||
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
|
||||
snapshot := evm.StateDB.Snapshot()
|
||||
evm.StateDB.CreateAccount(address)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ func Register[C params.ChainConfigHooks, R params.RulesHooks](tb testing.TB, ext
|
|||
type Stub struct {
|
||||
PrecompileOverrides map[common.Address]libevm.PrecompiledContract
|
||||
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
|
||||
|
|
@ -63,11 +63,11 @@ func (s Stub) CanExecuteTransaction(from common.Address, to *common.Address, sr
|
|||
|
||||
// CanCreateContract proxies arguments to the s.CanCreateContractFn function if
|
||||
// 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 {
|
||||
return f(cc, sr)
|
||||
return f(cc, gas, sr)
|
||||
}
|
||||
return nil
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
var _ interface {
|
||||
|
|
|
|||
|
|
@ -105,11 +105,12 @@ func (r RulesExtra) PrecompileOverride(addr common.Address) (_ libevm.Precompile
|
|||
// CanCreateContract implements the required [params.RuleHooks] method. Access
|
||||
// to state allows it to be configured on-chain however this is an optional
|
||||
// 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
|
||||
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
|
||||
|
|
|
|||
|
|
@ -32,7 +32,9 @@ type RulesHooks interface {
|
|||
// RulesAllowlistHooks are a subset of [RulesHooks] that gate actions, signalled
|
||||
// by returning a nil (allowed) or non-nil (blocked) error.
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -71,9 +73,10 @@ func (NOOPHooks) CanExecuteTransaction(_ common.Address, _ *common.Address, _ li
|
|||
return nil
|
||||
}
|
||||
|
||||
// CanCreateContract allows all (otherwise valid) contract deployment.
|
||||
func (NOOPHooks) CanCreateContract(*libevm.AddressContext, libevm.StateReader) error {
|
||||
return nil
|
||||
// CanCreateContract allows all (otherwise valid) contract deployment, not
|
||||
// consuming any more gas.
|
||||
func (NOOPHooks) CanCreateContract(_ *libevm.AddressContext, gas uint64, _ libevm.StateReader) (uint64, error) {
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
// PrecompileOverride instructs the EVM interpreter to use the default
|
||||
|
|
|
|||
Loading…
Reference in a new issue