Перейти к основному содержимому

Редакционная доработка ещё намеренно не завершена

Этот раздел скрыт из основной навигации. Перевод и редакторская полировка намеренно не считаются обязательными, пока раздел не станет публичным.

RPC & Submission

This section covers how транзакции are submitted via JSON-RPC, validated by the RpcHandler, and prepared for сеть propagation.

JSON-RPC Протокол Обзор​

NEAR nodes expose a JSON-RPC 2.0 interface for external interaction. Unlike REST APIs, JSON-RPC uses a single endpoint with method names in the запрос body.

Default Endpoint: http://localhost:3030 (configurable)

Запрос Format​

{
    "jsonrpc": "2.0",
    "id": "unique-request-id",
    "method": "method_name",
    "params": { /* method-specific parameters */ }
}

Ответ Formats​

Success:

{
    "jsonrpc": "2.0",
    "id": "unique-request-id",
    "result": { /* method-specific result */ }
}

Error:

{
    "jsonrpc": "2.0",
    "id": "unique-request-id",
    "error": {
        "code": -32000,
        "message": "Error message",
        "data": { /* optional additional info */ }
    }
}

RPC Configuration​

Source: chain/jsonrpc/src/lib.rs

pub struct RpcConfig {
    /// Address to bind the RPC server (default: "0.0.0.0:3030")
    pub addr: tcp::ListenerAddr,

    /// Optional Prometheus metrics endpoint
    pub prometheus_addr: Option<String>,

    /// CORS allowed origins (default: ["*"])
    pub cors_allowed_origins: Vec<String>,

    /// Polling configuration for tx_status waits
    pub polling_config: RpcPollingConfig,

    /// Request size limits (default: 10MB)
    pub limits_config: RpcLimitsConfig,

    /// Enable debug RPC endpoints (default: false)
    pub enable_debug_rpc: bool,
}

Транзакция Submission Methods​

NEAR provides three RPC methods for submitting транзакции, each with different waiting behavior.

1. broadcast_tx_async​

Purpose: Fire-and-forget транзакции submission

Submits the транзакции and immediately returns the транзакции hash without waiting for any confirmation.

When to use:

  • High-throughput applications that track статус separately
  • When you don't need immediate confirmation
  • Reducing клиент latency

Запрос:

{
    "jsonrpc": "2.0",
    "id": "1",
    "method": "broadcast_tx_async",
    "params": ["BASE64_ENCODED_SIGNED_TX"]
}

Ответ:

{
    "jsonrpc": "2.0",
    "id": "1",
    "result": "6zgh2u9DqHHiXzdy9ouTP7oGky2T4nugqzqt9wJZwNFm"
}

The result is the транзакции hash (base58-encoded CryptoHash).

2. broadcast_tx_commit​

Purpose: Submit and wait for execution

Submits the транзакции and waits until it's included in a final блока with execution results.

When to use:

  • Simple applications that need confirmation
  • Testing and debugging
  • When you need execution results immediately

Запрос:

{
    "jsonrpc": "2.0",
    "id": "1",
    "method": "broadcast_tx_commit",
    "params": ["BASE64_ENCODED_SIGNED_TX"]
}

Ответ (success):

{
    "jsonrpc": "2.0",
    "id": "1",
    "result": {
        "status": { "SuccessValue": "" },
        "transaction": {
            "signer_id": "sender.testnet",
            "public_key": "ed25519:...",
            "nonce": 1,
            "receiver_id": "receiver.testnet",
            "actions": [...],
            "signature": "ed25519:...",
            "hash": "6zgh2u9DqHHiXzdy9ouTP7oGky2T4nugqzqt9wJZwNFm"
        },
        "transaction_outcome": {
            "block_hash": "...",
            "id": "6zgh2u9DqHHiXzdy9ouTP7oGky2T4nugqzqt9wJZwNFm",
            "outcome": {
                "logs": [],
                "receipt_ids": ["..."],
                "gas_burnt": 424555062500,
                "tokens_burnt": "42455506250000000000",
                "executor_id": "sender.testnet",
                "status": { "SuccessReceiptId": "..." }
            }
        },
        "receipts_outcome": [...]
    }
}

Timeout: Default 10 seconds (configurable via polling_config)

3. send_tx​

Purpose: Flexible транзакции submission with configurable waiting

The modern API that combines the functionality of the above methods with additional control.

Запрос:

{
    "jsonrpc": "2.0",
    "id": "1",
    "method": "send_tx",
    "params": {
        "signed_tx_base64": "BASE64_ENCODED_SIGNED_TX",
        "wait_until": "INCLUDED_FINAL"
    }
}

wait_until Options:

ValueDescription
NONEDon't wait, return immediately (like broadcast_tx_async)
INCLUDEDWait until included in a блока
INCLUDED_FINALWait until included in a final блока
EXECUTEDWait until executed
EXECUTED_OPTIMISTICWait for execution with optimistic финальность
FINALWait for final execution (like broadcast_tx_commit)

Запрос Parsing and Decoding​

