Skip to content

Commit dba2420

Browse files
jewei1997philipsu522
authored andcommitted
Avoid panic tx error message in debug trace (#2057)
* checkpoint * fix * fix
1 parent de5a748 commit dba2420

File tree

6 files changed

+127
-7
lines changed

6 files changed

+127
-7
lines changed

contracts/test/EVMPrecompileTest.js

+43-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const fs = require('fs');
44
const path = require('path');
55

66
const { expectRevert } = require('@openzeppelin/test-helpers');
7-
const { setupSigners, getAdmin, deployWasm, storeWasm, execute, isDocker, ABI, createTokenFactoryTokenAndMint, getSeiBalance} = require("./lib");
7+
const { setupSigners, getAdmin, deployWasm, storeWasm, execute, isDocker, ABI, createTokenFactoryTokenAndMint, getSeiBalance, rawHttpDebugTraceWithCallTracer} = require("./lib");
88

99

1010
describe("EVM Precompile Tester", function () {
@@ -17,6 +17,32 @@ describe("EVM Precompile Tester", function () {
1717
admin = await getAdmin();
1818
})
1919

20+
describe("EVM Bank Precompile Tester", function () {
21+
const BankPrecompileContract = '0x0000000000000000000000000000000000001001';
22+
let bank;
23+
24+
before(async function () {
25+
const signer = accounts[0].signer
26+
const contractABIPath = '../../precompiles/bank/abi.json';
27+
const contractABI = require(contractABIPath);
28+
// Get a contract instance
29+
bank = new ethers.Contract(BankPrecompileContract, contractABI, signer);
30+
});
31+
32+
it("Fails with 'execution reverted' not 'panic occurred' when insufficient gas is provided", async function () {
33+
try {
34+
const bankSendTx = await bank.sendNative(accounts[1].seiAddress, {value: 1, gasLimit: 40000});
35+
await bankSendTx.wait();
36+
} catch (error) {
37+
const txHash = error.receipt.hash
38+
// should not get "panic occurred"
39+
const trace = await rawHttpDebugTraceWithCallTracer(txHash)
40+
expect(trace.result.error).to.not.include("panic")
41+
expect(trace.result.error).to.include("execution reverted")
42+
}
43+
});
44+
});
45+
2046
describe("EVM Addr Precompile Tester", function () {
2147
const AddrPrecompileContract = '0x0000000000000000000000000000000000001004';
2248
let addr;
@@ -72,6 +98,22 @@ describe("EVM Precompile Tester", function () {
7298
const seiAddr = await addr.getSeiAddr(unassociatedWallet.address);
7399
expect(seiAddr).to.not.be.null;
74100
});
101+
102+
it("Fails with 'execution reverted' not 'panic occurred' when insufficient gas is provided", async function () {
103+
const unassociatedWallet = hre.ethers.Wallet.createRandom();
104+
try {
105+
// provide less than gas than needed to execute the transaction
106+
const associatedAddrs = await addr.associatePubKey(unassociatedWallet.publicKey.slice(2), {gasLimit: 52000});
107+
await associatedAddrs.wait();
108+
expect.fail("Expected an error here since we provided insufficient gas");
109+
} catch (error) {
110+
const txHash = error.receipt.hash
111+
// should not get "panic occurred"
112+
const trace = await rawHttpDebugTraceWithCallTracer(txHash)
113+
expect(trace.result.error).to.not.include("panic");
114+
expect(trace.result.error).to.include("execution reverted");
115+
}
116+
});
75117
});
76118

77119
describe("EVM Gov Precompile Tester", function () {

contracts/test/lib.js

+39
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const { exec } = require("child_process");
22
const {ethers} = require("hardhat"); // Importing exec from child_process
3+
const axios = require("axios");
34

45
const adminKeyName = "admin"
56

@@ -198,6 +199,19 @@ async function incrementPointerVersion(provider, pointerType, offset) {
198199
}
199200
}
200201

202+
async function rawHttpDebugTraceWithCallTracer(txHash) {
203+
const payload = {
204+
jsonrpc: "2.0",
205+
method: "debug_traceTransaction",
206+
params: [txHash, {"tracer": "callTracer"}], // The second parameter is an optional trace config object
207+
id: 1,
208+
};
209+
const response = await axios.post("http://localhost:8545", payload, {
210+
headers: { "Content-Type": "application/json" },
211+
});
212+
return response.data;
213+
}
214+
201215
async function createTokenFactoryTokenAndMint(name, amount, recipient, from=adminKeyName) {
202216
const command = `seid tx tokenfactory create-denom ${name} --from ${from} --gas=5000000 --fees=1000000usei -y --broadcast-mode block -o json`
203217
const output = await execute(command);
@@ -211,6 +225,28 @@ async function createTokenFactoryTokenAndMint(name, amount, recipient, from=admi
211225
return token_denom
212226
}
213227

228+
async function getChainId() {
229+
const nodeUrl = 'http://localhost:8545';
230+
const response = await axios.post(nodeUrl, {
231+
method: 'eth_chainId',
232+
params: [],
233+
id: 1,
234+
jsonrpc: "2.0"
235+
})
236+
return response.data.result;
237+
}
238+
239+
async function getGasPrice() {
240+
const nodeUrl = 'http://localhost:8545';
241+
const response = await axios.post(nodeUrl, {
242+
method: 'eth_gasPrice',
243+
params: [],
244+
id: 1,
245+
jsonrpc: "2.0"
246+
})
247+
return response.data.result;
248+
}
249+
214250
async function getPointerForNative(name) {
215251
const command = `seid query evm pointer NATIVE ${name} -o json`
216252
const output = await execute(command);
@@ -526,10 +562,13 @@ module.exports = {
526562
deployWasm,
527563
instantiateWasm,
528564
createTokenFactoryTokenAndMint,
565+
getChainId,
566+
getGasPrice,
529567
execute,
530568
getSeiAddress,
531569
getEvmAddress,
532570
queryWasm,
571+
rawHttpDebugTraceWithCallTracer,
533572
executeWasm,
534573
getAdmin,
535574
setupSigners,

precompiles/addr/addr.go

+6
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ func (p PrecompileExecutor) RequiredGas(input []byte, method *abi.Method) uint64
8989
}
9090

9191
func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, _ common.Address, _ common.Address, args []interface{}, value *big.Int, readOnly bool, _ *vm.EVM, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) {
92+
// Needed to catch gas meter panics
93+
defer func() {
94+
if r := recover(); r != nil {
95+
err = fmt.Errorf("execution reverted: %v", r)
96+
}
97+
}()
9298
switch method.Name {
9399
case GetSeiAddressMethod:
94100
return p.getSeiAddr(ctx, method, args, value)

precompiles/addr/addr_test.go

+26-6
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,12 @@ func TestAssociatePubKey(t *testing.T) {
4545
happyPathOutput, _ := associatePubKey.Outputs.Pack(targetSeiAddress.String(), targetEvmAddress)
4646

4747
type args struct {
48-
evm *vm.EVM
49-
caller common.Address
50-
pubKey string
51-
value *big.Int
52-
readOnly bool
48+
evm *vm.EVM
49+
caller common.Address
50+
pubKey string
51+
value *big.Int
52+
readOnly bool
53+
suppliedGas uint64
5354
}
5455
tests := []struct {
5556
name string
@@ -116,6 +117,21 @@ func TestAssociatePubKey(t *testing.T) {
116117
wantErr: true,
117118
wantErrMsg: fmt.Sprintf("address %s is already associated with evm address %s", callerSeiAddress, callerEvmAddress),
118119
},
120+
{
121+
name: "fails if insufficient gas provided",
122+
args: args{
123+
evm: &vm.EVM{
124+
StateDB: state.NewDBImpl(ctx, k, true),
125+
TxContext: vm.TxContext{Origin: callerEvmAddress},
126+
},
127+
caller: callerEvmAddress,
128+
pubKey: targetPubKeyHex,
129+
value: big.NewInt(0),
130+
suppliedGas: 1,
131+
},
132+
wantErr: true,
133+
wantErrMsg: "execution reverted: {ReadFlat}",
134+
},
119135
{
120136
name: "happy path - associates addresses if signature is correct",
121137
args: args{
@@ -141,7 +157,11 @@ func TestAssociatePubKey(t *testing.T) {
141157
require.Nil(t, err)
142158

143159
// Make the call to associate.
144-
ret, _, err := p.RunAndCalculateGas(tt.args.evm, tt.args.caller, tt.args.caller, append(p.GetExecutor().(*addr.PrecompileExecutor).AssociatePubKeyID, inputs...), 40000, tt.args.value, nil, tt.args.readOnly, false)
160+
suppliedGas := uint64(40000)
161+
if tt.args.suppliedGas != 0 {
162+
suppliedGas = tt.args.suppliedGas
163+
}
164+
ret, _, err := p.RunAndCalculateGas(tt.args.evm, tt.args.caller, tt.args.caller, append(p.GetExecutor().(*addr.PrecompileExecutor).AssociatePubKeyID, inputs...), suppliedGas, tt.args.value, nil, tt.args.readOnly, false)
145165
if (err != nil) != tt.wantErr {
146166
t.Errorf("Run() error = %v, wantErr %v %v", err, tt.wantErr, string(ret))
147167
return

precompiles/bank/bank.go

+6
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,12 @@ func (p PrecompileExecutor) RequiredGas(input []byte, method *abi.Method) uint64
103103
}
104104

105105
func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller common.Address, callingContract common.Address, args []interface{}, value *big.Int, readOnly bool, evm *vm.EVM, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) {
106+
// Needed to catch gas meter panics
107+
defer func() {
108+
if r := recover(); r != nil {
109+
err = fmt.Errorf("execution reverted: %v", r)
110+
}
111+
}()
106112
switch method.Name {
107113
case SendMethod:
108114
return p.send(ctx, caller, method, args, value, readOnly)

precompiles/oracle/oracle.go

+7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package oracle
22

33
import (
44
"embed"
5+
"fmt"
56
"math/big"
67

78
sdk "github.com/cosmos/cosmos-sdk/types"
@@ -79,6 +80,12 @@ func (p PrecompileExecutor) RequiredGas(input []byte, method *abi.Method) uint64
7980
}
8081

8182
func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller common.Address, callingContract common.Address, args []interface{}, value *big.Int, readOnly bool, evm *vm.EVM, suppliedGas uint64) (bz []byte, remainingGas uint64, err error) {
83+
// Needed to catch gas meter panics
84+
defer func() {
85+
if r := recover(); r != nil {
86+
err = fmt.Errorf("execution reverted: %v", r)
87+
}
88+
}()
8289
switch method.Name {
8390
case GetExchangeRatesMethod:
8491
return p.getExchangeRates(ctx, method, args, value)

0 commit comments

Comments
 (0)