Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: RulesHooks.CanCreateContract() accepts and returns gas #28

Merged
merged 5 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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