Skip to content
This repository has been archived by the owner on Jan 22, 2025. It is now read-only.

Commit

Permalink
Update getSignatureStatus: support multiple signatures, include slot …
Browse files Browse the repository at this point in the history
…in each response item (#9022)

* Rename enable-rpc-get-confirmed-block

* Rename RpcTransactionStatus -> RpcTransactionStatusMeta

* Return simplified RpcTransactionStatus; Add support for multiple transactions

* Update docs

* typo
  • Loading branch information
CriesofCarrots authored and mvines committed Mar 23, 2020
1 parent a2e2db2 commit d4c31ee
Show file tree
Hide file tree
Showing 16 changed files with 136 additions and 120 deletions.
19 changes: 11 additions & 8 deletions client/src/mock_rpc_client_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{
client_error::Result,
generic_rpc_client_request::GenericRpcClientRequest,
rpc_request::RpcRequest,
rpc_response::{Response, RpcResponseContext},
rpc_response::{Response, RpcResponseContext, RpcTransactionStatus},
};
use serde_json::{Number, Value};
use solana_sdk::{
Expand Down Expand Up @@ -87,19 +87,22 @@ impl GenericRpcClientRequest for MockRpcClientRequest {
value: serde_json::to_value(FeeRateGovernor::default()).unwrap(),
})?,
RpcRequest::GetSignatureStatus => {
let response: Option<transaction::Result<()>> = if self.url == "account_in_use" {
Some(Err(TransactionError::AccountInUse))
let status: transaction::Result<()> = if self.url == "account_in_use" {
Err(TransactionError::AccountInUse)
} else if self.url == "instruction_error" {
Some(Err(TransactionError::InstructionError(
Err(TransactionError::InstructionError(
0,
InstructionError::UninitializedAccount,
)))
} else if self.url == "sig_not_found" {
))
} else {
Ok(())
};
let status = if self.url == "sig_not_found" {
None
} else {
Some(Ok(()))
Some(RpcTransactionStatus { status, slot: 1 })
};
serde_json::to_value(response).unwrap()
serde_json::to_value(vec![status])?
}
RpcRequest::GetTransactionCount => Value::Number(Number::from(1234)),
RpcRequest::GetSlot => Value::Number(Number::from(0)),
Expand Down
8 changes: 4 additions & 4 deletions client/src/rpc_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
rpc_response::{
Response, RpcAccount, RpcBlockhashFeeCalculator, RpcConfirmedBlock, RpcContactInfo,
RpcEpochInfo, RpcFeeCalculator, RpcFeeRateGovernor, RpcIdentity, RpcKeyedAccount,
RpcLeaderSchedule, RpcResult, RpcVersionInfo, RpcVoteAccountStatus,
RpcLeaderSchedule, RpcResult, RpcTransactionStatus, RpcVersionInfo, RpcVoteAccountStatus,
},
};
use bincode::serialize;
Expand Down Expand Up @@ -120,12 +120,12 @@ impl RpcClient {
) -> ClientResult<Option<transaction::Result<()>>> {
let signature_status = self.client.send(
&RpcRequest::GetSignatureStatus,
json!([signature.to_string(), commitment_config]),
json!([[signature.to_string()], commitment_config]),
5,
)?;
let result: Option<transaction::Result<()>> =
let result: Vec<Option<RpcTransactionStatus>> =
serde_json::from_value(signature_status).unwrap();
Ok(result)
Ok(result[0].clone().map(|status_meta| status_meta.status))
}

pub fn get_slot(&self) -> ClientResult<Slot> {
Expand Down
2 changes: 1 addition & 1 deletion client/src/rpc_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ impl RpcRequest {

#[derive(Debug, Error)]
pub enum RpcError {
#[error("rpc reques error: {0}")]
#[error("rpc request error: {0}")]
RpcRequestError(String),
#[error("parse error: expected {0}")]
ParseError(String), /* "expected" */
Expand Down
11 changes: 9 additions & 2 deletions client/src/rpc_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ pub struct RpcConfirmedBlock {
#[serde(rename_all = "camelCase")]
pub struct RpcTransactionWithStatusMeta {
pub transaction: RpcEncodedTransaction,
pub meta: Option<RpcTransactionStatus>,
pub meta: Option<RpcTransactionStatusMeta>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
Expand Down Expand Up @@ -136,13 +136,20 @@ pub struct RpcCompiledInstruction {

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcTransactionStatus {
pub struct RpcTransactionStatusMeta {
pub status: Result<()>,
pub fee: u64,
pub pre_balances: Vec<u64>,
pub post_balances: Vec<u64>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcTransactionStatus {
pub slot: Slot,
pub status: Result<()>,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RpcBlockhashFeeCalculator {
Expand Down
125 changes: 65 additions & 60 deletions core/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,7 @@ use crate::{
use bincode::serialize;
use jsonrpc_core::{Error, Metadata, Result};
use jsonrpc_derive::rpc;
use solana_client::rpc_response::{
Response, RpcAccount, RpcBlockCommitment, RpcBlockhashFeeCalculator, RpcConfirmedBlock,
RpcContactInfo, RpcEpochInfo, RpcFeeCalculator, RpcFeeRateGovernor, RpcIdentity,
RpcKeyedAccount, RpcLeaderSchedule, RpcResponseContext, RpcSignatureConfirmation,
RpcStorageTurn, RpcTransactionEncoding, RpcVersionInfo, RpcVoteAccountInfo,
RpcVoteAccountStatus,
};
use solana_client::rpc_response::*;
use solana_faucet::faucet::request_airdrop_transaction;
use solana_ledger::{
bank_forks::BankForks, blockstore::Blockstore, rooted_slot_iterator::RootedSlotIterator,
Expand All @@ -28,7 +22,7 @@ use solana_sdk::{
pubkey::Pubkey,
signature::Signature,
timing::slot_duration_from_slots_per_year,
transaction::{self, Transaction},
transaction::Transaction,
};
use solana_vote_program::vote_state::{VoteState, MAX_LOCKOUT_HISTORY};
use std::{
Expand All @@ -51,7 +45,7 @@ fn new_response<T>(bank: &Bank, value: T) -> RpcResponse<T> {
pub struct JsonRpcConfig {
pub enable_validator_exit: bool,
pub enable_set_log_filter: bool,
pub enable_get_confirmed_block: bool,
pub enable_rpc_transaction_history: bool,
pub identity_pubkey: Pubkey,
pub faucet_addr: Option<SocketAddr>,
}
Expand Down Expand Up @@ -373,7 +367,7 @@ impl JsonRpcRequestProcessor {
slot: Slot,
encoding: Option<RpcTransactionEncoding>,
) -> Result<Option<RpcConfirmedBlock>> {
if self.config.enable_get_confirmed_block {
if self.config.enable_rpc_transaction_history {
Ok(self.blockstore.get_confirmed_block(slot, encoding).ok())
} else {
Ok(None)
Expand Down Expand Up @@ -421,6 +415,27 @@ impl JsonRpcRequestProcessor {
.ok()
.unwrap_or(None))
}

pub fn get_signature_status(
&self,
signatures: Vec<Signature>,
commitment: Option<CommitmentConfig>,
) -> Result<Vec<Option<RpcTransactionStatus>>> {
let mut statuses: Vec<Option<RpcTransactionStatus>> = vec![];

let bank = self.bank(commitment);

for signature in signatures {
let status = bank.get_signature_confirmation_status(&signature).map(
|SignatureConfirmationStatus { slot, status, .. }| RpcTransactionStatus {
slot,
status,
},
);
statuses.push(status);
}
Ok(statuses)
}
}

fn get_tpu_addr(cluster_info: &Arc<RwLock<ClusterInfo>>) -> Result<SocketAddr> {
Expand Down Expand Up @@ -547,9 +562,9 @@ pub trait RpcSol {
fn get_signature_status(
&self,
meta: Self::Metadata,
signature_str: String,
signature_strs: Vec<String>,
commitment: Option<CommitmentConfig>,
) -> Result<Option<transaction::Result<()>>>;
) -> Result<Vec<Option<RpcTransactionStatus>>>;

#[rpc(meta, name = "getSlot")]
fn get_slot(&self, meta: Self::Metadata, commitment: Option<CommitmentConfig>) -> Result<u64>;
Expand Down Expand Up @@ -893,11 +908,17 @@ impl RpcSol for RpcSolImpl {
fn get_signature_status(
&self,
meta: Self::Metadata,
signature_str: String,
signature_strs: Vec<String>,
commitment: Option<CommitmentConfig>,
) -> Result<Option<transaction::Result<()>>> {
self.get_signature_confirmation(meta, signature_str, commitment)
.map(|res| res.map(|x| x.status))
) -> Result<Vec<Option<RpcTransactionStatus>>> {
let mut signatures: Vec<Signature> = vec![];
for signature_str in signature_strs {
signatures.push(verify_signature(&signature_str)?);
}
meta.request_processor
.read()
.unwrap()
.get_signature_status(signatures, commitment)
}

fn get_slot(&self, meta: Self::Metadata, commitment: Option<CommitmentConfig>) -> Result<u64> {
Expand Down Expand Up @@ -1208,7 +1229,7 @@ pub mod tests {
rpc_port,
signature::{Keypair, Signer},
system_transaction,
transaction::TransactionError,
transaction::{self, TransactionError},
};
use solana_vote_program::{
vote_instruction,
Expand Down Expand Up @@ -1337,7 +1358,7 @@ pub mod tests {

let request_processor = Arc::new(RwLock::new(JsonRpcRequestProcessor::new(
JsonRpcConfig {
enable_get_confirmed_block: true,
enable_rpc_transaction_history: true,
identity_pubkey: *pubkey,
..JsonRpcConfig::default()
},
Expand Down Expand Up @@ -1781,66 +1802,50 @@ pub mod tests {
meta,
blockhash,
alice,
confirmed_block_signatures,
..
} = start_rpc_handler_with_tx(&bob_pubkey);

let tx = system_transaction::transfer(&alice, &bob_pubkey, 20, blockhash);
let req = format!(
r#"{{"jsonrpc":"2.0","id":1,"method":"getSignatureStatus","params":["{}"]}}"#,
tx.signatures[0]
r#"{{"jsonrpc":"2.0","id":1,"method":"getSignatureStatus","params":[["{}"]]}}"#,
confirmed_block_signatures[0]
);
let res = io.handle_request_sync(&req, meta.clone());
let expected_res: Option<transaction::Result<()>> = Some(Ok(()));
let expected = json!({
"jsonrpc": "2.0",
"result": expected_res,
"id": 1
});
let expected: Response =
serde_json::from_value(expected).expect("expected response deserialization");
let result: Response = serde_json::from_str(&res.expect("actual response"))
.expect("actual response deserialization");
assert_eq!(expected, result);
let expected_res: transaction::Result<()> = Ok(());
let json: Value = serde_json::from_str(&res.unwrap()).unwrap();
let result: Vec<Option<RpcTransactionStatus>> =
serde_json::from_value(json["result"].clone())
.expect("actual response deserialization");
assert_eq!(expected_res, result[0].as_ref().unwrap().status);

// Test getSignatureStatus request on unprocessed tx
let tx = system_transaction::transfer(&alice, &bob_pubkey, 10, blockhash);
let req = format!(
r#"{{"jsonrpc":"2.0","id":1,"method":"getSignatureStatus","params":["{}"]}}"#,
r#"{{"jsonrpc":"2.0","id":1,"method":"getSignatureStatus","params":[["{}"]]}}"#,
tx.signatures[0]
);
let res = io.handle_request_sync(&req, meta.clone());
let expected_res: Option<String> = None;
let expected = json!({
"jsonrpc": "2.0",
"result": expected_res,
"id": 1
});
let expected: Response =
serde_json::from_value(expected).expect("expected response deserialization");
let result: Response = serde_json::from_str(&res.expect("actual response"))
.expect("actual response deserialization");
assert_eq!(expected, result);
let json: Value = serde_json::from_str(&res.unwrap()).unwrap();
let result: Vec<Option<RpcTransactionStatus>> =
serde_json::from_value(json["result"].clone())
.expect("actual response deserialization");
assert!(result[0].is_none());

// Test getSignatureStatus request on a TransactionError
let tx = system_transaction::transfer(&alice, &bob_pubkey, std::u64::MAX, blockhash);
let req = format!(
r#"{{"jsonrpc":"2.0","id":1,"method":"getSignatureStatus","params":["{}"]}}"#,
tx.signatures[0]
r#"{{"jsonrpc":"2.0","id":1,"method":"getSignatureStatus","params":[["{}"]]}}"#,
confirmed_block_signatures[1]
);
let res = io.handle_request_sync(&req, meta);
let expected_res: Option<transaction::Result<()>> = Some(Err(
TransactionError::InstructionError(0, InstructionError::CustomError(1)),
let res = io.handle_request_sync(&req, meta.clone());
let expected_res: transaction::Result<()> = Err(TransactionError::InstructionError(
0,
InstructionError::CustomError(1),
));
let expected = json!({
"jsonrpc": "2.0",
"result": expected_res,
"id": 1
});
let expected: Response =
serde_json::from_value(expected).expect("expected response deserialization");
let result: Response = serde_json::from_str(&res.expect("actual response"))
.expect("actual response deserialization");
assert_eq!(expected, result);
let json: Value = serde_json::from_str(&res.unwrap()).unwrap();
let result: Vec<Option<RpcTransactionStatus>> =
serde_json::from_value(json["result"].clone())
.expect("actual response deserialization");
assert_eq!(expected_res, result[0].as_ref().unwrap().status);
}

#[test]
Expand Down
4 changes: 2 additions & 2 deletions core/src/transaction_status_service.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crossbeam_channel::{Receiver, RecvTimeoutError};
use solana_client::rpc_response::RpcTransactionStatus;
use solana_client::rpc_response::RpcTransactionStatusMeta;
use solana_ledger::{blockstore::Blockstore, blockstore_processor::TransactionStatusBatch};
use solana_runtime::{
bank::{Bank, HashAgeKind},
Expand Down Expand Up @@ -73,7 +73,7 @@ impl TransactionStatusService {
blockstore
.write_transaction_status(
(slot, transaction.signatures[0]),
&RpcTransactionStatus {
&RpcTransactionStatusMeta {
status,
fee,
pre_balances,
Expand Down
4 changes: 2 additions & 2 deletions core/src/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ impl Validator {
});

let (transaction_status_sender, transaction_status_service) =
if rpc_service.is_some() && config.rpc_config.enable_get_confirmed_block {
if rpc_service.is_some() && config.rpc_config.enable_rpc_transaction_history {
let (transaction_status_sender, transaction_status_receiver) = unbounded();
(
Some(transaction_status_sender),
Expand All @@ -287,7 +287,7 @@ impl Validator {
};

let (rewards_recorder_sender, rewards_recorder_service) =
if rpc_service.is_some() && config.rpc_config.enable_get_confirmed_block {
if rpc_service.is_some() && config.rpc_config.enable_rpc_transaction_history {
let (rewards_recorder_sender, rewards_receiver) = unbounded();
(
Some(rewards_recorder_sender),
Expand Down
20 changes: 13 additions & 7 deletions docs/src/apps/jsonrpc-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -693,24 +693,30 @@ Returns the status of a given signature. This method is similar to [confirmTrans

#### Parameters:

* `<string>` - Signature of Transaction to confirm, as base-58 encoded string
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
* `<array>` - An array of transaction signatures to confirm, as base-58 encoded strings
* `<object>` - (optional) Extended Rpc configuration, containing the following optional fields:
* `commitment: <string>` - [Commitment](jsonrpc-api.md#configuring-state-commitment)
* `searchTransactionHistory: <bool>` - whether to search the ledger transaction status cache, which may be expensive

#### Results:

An array of:

* `<null>` - Unknown transaction
* `<object>` - Transaction status:
* `"Ok": <null>` - Transaction was successful
* `"Err": <ERR>` - Transaction failed with TransactionError [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L14)
* `<object>`
* `slot: <u64>` - The slot the transaction was processed
* `status: <object>` - Transaction status
* `"Ok": <null>` - Transaction was successful
* `"Err": <ERR>` - Transaction failed with TransactionError [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L14)

#### Example:

```bash
// Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getSignatureStatus", "params":["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"]}' http://localhost:8899
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getSignatureStatus", "params":[["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW", "5j7s6NiJS3JAkvgkoc18WVAsiSaci2pxB2A6ueCJP4tprA2TFg9wSyTLeYouxPBJEMzJinENTkpA52YStRW5Dia7"]]]}' http://localhost:8899

// Result
{"jsonrpc":"2.0","result":{"Ok": null},"id":1}
{"jsonrpc":"2.0","result":[{"slot": 72, "status": {"Ok": null}}, null],"id":1}
```

### getSlot
Expand Down
Loading

0 comments on commit d4c31ee

Please sign in to comment.