fix error codes

This commit is contained in:
Sina Mahmoodi 2023-10-26 18:05:43 +02:00
parent ed378218a6
commit f5ac0393e2
4 changed files with 109 additions and 48 deletions

View file

@ -1232,7 +1232,7 @@ func (s *BlockChainAPI) MulticallV1(ctx context.Context, opts mcOpts, blockNrOrH
if len(opts.BlockStateCalls) == 0 {
return nil, &invalidParamsError{message: "empty input"}
} else if len(opts.BlockStateCalls) > maxMulticallBlocks {
return nil, &invalidParamsError{message: "too many blocks"}
return nil, &clientLimitExceededError{message: "too many blocks"}
}
if blockNrOrHash == nil {
n := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)

View file

@ -1092,10 +1092,6 @@ func TestMulticallV1(t *testing.T) {
From: &randomAccounts[1].addr,
To: &randomAccounts[2].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
}, {
From: &randomAccounts[3].addr,
To: &randomAccounts[2].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
},
},
}},
@ -1125,12 +1121,45 @@ func TestMulticallV1(t *testing.T) {
GasUsed: "0x5208",
Logs: []log{},
Status: "0x1",
}, {
}},
}},
}, {
// insufficient funds
name: "insufficient-funds",
tag: latest,
blocks: []mcBlock{{
Calls: []TransactionArgs{{
From: &randomAccounts[0].addr,
To: &randomAccounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
}},
}},
want: nil,
expectErr: &invalidTxError{Message: fmt.Sprintf("err: insufficient funds for gas * price + value: address %s have 0 want 1000 (supplied gas 4712388)", randomAccounts[0].addr.String()), Code: errCodeInsufficientFunds},
}, {
// EVM error
name: "evm-error",
tag: latest,
blocks: []mcBlock{{
StateOverrides: &StateOverride{
randomAccounts[2].addr: OverrideAccount{Code: hex2Bytes("f3")},
},
Calls: []TransactionArgs{{
From: &randomAccounts[0].addr,
To: &randomAccounts[2].addr,
}},
}},
want: []blockRes{{
Number: "0xb",
GasLimit: "0x47e7c4",
GasUsed: "0x47e7c4",
FeeRecipient: coinbase,
Calls: []callRes{{
ReturnValue: "0x",
GasUsed: "0x0",
Error: callErr{Message: "stack underflow (0 <=> 2)", Code: errCodeVMError},
GasUsed: "0x47e7c4",
Logs: []log{},
Status: "0x0",
Error: callErr{Message: fmt.Sprintf("err: insufficient funds for gas * price + value: address %s have 0 want 1000 (supplied gas 4691388)", randomAccounts[3].addr.String()), Code: errCodeInsufficientFunds},
}},
}},
}, {
@ -1219,7 +1248,7 @@ func TestMulticallV1(t *testing.T) {
}},
}},
want: []blockRes{},
expectErr: errors.New("block numbers must be in order"),
expectErr: &invalidBlockNumberError{message: fmt.Sprintf("block numbers must be in order: 11 <= 12")},
},
// Test on solidity storage example. Set value in one call, read in next.
{
@ -1558,19 +1587,8 @@ func TestMulticallV1(t *testing.T) {
}},
}},
validation: &validation,
want: []blockRes{{
Number: "0xb",
GasLimit: "0x47e7c4",
GasUsed: "0x0",
FeeRecipient: coinbase,
Calls: []callRes{{
ReturnValue: "0x",
GasUsed: "0x0",
Logs: []log{},
Status: "0x0",
Error: callErr{Message: fmt.Sprintf("err: nonce too high: address %s, tx: 2 state: 0 (supplied gas 4712388)", accounts[2].addr), Code: errCodeNonceTooHigh},
}},
}},
want: nil,
expectErr: &invalidTxError{Message: fmt.Sprintf("err: nonce too high: address %s, tx: 2 state: 0 (supplied gas 4712388)", accounts[2].addr), Code: errCodeNonceTooHigh},
},
// Clear storage.
{

View file

@ -27,43 +27,60 @@ type callError struct {
Code int `json:"code"`
}
type invalidTxError struct {
Message string `json:"message"`
Code int `json:"code"`
}
func (e *invalidTxError) Error() string { return e.Message }
func (e *invalidTxError) ErrorCode() int { return e.Code }
const (
errCodeNonceTooHigh = -38011
errCodeNonceTooLow = -38010
errCodeInsufficientFunds = -38014
errCodeIntrinsicGas = -38013
errCodeInternalError = -32603
errCodeInvalidParams = -32602
errCodeNonceTooHigh = -38011
errCodeNonceTooLow = -38010
errCodeIntrinsicGas = -38013
errCodeInsufficientFunds = -38014
errCodeBlockGasLimitReached = -38015
errCodeBlockNumberInvalid = -38020
errCodeBlockTimestampInvalid = -38021
errCodeSenderIsNotEOA = -38024
errCodeMaxInitCodeSizeExceeded = -38025
errCodeClientLimitExceeded = -38026
errCodeInternalError = -32603
errCodeInvalidParams = -32602
errCodeReverted = -32000
errCodeVMError = -32015
)
func callErrorFromError(err error) *callError {
func txValidationError(err error) *invalidTxError {
if err == nil {
return nil
}
switch {
case errors.Is(err, core.ErrNonceTooHigh):
return &callError{Message: err.Error(), Code: errCodeNonceTooHigh}
return &invalidTxError{Message: err.Error(), Code: errCodeNonceTooHigh}
case errors.Is(err, core.ErrNonceTooLow):
return &callError{Message: err.Error(), Code: errCodeNonceTooLow}
return &invalidTxError{Message: err.Error(), Code: errCodeNonceTooLow}
case errors.Is(err, core.ErrSenderNoEOA):
// TODO
return &invalidTxError{Message: err.Error(), Code: errCodeSenderIsNotEOA}
case errors.Is(err, core.ErrFeeCapVeryHigh):
return &callError{Message: err.Error(), Code: errCodeInvalidParams}
return &invalidTxError{Message: err.Error(), Code: errCodeInvalidParams}
case errors.Is(err, core.ErrTipVeryHigh):
return &callError{Message: err.Error(), Code: errCodeInvalidParams}
return &invalidTxError{Message: err.Error(), Code: errCodeInvalidParams}
case errors.Is(err, core.ErrTipAboveFeeCap):
return &callError{Message: err.Error(), Code: errCodeInvalidParams}
return &invalidTxError{Message: err.Error(), Code: errCodeInvalidParams}
case errors.Is(err, core.ErrFeeCapTooLow):
// TODO
return &callError{Message: err.Error(), Code: errCodeInvalidParams}
return &invalidTxError{Message: err.Error(), Code: errCodeInvalidParams}
case errors.Is(err, core.ErrInsufficientFunds):
return &callError{Message: err.Error(), Code: errCodeInsufficientFunds}
return &invalidTxError{Message: err.Error(), Code: errCodeInsufficientFunds}
case errors.Is(err, core.ErrIntrinsicGas):
return &callError{Message: err.Error(), Code: errCodeIntrinsicGas}
return &invalidTxError{Message: err.Error(), Code: errCodeIntrinsicGas}
case errors.Is(err, core.ErrInsufficientFundsForTransfer):
return &callError{Message: err.Error(), Code: errCodeInsufficientFunds}
return &invalidTxError{Message: err.Error(), Code: errCodeInsufficientFunds}
case errors.Is(err, core.ErrMaxInitCodeSizeExceeded):
return &invalidTxError{Message: err.Error(), Code: errCodeMaxInitCodeSizeExceeded}
}
return &callError{
return &invalidTxError{
Message: err.Error(),
Code: errCodeInternalError,
}
@ -73,3 +90,23 @@ type invalidParamsError struct{ message string }
func (e *invalidParamsError) Error() string { return e.message }
func (e *invalidParamsError) ErrorCode() int { return errCodeInvalidParams }
type clientLimitExceededError struct{ message string }
func (e *clientLimitExceededError) Error() string { return e.message }
func (e *clientLimitExceededError) ErrorCode() int { return errCodeClientLimitExceeded }
type invalidBlockNumberError struct{ message string }
func (e *invalidBlockNumberError) Error() string { return e.message }
func (e *invalidBlockNumberError) ErrorCode() int { return errCodeBlockNumberInvalid }
type invalidBlockTimestampError struct{ message string }
func (e *invalidBlockTimestampError) Error() string { return e.message }
func (e *invalidBlockTimestampError) ErrorCode() int { return errCodeBlockTimestampInvalid }
type blockGasLimitReachedError struct{ message string }
func (e *blockGasLimitReachedError) Error() string { return e.message }
func (e *blockGasLimitReachedError) ErrorCode() int { return errCodeBlockGasLimitReached }

View file

@ -164,6 +164,13 @@ func (mc *multicall) execute(ctx context.Context, opts mcOpts) ([]mcBlockResult,
nonce := state.GetNonce(call.from())
call.Nonce = (*hexutil.Uint64)(&nonce)
}
var gas uint64
if call.Gas != nil {
gas = uint64(*call.Gas)
}
if gasUsed+gas > blockContext.GasLimit {
return nil, &blockGasLimitReachedError{fmt.Sprintf("block gas limit reached: %d >= %d", gasUsed, blockContext.GasLimit)}
}
// Let the call run wild unless explicitly specified.
if call.Gas == nil {
remaining := blockContext.GasLimit - gasUsed
@ -187,9 +194,8 @@ func (mc *multicall) execute(ctx context.Context, opts mcOpts) ([]mcBlockResult,
}
result, err := applyMessage(ctx, mc.b, call, state, header, timeout, gp, &blockContext, vmConfig, precompiles, opts.Validation)
if err != nil {
callErr := callErrorFromError(err)
callResults[i] = mcCallResult{Error: callErr, Status: hexutil.Uint64(types.ReceiptStatusFailed)}
continue
txErr := txValidationError(err)
return nil, txErr
}
// If the result contains a revert reason, try to unpack it.
if len(result.Revert()) > 0 {
@ -200,9 +206,9 @@ func (mc *multicall) execute(ctx context.Context, opts mcOpts) ([]mcBlockResult,
if result.Failed() {
callRes.Status = hexutil.Uint64(types.ReceiptStatusFailed)
if errors.Is(result.Err, vm.ErrExecutionReverted) {
callRes.Error = &callError{Message: result.Err.Error(), Code: -32000}
callRes.Error = &callError{Message: result.Err.Error(), Code: errCodeReverted}
} else {
callRes.Error = &callError{Message: result.Err.Error(), Code: -32015}
callRes.Error = &callError{Message: result.Err.Error(), Code: errCodeVMError}
}
} else {
callRes.Status = hexutil.Uint64(types.ReceiptStatusSuccessful)
@ -327,7 +333,7 @@ func makeHeaders(config *params.ChainConfig, blocks []mcBlock, base *types.Heade
n := new(big.Int).Add(big.NewInt(int64(prevNumber)), big.NewInt(1))
overrides.Number = (*hexutil.Big)(n)
} else if overrides.Number.ToInt().Uint64() <= prevNumber {
return nil, fmt.Errorf("block numbers must be in order")
return nil, &invalidBlockNumberError{fmt.Sprintf("block numbers must be in order: %d <= %d", overrides.Number.ToInt().Uint64(), prevNumber)}
}
prevNumber = overrides.Number.ToInt().Uint64()
@ -335,7 +341,7 @@ func makeHeaders(config *params.ChainConfig, blocks []mcBlock, base *types.Heade
t := prevTimestamp + 1
overrides.Time = (*hexutil.Uint64)(&t)
} else if time := (*uint64)(overrides.Time); *time <= prevTimestamp {
return nil, fmt.Errorf("timestamps must be in order")
return nil, &invalidBlockTimestampError{fmt.Sprintf("block timestamps must be in order: %d <= %d", *time, prevTimestamp)}
}
prevTimestamp = uint64(*overrides.Time)