mirror of
https://github.com/filecoin-project/lotus.git
synced 2025-08-06 16:41:19 +08:00

decode the cbor return value for reverts and present that, as is expected by Ethereum tooling
216 lines
7.2 KiB
Go
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
|
|
}
|