Skip to content

Commit c130fa0

Browse files
committed
fix(libevm/legacy): allow to use all the gas in PrecompiledStatefulContract
1 parent 8d288b7 commit c130fa0

File tree

3 files changed

+166
-8
lines changed

3 files changed

+166
-8
lines changed

libevm/legacy/legacy.go

+3-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2024 the libevm authors.
1+
// Copyright 2024-2025 the libevm authors.
22
//
33
// The libevm additions to go-ethereum are free software: you can redistribute
44
// them and/or modify them under the terms of the GNU Lesser General Public License
@@ -19,7 +19,6 @@
1919
package legacy
2020

2121
import (
22-
"errors"
2322
"fmt"
2423

2524
"github.com/ava-labs/libevm/core/vm"
@@ -31,19 +30,15 @@ import (
3130
// gas-management methods as this may result in unexpected behaviour.
3231
type PrecompiledStatefulContract func(env vm.PrecompileEnvironment, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error)
3332

34-
var (
35-
ErrGasRemainingExceedsGasSupplied = errors.New("remaining gas exceeds supplied gas")
36-
)
37-
3833
// Upgrade converts the legacy precompile signature into the now-required form.
3934
func (c PrecompiledStatefulContract) Upgrade() vm.PrecompiledStatefulContract {
4035
return func(env vm.PrecompileEnvironment, input []byte) ([]byte, error) {
4136
gas := env.Gas()
4237
ret, remainingGas, err := c(env, input, gas)
4338
if remainingGas > gas {
44-
return nil, fmt.Errorf("%w: %d > %d", ErrGasRemainingExceedsGasSupplied, remainingGas, gas)
39+
return nil, fmt.Errorf("remaining gas %d exceeds supplied gas %d", remainingGas, gas)
4540
}
46-
if used := gas - remainingGas; used < gas {
41+
if used := gas - remainingGas; used <= gas {
4742
env.UseGas(used)
4843
}
4944
return ret, err

libevm/legacy/legacy_test.go

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package legacy
2+
3+
import (
4+
"errors"
5+
"testing"
6+
7+
"github.com/ava-labs/libevm/core/vm"
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func TestPrecompiledStatefulContract_Upgrade(t *testing.T) {
12+
t.Parallel()
13+
14+
testCases := map[string]struct {
15+
env *stubPrecompileEnvironment
16+
input []byte
17+
cRet []byte
18+
cRemainingGas uint64
19+
cErr error
20+
wantRet []byte
21+
wantErr string
22+
wantEnv *stubPrecompileEnvironment
23+
}{
24+
"call_error": {
25+
env: &stubPrecompileEnvironment{
26+
gasToReturn: 10,
27+
},
28+
input: []byte{1},
29+
cRet: []byte{2},
30+
cRemainingGas: 6,
31+
cErr: errors.New("test error"),
32+
wantRet: []byte{2},
33+
wantErr: "test error",
34+
wantEnv: &stubPrecompileEnvironment{
35+
gasToReturn: 10,
36+
gasUsed: 4,
37+
},
38+
},
39+
"remaining_gas_exceeds_supplied_gas": {
40+
env: &stubPrecompileEnvironment{
41+
gasToReturn: 10,
42+
},
43+
input: []byte{1},
44+
cRet: []byte{2},
45+
cRemainingGas: 11,
46+
wantErr: "remaining gas 11 exceeds supplied gas 10",
47+
wantEnv: &stubPrecompileEnvironment{
48+
gasToReturn: 10,
49+
gasUsed: 0,
50+
},
51+
},
52+
"zero_remaining_gas": {
53+
env: &stubPrecompileEnvironment{
54+
gasToReturn: 10,
55+
},
56+
input: []byte{1},
57+
cRet: []byte{2},
58+
wantRet: []byte{2},
59+
wantEnv: &stubPrecompileEnvironment{
60+
gasToReturn: 10,
61+
gasUsed: 10,
62+
},
63+
},
64+
"used_one_gas": {
65+
env: &stubPrecompileEnvironment{
66+
gasToReturn: 10,
67+
},
68+
input: []byte{1},
69+
cRet: []byte{2},
70+
cRemainingGas: 9,
71+
wantRet: []byte{2},
72+
wantEnv: &stubPrecompileEnvironment{
73+
gasToReturn: 10,
74+
gasUsed: 1,
75+
},
76+
},
77+
}
78+
79+
for name, testCase := range testCases {
80+
t.Run(name, func(t *testing.T) {
81+
c := PrecompiledStatefulContract(func(env vm.PrecompileEnvironment, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) {
82+
return testCase.cRet, testCase.cRemainingGas, testCase.cErr
83+
})
84+
85+
upgraded := c.Upgrade()
86+
87+
ret, err := upgraded(testCase.env, testCase.input)
88+
if testCase.wantErr == "" {
89+
assert.NoError(t, err)
90+
} else {
91+
assert.EqualError(t, err, testCase.wantErr)
92+
}
93+
assert.Equal(t, testCase.wantRet, ret)
94+
assert.Equal(t, testCase.wantEnv, testCase.env)
95+
})
96+
}
97+
}

libevm/legacy/stubs_test.go

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright 2025 the libevm authors.
2+
//
3+
// The libevm additions to go-ethereum are free software: you can redistribute
4+
// them and/or modify them under the terms of the GNU Lesser General Public License
5+
// as published by the Free Software Foundation, either version 3 of the License,
6+
// or (at your option) any later version.
7+
//
8+
// The libevm additions are distributed in the hope that they will be useful,
9+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
11+
// General Public License for more details.
12+
//
13+
// You should have received a copy of the GNU Lesser General Public License
14+
// along with the go-ethereum library. If not, see
15+
// <http://www.gnu.org/licenses/>.
16+
17+
// Package legacy provides converters between legacy types and their refactored
18+
// equivalents.
19+
20+
package legacy
21+
22+
import (
23+
"math/big"
24+
25+
"github.com/ava-labs/libevm/common"
26+
"github.com/ava-labs/libevm/core/types"
27+
"github.com/ava-labs/libevm/core/vm"
28+
"github.com/ava-labs/libevm/libevm"
29+
"github.com/ava-labs/libevm/params"
30+
"github.com/holiman/uint256"
31+
)
32+
33+
var _ vm.PrecompileEnvironment = (*stubPrecompileEnvironment)(nil)
34+
35+
// stubPrecompileEnvironment implements [vm.PrecompileEnvironment] for testing.
36+
type stubPrecompileEnvironment struct {
37+
gasToReturn uint64
38+
gasUsed uint64
39+
}
40+
41+
// Gas returns the gas supplied to the precompile.
42+
func (s *stubPrecompileEnvironment) Gas() uint64 {
43+
return s.gasToReturn
44+
}
45+
46+
// UseGas records the gas used by the precompile.
47+
func (s *stubPrecompileEnvironment) UseGas(gas uint64) bool {
48+
s.gasUsed += gas
49+
return true
50+
}
51+
52+
func (s *stubPrecompileEnvironment) Call(addr common.Address, input []byte, gas uint64, value *uint256.Int, _ ...vm.CallOption) (ret []byte, _ error) {
53+
return nil, nil
54+
}
55+
56+
func (s *stubPrecompileEnvironment) ChainConfig() *params.ChainConfig { return nil }
57+
func (s *stubPrecompileEnvironment) Rules() params.Rules { return params.Rules{} }
58+
func (s *stubPrecompileEnvironment) StateDB() vm.StateDB { return nil }
59+
func (s *stubPrecompileEnvironment) ReadOnlyState() libevm.StateReader { return nil }
60+
func (s *stubPrecompileEnvironment) IncomingCallType() vm.CallType { return vm.Call }
61+
func (s *stubPrecompileEnvironment) Addresses() *libevm.AddressContext { return nil }
62+
func (s *stubPrecompileEnvironment) ReadOnly() bool { return false }
63+
func (s *stubPrecompileEnvironment) Value() *uint256.Int { return nil }
64+
func (s *stubPrecompileEnvironment) BlockHeader() (h types.Header, err error) { return h, nil }
65+
func (s *stubPrecompileEnvironment) BlockNumber() *big.Int { return nil }
66+
func (s *stubPrecompileEnvironment) BlockTime() uint64 { return 0 }

0 commit comments

Comments
 (0)