// Copyright (C) 2018 Zilliqa
//
// This file is part of zilliqa-js
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
import "cross-fetch/polyfill";
import { WithRequest } from "./util";
/**
* blockchain-side.
*/
export enum RPCMethod {
// Network-related methods
GetNetworkId = "GetNetworkId",
// Blockchain-related methods
GetBlockchainInfo = "GetBlockchainInfo",
GetShardingStructure = "GetShardingStructure",
GetDSBlock = "GetDsBlock",
GetLatestDSBlock = "GetLatestDsBlock",
GetNumDSBlocks = "GetNumDSBlocks",
GetDSBlockRate = "GetDSBlockRate",
DSBlockListing = "DSBlockListing",
GetTxBlock = "GetTxBlock",
GetLatestTxBlock = "GetLatestTxBlock",
GetNumTxBlocks = "GetNumTxBlocks",
GetTxBlockRate = "GetTxBlockRate",
TxBlockListing = "TxBlockListing",
GetNumTransactions = "GetNumTransactions",
GetTransactionRate = "GetTransactionRate",
GetCurrentMiniEpoch = "GetCurrentMiniEpoch",
GetCurrentDSEpoch = "GetCurrentDSEpoch",
GetPrevDifficulty = "GetPrevDifficulty",
GetPrevDSDifficulty = "GetPrevDSDifficulty",
GetTotalCoinSupply = "GetTotalCoinSupply",
GetMinerInfo = "GetMinerInfo",
// Transaction-related methods
CreateTransaction = "CreateTransaction",
GetTransaction = "GetTransaction",
GetTransactionStatus = "GetTransactionStatus",
GetRecentTransactions = "GetRecentTransactions",
GetTransactionsForTxBlock = "GetTransactionsForTxBlock",
GetTransactionsForTxBlockEx = "GetTransactionsForTxBlockEx",
GetTxnBodiesForTxBlock = "GetTxnBodiesForTxBlock",
GetTxnBodiesForTxBlockEx = "GetTxnBodiesForTxBlockEx",
GetNumTxnsTxEpoch = "GetNumTxnsTxEpoch",
GetNumTxnsDSEpoch = "GetNumTxnsDSEpoch",
GetMinimumGasPrice = "GetMinimumGasPrice",
// Contract-related methods
GetContractAddressFromTransactionID = "GetContractAddressFromTransactionID",
GetSmartContracts = "GetSmartContracts",
GetSmartContractCode = "GetSmartContractCode",
GetSmartContractInit = "GetSmartContractInit",
GetSmartContractState = "GetSmartContractState",
GetSmartContractSubState = "GetSmartContractSubState",
GetStateProof = "GetStateProof",
// Account-related methods
GetBalance = "GetBalance",
}
export enum RPCErrorCode {
// Standard JSON-RPC 2.0 errors
// RPC_INVALID_REQUEST is internally mapped to HTTP_BAD_REQUEST (400).
// It should not be used for application-layer errors.
RPC_INVALID_REQUEST = -32600,
// RPC_METHOD_NOT_FOUND is internally mapped to HTTP_NOT_FOUND (404).
// It should not be used for application-layer errors.
RPC_METHOD_NOT_FOUND = -32601,
RPC_INVALID_PARAMS = -32602,
// RPC_INTERNAL_ERROR should only be used for genuine errors in bitcoind
// (for example datadir corruption).
RPC_INTERNAL_ERROR = -32603,
RPC_PARSE_ERROR = -32700,
// General application defined errors
RPC_MISC_ERROR = -1, // std::exception thrown in command handling
RPC_TYPE_ERROR = -3, // Unexpected type was passed as parameter
RPC_INVALID_ADDRESS_OR_KEY = -5, // Invalid address or key
RPC_INVALID_PARAMETER = -8, // Invalid, missing or duplicate parameter
RPC_DATABASE_ERROR = -20, // Database error
RPC_DESERIALIZATION_ERROR = -22, // Error parsing or validating structure in raw format
RPC_VERIFY_ERROR = -25, // General error during transaction or block submission
RPC_VERIFY_REJECTED = -26, // Transaction or block was rejected by network rules
RPC_IN_WARMUP = -28, // Client still warming up
RPC_METHOD_DEPRECATED = -32, // RPC method is deprecated
}
export interface RPCRequestPayload {
id: number;
jsonrpc: "2.0";
method: RPCMethod;
params: T;
}
interface RPCRequestOptions {
headers?: Headers;
method?: string;
}
export interface RPCRequest {
url: string;
payload: RPCRequestPayload | RPCRequestPayload[];
options?: RPCRequestOptions;
}
interface RPCResponseBase {
jsonrpc: "2.0";
id: number;
}
export interface RPCResponseSuccess extends RPCResponseBase {
batch_result?: R; // for batch response
result: R; // for non-batch response
error: undefined;
}
export interface RPCResponseError extends RPCResponseBase {
result: undefined;
error: RPCError;
}
export interface RPCError {
code: RPCErrorCode;
message: string;
data?: E;
}
export type RPCResponse = RPCResponseSuccess | RPCResponseError;
export type RPCResponseHandler = (
response: WithRequest>
) => T;
const DEFAULT_HEADERS = { "Content-Type": "application/json" };
export const performRPC = async >(
request: RPCRequest,
handler: RPCResponseHandler
): Promise => {
try {
const response = await fetch(request.url, {
method: "POST",
cache: "no-cache",
mode: "cors",
redirect: "follow",
body: JSON.stringify(request.payload),
headers: {
...DEFAULT_HEADERS,
...((request.options && request.options.headers) || {}),
} as HeadersInit,
});
return response
.json()
.then((body) => {
return { ...body, req: request };
})
.then(handler);
} catch (err) {
throw err;
}
};
// identical to performRPC; difference is the response
export const performBatchRPC = async <
R,
E,
D extends any[],
T = RPCResponse
>(
request: RPCRequest,
handler: RPCResponseHandler // eslint-disable-line @typescript-eslint/no-unused-vars
): Promise => {
try {
const response = await fetch(request.url, {
method: "POST",
cache: "no-cache",
mode: "cors",
redirect: "follow",
referrer: "no-referrer",
body: JSON.stringify(request.payload),
headers: {
...DEFAULT_HEADERS,
...((request.options && request.options.headers) || {}),
} as HeadersInit,
});
return (
response
.json()
.then((batch_result) => {
return { batch_result, req: request };
})
// no handler as compared to performRPC to preserve the body array
// e.g. response
/*
{ body:
[ { id: 1, jsonrpc: '2.0', result: [Object] },
{ id: 1, jsonrpc: '2.0', result: [Object] } ],
req:
{ url: 'https://dev-api.zilliqa.com',
payload: [ [Object], [Object] ] } }
*/
.then()
);
} catch (err) {
throw err;
}
};