mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 06:06:44 +00:00
Merge pull request #38 from sei-protocol/trace-tx-panic-recover
Add defer recovery for failed txs when tracing
This commit is contained in:
commit
1c17bae15b
2 changed files with 76 additions and 1 deletions
|
|
@ -944,13 +944,20 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
|
||||||
// traceTx configures a new tracer according to the provided configuration, and
|
// traceTx configures a new tracer according to the provided configuration, and
|
||||||
// executes the given message in the provided environment. The return value will
|
// executes the given message in the provided environment. The return value will
|
||||||
// be tracer dependent.
|
// be tracer dependent.
|
||||||
func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *core.Message, txctx *Context, vmctx vm.BlockContext, statedb vm.StateDB, config *TraceConfig) (interface{}, error) {
|
func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *core.Message, txctx *Context, vmctx vm.BlockContext, statedb vm.StateDB, config *TraceConfig) (value interface{}, returnErr error) {
|
||||||
|
// Note that traceTx could panic, so we want to catch it and send a generic error message
|
||||||
var (
|
var (
|
||||||
tracer *Tracer
|
tracer *Tracer
|
||||||
err error
|
err error
|
||||||
timeout = defaultTraceTimeout
|
timeout = defaultTraceTimeout
|
||||||
usedGas uint64
|
usedGas uint64
|
||||||
)
|
)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
value = nil
|
||||||
|
returnErr = fmt.Errorf("panic occured: %v, could not trace tx: %s", r, tx.Hash())
|
||||||
|
}
|
||||||
|
}()
|
||||||
if config == nil {
|
if config == nil {
|
||||||
config = &TraceConfig{}
|
config = &TraceConfig{}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -457,6 +457,74 @@ func TestTraceTransaction(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTracePanicTransaction(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Initialize test accounts
|
||||||
|
accounts := newAccounts(2)
|
||||||
|
genesis := &core.Genesis{
|
||||||
|
Config: params.TestChainConfig,
|
||||||
|
Alloc: types.GenesisAlloc{
|
||||||
|
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
target := common.Hash{}
|
||||||
|
signer := types.HomesteadSigner{}
|
||||||
|
backend := newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) {
|
||||||
|
// Transfer from account[0] to account[1]
|
||||||
|
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
|
||||||
|
Nonce: uint64(i),
|
||||||
|
To: &accounts[1].addr,
|
||||||
|
Value: big.NewInt(1000),
|
||||||
|
Gas: params.TxGas,
|
||||||
|
GasPrice: b.BaseFee(),
|
||||||
|
Data: nil}),
|
||||||
|
signer, accounts[0].key)
|
||||||
|
b.AddTx(tx)
|
||||||
|
target = tx.Hash()
|
||||||
|
})
|
||||||
|
defer backend.chain.Stop()
|
||||||
|
|
||||||
|
// Create API with a backend that uses a panic-inducing StateDB
|
||||||
|
panicBackend := &panicBackend{backend}
|
||||||
|
api := NewAPI(panicBackend)
|
||||||
|
|
||||||
|
result, err := api.TraceTransaction(context.Background(), target, nil)
|
||||||
|
|
||||||
|
// Verify panic was caught and handled
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Expected error from panic recovery, got nil")
|
||||||
|
}
|
||||||
|
if result != nil {
|
||||||
|
t.Errorf("Expected nil result after panic, got %v", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type panicBackend struct {
|
||||||
|
Backend
|
||||||
|
}
|
||||||
|
|
||||||
|
// StateAtTransaction overrides the backend's StateAtTransaction to use a panic-inducing StateDB
|
||||||
|
func (b *panicBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, vm.StateDB, StateReleaseFunc, error) {
|
||||||
|
tx, vmctx, _, release, err := b.Backend.StateAtTransaction(ctx, block, txIndex, reexec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, vm.BlockContext{}, nil, nil, err
|
||||||
|
}
|
||||||
|
// Return a StateDB that panics on ApplyTransactionWithEVM
|
||||||
|
return tx, vmctx, &panicStateDB{}, release, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// panicStateDB is a mock StateDB that panics during ApplyTransactionWithEVM
|
||||||
|
type panicStateDB struct {
|
||||||
|
vm.StateDB
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyTransactionWithEVM is overridden to panic
|
||||||
|
func (s *panicStateDB) ApplyTransactionWithEVM(message *core.Message, config *params.ChainConfig, gasPool *core.GasPool, statedb vm.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) ([]byte, error) {
|
||||||
|
panic("intentional panic for testing")
|
||||||
|
}
|
||||||
|
|
||||||
func TestTraceBlock(t *testing.T) {
|
func TestTraceBlock(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue