Skip to content

Commit 6a8fb2e

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

File tree

3 files changed

+170
-8
lines changed

3 files changed

+170
-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

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

libevm/legacy/stubs_test.go

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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/holiman/uint256"
26+
27+
"github.com/ava-labs/libevm/common"
28+
"github.com/ava-labs/libevm/core/types"
29+
"github.com/ava-labs/libevm/core/vm"
30+
"github.com/ava-labs/libevm/libevm"
31+
"github.com/ava-labs/libevm/params"
32+
)
33+
34+
var _ vm.PrecompileEnvironment = (*stubPrecompileEnvironment)(nil)
35+
36+
// stubPrecompileEnvironment implements [vm.PrecompileEnvironment] for testing.
37+
type stubPrecompileEnvironment struct {
38+
gasToReturn uint64
39+
gasUsed uint64
40+
}
41+
42+
// Gas returns the gas supplied to the precompile.
43+
func (s *stubPrecompileEnvironment) Gas() uint64 {
44+
return s.gasToReturn
45+
}
46+
47+
// UseGas records the gas used by the precompile.
48+
func (s *stubPrecompileEnvironment) UseGas(gas uint64) bool {
49+
s.gasUsed += gas
50+
return true
51+
}
52+
53+
func (s *stubPrecompileEnvironment) Call(addr common.Address, input []byte, gas uint64, value *uint256.Int, _ ...vm.CallOption) (ret []byte, _ error) {
54+
return nil, nil
55+
}
56+
57+
func (s *stubPrecompileEnvironment) ChainConfig() *params.ChainConfig { return nil }
58+
func (s *stubPrecompileEnvironment) Rules() params.Rules { return params.Rules{} }
59+
func (s *stubPrecompileEnvironment) StateDB() vm.StateDB { return nil }
60+
func (s *stubPrecompileEnvironment) ReadOnlyState() libevm.StateReader { return nil }
61+
func (s *stubPrecompileEnvironment) IncomingCallType() vm.CallType { return vm.Call }
62+
func (s *stubPrecompileEnvironment) Addresses() *libevm.AddressContext { return nil }
63+
func (s *stubPrecompileEnvironment) ReadOnly() bool { return false }
64+
func (s *stubPrecompileEnvironment) Value() *uint256.Int { return nil }
65+
func (s *stubPrecompileEnvironment) BlockHeader() (h types.Header, err error) { return h, nil }
66+
func (s *stubPrecompileEnvironment) BlockNumber() *big.Int { return nil }
67+
func (s *stubPrecompileEnvironment) BlockTime() uint64 { return 0 }

0 commit comments

Comments
 (0)