| Revision no. |
|---|
| 0 |
This chapter specifies the ParallelChain Remote Procedure Call (RPC) API, an HTTP API that Fullnodes make available to clients.
This chapter is organized into four sections. The first section, Calling RPCs, spells out the general properties of the API, including chiefly how it is implemented over the HTTP protocol. The next three sections then lists all of the RPCs available in API, grouping them into three categories:
- Transaction RPCs: RPCs for querying transactions and transaction-related information such as receipts, as well as for submitting transactions.
- Block RPCs: RPCs for querying blocks and block-related information, such as block headers.
- State RPCs: RPCs for querying the world state, e.g., for account balances, keys in storage tries, or information in the network account.
A ParallelChain RPC call begins with a client sending an HTTP request over to a Fullnode and ends with that Fullnode sending over an HTTP response to the client in return. This HTTP request-response cycle must abide by the following parameters:
| Parameter | Value |
|---|---|
| Route | Each RPC offered by a Fullnode is reachable at a URL of the form "http://{host}/{shared_path}/{rpc_name}":
For example, "http://example.com/fullnode_rpcs/submit_transaction" is a valid RPC route, and if a Fullnode serves the "submit_transaction" RPC on said route, the "block" RPC should be available on "http://example.com/fullnode_rpcs/block". |
| HTTP Method | Always "POST". |
| Request Content-Type | Always "application/octet-stream". |
| Response Status Code | We make the deliberate choice not to rely on HTTP status codes to report error cases to clients, as we find HTTP status codes restrictive and often misleading. Instead, we report errors in response payloads. Therefore, the protocol assigns meaning only to two specific status codes:
Implementations may choose to return other HTTP status codes (e.g., to report errors related to lower levels of the networking stack), but these have no protocol specified meaning. |
| Message Body | See Requests and responses. |
ParallelChain RPC calls operate based on a request-response cycle, with an RPC request data structure being carried on the HTTP request, and an RPC response data structure carried on the HTTP response.
RPC requests and responses are strongly typed, and each RPC endpoint has its own request and response types (which are specified for each endpoint later in the chapter).
In order to be transmitted over HTTP, RPC requests and responses are to be serialized using Borsh, and placed inside the Body of an HTTP request or HTTP response, respectively. Each message body should contain the serialization of one RPC request, or of one RPC response, and no other bytes.
Transaction RPCs allow clients to query transactions and transaction-related information such as receipts, as well as submit transactions.
Try to insert a Transaction into the Mempool. If the submitted transaction is successfully inserted into the target Fullnode's Mempool, it will broadcast the transaction to other Fullnodes in the P2P network.
struct SubmitTransactionRequest {
/// The transaction to be inserted into the mempool.
transaction: Transaction,
}struct SubmitTransactionResponse {
/// The reason why the submitted transaction was not added to the mempool.
error: Option<SubmitTransactionError>,
}Where SubmitTransactionError:
enum SubmitTransactionError {
/// The submitted transaction's Nonce (`transaction.nonce`) is either "too small" or "too large".
///
/// What "too small" or "too large" means exactly is up to implementations, but the following general
/// guidelines apply:
/// 1. Too small: if `transaction.nonce` is less-than or equal to `transaction.signer`'s nonce in the
/// committed world state, then implementations should reject `transaction`, since it is impossible
/// at this point for `transaction` to be committed.
/// 2. Too large: if `transaction.nonce` is *much* bigger than `transaction.signer`'s nonce in the
/// committed world state, then implementations can choose to reject `transaction`, on the grounds
/// that the Fullnode will have to wait too long before the transaction can be committed, and it
/// would rather use the precious space in its local mempool to store transactions that are more likely
/// to be committed in the near term.
/// 3. Existing nonce: if the local mempool already contains a transaction with the same nonce and signer
/// as the `transaction`, the Fullnode can choose to reject `transaction`, e.g., if the existing
/// transaction has a higher Priority Fee than the submitted transaction.
UnacceptableNonce,
/// The Fullnode's local Mempool does not have the capacity to include the submitted transaction.
MempoolFull,
/// The Fullnode rejected the submitted transaction for reasons other than what is explicitly enumerated
/// by the protocol.
Other,
}Query a committed Transaction by its transaction_hash, returning not only the matching transaction but also the block_hash of the block that includes the transaction, the transaction's position within that block, and optionally, its receipt.
struct TransactionRequest {
/// Hash of the transaction to be queried.
transaction_hash: CryptoHash,
/// Whether or not the queried transaction's Receipt should be included in the response.
include_receipt: bool,
}struct TransactionResponse {
/// The transaction with the specified `transaction_hash`, if it exists and is already committed.
///
/// # Optionality of the rest of
///
/// If `transaction` is `None`, then the rest of `TransactionResponse`s fields will also `None`.
/// Likewise if `transaction` is `Some`, then the rest of the response fields will also be `Some`
/// (`receipt` will only be `Some` if `include_receipt` is set to `true in the request).
transaction: Option<Transaction>,
/// The receipt of the returned transaction.
receipt: Option<Receipt>,
/// The Hash of the committed block that contains the returned transaction.
block_hash: Option<CryptoHash>,
/// The index of the returned transaction in the committed block's Transactions vector.
position: Option<u32>,
}Query the block_hash of the committed Block that includes the Transaction identified by transaction_hash, as well as the position of the transaction within said block.
struct TransactionPositionRequest {
/// Hash of the transaction whose position is to be queried.
transaction_hash: CryptoHash,
}Note: 1.
struct TransactionPositionResponse {
/// See Note: [1] above.
transaction_hash: Option<CryptoHash>,
/// If the requested `transaction_hash` matches a transaction in a committed block, then, the block's
/// hash. Else `None`.
block_hash: Option<CryptoHash>,
/// If `block_hash` is `Some`, then , the index of the queried transaction in the committed block's
/// Transactions vector.
position: Option<u32>,
}Query the Receipt of the committed Transaction identified by transaction_hash, returning also the hash of the block that said transaction is included in, and the position of the transaction in the block.
struct ReceiptRequest {
/// Hash of the transaction whose receipt is to be queried.
transaction_hash: CryptoHash,
}struct ReceiptResponse {
/// Hash of the transaction whose receipt was queried (this takes the same value as the
/// `transaction_hash` field of the request corresponding to this response).
transaction_hash: CryptoHash,
/// The queried transaction's receipt, if said transaction has been included in a committed block.
receipt: Option<Receipt>,
/// Hash of the committed block in which the queried transaction was included in.
block_hash: Option<CryptoHash>,
/// Index of the queried transaction in the Transactions vector of the committed block in which it is
/// included. This equals the index of the transaction's receipt in the same block's Receipts vector.
position: Option<u32>,
}Block RPCs allow users to query blocks and block-related information like block headers.
Query a Block (pending or committed) by its block_hash.
struct BlockRequest {
/// Hash of the block to be queried.
block_hash: CryptoHash,
}struct BlockResponse {
/// The block with the queried `block_hash`. This block may be pending, or committed.
block: Option<Block>,
}Get the Block Header of a Block (pending or committed) by its block_hash.
struct BlockHeaderRequest {
/// Hash of the block whose header is to be queried.
block_hash: CryptoHash,
}struct BlockHeaderResponse {
/// The header of the block with the queried `block_hash`. The block may be pending, or committed.
block_header: Option<BlockHeader>,
}Get the height of the committed Block identified by block_hash.
struct BlockHeightByHashRequest {
/// Hash of the block whose height will be queried.
block_hash: CryptoHash,
}struct BlockHeightByHashResponse {
/// Hash of the block whose height was queried (this takes the same value as the `block_hash` field
/// of the request corresponding to this response).
block_hash: CryptoHash,
/// Height of the queried block. `None` if the block does not exist or has not been committed yet.
block_height: Option<BlockHeight>,
}Get the hash of the committed Block at the specified block_height.
struct BlockHashByHeightRequest {
/// Height of the block whose hash will be queried.
block_height: BlockHeight,
}struct BlockHashByHeightResponse {
/// Height of the block whose hash was queried (this takes the same value as the `block_height` field
/// of the request corresponding to this response).
block_height: BlockHeight,
/// Hash of the queried block. `None` if the block does not exist or has not been committed yet.
block_hash: Option<CryptoHash>,
}Get the hash of the current highest committed Block (the committed block with the highest block height).
Empty.
struct HighestCommittedBlockResponse {
/// Hash of the current highest committed block. `None` if no blocks have been committed yet.
block_hash: Option<CryptoHash>,
}State RPCs allow clients to query the world state, for example for account balances, keys in storage tries, or information in the network account.
State RPCs return multiple entities in the world state in a single response. This allows clients to get a consistent snapshot of the world state in a single call. Every response structure includes the hash of the highest committed block when the snapshot is taken in a field called block_hash.
Some of the following RPCs' response structures reference types unique to this document. These are specified in types featuring in state-related RPC responses.
Get the fields of a set of accounts (choosing whether or not to include their contract code(s)), and/or a set of storage_keys.
struct StateRequest {
/// The set of accounts whose fields (Nonce, Balance, CBI version, and Storage hash) will be queried.
accounts: HashSet<PublicAddress>,
/// Whether the Contract field should also be queried for the requested accounts.
include_contracts: bool,
/// A set of accounts (`PublicAddress`) mapped to the set of storage keys (`HashSet<Vec<u8>>`) that
/// should be queried for each account.
storage_keys: HashMap<PublicAddress, HashSet<Vec<u8>>>,
}struct StateResponse {
/// The queried accounts.
accounts: HashMap<PublicAddress, Account>,
/// The queried storage tuples.
storage_tuples: HashMap<PublicAddress, HashMap<Vec<u8>, Vec<u8>>>,
/// Hash of the highest committed block at the point in time in which the world state was queried.
block_hash: CryptoHash,
}See type definition: Account.
Get information about the Previous, Current, and Next Validator Sets, optionally including the stakes delegated to them.
struct ValidatorSetsRequest {
/// Whether to query the previous validator set (PVS).
include_prev: bool,
/// Whether to include the delegators included in the PVS.
include_prev_delegators: bool,
/// Whether to query the current validator set (CVS).
include_curr: bool,
/// Whether to include the delegators included in the CVS.
include_curr_delegators: bool,
/// Whether to query the next validator set (NVS).
include_next: bool,
/// Whether to include the delegators included in the NVS.
include_next_delegators: bool,
}struct ValidatorSetsResponse {
/// The previous validator set.
///
/// This is `None` if the request specifies `!include_prev`, `Some(None)` if the request specifies
/// `include_prev` but the current epoch number is 0, and `Some(Some(pvs))` otherwise.
prev_validator_set: Option<Option<ValidatorSet>>,
/// The current validator set.
///
/// This is `None` if the request specifies `!include_prev`.
curr_validator_set: Option<ValidatorSet>,
/// The next validator set.
///
/// This is `None` if the request specifies `!include_prev`.
next_validator_set: Option<ValidatorSet>,
/// Hash of the highest committed block at the point in time in which the PVS, CVS, and NVS were
/// queried.
block_hash: CryptoHash,
}Get information about the pools operated by a given set of operators, optionally including the stakes currently contained in each pool.
struct PoolsRequest {
/// The operators whose pools will be queried.
operators: HashSet<PublicAddress>,
/// Whether or not to include the stakes currently contained in each pool in the response.
include_stakes: bool,
}struct PoolsResponse {
/// The queried Pools.
///
/// If the requested operator does not currently operate a pool, the value mapped to it will be `None`.
pools: HashMap<PublicAddress, Option<Pool>>,
/// Hash of the highest committed block at the point in time in which the requested pools were
/// queried.
block_hash: CryptoHash,
}See type definition: Pool.
Get information about the deposits associated with a given set of (operator, owner) pairs.
Each (operator, owner) pair asks to query information about the deposit owned by owner which is assigned to the pool operated by operator.
struct DepositsRequest {
/// The `(operator, owner)` pairs that identify the deposits to be queried.
deposits: HashSet<(PublicAddress, PublicAddress)>,
}struct DepositsResponse {
/// The queried deposits.
///
/// If the requested `(owner, operator)` pair is not currently associated with a deposit, it will be
/// mapped to `None`.
deposits: HashMap<(PublicAddress, PublicAddress), Option<Deposit>>,
/// Hash of the highest committed block at the point in time in which the requested deposits were
/// queried.
block_hash: CryptoHash,
}See type definition: Deposit.
Get information about the stakes associated with a given set of (operator, owner) pairs in the current Next Validator Set.
Each (operator, owner) pair asks to query information about the stake owned by owner which is currently staked to the pool operated by operator.
For information about the stakes in the current Previous Validator Set and Current Validator Set, consider calling the validator_sets RPC with the include_prev_delegators or include_curr_delegators flags enabled, respectively.
struct StakesRequest {
/// The `(operator, owner)` pairs that identify the stakes to be queried.
stakes: HashSet<(PublicAddress, PublicAddress)>,
}struct StakesResponse {
/// The queried stakes.
///
/// If the requested `(operator, owner)` pair is not currently associated with a stake, it will be
/// mapped to `None`.
stakes: HashMap<(PublicAddress, PublicAddress), Option<Stake>>,
/// Hash of the highest committed block at the point in time in which the requested stakes were
/// queried.
block_hash: CryptoHash,
}See type definition: Stake.
View-call a given method in a target contract and get the Command Receipt describing the results of the execution.
struct ViewRequest {
/// Address of the contract that will be view-called.
target: PublicAddress,
/// The name of the method to view-call.
method: Vec<u8>,
/// The arguments to pass to the view call.
arguments: Option<Vec<Vec<u8>>>,
}struct ViewResponse {
/// Command receipt describing the results of the view call execution.
receipt: CommandReceipt,
}enum Account {
WithContract(AccountWithContract),
WithoutContract(AccountWithoutContract),
}struct AccountWithContract {
nonce: Nonce,
balance: Balance,
contract: Option<Vec<u8>>,
cbi_version: Option<CBIVersion>,
storage_hash: Option<CryptoHash>,
}struct AccountWithoutContract {
nonce: Nonce,
balance: Balance,
cbi_version: Option<CBIVersion>,
storage_hash: Option<CryptoHash>,
}enum ValidatorSet {
WithDelegators(Vec<PoolWithDelegators>),
WithoutDelegators(Vec<PoolWithoutDelegators>),
}enum Pool {
WithStakes(PoolWithDelegators),
WithoutStakes(PoolWithoutDelegators),
}struct PoolWithDelegators {
operator: PublicAddress,
power: Balance,
commission_rate: u8,
operator_stake: Option<Stake>,
delegated_stakes: Vec<Stake>,
}struct PoolWithoutDelegators {
operator: PublicAddress,
power: Balance,
commission_rate: u8,
operator_stake: Stake,
}struct Deposit {
owner: PublicAddress,
balance: u64,
auto_stake_rewards: bool,
}struct Stake {
owner: PublicAddress,
power: Balance,
}Footnotes
-
This field is an erratum and has no semantic meaning. Fullnodes should include this field in their
TransactionPositionResponses, but clients should always ignore its value. ↩