Skip to content

Commit

Permalink
neorpc: add error codes and response errors
Browse files Browse the repository at this point in the history
Close nspcc-dev#2248

Signed-off-by: Tatiana Nesterenko <tatiana@nspcc.io>
  • Loading branch information
tatiana-nspcc committed Jul 17, 2023
1 parent e7a83cb commit cd0f574
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 43 deletions.
88 changes: 75 additions & 13 deletions pkg/neorpc/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,33 +30,95 @@ const (
const (
// RPCErrorCode is returned on RPC request processing error.
RPCErrorCode = -100
// RPCErrorUnknownBlockCode is returned on RPC request processing error: Unknown block
RPCErrorUnknownBlockCode = -101
// RPCErrorUnknownContractCode is returned on RPC request processing error: Unknown contract
RPCErrorUnknownContractCode = -102
// RPCErrorUnknownTransactionCode is returned on RPC request processing error: Unknown transaction
RPCErrorUnknownTransactionCode = -103
// RPCErrorUnknownStorageItemCode is returned on RPC request processing error: Unknown storage item
RPCErrorUnknownStorageItemCode = -104
// RPCErrorUnknownScriptContainerCode is returned on RPC request processing error: Unknown script container
RPCErrorUnknownScriptContainerCode = -105
// RPCErrorUnknownStateRootCode is returned on RPC request processing error: Unknown state root
RPCErrorUnknownStateRootCode = -106
// RPCErrorUnknownHeaderCode is returned on RPC request processing error: Unknown header
RPCErrorUnknownHeaderCode = -107
)

var (
// ErrInvalidParams represents a generic 'invalid parameters' error.
ErrInvalidParams = NewInvalidParamsError("invalid params")

// ErrUnknownBlock is returned if requested block is not found.
ErrUnknownBlock = NewError(RPCErrorCode, "Unknown block", "")
ErrUnknownBlock = NewError(RPCErrorUnknownBlockCode, "Unknown block", "")
// ErrUnknownContract is returned when requested unknown contract
ErrUnknownContract = NewError(RPCErrorUnknownContractCode, "Unknown contract", "")
// ErrUnknownTransaction is returned if requested transaction is not found.
ErrUnknownTransaction = NewError(RPCErrorCode, "Unknown transaction", "")
// ErrUnknownHeader is returned when requested header is not found.
ErrUnknownHeader = NewError(RPCErrorCode, "Unknown header", "")
ErrUnknownTransaction = NewError(RPCErrorUnknownTransactionCode, "Unknown transaction", "")
// ErrUnknownStorageItem is returned when requested unknown storage item
ErrUnknownStorageItem = NewError(RPCErrorUnknownStorageItemCode, "Unknown storage item", "")
// ErrUnknownScriptContainer is returned when requested block or transaction is not found.
ErrUnknownScriptContainer = NewError(RPCErrorCode, "Unknown script container", "")
ErrUnknownScriptContainer = NewError(RPCErrorUnknownScriptContainerCode, "Unknown script container", "")
// ErrUnknownStateRoot is returned when requested state root is not found.
ErrUnknownStateRoot = NewError(RPCErrorCode, "Unknown state root", "")
ErrUnknownStateRoot = NewError(RPCErrorUnknownStateRootCode, "Unknown state root", "")
// ErrUnknownHeader is returned when requested header is not found.
ErrUnknownHeader = NewError(RPCErrorUnknownHeaderCode, "Unknown header", "")

// ErrInvalidHeight represents SubmitError with code -201.
ErrInvalidHeight = NewSubmitError(-201, "Invalid height")
// ErrInvalidVerificationFunction represents SubmitError with code -202.
ErrInvalidVerificationFunction = NewSubmitError(-202, "Invalid verification function")

// ErrUnknown represents SubmitError with code -500.
ErrUnknown = NewSubmitError(-500, "Unclassified inventory verification error")
// ErrAlreadyExists represents SubmitError with code -501.
ErrAlreadyExists = NewSubmitError(-501, "Block or transaction already exists and cannot be sent repeatedly.")
ErrAlreadyExists = NewSubmitError(-501, "Inventory already exists on chain")
// ErrOutOfMemory represents SubmitError with code -502.
ErrOutOfMemory = NewSubmitError(-502, "The memory pool is full and no more transactions can be sent.")
ErrOutOfMemory = NewSubmitError(-502, "The memory pool is full and no more transactions can be sent")
// ErrUnableToVerify represents SubmitError with code -503.
ErrUnableToVerify = NewSubmitError(-503, "The block cannot be validated.")
ErrUnableToVerify = NewSubmitError(-503, "Transaction already exists in the memory pool")
// ErrValidationFailed represents SubmitError with code -504.
ErrValidationFailed = NewSubmitError(-504, "Block or transaction validation failed.")
ErrValidationFailed = NewSubmitError(-504, "Validation failed")
// ErrPolicyFail represents SubmitError with code -505.
ErrPolicyFail = NewSubmitError(-505, "One of the Policy filters failed.")
// ErrUnknown represents SubmitError with code -500.
ErrUnknown = NewSubmitError(-500, "Unknown error.")
ErrPolicyFail = NewSubmitError(-505, "One of the Policy filters failed")
// ErrInvalidScript represents SubmitError with code -506.
ErrInvalidScript = NewSubmitError(-506, "Invalid script")
// ErrTransactionAttr represents SubmitError with code -507.
ErrTransactionAttr = NewSubmitError(-507, "Invalid transaction attribute")
// ErrInvalidSignature represents SubmitError with code -508.
ErrInvalidSignature = NewSubmitError(-508, "Invalid signature")
// ErrInvalidSize represents SubmitError with code -509.
ErrInvalidSize = NewSubmitError(-509, "Invalid inventory size")
// ErrExpiredTransaction represents SubmitError with code -510.
ErrExpiredTransaction = NewSubmitError(-510, "Expired transaction")
// ErrInsufficientFunds represents SubmitError with code -511.
ErrInsufficientFunds = NewSubmitError(-511, "Insufficient funds")

// ErrOracleNotRun represents SubmitError with code -601.
ErrOracleNotRun = NewSubmitError(-601, "Oracle service is not running")
// ErrOracleFinished represents SubmitError with code -602.
ErrOracleFinished = NewSubmitError(-602, "Oracle request has already been finished")
// ErrOracleNotFound represents SubmitError with code -603.
ErrOracleNotFound = NewSubmitError(-603, "Oracle request is not found")
// ErrOracleNotDesignatedNode represents SubmitError with code -604.
ErrOracleNotDesignatedNode = NewSubmitError(-604, "Not a designated oracle node")

// ErrOldState represents SubmitError with code -701.
ErrOldState = NewSubmitError(-701, "Old state requests are not supported")
// ErrProof represents SubmitError with code -702.
ErrProof = NewSubmitError(-702, "Invalid proof")

// ErrNotarySvcNotRun represents SubmitError with code -801.
ErrNotarySvcNotRun = NewSubmitError(-801, "Notary service is not running")
// ErrNotaryFallback represents SubmitError with code -802.
ErrNotaryFallback = NewSubmitError(-802, "Invalid fallback transaction sender")
// ErrNotaryUnableToVerify represents SubmitError with code -803.
ErrNotaryUnableToVerify = NewSubmitError(-803, "Fallback transaction is valid after deposit is unlocked")
// ErrNotaryNode represents SubmitError with code -804.
ErrNotaryNode = NewSubmitError(-804, "Not a designated notary node")
// ErrNotarySignature represents SubmitError with code -805.
ErrNotarySignature = NewSubmitError(-805, "Invalid notary request signature")
)

// NewError is an Error constructor that takes Error contents from its parameters.
Expand Down
4 changes: 3 additions & 1 deletion pkg/services/rpcsrv/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ func getHTTPCodeForError(respErr *neorpc.Error) int {
switch respErr.Code {
case neorpc.BadRequestCode:
httpCode = http.StatusBadRequest
case neorpc.InvalidRequestCode, neorpc.RPCErrorCode, neorpc.InvalidParamsCode:
case neorpc.InvalidRequestCode, neorpc.RPCErrorCode, neorpc.InvalidParamsCode, neorpc.RPCErrorUnknownBlockCode,
neorpc.RPCErrorUnknownContractCode, neorpc.RPCErrorUnknownTransactionCode, neorpc.RPCErrorUnknownStorageItemCode,
neorpc.RPCErrorUnknownScriptContainerCode, neorpc.RPCErrorUnknownStateRootCode, neorpc.RPCErrorUnknownHeaderCode:
httpCode = http.StatusUnprocessableEntity
case neorpc.MethodNotFoundCode:
httpCode = http.StatusMethodNotAllowed
Expand Down
51 changes: 22 additions & 29 deletions pkg/services/rpcsrv/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,10 +250,6 @@ var rpcWsHandlers = map[string]func(*Server, params.Params, *subscriber) (any, *
"unsubscribe": (*Server).unsubscribe,
}

var invalidBlockHeightError = func(index int, height int) *neorpc.Error {
return neorpc.NewRPCError("Invalid block height", fmt.Sprintf("param at index %d should be greater than or equal to 0 and less then or equal to current block height, got: %d", index, height))
}

// New creates a new Server struct.
func New(chain Ledger, conf config.RPC, coreServer *network.Server,
orc OracleHandler, log *zap.Logger, errChan chan<- error) Server {
Expand Down Expand Up @@ -782,7 +778,7 @@ func (s *Server) getBlock(reqParams params.Params) (any, *neorpc.Error) {

block, err := s.chain.GetBlock(hash)
if err != nil {
return nil, neorpc.NewRPCError("Failed to get block", err.Error())
return nil, neorpc.WrapErrorWithData(neorpc.ErrUnknownBlock, err.Error())
}

if v, _ := reqParams.Value(1).GetBoolean(); v {
Expand All @@ -800,7 +796,7 @@ func (s *Server) getBlock(reqParams params.Params) (any, *neorpc.Error) {
func (s *Server) getBlockHash(reqParams params.Params) (any, *neorpc.Error) {
num, err := s.blockHeightFromParam(reqParams.Value(0))
if err != nil {
return nil, neorpc.ErrInvalidParams
return nil, err
}

return s.chain.GetHeaderHash(num), nil
Expand Down Expand Up @@ -899,11 +895,11 @@ func (s *Server) calculateNetworkFee(reqParams params.Params) (any, *neorpc.Erro
if len(w.VerificationScript) == 0 { // Contract-based verification
cs := s.chain.GetContractState(signer.Account)
if cs == nil {
return 0, neorpc.WrapErrorWithData(neorpc.ErrInvalidParams, fmt.Sprintf("signer %d has no verification script and no deployed contract", i))
return 0, neorpc.WrapErrorWithData(neorpc.ErrInvalidScript, fmt.Sprintf("signer %d has no verification script and no deployed contract", i))
}
md := cs.Manifest.ABI.GetMethod(manifest.MethodVerify, -1)
if md == nil || md.ReturnType != smartcontract.BoolType {
return 0, neorpc.WrapErrorWithData(neorpc.ErrInvalidParams, fmt.Sprintf("signer %d has no verify method in deployed contract", i))
return 0, neorpc.WrapErrorWithData(neorpc.ErrInvalidScript, fmt.Sprintf("signer %d has no verify method in deployed contract", i))
}
paramz = md.Parameters // Might as well have none params and it's OK.
} else { // Regular signature verification.
Expand Down Expand Up @@ -1425,7 +1421,7 @@ func (s *Server) contractIDFromParam(param *params.Param) (int32, *neorpc.Error)
if scriptHash, err := param.GetUint160FromHex(); err == nil {
cs := s.chain.GetContractState(scriptHash)
if cs == nil {
return 0, neorpc.ErrUnknown
return 0, neorpc.ErrUnknownContract
}
result = cs.ID
} else {
Expand Down Expand Up @@ -1468,7 +1464,7 @@ func (s *Server) contractScriptHashFromParam(param *params.Param) (util.Uint160,
}
result, err = s.chain.GetContractScriptHash(int32(id))
if err != nil {
return result, neorpc.NewRPCError("Unknown contract", "")
return result, neorpc.ErrUnknownContract
}
return result, nil
}
Expand All @@ -1484,7 +1480,7 @@ var errKeepOnlyLatestState = errors.New("'KeepOnlyLatestState' setting is enable

func (s *Server) getProof(ps params.Params) (any, *neorpc.Error) {
if s.chain.GetConfig().Ledger.KeepOnlyLatestState {
return nil, neorpc.NewInvalidRequestError(fmt.Sprintf("'getproof' is not supported: %s", errKeepOnlyLatestState))
return nil, neorpc.WrapErrorWithData(neorpc.ErrOldState, fmt.Sprintf("'getproof' is not supported: %s", errKeepOnlyLatestState))
}
root, err := ps.Value(0).GetUint256()
if err != nil {
Expand Down Expand Up @@ -1515,7 +1511,7 @@ func (s *Server) getProof(ps params.Params) (any, *neorpc.Error) {

func (s *Server) verifyProof(ps params.Params) (any, *neorpc.Error) {
if s.chain.GetConfig().Ledger.KeepOnlyLatestState {
return nil, neorpc.NewInvalidRequestError(fmt.Sprintf("'verifyproof' is not supported: %s", errKeepOnlyLatestState))
return nil, neorpc.WrapErrorWithData(neorpc.ErrOldState, fmt.Sprintf("'verifyproof' is not supported: %s", errKeepOnlyLatestState))
}
root, err := ps.Value(0).GetUint256()
if err != nil {
Expand All @@ -1527,7 +1523,7 @@ func (s *Server) verifyProof(ps params.Params) (any, *neorpc.Error) {
}
var p result.ProofWithKey
if err := p.FromString(proofStr); err != nil {
return nil, neorpc.ErrInvalidParams
return nil, neorpc.ErrProof
}
vp := new(result.VerifyProof)
val, ok := mpt.VerifyProof(root, p.Key, p.Proof)
Expand All @@ -1548,7 +1544,7 @@ func (s *Server) getState(ps params.Params) (any, *neorpc.Error) {
return nil, neorpc.NewInternalServerError(fmt.Sprintf("failed to get current stateroot: %s", err))
}
if !curr.Root.Equals(root) {
return nil, neorpc.NewInvalidRequestError(fmt.Sprintf("'getstate' is not supported for old states: %s", errKeepOnlyLatestState))
return nil, neorpc.WrapErrorWithData(neorpc.ErrOldState, fmt.Sprintf("'getstate' is not supported for old states: %s", errKeepOnlyLatestState))
}
}
csHash, err := ps.Value(1).GetUint160FromHex()
Expand Down Expand Up @@ -1582,7 +1578,7 @@ func (s *Server) findStates(ps params.Params) (any, *neorpc.Error) {
return nil, neorpc.NewInternalServerError(fmt.Sprintf("failed to get current stateroot: %s", err))
}
if !curr.Root.Equals(root) {
return nil, neorpc.NewInvalidRequestError(fmt.Sprintf("'findstates' is not supported for old states: %s", errKeepOnlyLatestState))
return nil, neorpc.WrapErrorWithData(neorpc.ErrOldState, fmt.Sprintf("'findstates' is not supported for old states: %s", errKeepOnlyLatestState))
}
}
csHash, err := ps.Value(1).GetUint160FromHex()
Expand Down Expand Up @@ -1669,7 +1665,7 @@ func (s *Server) getHistoricalContractState(root util.Uint256, csHash util.Uint1
csKey := makeStorageKey(native.ManagementContractID, native.MakeContractKey(csHash))
csBytes, err := s.chain.GetStateModule().GetState(root, csKey)
if err != nil {
return nil, neorpc.NewRPCError("Failed to get historical contract state", err.Error())
return nil, neorpc.WrapErrorWithData(neorpc.ErrUnknownContract, fmt.Sprintf("Failed to get historical contract state: %s", err.Error()))
}
contract := new(state.Contract)
err = stackitem.DeserializeConvertible(csBytes, contract)
Expand Down Expand Up @@ -1719,9 +1715,6 @@ func (s *Server) getStateRoot(ps params.Params) (any, *neorpc.Error) {

func (s *Server) getStorage(ps params.Params) (any, *neorpc.Error) {
id, rErr := s.contractIDFromParam(ps.Value(0))
if rErr == neorpc.ErrUnknown {
return nil, nil
}
if rErr != nil {
return nil, rErr
}
Expand All @@ -1733,7 +1726,7 @@ func (s *Server) getStorage(ps params.Params) (any, *neorpc.Error) {

item := s.chain.GetStorageItem(id, key)
if item == nil {
return "", nil
return "", neorpc.ErrUnknownStorageItem
}

return []byte(item), nil
Expand Down Expand Up @@ -1801,7 +1794,7 @@ func (s *Server) getContractState(reqParams params.Params) (any, *neorpc.Error)
}
cs := s.chain.GetContractState(scriptHash)
if cs == nil {
return nil, neorpc.NewRPCError("Unknown contract", "")
return nil, neorpc.ErrUnknownContract
}
return cs, nil
}
Expand All @@ -1814,7 +1807,7 @@ func (s *Server) getNativeContracts(_ params.Params) (any, *neorpc.Error) {
func (s *Server) getBlockSysFee(reqParams params.Params) (any, *neorpc.Error) {
num, err := s.blockHeightFromParam(reqParams.Value(0))
if err != nil {
return 0, neorpc.NewRPCError("Invalid height", "invalid block identifier")
return 0, err
}

headerHash := s.chain.GetHeaderHash(num)
Expand Down Expand Up @@ -2097,7 +2090,7 @@ func (s *Server) getInvokeContractVerifyParams(reqParams params.Params) (util.Ui
if len(reqParams) > 1 {
args, err := reqParams[1].GetArray() // second `invokecontractverify` parameter is an array of arguments for `verify` method
if err != nil {
return util.Uint160{}, nil, nil, neorpc.WrapErrorWithData(neorpc.ErrInvalidParams, err.Error())
return util.Uint160{}, nil, nil, neorpc.WrapErrorWithData(neorpc.ErrInvalidVerificationFunction, err.Error())
}
if len(args) > 0 {
err := params.ExpandArrayIntoScript(bw.BinWriter, args)
Expand Down Expand Up @@ -2129,7 +2122,7 @@ func (s *Server) getInvokeContractVerifyParams(reqParams params.Params) (util.Ui
// handling consistency.
func (s *Server) getHistoricParams(reqParams params.Params) (uint32, *neorpc.Error) {
if s.chain.GetConfig().Ledger.KeepOnlyLatestState {
return 0, neorpc.NewInvalidRequestError(fmt.Sprintf("only latest state is supported: %s", errKeepOnlyLatestState))
return 0, neorpc.WrapErrorWithData(neorpc.ErrOldState, fmt.Sprintf("only latest state is supported: %s", errKeepOnlyLatestState))
}
if len(reqParams) < 1 {
return 0, neorpc.ErrInvalidParams
Expand Down Expand Up @@ -2463,7 +2456,7 @@ func getRelayResult(err error, hash util.Uint256) (any, *neorpc.Error) {
func (s *Server) submitOracleResponse(ps params.Params) (any, *neorpc.Error) {
oracle := s.oracle.Load().(*OracleHandler)
if oracle == nil || *oracle == nil {
return nil, neorpc.NewRPCError("Oracle is not enabled", "")
return nil, neorpc.ErrOracleNotRun
}
var pub *keys.PublicKey
pubBytes, err := ps.Value(0).GetBytesBase64()
Expand All @@ -2479,15 +2472,15 @@ func (s *Server) submitOracleResponse(ps params.Params) (any, *neorpc.Error) {
}
txSig, err := ps.Value(2).GetBytesBase64()
if err != nil {
return nil, neorpc.NewInvalidParamsError(fmt.Sprintf("tx signature is missing: %s", err))
return nil, neorpc.WrapErrorWithData(neorpc.ErrInvalidSignature, fmt.Sprintf("tx signature is missing: %s", err))
}
msgSig, err := ps.Value(3).GetBytesBase64()
if err != nil {
return nil, neorpc.NewInvalidParamsError(fmt.Sprintf("msg signature is missing: %s", err))
return nil, neorpc.WrapErrorWithData(neorpc.ErrInvalidSignature, fmt.Sprintf("msg signature is missing: %s", err))
}
data := broadcaster.GetMessage(pubBytes, uint64(reqID), txSig)
if !pub.Verify(msgSig, hash.Sha256(data).BytesBE()) {
return nil, neorpc.NewRPCError("Invalid request signature", "")
return nil, neorpc.ErrInvalidSignature
}
(*oracle).AddResponse(pub, uint64(reqID), txSig)
return json.RawMessage([]byte("{}")), nil
Expand Down Expand Up @@ -2801,7 +2794,7 @@ func (s *Server) blockHeightFromParam(param *params.Param) (uint32, *neorpc.Erro
}

if num < 0 || int64(num) > int64(s.chain.BlockHeight()) {
return 0, invalidBlockHeightError(0, num)
return 0, neorpc.WrapErrorWithData(neorpc.ErrInvalidHeight, fmt.Sprintf("param at index %d should be greater than or equal to 0 and less then or equal to current block height, got: %d", 0, num))
}
return uint32(num), nil
}
Expand Down

0 comments on commit cd0f574

Please sign in to comment.