When a транзакции запрос arrives at the RPC layer, it goes through several parsing stages.

Step 1: JSON-RPC Dispatch​

The main RPC handler routes the запрос based on the method name.

Step 2: Parameter Parsing​

Multiple input formats are supported:

// Array format
{"params": ["BASE64_TX"]}

// Object format (modern)
{"params": {"signed_tx_base64": "BASE64_TX", "wait_until": "FINAL"}}

Step 3: Транзакция Decoding​

The critical decoding function converts base64 to SignedTransaction:

fn decode_signed_transaction(value: String) -> Result<SignedTransaction, RpcParseError> {
    // Step 1: Base64 decode to bytes
    let bytes = near_primitives::serialize::from_base64(&value)?;

    // Step 2: Borsh deserialize to SignedTransaction
    SignedTransaction::try_from_slice(&bytes)?
}

This is where the encoding boundary happens:

  • Input: Base64 string containing Borsh bytes
  • Output: Rust SignedTransaction struct

Error Handling at the RPC Layer​

Parse Errors (Код -32700)​

Returned when the запрос cannot be parsed:

{
    "error": {
        "code": -32700,
        "message": "Parse error",
        "data": "Failed to decode transaction: invalid base64"
    }
}

Common causes:

  • Invalid base64 encoding
  • Corrupted borsh data
  • Wrong parameter format

Invalid Транзакция Errors​

When the транзакции is parseable but invalid:

{
    "error": {
        "code": -32000,
        "message": "Server error",
        "data": {
            "InvalidTransaction": {
                "InvalidNonce": {
                    "tx_nonce": 5,
                    "ak_nonce": 10
                }
            }
        }
    }
}

Error Код Справочник​

КодMeaning
-32700Parse error (invalid JSON)
-32600Invalid запрос
-32601Method not found
-32602Invalid params
-32603Internal error
-32000Server error (NEAR-specific errors)

Просмотр Calls: The Read-Only Path​

Not all RPC calls go through the транзакции flow. Просмотр calls are read-only function executions that bypass транзакции, квитанции, and состояние changes entirely.

Key Differences​

AspectТранзакцияПросмотр Вызов
SigningRequiredNot needed
Газ costPaid by signerFree
Состояние changesYesNo (read-only)
Creates квитанцииYesNo
Cross-контракта callsYesNo
ФинальностьEventualImmediate
Блок inclusionRequiredN/A

The Запрос RPC Method​

{
    "jsonrpc": "2.0",
    "id": "1",
    "method": "query",
    "params": {
        "request_type": "call_function",
        "finality": "final",
        "account_id": "contract.near",
        "method_name": "get_status",
        "args_base64": "e30="
    }
}

Запрос types:

TypePurpose
view_accountGet аккаунта balance, storage, код hash
view_codeGet контракта WASM код
view_stateGet контракта storage ключ-values
view_access_keyGet specific доступа ключ info
view_access_key_listList all доступа ключи for аккаунта
call_functionExecute read-only контракта method

What's Prohibited in Просмотр Calls​

CategoryBlocked Functions
Состояние modificationstorage_write, storage_remove
Signing contextsigner_account_id, signer_account_pk, predecessor_account_id
Газ infoprepaid_gas, used_gas
All promisespromise_create, promise_then, promise_batch_*
TransfersAny deposit (always 0 in просмотр)

Allowed operations:

CategoryAllowed Functions
Состояние readingstorage_read, storage_has_key
Блок infoblock_height, block_timestamp, epoch_height
Аккаунт infocurrent_account_id, account_balance, account_locked_balance
Cryptosha256, keccak256, ed25519_verify

Транзакция Validation​

After the RPC layer parses the транзакции, it hands it off to the RpcHandler actor for validation.

The RpcHandler Actor​

The RpcHandler is a multi-threaded actor that validates транзакции and manages the транзакции pool.

Source: chain/client/src/rpc_handler.rs

pub struct RpcHandler {
    config: RpcHandlerConfig,

    /// The transaction mempool
    tx_pool: Arc<Mutex<ShardedTransactionPool>>,

    /// Access to chain state
    chain_store: ChainStoreAdapter,

    /// Epoch management (validators, shards)
    epoch_manager: Arc<dyn EpochManagerAdapter>,

    /// Tracks which shards this node handles
    shard_tracker: ShardTracker,

    /// Runtime for state access and tx validation
    runtime: Arc<dyn RuntimeAdapter>,

    /// Network interface for forwarding
    network_adapter: PeerManagerAdapter,
}

Validity Period Checks​

The first validation step ensures the транзакции isn't expired or from a different fork.

check_transaction_validity_period(
    &self.chain_store,
    &cur_block_header,
    signed_tx.transaction.block_hash(),
    self.config.transaction_validity_period,
)

The check involves:

  1. Блок Hash Поиск: Find the блока referenced by transaction.block_hash
  2. Chain Membership: Verify the блока is an ancestor of the текущий head
  3. Height Check: Ensure current_height - referenced_height < validity_period

