Skip to content

Commit

Permalink
Introduce rpc.ErrTransactionExecutionError
Browse files Browse the repository at this point in the history
  • Loading branch information
omerfirmak committed Nov 29, 2023
1 parent 3417dbe commit 6e23e06
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 19 deletions.
35 changes: 30 additions & 5 deletions rpc/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ var (
ErrInvalidContinuationToken = &jsonrpc.Error{Code: 33, Message: "Invalid continuation token"}
ErrTooManyKeysInFilter = &jsonrpc.Error{Code: 34, Message: "Too many keys provided in a filter"}
ErrContractError = &jsonrpc.Error{Code: 40, Message: "Contract error"}
ErrTransactionExecutionError = &jsonrpc.Error{Code: 41, Message: "Transaction execution error"}
ErrInvalidContractClass = &jsonrpc.Error{Code: 50, Message: "Invalid contract class"}
ErrClassAlreadyDeclared = &jsonrpc.Error{Code: 51, Message: "Class already declared"}
ErrInternal = &jsonrpc.Error{Code: jsonrpc.InternalError, Message: "Internal error"}
Expand Down Expand Up @@ -1221,14 +1222,28 @@ func (h *Handler) Call(call FunctionCall, id BlockID) ([]*felt.Felt, *jsonrpc.Er
return res, nil
}

type ContractErrorData struct {
RevertError string `json:"revert_error"`
}

func makeContractError(err error) *jsonrpc.Error {
return ErrContractError.CloneWithData(struct {
RevertError string `json:"revert_error"`
}{
return ErrContractError.CloneWithData(ContractErrorData{
RevertError: err.Error(),
})
}

type TransactionExecutionErrorData struct {
TransactionIndex uint64 `json:"transaction_index"`
ExecutionError string `json:"execution_error"`
}

func makeTransactionExecutionError(err *vm.TransactionExecutionError) *jsonrpc.Error {
return ErrTransactionExecutionError.CloneWithData(TransactionExecutionErrorData{
TransactionIndex: err.Index,
ExecutionError: err.Cause.Error(),
})
}

func (h *Handler) TransactionStatus(ctx context.Context, hash felt.Felt) (*TransactionStatus, *jsonrpc.Error) {
receipt, txErr := h.TransactionReceiptByHash(hash)
switch txErr {
Expand Down Expand Up @@ -1318,6 +1333,10 @@ func (h *Handler) EstimateMessageFee(msg MsgFromL1, id BlockID) (*FeeEstimate, *
}
estimates, rpcErr := h.EstimateFee([]BroadcastedTransaction{tx}, nil, id)
if rpcErr != nil {
if rpcErr.Code == ErrTransactionExecutionError.Code {
data := rpcErr.Data.(TransactionExecutionErrorData)
return nil, makeContractError(errors.New(data.ExecutionError))
}
return nil, rpcErr
}
return &estimates[0], nil
Expand Down Expand Up @@ -1437,7 +1456,11 @@ func (h *Handler) simulateTransactions(id BlockID, transactions []BroadcastedTra
if errors.Is(err, utils.ErrResourceBusy) {
return nil, ErrInternal.CloneWithData(err.Error())
}
return nil, makeContractError(err)
var txnExecutionError vm.TransactionExecutionError
if errors.As(err, &txnExecutionError) {
return nil, makeTransactionExecutionError(&txnExecutionError)
}
return nil, ErrUnexpectedError.CloneWithData(err.Error())
}

var result []SimulatedTransaction
Expand Down Expand Up @@ -1574,7 +1597,9 @@ func (h *Handler) traceBlockTransactions(ctx context.Context, block *core.Block,
if errors.Is(err, utils.ErrResourceBusy) {
return nil, ErrInternal.CloneWithData(err.Error())
}
return nil, makeContractError(err)
// Since we are tracing an existing block, we know that there should be no errors during execution. If we encounter any,
// report them as unexpected errors
return nil, ErrUnexpectedError.CloneWithData(err.Error())
}

var result []TracedBlockTransaction
Expand Down
34 changes: 20 additions & 14 deletions rpc/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
adaptfeeder "github.com/NethermindEth/juno/starknetdata/feeder"
"github.com/NethermindEth/juno/sync"
"github.com/NethermindEth/juno/utils"
"github.com/NethermindEth/juno/vm"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -2937,35 +2938,40 @@ func TestSimulateTransactions(t *testing.T) {
log := utils.NewNopZapLogger()
handler := rpc.New(mockReader, nil, network, nil, nil, mockVM, "", log)

//nolint:dupl
t.Run("ok with zero values, skip fee", func(t *testing.T) {
mockState := mocks.NewMockStateHistoryReader(mockCtrl)

mockReader.EXPECT().HeadState().Return(mockState, nopCloser, nil)
mockReader.EXPECT().HeadsHeader().Return(&core.Header{}, nil)
mockState := mocks.NewMockStateHistoryReader(mockCtrl)
mockReader.EXPECT().HeadState().Return(mockState, nopCloser, nil).AnyTimes()
mockReader.EXPECT().HeadsHeader().Return(&core.Header{}, nil).AnyTimes()
sequencerAddress := core.NetworkBlockHashMetaInfo(network).FallBackSequencerAddress

sequencerAddress := core.NetworkBlockHashMetaInfo(network).FallBackSequencerAddress
t.Run("ok with zero values, skip fee", func(t *testing.T) {
mockVM.EXPECT().Execute(nil, nil, uint64(0), uint64(0), sequencerAddress, mockState, network, []*felt.Felt{}, true, false, nil, nil, false).
Return([]*felt.Felt{}, []json.RawMessage{}, nil)

_, err := handler.SimulateTransactions(rpc.BlockID{Latest: true}, []rpc.BroadcastedTransaction{}, []rpc.SimulationFlag{rpc.SkipFeeChargeFlag})
require.Nil(t, err)
})

//nolint:dupl
t.Run("ok with zero values, skip validate", func(t *testing.T) {
mockState := mocks.NewMockStateHistoryReader(mockCtrl)

mockReader.EXPECT().HeadState().Return(mockState, nopCloser, nil)
mockReader.EXPECT().HeadsHeader().Return(&core.Header{}, nil)

sequencerAddress := core.NetworkBlockHashMetaInfo(network).FallBackSequencerAddress
mockVM.EXPECT().Execute(nil, nil, uint64(0), uint64(0), sequencerAddress, mockState, network, []*felt.Felt{}, false, true, nil, nil, false).
Return([]*felt.Felt{}, []json.RawMessage{}, nil)

_, err := handler.SimulateTransactions(rpc.BlockID{Latest: true}, []rpc.BroadcastedTransaction{}, []rpc.SimulationFlag{rpc.SkipValidateFlag})
require.Nil(t, err)
})

t.Run("transaction execution error", func(t *testing.T) {
mockVM.EXPECT().Execute(nil, nil, uint64(0), uint64(0), sequencerAddress, mockState, network, []*felt.Felt{}, false, true, nil, nil, false).
Return(nil, nil, vm.TransactionExecutionError{
Index: 44,
Cause: errors.New("oops"),
})

_, err := handler.SimulateTransactions(rpc.BlockID{Latest: true}, []rpc.BroadcastedTransaction{}, []rpc.SimulationFlag{rpc.SkipValidateFlag})
require.Equal(t, rpc.ErrTransactionExecutionError.CloneWithData(rpc.TransactionExecutionErrorData{
TransactionIndex: 44,
ExecutionError: "oops",
}), err)
})
}

func TestTraceBlockTransactions(t *testing.T) {
Expand Down

0 comments on commit 6e23e06

Please sign in to comment.