Skip to content

Commit 08a8bef

Browse files
committed
Problem: stateful precompiles have many boilerplate code
Solution: - refactor the precompiles code with (hopefully) equivalent transformations. changelog cleanup handle balance handler error return cleanup avoid naked return fix lint fix werc20 fix staking precompile fix panic recovery return error fix staking test case NewPrecompile don't return error
1 parent ec57b96 commit 08a8bef

File tree

28 files changed

+417
-618
lines changed

28 files changed

+417
-618
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
- [\#352](https://github.com/cosmos/evm/pull/352) Remove the creation of a Geth EVM instance, stateDB during the AnteHandler balance check.
2222
- [\#496](https://github.com/cosmos/evm/pull/496) Simplify mempool instantiation by using configs instead of objects.
2323
- [\#511](https://github.com/cosmos/evm/pull/511) Minor code cleanup for `AddPrecompileFn`.
24+
- [\#577](https://github.com/cosmos/evm/pull/577) Cleanup precompiles boilerplate code.
2425

2526
### FEATURES
2627

evmd/precompiles.go

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -101,65 +101,47 @@ func NewAvailableStaticPrecompiles(
101101
panic(fmt.Errorf("failed to instantiate bech32 precompile: %w", err))
102102
}
103103

104-
stakingPrecompile, err := stakingprecompile.NewPrecompile(
104+
stakingPrecompile := stakingprecompile.NewPrecompile(
105105
stakingKeeper,
106106
stakingkeeper.NewMsgServerImpl(&stakingKeeper),
107107
stakingkeeper.NewQuerier(&stakingKeeper),
108108
bankKeeper,
109109
options.AddressCodec,
110110
)
111-
if err != nil {
112-
panic(fmt.Errorf("failed to instantiate staking precompile: %w", err))
113-
}
114111

115-
distributionPrecompile, err := distprecompile.NewPrecompile(
112+
distributionPrecompile := distprecompile.NewPrecompile(
116113
distributionKeeper,
117114
distributionkeeper.NewMsgServerImpl(distributionKeeper),
118115
distributionkeeper.NewQuerier(distributionKeeper),
119116
stakingKeeper,
120117
bankKeeper,
121118
options.AddressCodec,
122119
)
123-
if err != nil {
124-
panic(fmt.Errorf("failed to instantiate distribution precompile: %w", err))
125-
}
126120

127-
ibcTransferPrecompile, err := ics20precompile.NewPrecompile(
121+
ibcTransferPrecompile := ics20precompile.NewPrecompile(
128122
bankKeeper,
129123
stakingKeeper,
130124
transferKeeper,
131125
channelKeeper,
132126
)
133-
if err != nil {
134-
panic(fmt.Errorf("failed to instantiate ICS20 precompile: %w", err))
135-
}
136127

137-
bankPrecompile, err := bankprecompile.NewPrecompile(bankKeeper, erc20Keeper)
138-
if err != nil {
139-
panic(fmt.Errorf("failed to instantiate bank precompile: %w", err))
140-
}
128+
bankPrecompile := bankprecompile.NewPrecompile(bankKeeper, erc20Keeper)
141129

142-
govPrecompile, err := govprecompile.NewPrecompile(
130+
govPrecompile := govprecompile.NewPrecompile(
143131
govkeeper.NewMsgServerImpl(&govKeeper),
144132
govkeeper.NewQueryServer(&govKeeper),
145133
bankKeeper,
146134
codec,
147135
options.AddressCodec,
148136
)
149-
if err != nil {
150-
panic(fmt.Errorf("failed to instantiate gov precompile: %w", err))
151-
}
152137

153-
slashingPrecompile, err := slashingprecompile.NewPrecompile(
138+
slashingPrecompile := slashingprecompile.NewPrecompile(
154139
slashingKeeper,
155140
slashingkeeper.NewMsgServerImpl(slashingKeeper),
156141
bankKeeper,
157142
options.ValidatorAddrCodec,
158143
options.ConsensusAddrCodec,
159144
)
160-
if err != nil {
161-
panic(fmt.Errorf("failed to instantiate slashing precompile: %w", err))
162-
}
163145

164146
// Stateless precompiles
165147
precompiles[bech32Precompile.Address()] = bech32Precompile

evmd/tests/ibc/ics20_precompile_transfer_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,14 @@ func (suite *ICS20TransferTestSuite) SetupTest() {
4646
suite.chainB = suite.coordinator.GetChain(evmibctesting.GetEvmChainID(2))
4747

4848
evmAppA := suite.chainA.App.(*evmd.EVMD)
49-
suite.chainAPrecompile, _ = ics20.NewPrecompile(
49+
suite.chainAPrecompile = ics20.NewPrecompile(
5050
evmAppA.BankKeeper,
5151
*evmAppA.StakingKeeper,
5252
evmAppA.TransferKeeper,
5353
evmAppA.IBCKeeper.ChannelKeeper,
5454
)
5555
evmAppB := suite.chainB.App.(*evmd.EVMD)
56-
suite.chainBPrecompile, _ = ics20.NewPrecompile(
56+
suite.chainBPrecompile = ics20.NewPrecompile(
5757
evmAppB.BankKeeper,
5858
*evmAppB.StakingKeeper,
5959
evmAppB.TransferKeeper,

evmd/tests/ibc/v2_ics20_precompile_transfer_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,14 @@ func (suite *ICS20TransferV2TestSuite) SetupTest() {
4747
suite.chainB = suite.coordinator.GetChain(evmibctesting.GetEvmChainID(2))
4848

4949
evmAppA := suite.chainA.App.(*evmd.EVMD)
50-
suite.chainAPrecompile, _ = ics20.NewPrecompile(
50+
suite.chainAPrecompile = ics20.NewPrecompile(
5151
evmAppA.BankKeeper,
5252
*evmAppA.StakingKeeper,
5353
evmAppA.TransferKeeper,
5454
evmAppA.IBCKeeper.ChannelKeeper,
5555
)
5656
evmAppB := suite.chainB.App.(*evmd.EVMD)
57-
suite.chainBPrecompile, _ = ics20.NewPrecompile(
57+
suite.chainBPrecompile = ics20.NewPrecompile(
5858
evmAppB.BankKeeper,
5959
*evmAppB.StakingKeeper,
6060
evmAppB.TransferKeeper,

precompiles/bank/bank.go

Lines changed: 33 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ import (
1111

1212
"github.com/ethereum/go-ethereum/accounts/abi"
1313
"github.com/ethereum/go-ethereum/common"
14-
"github.com/ethereum/go-ethereum/core/tracing"
1514
"github.com/ethereum/go-ethereum/core/vm"
1615

1716
cmn "github.com/cosmos/evm/precompiles/common"
1817
evmtypes "github.com/cosmos/evm/x/vm/types"
1918

2019
storetypes "cosmossdk.io/store/types"
20+
21+
sdk "github.com/cosmos/cosmos-sdk/types"
2122
)
2223

2324
const (
@@ -31,16 +32,32 @@ const (
3132
GasSupplyOf = 2_477
3233
)
3334

34-
var _ vm.PrecompiledContract = &Precompile{}
35+
var (
36+
_ vm.PrecompiledContract = &Precompile{}
37+
_ cmn.NativeExecutor = &Precompile{}
38+
)
3539

36-
// Embed abi json file to the executable binary. Needed when importing as dependency.
37-
//
38-
//go:embed abi.json
39-
var f embed.FS
40+
var (
41+
// Embed abi json file to the executable binary. Needed when importing as dependency.
42+
//
43+
//go:embed abi.json
44+
f embed.FS
45+
ABI abi.ABI
46+
)
47+
48+
func init() {
49+
var err error
50+
ABI, err = cmn.LoadABI(f, "abi.json")
51+
if err != nil {
52+
panic(err)
53+
}
54+
}
4055

4156
// Precompile defines the bank precompile
4257
type Precompile struct {
4358
cmn.Precompile
59+
60+
abi.ABI
4461
bankKeeper cmn.BankKeeper
4562
erc20Keeper cmn.ERC20Keeper
4663
}
@@ -50,28 +67,21 @@ type Precompile struct {
5067
func NewPrecompile(
5168
bankKeeper cmn.BankKeeper,
5269
erc20Keeper cmn.ERC20Keeper,
53-
) (*Precompile, error) {
54-
newABI, err := cmn.LoadABI(f, "abi.json")
55-
if err != nil {
56-
return nil, err
57-
}
58-
70+
) *Precompile {
5971
// NOTE: we set an empty gas configuration to avoid extra gas costs
6072
// during the run execution
6173
p := &Precompile{
6274
Precompile: cmn.Precompile{
63-
ABI: newABI,
6475
KvGasConfig: storetypes.GasConfig{},
6576
TransientKVGasConfig: storetypes.GasConfig{},
77+
ContractAddress: common.HexToAddress(evmtypes.BankPrecompileAddress),
6678
},
79+
ABI: ABI,
6780
bankKeeper: bankKeeper,
6881
erc20Keeper: erc20Keeper,
6982
}
70-
71-
// SetAddress defines the address of the bank compile contract.
72-
p.SetAddress(common.HexToAddress(evmtypes.BankPrecompileAddress))
73-
74-
return p, nil
83+
p.Executor = p
84+
return p
7585
}
7686

7787
// RequiredGas calculates the precompiled contract's base gas rate.
@@ -101,17 +111,14 @@ func (p Precompile) RequiredGas(input []byte) uint64 {
101111
return 0
102112
}
103113

104-
// Run executes the precompiled contract bank query methods defined in the ABI.
105-
func (p Precompile) Run(evm *vm.EVM, contract *vm.Contract, readOnly bool) (bz []byte, err error) {
106-
ctx, _, method, initialGas, args, err := p.RunSetup(evm, contract, readOnly, p.IsTransaction)
114+
// Execute executes the precompiled contract bank query methods defined in the ABI.
115+
func (p Precompile) Execute(ctx sdk.Context, evm *vm.EVM, contract *vm.Contract, readOnly bool) ([]byte, error) {
116+
method, args, err := cmn.SetupABI(p.ABI, contract, readOnly, p.IsTransaction)
107117
if err != nil {
108118
return nil, err
109119
}
110120

111-
// This handles any out of gas errors that may occur during the execution of a precompile query.
112-
// It avoids panics and returns the out of gas error so the EVM can continue gracefully.
113-
defer cmn.HandleGasError(ctx, contract, initialGas, &err)()
114-
121+
var bz []byte
115122
switch method.Name {
116123
// Bank queries
117124
case BalancesMethod:
@@ -124,17 +131,7 @@ func (p Precompile) Run(evm *vm.EVM, contract *vm.Contract, readOnly bool) (bz [
124131
return nil, fmt.Errorf(cmn.ErrUnknownMethod, method.Name)
125132
}
126133

127-
if err != nil {
128-
return nil, err
129-
}
130-
131-
cost := ctx.GasMeter().GasConsumed() - initialGas
132-
133-
if !contract.UseGas(cost, nil, tracing.GasChangeCallPrecompiledContract) {
134-
return nil, vm.ErrOutOfGas
135-
}
136-
137-
return bz, nil
134+
return bz, err
138135
}
139136

140137
// IsTransaction checks if the given method name corresponds to a transaction or query.

precompiles/bech32/bech32.go

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,21 @@ import (
1414

1515
var _ vm.PrecompiledContract = &Precompile{}
1616

17-
// Embed abi json file to the executable binary. Needed when importing as dependency.
18-
//
19-
//go:embed abi.json
20-
var f embed.FS
17+
var (
18+
// Embed abi json file to the executable binary. Needed when importing as dependency.
19+
//
20+
//go:embed abi.json
21+
f embed.FS
22+
ABI abi.ABI
23+
)
24+
25+
func init() {
26+
var err error
27+
ABI, err = cmn.LoadABI(f, "abi.json")
28+
if err != nil {
29+
panic(err)
30+
}
31+
}
2132

2233
// Precompile defines the precompiled contract for Bech32 encoding.
2334
type Precompile struct {
@@ -28,17 +39,12 @@ type Precompile struct {
2839
// NewPrecompile creates a new bech32 Precompile instance as a
2940
// PrecompiledContract interface.
3041
func NewPrecompile(baseGas uint64) (*Precompile, error) {
31-
newABI, err := cmn.LoadABI(f, "abi.json")
32-
if err != nil {
33-
return nil, err
34-
}
35-
3642
if baseGas == 0 {
3743
return nil, fmt.Errorf("baseGas cannot be zero")
3844
}
3945

4046
return &Precompile{
41-
ABI: newABI,
47+
ABI: ABI,
4248
baseGas: baseGas,
4349
}, nil
4450
}

0 commit comments

Comments
 (0)