## Description Closes: #13830 #13827 - Rosetta `/network/status` response: * populates `oldest_block_identifier` with `EarliestBlockHeight` from rpc syncInfo * tries to populate `genesis_block_identifier` with block of height 1 * if block is not queryable, sets value of `InitialHeight` from genesis doc * if not queryable sets same value as `oldest_block_identifier` - Wrap `github.com/tendermint/tendermint/rpc/jsonrpc/types` errors to rosetta errors - Split services into its own go files (account, block, mempool, network) --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [x] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#pr-targeting)) - [x] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/main/docs/building-modules) - [x] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#testing) - [x] added a changelog entry to `CHANGELOG.md` - [x] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
163 lines
6.6 KiB
Go
163 lines
6.6 KiB
Go
package errors
|
|
|
|
// errors.go contains all the errors returned by the adapter implementation
|
|
// plus some extra utilities to parse those errors
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
grpccodes "google.golang.org/grpc/codes"
|
|
grpcstatus "google.golang.org/grpc/status"
|
|
|
|
"github.com/coinbase/rosetta-sdk-go/types"
|
|
tmtypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
|
|
)
|
|
|
|
// ListErrors lists all the registered errors
|
|
func ListErrors() []*types.Error {
|
|
return registry.list()
|
|
}
|
|
|
|
// SealAndListErrors seals the registry and lists its errors
|
|
func SealAndListErrors() []*types.Error {
|
|
registry.seal()
|
|
return registry.list()
|
|
}
|
|
|
|
// Error defines an error that can be converted to a Rosetta API error.
|
|
type Error struct {
|
|
rosErr *types.Error
|
|
}
|
|
|
|
func (e *Error) Error() string {
|
|
if e.rosErr == nil {
|
|
return ErrUnknown.Error()
|
|
}
|
|
return fmt.Sprintf("rosetta: (%d) %s", e.rosErr.Code, e.rosErr.Message)
|
|
}
|
|
|
|
// Is implements errors.Is for *Error, two errors are considered equal
|
|
// if their error codes are identical
|
|
func (e *Error) Is(err error) bool {
|
|
// assert it can be casted
|
|
rosErr, ok := err.(*Error)
|
|
if rosErr == nil || !ok {
|
|
return false
|
|
}
|
|
// check that both *Error's are correctly initialized to avoid dereference panics
|
|
if rosErr.rosErr == nil || e.rosErr == nil {
|
|
return false
|
|
}
|
|
// messages are equal if their error codes match
|
|
return rosErr.rosErr.Code == e.rosErr.Code
|
|
}
|
|
|
|
// WrapError wraps the rosetta error with additional context
|
|
func WrapError(err *Error, msg string) *Error {
|
|
return &Error{rosErr: &types.Error{
|
|
Code: err.rosErr.Code,
|
|
Message: err.rosErr.Message,
|
|
Description: err.rosErr.Description,
|
|
Retriable: err.rosErr.Retriable,
|
|
Details: map[string]interface{}{
|
|
"info": msg,
|
|
},
|
|
}}
|
|
}
|
|
|
|
// ToRosetta attempts to converting an error into a rosetta
|
|
// error, if the error cannot be converted it will be parsed as unknown
|
|
func ToRosetta(err error) *types.Error {
|
|
// if it's null or not known
|
|
rosErr, ok := err.(*Error)
|
|
if rosErr == nil || !ok {
|
|
tmErr, ok := err.(*tmtypes.RPCError)
|
|
if tmErr != nil && ok {
|
|
return fromTendermintToRosettaError(tmErr).rosErr
|
|
}
|
|
return ToRosetta(WrapError(ErrUnknown, ErrUnknown.Error()))
|
|
}
|
|
return rosErr.rosErr
|
|
}
|
|
|
|
// fromTendermintToRosettaError converts a tendermint jsonrpc error to rosetta error
|
|
func fromTendermintToRosettaError(err *tmtypes.RPCError) *Error {
|
|
return &Error{rosErr: &types.Error{
|
|
Code: int32(err.Code),
|
|
Message: err.Message,
|
|
Details: map[string]interface{}{
|
|
"info": err.Data,
|
|
},
|
|
}}
|
|
}
|
|
|
|
// FromGRPCToRosettaError converts a gRPC error to rosetta error
|
|
func FromGRPCToRosettaError(err error) *Error {
|
|
status, ok := grpcstatus.FromError(err)
|
|
if !ok {
|
|
return WrapError(ErrUnknown, err.Error())
|
|
}
|
|
switch status.Code() {
|
|
case grpccodes.NotFound:
|
|
return WrapError(ErrNotFound, status.Message())
|
|
case grpccodes.FailedPrecondition:
|
|
return WrapError(ErrBadArgument, status.Message())
|
|
case grpccodes.InvalidArgument:
|
|
return WrapError(ErrBadArgument, status.Message())
|
|
case grpccodes.Internal:
|
|
return WrapError(ErrInternal, status.Message())
|
|
default:
|
|
return WrapError(ErrUnknown, status.Message())
|
|
}
|
|
}
|
|
|
|
func RegisterError(code int32, message string, retryable bool, description string) *Error {
|
|
e := &Error{rosErr: &types.Error{
|
|
Code: code,
|
|
Message: message,
|
|
Description: &description,
|
|
Retriable: retryable,
|
|
Details: nil,
|
|
}}
|
|
registry.add(e)
|
|
return e
|
|
}
|
|
|
|
// Default error list
|
|
var (
|
|
// ErrUnknown defines an unknown error, if this is returned it means
|
|
// the library is ignoring an error
|
|
ErrUnknown = RegisterError(0, "unknown", false, "unknown error")
|
|
// ErrOffline is returned when there is an attempt to query an endpoint in offline mode
|
|
ErrOffline = RegisterError(1, "cannot query endpoint in offline mode", false, "returned when querying an online endpoint in offline mode")
|
|
// ErrNetworkNotSupported is returned when there is an attempt to query a network which is not supported
|
|
ErrNetworkNotSupported = RegisterError(2, "network is not supported", false, "returned when querying a non supported network")
|
|
// ErrCodec is returned when there's an error while marshalling or unmarshalling data
|
|
ErrCodec = RegisterError(3, "encode/decode error", true, "returned when there are errors encoding or decoding information to and from the node")
|
|
// ErrInvalidOperation is returned when the operation supplied to rosetta is not a valid one
|
|
ErrInvalidOperation = RegisterError(4, "invalid operation", false, "returned when the operation is not valid")
|
|
// ErrInvalidTransaction is returned when the provided hex bytes of a TX are not valid
|
|
ErrInvalidTransaction = RegisterError(5, "invalid transaction", false, "returned when the transaction is invalid")
|
|
// ErrInvalidAddress is returned when the byte of the address are bad
|
|
ErrInvalidAddress = RegisterError(7, "invalid address", false, "returned when the address is malformed")
|
|
// ErrInvalidPubkey is returned when the public key is invalid
|
|
ErrInvalidPubkey = RegisterError(8, "invalid pubkey", false, "returned when the public key is invalid")
|
|
// ErrInterpreting is returned when there are errors interpreting the data from the node, most likely related to breaking changes, version incompatibilities
|
|
ErrInterpreting = RegisterError(9, "error interpreting data from node", false, "returned when there are issues interpreting requests or response from node")
|
|
ErrInvalidMemo = RegisterError(11, "invalid memo", false, "returned when the memo is invalid")
|
|
// ErrBadArgument is returned when the request is malformed
|
|
ErrBadArgument = RegisterError(400, "bad argument", false, "request is malformed")
|
|
// ErrNotFound is returned when the required object was not found
|
|
// retry is set to true because something that is not found now
|
|
// might be found later, example: a TX
|
|
ErrNotFound = RegisterError(404, "not found", true, "returned when the node does not find what the client is asking for")
|
|
// ErrInternal is returned when the node is experiencing internal errors
|
|
ErrInternal = RegisterError(500, "internal error", false, "returned when the node experiences internal errors")
|
|
// ErrBadGateway is returned when there are problems interacting with the nodes
|
|
ErrBadGateway = RegisterError(502, "bad gateway", true, "return when the node is unreachable")
|
|
// ErrNotImplemented is returned when a method is not implemented yet
|
|
ErrNotImplemented = RegisterError(14, "not implemented", false, "returned when querying an endpoint which is not implemented")
|
|
// ErrUnsupportedCurve is returned when the curve specified is not supported
|
|
ErrUnsupportedCurve = RegisterError(15, "unsupported curve, expected secp256k1", false, "returned when using an unsupported crypto curve")
|
|
)
|