Skip to content

Commit c5da3ca

Browse files
authored
refactor!: gas consumption for stateful precompiles (#26)
* refactor!: gas consumption for stateful precompiles * chore: remove receiver & arg names on `statefulPrecompile.RequiredGas()` * doc: `vm.statefulPrecompile`
1 parent 38eaaab commit c5da3ca

File tree

3 files changed

+27
-33
lines changed

3 files changed

+27
-33
lines changed

core/vm/contracts.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,7 @@ func (args *evmCallArgs) RunPrecompiledContract(p PrecompiledContract, input []b
174174
return nil, 0, ErrOutOfGas
175175
}
176176
suppliedGas -= gasCost
177-
output, err := args.run(p, input)
178-
return output, suppliedGas, err
177+
return args.run(p, input, suppliedGas)
179178
}
180179

181180
// ECRECOVER implemented as a native contract.

core/vm/contracts.libevm.go

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -50,42 +50,43 @@ const (
5050

5151
// run runs the [PrecompiledContract], differentiating between stateful and
5252
// regular types.
53-
func (args *evmCallArgs) run(p PrecompiledContract, input []byte) (ret []byte, err error) {
53+
func (args *evmCallArgs) run(p PrecompiledContract, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) {
5454
if p, ok := p.(statefulPrecompile); ok {
55-
return p.run(args, input)
55+
return p(args, input, suppliedGas)
5656
}
57-
return p.Run(input)
57+
// Gas consumption for regular precompiles was already handled by the native
58+
// RunPrecompiledContract(), which called this method.
59+
ret, err = p.Run(input)
60+
return ret, suppliedGas, err
5861
}
5962

60-
// PrecompiledStatefulRun is the stateful equivalent of the Run() method of a
63+
// PrecompiledStatefulContract is the stateful equivalent of a
6164
// [PrecompiledContract].
62-
type PrecompiledStatefulRun func(env PrecompileEnvironment, input []byte) ([]byte, error)
65+
type PrecompiledStatefulContract func(env PrecompileEnvironment, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error)
6366

6467
// NewStatefulPrecompile constructs a new PrecompiledContract that can be used
6568
// via an [EVM] instance but MUST NOT be called directly; a direct call to Run()
6669
// reserves the right to panic. See other requirements defined in the comments
6770
// on [PrecompiledContract].
68-
func NewStatefulPrecompile(run PrecompiledStatefulRun, requiredGas func([]byte) uint64) PrecompiledContract {
69-
return statefulPrecompile{
70-
gas: requiredGas,
71-
run: run,
72-
}
71+
func NewStatefulPrecompile(run PrecompiledStatefulContract) PrecompiledContract {
72+
return statefulPrecompile(run)
7373
}
7474

75-
type statefulPrecompile struct {
76-
gas func([]byte) uint64
77-
run PrecompiledStatefulRun
78-
}
75+
// statefulPrecompile implements the [PrecompiledContract] interface to allow a
76+
// [PrecompiledStatefulContract] to be carried with regular geth plumbing. The
77+
// methods are defined on this unexported type instead of directly on
78+
// [PrecompiledStatefulContract] to hide implementation details.
79+
type statefulPrecompile PrecompiledStatefulContract
7980

80-
func (p statefulPrecompile) RequiredGas(input []byte) uint64 {
81-
return p.gas(input)
82-
}
81+
// RequiredGas always returns zero as this gas is consumed by native geth code
82+
// before the contract is run.
83+
func (statefulPrecompile) RequiredGas([]byte) uint64 { return 0 }
8384

8485
func (p statefulPrecompile) Run([]byte) ([]byte, error) {
8586
// https://google.github.io/styleguide/go/best-practices.html#when-to-panic
8687
// This would indicate an API misuse and would occur in tests, not in
8788
// production.
88-
panic(fmt.Sprintf("BUG: call to %T.Run(); MUST call %T", p, p.run))
89+
panic(fmt.Sprintf("BUG: call to %T.Run(); MUST call %T itself", p, p))
8990
}
9091

9192
// A PrecompileEnvironment provides information about the context in which a

core/vm/contracts.libevm_test.go

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -93,23 +93,18 @@ func TestNewStatefulPrecompile(t *testing.T) {
9393
caller, self, stateVal, readOnly, input,
9494
))
9595
}
96-
run := func(env vm.PrecompileEnvironment, input []byte) ([]byte, error) {
96+
run := func(env vm.PrecompileEnvironment, input []byte, suppliedGas uint64) ([]byte, uint64, error) {
9797
if got, want := env.StateDB() != nil, !env.ReadOnly(); got != want {
98-
return nil, fmt.Errorf("PrecompileEnvironment().StateDB() must be non-nil i.f.f. not read-only; got non-nil? %t; want %t", got, want)
98+
return nil, 0, fmt.Errorf("PrecompileEnvironment().StateDB() must be non-nil i.f.f. not read-only; got non-nil? %t; want %t", got, want)
9999
}
100100

101101
addrs := env.Addresses()
102102
val := env.ReadOnlyState().GetState(precompile, slot)
103-
return makeOutput(addrs.Caller, addrs.Self, input, val, env.ReadOnly()), nil
103+
return makeOutput(addrs.Caller, addrs.Self, input, val, env.ReadOnly()), suppliedGas - gasCost, nil
104104
}
105105
hooks := &hookstest.Stub{
106106
PrecompileOverrides: map[common.Address]libevm.PrecompiledContract{
107-
precompile: vm.NewStatefulPrecompile(
108-
run,
109-
func(b []byte) uint64 {
110-
return gasCost
111-
},
112-
),
107+
precompile: vm.NewStatefulPrecompile(run),
113108
},
114109
}
115110
hooks.Register(t)
@@ -204,13 +199,12 @@ func TestInheritReadOnly(t *testing.T) {
204199
hooks := &hookstest.Stub{
205200
PrecompileOverrides: map[common.Address]libevm.PrecompiledContract{
206201
precompile: vm.NewStatefulPrecompile(
207-
func(env vm.PrecompileEnvironment, input []byte) ([]byte, error) {
202+
func(env vm.PrecompileEnvironment, input []byte, suppliedGas uint64) ([]byte, uint64, error) {
208203
if env.ReadOnly() {
209-
return []byte{ifReadOnly}, nil
204+
return []byte{ifReadOnly}, suppliedGas, nil
210205
}
211-
return []byte{ifNotReadOnly}, nil
206+
return []byte{ifNotReadOnly}, suppliedGas, nil
212207
},
213-
func([]byte) uint64 { return 0 },
214208
),
215209
},
216210
}

0 commit comments

Comments
 (0)