If the referenced блока is:

  • Not found: InvalidChain error
  • On a different fork: InvalidChain error
  • Too old: Expired error

Why Validity Periods?​

  1. DoS Prevention: Without expiration, old транзакции could be replayed indefinitely
  2. Fork Protection: Ensures транзакции are only valid on the intended chain
  3. Mempool Cleanup: Nodes can safely discard old unconfirmed транзакции
  4. User Safety: Prevents very old транзакции from suddenly confirming

On mainnet, transaction_validity_period is 86,400 блоки (~24 hours).

Signature Verification​

The signature is verified using the public ключ embedded in the транзакции.

Source: core/primitives/src/transaction.rs

if !signed_tx
    .signature
    .verify(signed_tx.get_hash().as_ref(), signed_tx.transaction.public_key())
{
    return Err((InvalidTxError::InvalidSignature, signed_tx));
}

What Gets Signed?​

The signature covers the Borsh-serialized транзакции (not including the signature itself):

impl Transaction {
    pub fn get_hash_and_size(&self) -> (CryptoHash, u64) {
        let bytes = borsh::to_vec(&self).expect("Failed to deserialize");
        (hash(&bytes), bytes.len() as u64)  // SHA-256 hash
    }
}

Nonce Management​

Nonces prevent replay attacks by ensuring each транзакции is unique.

Nonce Rules​

  1. Strictly Increasing: tx_nonce > access_key_nonce
  2. Per доступ Key: Each (аккаунта, public_key) pair has its own nonce
  3. Upper Bound: tx_nonce < current_block_height * 1_000_000

Common Nonce Issues​

"Nonce too low": The транзакции's nonce has already been used. Causes:

  • Replaying an old транзакции
  • Previous транзакции confirmed faster than expected
  • Using stale nonce from cache

"Nonce too high": The nonce is unreasonably large. Sanity check to prevent:

  • Integer overflow issues
  • Denial of service via nonce exhaustion

Best Practices​

// Get current nonce
const accessKey = await account.connection.provider.query({
    request_type: "view_access_key",
    finality: "final",
    account_id: accountId,
    public_key: publicKey
});

// Use nonce + 1 for the transaction
const nonce = accessKey.nonce + 1;

доступ Key Validation​

доступ ключи control what actions a транзакции can perform.

доступ Key Types​

Full доступ Keys:

  • Can perform any action
  • Can add/remove other ключи
  • Typically used for аккаунта owners

Function Вызов Keys:

  • Limited to calling specific methods
  • Can have an allowance (газ spending limit)
  • Used for dApp integrations

Validation Logic​

pub struct FunctionCallPermission {
    /// Gas allowance remaining (None = unlimited)
    pub allowance: Option<Balance>,

    /// Must match transaction receiver_id
    pub receiver_id: AccountId,

    /// If non-empty, method_name must be in this list
    pub method_names: Vec<String>,
}

Checks performed:

  1. Key Exists: The public ключ must be registered on the signer аккаунта
  2. Permission Type: Full доступа required for non-FunctionCall actions
  3. Receiver Match: Function вызов ключи can only вызов their designated receiver
  4. Method Match: If method_names is non-empty, the called method must be listed
  5. Allowance: Must have sufficient remaining allowance for газ

Error Types​

ErrorDescription
AccessKeyNotFoundKey doesn't exist on the аккаунта
RequiresFullAccessTrying to use function вызов ключ for non-вызов action
ReceiverMismatchReceiver doesn't match function вызов ключ
MethodNameMismatchMethod not in allowed list
NotEnoughAllowanceInsufficient allowance
DepositWithFunctionCallFunction вызов ключи can't отправка deposits

Газ and Balance Checks​

Before a транзакции can be accepted, the signer must have sufficient resources.

Газ Calculation​

Each action has associated газ costs defined in the протокола parameters:

total_gas = sum(action_gas) + receipt_creation_gas

Balance Requirements​

The signer must have:

balance >= gas_price * total_gas + sum(deposits) + storage_stake_required

Where:

  • gas_price: Текущий price of газ (set by протокола)
  • deposits: Sum of all Transfer and FunctionCall deposits
  • storage_stake_required: Stake needed for аккаунта storage

Validation Errors​

ErrorDescription
NotEnoughBalanceSigner doesn't have enough NEAR
LackBalanceForStateCan't cover storage stake
CostOverflowInteger overflow during calculation

Shard Routing Logic​

NEAR shards the состояние by аккаунта. Each транзакции must be routed to the correct shard.

Shard Assignment​

let shard_uid = shard_layout.account_id_to_shard_uid(signed_tx.transaction.signer_id());
let shard_id = shard_uid.shard_id();

The signer's аккаунта determines which shard processes the транзакции initially. If the receiver is on a different shard, cross-shard квитанции handle the communication.

Routing Decision​

The RpcHandler determines:

  1. Which shard the транзакции belongs to (based on signer_id)
  2. Whether this node tracks that shard
  3. Whether this node is the chunk producer for that shard
  4. If not, forward to the appropriate chunk producer via P2P