Skip to content

Commit

Permalink
feat!: RulesHooks.CanCreateContract() accepts and returns gas (#28)
Browse files Browse the repository at this point in the history
* 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)
  • Loading branch information
ARR4N authored Sep 17, 2024
1 parent 58f3883 commit 38eaaab
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 21 deletions.
17 changes: 11 additions & 6 deletions core/vm/contracts.libevm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,20 +296,23 @@ 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)

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)
Expand All @@ -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),
},
Expand All @@ -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
})
}
}
17 changes: 13 additions & 4 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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)
Expand Down
8 changes: 4 additions & 4 deletions libevm/hookstest/stub.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
7 changes: 4 additions & 3 deletions params/example.libevm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 7 additions & 4 deletions params/hooks.libevm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 38eaaab

Please sign in to comment.