Files
lotus/api/api_errors.go
Rod Vagg e9393dfe8c fix(eth): present revert "data" as plain bytes
decode the cbor return value for reverts and present that, as is expected by
Ethereum tooling
2024-11-07 16:29:22 +11:00

216 lines
7.2 KiB
Go

package api
import (
"errors"
"fmt"
"reflect"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/exitcode"
)
var invalidExecutionRevertedMsg = xerrors.New("invalid execution reverted error")
const (
EOutOfGas = iota + jsonrpc.FirstUserCode
EActorNotFound
EF3Disabled
EF3ParticipationTicketInvalid
EF3ParticipationTicketExpired
EF3ParticipationIssuerMismatch
EF3ParticipationTooManyInstances
EF3ParticipationTicketStartBeforeExisting
EF3NotReady
EExecutionReverted
ENullRound
)
var (
RPCErrors = jsonrpc.NewErrors()
// ErrF3Disabled signals that F3 consensus process is disabled.
ErrF3Disabled = &errF3Disabled{}
// ErrF3ParticipationTicketInvalid signals that F3ParticipationTicket cannot be decoded.
ErrF3ParticipationTicketInvalid = &errF3ParticipationTicketInvalid{}
// ErrF3ParticipationTicketExpired signals that the current GPBFT instance as surpassed the expiry of the ticket.
ErrF3ParticipationTicketExpired = &errF3ParticipationTicketExpired{}
// ErrF3ParticipationIssuerMismatch signals that the ticket is not issued by the current node.
ErrF3ParticipationIssuerMismatch = &errF3ParticipationIssuerMismatch{}
// ErrF3ParticipationTooManyInstances signals that participation ticket cannot be
// issued because it asks for too many instances.
ErrF3ParticipationTooManyInstances = &errF3ParticipationTooManyInstances{}
// ErrF3ParticipationTicketStartBeforeExisting signals that participation ticket
// is before the start instance of an existing lease held by the miner.
ErrF3ParticipationTicketStartBeforeExisting = &errF3ParticipationTicketStartBeforeExisting{}
// ErrF3NotReady signals that the F3 instance isn't ready for participation yet. The caller
// should back off and try again later.
ErrF3NotReady = &errF3NotReady{}
_ error = (*ErrOutOfGas)(nil)
_ error = (*ErrActorNotFound)(nil)
_ error = (*errF3Disabled)(nil)
_ error = (*errF3ParticipationTicketInvalid)(nil)
_ error = (*errF3ParticipationTicketExpired)(nil)
_ error = (*errF3ParticipationIssuerMismatch)(nil)
_ error = (*errF3NotReady)(nil)
_ error = (*ErrExecutionReverted)(nil)
_ jsonrpc.RPCErrorCodec = (*ErrExecutionReverted)(nil)
_ error = (*ErrNullRound)(nil)
_ jsonrpc.RPCErrorCodec = (*ErrNullRound)(nil)
)
func init() {
RPCErrors.Register(EOutOfGas, new(*ErrOutOfGas))
RPCErrors.Register(EActorNotFound, new(*ErrActorNotFound))
RPCErrors.Register(EF3Disabled, new(*errF3Disabled))
RPCErrors.Register(EF3ParticipationTicketInvalid, new(*errF3ParticipationTicketInvalid))
RPCErrors.Register(EF3ParticipationTicketExpired, new(*errF3ParticipationTicketExpired))
RPCErrors.Register(EF3ParticipationIssuerMismatch, new(*errF3ParticipationIssuerMismatch))
RPCErrors.Register(EF3ParticipationTooManyInstances, new(*errF3ParticipationTooManyInstances))
RPCErrors.Register(EF3ParticipationTicketStartBeforeExisting, new(*errF3ParticipationTicketStartBeforeExisting))
RPCErrors.Register(EF3NotReady, new(*errF3NotReady))
RPCErrors.Register(EExecutionReverted, new(*ErrExecutionReverted))
RPCErrors.Register(ENullRound, new(*ErrNullRound))
}
func ErrorIsIn(err error, errorTypes []error) bool {
for _, etype := range errorTypes {
tmp := reflect.New(reflect.PointerTo(reflect.ValueOf(etype).Elem().Type())).Interface()
if errors.As(err, tmp) {
return true
}
}
return false
}
// ErrOutOfGas signals that a call failed due to insufficient gas.
type ErrOutOfGas struct{}
func (ErrOutOfGas) Error() string { return "call ran out of gas" }
// ErrActorNotFound signals that the actor is not found.
type ErrActorNotFound struct{}
func (ErrActorNotFound) Error() string { return "actor not found" }
type errF3Disabled struct{}
func (errF3Disabled) Error() string { return "f3 is disabled" }
type errF3ParticipationTicketInvalid struct{}
func (errF3ParticipationTicketInvalid) Error() string { return "ticket is not valid" }
type errF3ParticipationTicketExpired struct{}
func (errF3ParticipationTicketExpired) Error() string { return "ticket has expired" }
type errF3ParticipationIssuerMismatch struct{}
func (errF3ParticipationIssuerMismatch) Error() string { return "issuer does not match current node" }
type errF3ParticipationTooManyInstances struct{}
func (errF3ParticipationTooManyInstances) Error() string { return "requested instance count too high" }
type errF3ParticipationTicketStartBeforeExisting struct{}
func (errF3ParticipationTicketStartBeforeExisting) Error() string {
return "ticket starts before existing lease"
}
type errF3NotReady struct{}
func (errF3NotReady) Error() string { return "f3 isn't yet ready to participate" }
// ErrExecutionReverted is used to return execution reverted with a reason for a revert in the `data` field.
type ErrExecutionReverted struct {
Message string
Data string
}
// Error returns the error message.
func (e *ErrExecutionReverted) Error() string { return e.Message }
// FromJSONRPCError converts a JSONRPCError to ErrExecutionReverted.
func (e *ErrExecutionReverted) FromJSONRPCError(jerr jsonrpc.JSONRPCError) error {
if jerr.Code != EExecutionReverted || jerr.Message == "" || jerr.Data == nil {
return invalidExecutionRevertedMsg
}
data, ok := jerr.Data.(string)
if !ok {
return xerrors.Errorf("expected string data in execution reverted error, got %T", jerr.Data)
}
e.Message = jerr.Message
e.Data = data
return nil
}
// ToJSONRPCError converts ErrExecutionReverted to a JSONRPCError.
func (e *ErrExecutionReverted) ToJSONRPCError() (jsonrpc.JSONRPCError, error) {
return jsonrpc.JSONRPCError{
Code: EExecutionReverted,
Message: e.Message,
Data: e.Data,
}, nil
}
// NewErrExecutionReverted creates a new ErrExecutionReverted with the given reason.
func NewErrExecutionReverted(exitCode exitcode.ExitCode, error, reason string, data []byte) *ErrExecutionReverted {
return &ErrExecutionReverted{
Message: fmt.Sprintf("message execution failed (exit=[%s], revert reason=[%s], vm error=[%s])", exitCode, reason, error),
Data: fmt.Sprintf("0x%x", data),
}
}
type ErrNullRound struct {
Epoch abi.ChainEpoch
Message string
}
func NewErrNullRound(epoch abi.ChainEpoch) *ErrNullRound {
return &ErrNullRound{
Epoch: epoch,
Message: fmt.Sprintf("requested epoch was a null round (%d)", epoch),
}
}
func (e *ErrNullRound) Error() string {
return e.Message
}
func (e *ErrNullRound) FromJSONRPCError(jerr jsonrpc.JSONRPCError) error {
if jerr.Code != ENullRound {
return fmt.Errorf("unexpected error code: %d", jerr.Code)
}
epoch, ok := jerr.Data.(float64)
if !ok {
return fmt.Errorf("expected number data in null round error, got %T", jerr.Data)
}
e.Epoch = abi.ChainEpoch(epoch)
e.Message = jerr.Message
return nil
}
func (e *ErrNullRound) ToJSONRPCError() (jsonrpc.JSONRPCError, error) {
return jsonrpc.JSONRPCError{
Code: ENullRound,
Message: e.Message,
Data: e.Epoch,
}, nil
}
// Is performs a non-strict type check, we only care if the target is an ErrNullRound
// and will ignore the contents (specifically there is no matching on Epoch).
func (e *ErrNullRound) Is(target error) bool {
_, ok := target.(*ErrNullRound)
return ok
}