RPC: add err field to TransactionStatus, alongside the now deprecated status field (#9296) (#9303)

automerge
This commit is contained in:
mergify[bot]
2020-04-04 21:58:44 -07:00
committed by GitHub
parent 33c19130b5
commit 11b4da4146
12 changed files with 143 additions and 60 deletions

View File

@ -116,10 +116,12 @@ impl GenericRpcClientRequest for MockRpcClientRequest {
let status = if self.url == "sig_not_found" { let status = if self.url == "sig_not_found" {
None None
} else { } else {
let err = status.clone().err();
Some(TransactionStatus { Some(TransactionStatus {
status, status,
slot: 1, slot: 1,
confirmations: Some(0), confirmations: Some(0),
err,
}) })
}; };
serde_json::to_value(Response { serde_json::to_value(Response {

View File

@ -4,7 +4,7 @@ use solana_sdk::{
clock::{Epoch, Slot}, clock::{Epoch, Slot},
fee_calculator::{FeeCalculator, FeeRateGovernor}, fee_calculator::{FeeCalculator, FeeRateGovernor},
pubkey::Pubkey, pubkey::Pubkey,
transaction::Result, transaction::{Result, TransactionError},
}; };
use std::{collections::HashMap, net::SocketAddr, str::FromStr}; use std::{collections::HashMap, net::SocketAddr, str::FromStr};
@ -54,6 +54,12 @@ pub struct RpcKeyedAccount {
pub account: RpcAccount, pub account: RpcAccount,
} }
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RpcSignatureResult {
pub err: Option<TransactionError>,
}
/// A duplicate representation of a Message for pretty JSON serialization /// A duplicate representation of a Message for pretty JSON serialization
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]

View File

@ -1982,10 +1982,20 @@ mod tests {
{ {
if let EncodedTransaction::Json(transaction) = transaction { if let EncodedTransaction::Json(transaction) = transaction {
if transaction.signatures[0] == success_signature.to_string() { if transaction.signatures[0] == success_signature.to_string() {
assert_eq!(meta.unwrap().status, Ok(())); let meta = meta.unwrap();
assert_eq!(meta.err, None);
assert_eq!(meta.status, Ok(()));
} else if transaction.signatures[0] == ix_error_signature.to_string() { } else if transaction.signatures[0] == ix_error_signature.to_string() {
let meta = meta.unwrap();
assert_eq!( assert_eq!(
meta.unwrap().status, meta.err,
Some(TransactionError::InstructionError(
0,
InstructionError::CustomError(1)
))
);
assert_eq!(
meta.status,
Err(TransactionError::InstructionError( Err(TransactionError::InstructionError(
0, 0,
InstructionError::CustomError(1) InstructionError::CustomError(1)

View File

@ -2530,10 +2530,20 @@ pub(crate) mod tests {
{ {
if let EncodedTransaction::Json(transaction) = transaction { if let EncodedTransaction::Json(transaction) = transaction {
if transaction.signatures[0] == signatures[0].to_string() { if transaction.signatures[0] == signatures[0].to_string() {
assert_eq!(meta.unwrap().status, Ok(())); let meta = meta.unwrap();
assert_eq!(meta.err, None);
assert_eq!(meta.status, Ok(()));
} else if transaction.signatures[0] == signatures[1].to_string() { } else if transaction.signatures[0] == signatures[1].to_string() {
let meta = meta.unwrap();
assert_eq!( assert_eq!(
meta.unwrap().status, meta.err,
Some(TransactionError::InstructionError(
0,
InstructionError::CustomError(1)
))
);
assert_eq!(
meta.status,
Err(TransactionError::InstructionError( Err(TransactionError::InstructionError(
0, 0,
InstructionError::CustomError(1) InstructionError::CustomError(1)

View File

@ -460,10 +460,12 @@ impl JsonRpcRequestProcessor {
.get_confirmation_count(slot) .get_confirmation_count(slot)
.or(Some(0)) .or(Some(0))
}; };
let err = status.clone().err();
TransactionStatus { TransactionStatus {
slot, slot,
status, status,
confirmations, confirmations,
err,
} }
}) })
} }
@ -2416,11 +2418,21 @@ pub mod tests {
{ {
if let EncodedTransaction::Json(transaction) = transaction { if let EncodedTransaction::Json(transaction) = transaction {
if transaction.signatures[0] == confirmed_block_signatures[0].to_string() { if transaction.signatures[0] == confirmed_block_signatures[0].to_string() {
let meta = meta.unwrap();
assert_eq!(transaction.message.recent_blockhash, blockhash.to_string()); assert_eq!(transaction.message.recent_blockhash, blockhash.to_string());
assert_eq!(meta.unwrap().status, Ok(())); assert_eq!(meta.status, Ok(()));
assert_eq!(meta.err, None);
} else if transaction.signatures[0] == confirmed_block_signatures[1].to_string() { } else if transaction.signatures[0] == confirmed_block_signatures[1].to_string() {
let meta = meta.unwrap();
assert_eq!( assert_eq!(
meta.unwrap().status, meta.err,
Some(TransactionError::InstructionError(
0,
InstructionError::CustomError(1)
))
);
assert_eq!(
meta.status,
Err(TransactionError::InstructionError( Err(TransactionError::InstructionError(
0, 0,
InstructionError::CustomError(1) InstructionError::CustomError(1)
@ -2450,11 +2462,21 @@ pub mod tests {
let decoded_transaction: Transaction = let decoded_transaction: Transaction =
deserialize(&bs58::decode(&transaction).into_vec().unwrap()).unwrap(); deserialize(&bs58::decode(&transaction).into_vec().unwrap()).unwrap();
if decoded_transaction.signatures[0] == confirmed_block_signatures[0] { if decoded_transaction.signatures[0] == confirmed_block_signatures[0] {
let meta = meta.unwrap();
assert_eq!(decoded_transaction.message.recent_blockhash, blockhash); assert_eq!(decoded_transaction.message.recent_blockhash, blockhash);
assert_eq!(meta.unwrap().status, Ok(())); assert_eq!(meta.status, Ok(()));
assert_eq!(meta.err, None);
} else if decoded_transaction.signatures[0] == confirmed_block_signatures[1] { } else if decoded_transaction.signatures[0] == confirmed_block_signatures[1] {
let meta = meta.unwrap();
assert_eq!( assert_eq!(
meta.unwrap().status, meta.err,
Some(TransactionError::InstructionError(
0,
InstructionError::CustomError(1)
))
);
assert_eq!(
meta.status,
Err(TransactionError::InstructionError( Err(TransactionError::InstructionError(
0, 0,
InstructionError::CustomError(1) InstructionError::CustomError(1)

View File

@ -4,8 +4,10 @@ use crate::rpc_subscriptions::{Confirmations, RpcSubscriptions, SlotInfo};
use jsonrpc_core::{Error, ErrorCode, Result}; use jsonrpc_core::{Error, ErrorCode, Result};
use jsonrpc_derive::rpc; use jsonrpc_derive::rpc;
use jsonrpc_pubsub::{typed::Subscriber, Session, SubscriptionId}; use jsonrpc_pubsub::{typed::Subscriber, Session, SubscriptionId};
use solana_client::rpc_response::{Response as RpcResponse, RpcAccount, RpcKeyedAccount}; use solana_client::rpc_response::{
use solana_sdk::{clock::Slot, pubkey::Pubkey, signature::Signature, transaction}; Response as RpcResponse, RpcAccount, RpcKeyedAccount, RpcSignatureResult,
};
use solana_sdk::{clock::Slot, pubkey::Pubkey, signature::Signature};
use std::sync::{atomic, Arc}; use std::sync::{atomic, Arc};
// Suppress needless_return due to // Suppress needless_return due to
@ -74,7 +76,7 @@ pub trait RpcSolPubSub {
fn signature_subscribe( fn signature_subscribe(
&self, &self,
meta: Self::Metadata, meta: Self::Metadata,
subscriber: Subscriber<RpcResponse<transaction::Result<()>>>, subscriber: Subscriber<RpcResponse<RpcSignatureResult>>,
signature_str: String, signature_str: String,
confirmations: Option<Confirmations>, confirmations: Option<Confirmations>,
); );
@ -225,7 +227,7 @@ impl RpcSolPubSub for RpcSolPubSubImpl {
fn signature_subscribe( fn signature_subscribe(
&self, &self,
_meta: Self::Metadata, _meta: Self::Metadata,
subscriber: Subscriber<RpcResponse<transaction::Result<()>>>, subscriber: Subscriber<RpcResponse<RpcSignatureResult>>,
signature_str: String, signature_str: String,
confirmations: Option<Confirmations>, confirmations: Option<Confirmations>,
) { ) {
@ -385,7 +387,7 @@ mod tests {
// Test signature confirmation notification // Test signature confirmation notification
let (response, _) = robust_poll_or_panic(receiver); let (response, _) = robust_poll_or_panic(receiver);
let expected_res: Option<transaction::Result<()>> = Some(Ok(())); let expected_res = RpcSignatureResult { err: None };
let expected = json!({ let expected = json!({
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": "signatureNotification", "method": "signatureNotification",

View File

@ -8,7 +8,9 @@ use jsonrpc_pubsub::{
SubscriptionId, SubscriptionId,
}; };
use serde::Serialize; use serde::Serialize;
use solana_client::rpc_response::{Response, RpcAccount, RpcKeyedAccount, RpcResponseContext}; use solana_client::rpc_response::{
Response, RpcAccount, RpcKeyedAccount, RpcResponseContext, RpcSignatureResult,
};
use solana_ledger::bank_forks::BankForks; use solana_ledger::bank_forks::BankForks;
use solana_runtime::bank::Bank; use solana_runtime::bank::Bank;
use solana_sdk::{ use solana_sdk::{
@ -66,7 +68,7 @@ type RpcProgramSubscriptions = RwLock<
type RpcSignatureSubscriptions = RwLock< type RpcSignatureSubscriptions = RwLock<
HashMap< HashMap<
Signature, Signature,
HashMap<SubscriptionId, (Sink<Response<transaction::Result<()>>>, Confirmations)>, HashMap<SubscriptionId, (Sink<Response<RpcSignatureResult>>, Confirmations)>,
>, >,
>; >;
type RpcSlotSubscriptions = RwLock<HashMap<SubscriptionId, Sink<SlotInfo>>>; type RpcSlotSubscriptions = RwLock<HashMap<SubscriptionId, Sink<SlotInfo>>>;
@ -207,11 +209,15 @@ fn filter_account_result(
Box::new(iter::empty()) Box::new(iter::empty())
} }
fn filter_signature_result<S>(result: Option<S>, _root: Slot) -> Box<dyn Iterator<Item = S>> fn filter_signature_result(
where result: Option<transaction::Result<()>>,
S: 'static + Clone + Serialize, _root: Slot,
{ ) -> Box<dyn Iterator<Item = RpcSignatureResult>> {
Box::new(result.into_iter()) Box::new(
result
.into_iter()
.map(|result| RpcSignatureResult { err: result.err() }),
)
} }
fn filter_program_results( fn filter_program_results(
@ -430,7 +436,7 @@ impl RpcSubscriptions {
signature: Signature, signature: Signature,
confirmations: Option<Confirmations>, confirmations: Option<Confirmations>,
sub_id: SubscriptionId, sub_id: SubscriptionId,
subscriber: Subscriber<Response<transaction::Result<()>>>, subscriber: Subscriber<Response<RpcSignatureResult>>,
) { ) {
let mut subscriptions = self.signature_subscriptions.write().unwrap(); let mut subscriptions = self.signature_subscriptions.write().unwrap();
add_subscription( add_subscription(
@ -890,7 +896,7 @@ pub(crate) mod tests {
} }
subscriptions.notify_subscribers(1, &bank_forks); subscriptions.notify_subscribers(1, &bank_forks);
let expected_res: Option<transaction::Result<()>> = Some(Ok(())); let expected_res = RpcSignatureResult { err: None };
struct Notification { struct Notification {
slot: Slot, slot: Slot,

View File

@ -9,16 +9,12 @@ use reqwest::{self, header::CONTENT_TYPE};
use serde_json::{json, Value}; use serde_json::{json, Value};
use solana_client::{ use solana_client::{
rpc_client::{get_rpc_request_str, RpcClient}, rpc_client::{get_rpc_request_str, RpcClient},
rpc_response::Response, rpc_response::{Response, RpcSignatureResult},
}; };
use solana_core::{rpc_pubsub::gen_client::Client as PubsubClient, validator::TestValidator}; use solana_core::{rpc_pubsub::gen_client::Client as PubsubClient, validator::TestValidator};
use solana_sdk::{ use solana_sdk::{
commitment_config::CommitmentConfig, commitment_config::CommitmentConfig, hash::Hash, pubkey::Pubkey, signature::Signer,
hash::Hash, system_transaction, transaction::Transaction,
pubkey::Pubkey,
signature::Signer,
system_transaction,
transaction::{self, Transaction},
}; };
use std::{ use std::{
collections::HashSet, collections::HashSet,
@ -225,7 +221,7 @@ fn test_rpc_subscriptions() {
// Track when subscriptions are ready // Track when subscriptions are ready
let (ready_sender, ready_receiver) = channel::<()>(); let (ready_sender, ready_receiver) = channel::<()>();
// Track when status notifications are received // Track when status notifications are received
let (status_sender, status_receiver) = channel::<(String, Response<transaction::Result<()>>)>(); let (status_sender, status_receiver) = channel::<(String, Response<RpcSignatureResult>)>();
// Create the pub sub runtime // Create the pub sub runtime
let mut rt = Runtime::new().unwrap(); let mut rt = Runtime::new().unwrap();
@ -305,7 +301,7 @@ fn test_rpc_subscriptions() {
let timeout = deadline.saturating_duration_since(Instant::now()); let timeout = deadline.saturating_duration_since(Instant::now());
match status_receiver.recv_timeout(timeout) { match status_receiver.recv_timeout(timeout) {
Ok((sig, result)) => { Ok((sig, result)) => {
assert!(result.value.is_ok()); assert!(result.value.err.is_none());
assert!(signature_set.remove(&sig)); assert!(signature_set.remove(&sig));
} }
Err(_err) => { Err(_err) => {

View File

@ -299,12 +299,13 @@ The result field will be an object with the following fields:
* `transactions: <array>` - an array of JSON objects containing: * `transactions: <array>` - an array of JSON objects containing:
* `transaction: <object|string>` - [Transaction](#transaction-structure) object, either in JSON format or base-58 encoded binary data, depending on encoding parameter * `transaction: <object|string>` - [Transaction](#transaction-structure) object, either in JSON format or base-58 encoded binary data, depending on encoding parameter
* `meta: <object>` - transaction status metadata object, containing `null` or: * `meta: <object>` - transaction status metadata object, containing `null` or:
* `status: <object>` - Transaction status: * `err: <object | null>` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L14)
* `"Ok": null` - Transaction was successful * `fee: <u64>` - fee this transaction was charged, as u64 integer
* `"Err": <ERR>` - Transaction failed with TransactionError [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L18) * `preBalances: <array>` - array of u64 account balances from before the transaction was processed
* `fee: <u64>` - fee this transaction was charged, as u64 integer * `postBalances: <array>` - array of u64 account balances after the transaction was processed
* `preBalances: <array>` - array of u64 account balances from before the transaction was processed * DEPRECATED: `status: <object>` - Transaction status
* `postBalances: <array>` - array of u64 account balances after the transaction was processed * `"Ok": <null>` - Transaction was successful
* `"Err": <ERR>` - Transaction failed with TransactionError
* `rewards: <array>` - an array of JSON objects containing: * `rewards: <array>` - an array of JSON objects containing:
* `pubkey: <string>` - The public key, as base-58 encoded string, of the account that received the reward * `pubkey: <string>` - The public key, as base-58 encoded string, of the account that received the reward
* `lamports: <i64>`- number of reward lamports credited or debited by the account, as a i64 * `lamports: <i64>`- number of reward lamports credited or debited by the account, as a i64
@ -316,13 +317,13 @@ The result field will be an object with the following fields:
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedBlock","params":[430, "json"]}' localhost:8899 curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedBlock","params":[430, "json"]}' localhost:8899
// Result // Result
{"jsonrpc":"2.0","result":{"blockhash":"Gp3t5bfDsJv1ovP8cB1SuRhXVuoTqDv7p3tymyubYg5","parentSlot":429,"previousBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA","transactions":[{"transaction":{"message":{"accountKeys":["6H94zdiaYfRfPfKjYLjyr2VFBg6JHXygy84r3qhc3NsC","39UAy8hsoYPywGPGdmun747omSr79zLSjqvPJN3zetoH","SysvarS1otHashes111111111111111111111111111","SysvarC1ock11111111111111111111111111111111","Vote111111111111111111111111111111111111111"],"header":{"numReadonlySignedAccounts":0,"numReadonlyUnsignedAccounts":3,"numRequiredSignatures":2},"instructions":[{"accounts":[1,2,3],"data":"29z5mr1JoRmJYQ6ynmk3pf31cGFRziAF1M3mT3L6sFXf5cKLdkEaMXMT8AqLpD4CpcupHmuMEmtZHpomrwfdZetSomNy3d","programIdIndex":4}],"recentBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA"},"signatures":["35YGay1Lwjwgxe9zaH6APSHbt9gYQUCtBWTNL3aVwVGn9xTFw2fgds7qK5AL29mP63A9j3rh8KpN1TgSR62XCaby","4vANMjSKiwEchGSXwVrQkwHnmsbKQmy9vdrsYxWdCup1bLsFzX8gKrFTSVDCZCae2dbxJB9mPNhqB2sD1vvr4sAD"]},"meta":{"fee":18000,"postBalances":[499999972500,15298080,1,1,1],"preBalances":[499999990500,15298080,1,1,1],"status":{"Ok":null}}}]},"id":1} {"jsonrpc":"2.0","result":{"blockhash":"Gp3t5bfDsJv1ovP8cB1SuRhXVuoTqDv7p3tymyubYg5","parentSlot":429,"previousBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA","transactions":[{"transaction":{"message":{"accountKeys":["6H94zdiaYfRfPfKjYLjyr2VFBg6JHXygy84r3qhc3NsC","39UAy8hsoYPywGPGdmun747omSr79zLSjqvPJN3zetoH","SysvarS1otHashes111111111111111111111111111","SysvarC1ock11111111111111111111111111111111","Vote111111111111111111111111111111111111111"],"header":{"numReadonlySignedAccounts":0,"numReadonlyUnsignedAccounts":3,"numRequiredSignatures":2},"instructions":[{"accounts":[1,2,3],"data":"29z5mr1JoRmJYQ6ynmk3pf31cGFRziAF1M3mT3L6sFXf5cKLdkEaMXMT8AqLpD4CpcupHmuMEmtZHpomrwfdZetSomNy3d","programIdIndex":4}],"recentBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA"},"signatures":["35YGay1Lwjwgxe9zaH6APSHbt9gYQUCtBWTNL3aVwVGn9xTFw2fgds7qK5AL29mP63A9j3rh8KpN1TgSR62XCaby","4vANMjSKiwEchGSXwVrQkwHnmsbKQmy9vdrsYxWdCup1bLsFzX8gKrFTSVDCZCae2dbxJB9mPNhqB2sD1vvr4sAD"]},"meta":{"err":null,"fee":18000,"postBalances":[499999972500,15298080,1,1,1],"preBalances":[499999990500,15298080,1,1,1],"status":{"Ok":null}}}]},"id":1}
// Request // Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedBlock","params":[430, "binary"]}' localhost:8899 curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedBlock","params":[430, "binary"]}' localhost:8899
// Result // Result
{"jsonrpc":"2.0","result":{"blockhash":"Gp3t5bfDsJv1ovP8cB1SuRhXVuoTqDv7p3tymyubYg5","parentSlot":429,"previousBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA","transactions":[{"transaction":"81UZJt4dh4Do66jDhrgkQudS8J2N6iG3jaVav7gJrqJSFY4Ug53iA9JFJZh2gxKWcaFdLJwhHx9mRdg9JwDAWB4ywiu5154CRwXV4FMdnPLg7bhxRLwhhYaLsVgMF5AyNRcTzjCVoBvqFgDU7P8VEKDEiMvD3qxzm1pLZVxDG1LTQpT3Dz4Uviv4KQbFQNuC22KupBoyHFB7Zh6KFdMqux4M9PvhoqcoJsJKwXjWpKu7xmEKnnrSbfLadkgjBmmjhW3fdTrFvnhQdTkhtdJxUL1xS9GMuJQer8YgSKNtUXB1eXZQwXU8bU2BjYkZE6Q5Xww8hu9Z4E4Mo4QsooVtHoP6BM3NKw8zjVbWfoCQqxTrwuSzrNCWCWt58C24LHecH67CTt2uXbYSviixvrYkK7A3t68BxTJcF1dXJitEPTFe2ceTkauLJqrJgnER4iUrsjr26T8YgWvpY9wkkWFSviQW6wV5RASTCUasVEcrDiaKj8EQMkgyDoe9HyKitSVg67vMWJFpUXpQobseWJUs5FTWWzmfHmFp8FZ","meta":{"fee":18000,"postBalances":[499999972500,15298080,1,1,1],"preBalances":[499999990500,15298080,1,1,1],"status":{"Ok":null}}}]},"id":1} {"jsonrpc":"2.0","result":{"blockhash":"Gp3t5bfDsJv1ovP8cB1SuRhXVuoTqDv7p3tymyubYg5","parentSlot":429,"previousBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA","transactions":[{"transaction":"81UZJt4dh4Do66jDhrgkQudS8J2N6iG3jaVav7gJrqJSFY4Ug53iA9JFJZh2gxKWcaFdLJwhHx9mRdg9JwDAWB4ywiu5154CRwXV4FMdnPLg7bhxRLwhhYaLsVgMF5AyNRcTzjCVoBvqFgDU7P8VEKDEiMvD3qxzm1pLZVxDG1LTQpT3Dz4Uviv4KQbFQNuC22KupBoyHFB7Zh6KFdMqux4M9PvhoqcoJsJKwXjWpKu7xmEKnnrSbfLadkgjBmmjhW3fdTrFvnhQdTkhtdJxUL1xS9GMuJQer8YgSKNtUXB1eXZQwXU8bU2BjYkZE6Q5Xww8hu9Z4E4Mo4QsooVtHoP6BM3NKw8zjVbWfoCQqxTrwuSzrNCWCWt58C24LHecH67CTt2uXbYSviixvrYkK7A3t68BxTJcF1dXJitEPTFe2ceTkauLJqrJgnER4iUrsjr26T8YgWvpY9wkkWFSviQW6wV5RASTCUasVEcrDiaKj8EQMkgyDoe9HyKitSVg67vMWJFpUXpQobseWJUs5FTWWzmfHmFp8FZ","meta":{"err":null,"fee":18000,"postBalances":[499999972500,15298080,1,1,1],"preBalances":[499999990500,15298080,1,1,1],"status":{"Ok":null}}}]},"id":1}
``` ```
#### Transaction Structure #### Transaction Structure
@ -703,9 +704,10 @@ An array of:
* `<object>` * `<object>`
* `slot: <u64>` - The slot the transaction was processed * `slot: <u64>` - The slot the transaction was processed
* `confirmations: <usize | null>` - Number of blocks since signature confirmation, null if rooted * `confirmations: <usize | null>` - Number of blocks since signature confirmation, null if rooted
* `status: <object>` - Transaction status * `err: <object | null>` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L14)
* DEPRECATED: `status: <object>` - Transaction status
* `"Ok": <null>` - Transaction was successful * `"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) * `"Err": <ERR>` - Transaction failed with TransactionError
#### Example: #### Example:
@ -714,10 +716,10 @@ An array of:
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getSignatureStatus", "params":[["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW", "5j7s6NiJS3JAkvgkoc18WVAsiSaci2pxB2A6ueCJP4tprA2TFg9wSyTLeYouxPBJEMzJinENTkpA52YStRW5Dia7"]]]}' 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 // Result
{"jsonrpc":"2.0","result":{"context":{"slot":82},"value":[{"slot": 72, "confirmations": 10, "status": {"Ok": null}}, null]},"id":1} {"jsonrpc":"2.0","result":{"context":{"slot":82},"value":[{"slot": 72, "confirmations": 10, "err": null, "status": {"Ok": null}}, null]},"id":1}
// Result, first transaction rooted // Result, first transaction rooted
{"jsonrpc":"2.0","result":{"context":{"slot":82},"value":[{"slot": 48, "confirmations": null, "status": {"Ok": null}}, null]},"id":1} {"jsonrpc":"2.0","result":{"context":{"slot":82},"value":[{"slot": 48, "confirmations": null, "err": null, "status": {"Ok": null}}, null]},"id":1}
``` ```
### getSlot ### getSlot
@ -1226,7 +1228,7 @@ Subscribe to a transaction signature to receive notification when the transactio
#### Notification Format: #### Notification Format:
```bash ```bash
{"jsonrpc": "2.0","method": "signatureNotification", "params": {"result": "Confirmed","subscription":0}} {"jsonrpc": "2.0","method": "signatureNotification", "params": {"result": {"err": null}, "subscription":0}}
``` ```
### signatureUnsubscribe ### signatureUnsubscribe

View File

@ -37,8 +37,8 @@ use solana_sdk::{
transaction::Transaction, transaction::Transaction,
}; };
use solana_transaction_status::{ use solana_transaction_status::{
ConfirmedBlock, EncodedTransaction, Rewards, TransactionEncoding, TransactionStatusMeta, ConfirmedBlock, EncodedTransaction, Rewards, RpcTransactionStatusMeta, TransactionEncoding,
TransactionWithStatusMeta, TransactionStatusMeta, TransactionWithStatusMeta,
}; };
use solana_vote_program::{vote_instruction::VoteInstruction, vote_state::TIMESTAMP_SLOT_INTERVAL}; use solana_vote_program::{vote_instruction::VoteInstruction, vote_state::TIMESTAMP_SLOT_INTERVAL};
use std::{ use std::{
@ -1500,7 +1500,8 @@ impl Blockstore {
meta: self meta: self
.transaction_status_cf .transaction_status_cf
.get((slot, signature)) .get((slot, signature))
.expect("Expect database get to succeed"), .expect("Expect database get to succeed")
.map(RpcTransactionStatusMeta::from),
} }
}) })
.collect() .collect()
@ -4848,7 +4849,7 @@ pub mod tests {
.put_meta_bytes(slot - 1, &serialize(&parent_meta).unwrap()) .put_meta_bytes(slot - 1, &serialize(&parent_meta).unwrap())
.unwrap(); .unwrap();
let expected_transactions: Vec<(Transaction, Option<TransactionStatusMeta>)> = entries let expected_transactions: Vec<(Transaction, Option<RpcTransactionStatusMeta>)> = entries
.iter() .iter()
.cloned() .cloned()
.filter(|entry| !entry.is_tick()) .filter(|entry| !entry.is_tick())
@ -4887,12 +4888,15 @@ pub mod tests {
.unwrap(); .unwrap();
( (
transaction, transaction,
Some(TransactionStatusMeta { Some(
status: Ok(()), TransactionStatusMeta {
fee: 42, status: Ok(()),
pre_balances, fee: 42,
post_balances, pre_balances,
}), post_balances,
}
.into(),
),
) )
}) })
.collect(); .collect();

View File

@ -4,7 +4,7 @@ use solana_client::{client_error::Result as ClientResult, rpc_client::RpcClient}
use solana_metrics::{datapoint_error, datapoint_info}; use solana_metrics::{datapoint_error, datapoint_info};
use solana_sdk::{clock::Slot, program_utils::limited_deserialize, transaction::Transaction}; use solana_sdk::{clock::Slot, program_utils::limited_deserialize, transaction::Transaction};
use solana_stake_program::{stake_instruction::StakeInstruction, stake_state::Lockup}; use solana_stake_program::{stake_instruction::StakeInstruction, stake_state::Lockup};
use solana_transaction_status::{ConfirmedBlock, TransactionEncoding, TransactionStatusMeta}; use solana_transaction_status::{ConfirmedBlock, RpcTransactionStatusMeta, TransactionEncoding};
use std::{collections::HashMap, thread::sleep, time::Duration}; use std::{collections::HashMap, thread::sleep, time::Duration};
pub type PubkeyString = String; pub type PubkeyString = String;
@ -41,7 +41,7 @@ pub struct StakeAccountsInfo {
fn process_transaction( fn process_transaction(
slot: Slot, slot: Slot,
transaction: &Transaction, transaction: &Transaction,
meta: &TransactionStatusMeta, meta: &RpcTransactionStatusMeta,
stake_accounts: &mut HashMap<PubkeyString, StakeAccountInfo>, stake_accounts: &mut HashMap<PubkeyString, StakeAccountInfo>,
) { ) {
let mut last_instruction = true; let mut last_instruction = true;
@ -194,7 +194,7 @@ fn process_confirmed_block(
); );
} }
Some(meta) => { Some(meta) => {
if meta.status.is_ok() { if meta.err.is_none() {
if let Some(transaction) = rpc_transaction.transaction.decode() { if let Some(transaction) = rpc_transaction.transaction.decode() {
if transaction.verify().is_ok() { if transaction.verify().is_ok() {
process_transaction(slot, &transaction, &meta, stake_accounts); process_transaction(slot, &transaction, &meta, stake_accounts);

View File

@ -5,7 +5,7 @@ use bincode;
use solana_sdk::{ use solana_sdk::{
clock::Slot, clock::Slot,
message::MessageHeader, message::MessageHeader,
transaction::{Result, Transaction}, transaction::{Result, Transaction, TransactionError},
}; };
/// A duplicate representation of a Message for pretty JSON serialization /// A duplicate representation of a Message for pretty JSON serialization
@ -26,12 +26,35 @@ pub struct TransactionStatusMeta {
pub post_balances: Vec<u64>, pub post_balances: Vec<u64>,
} }
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcTransactionStatusMeta {
pub err: Option<TransactionError>,
pub status: Result<()>, // This field is deprecated. See https://github.com/solana-labs/solana/issues/9302
pub fee: u64,
pub pre_balances: Vec<u64>,
pub post_balances: Vec<u64>,
}
impl From<TransactionStatusMeta> for RpcTransactionStatusMeta {
fn from(meta: TransactionStatusMeta) -> Self {
Self {
err: meta.status.clone().err(),
status: meta.status,
fee: meta.fee,
pre_balances: meta.pre_balances,
post_balances: meta.post_balances,
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct TransactionStatus { pub struct TransactionStatus {
pub slot: Slot, pub slot: Slot,
pub confirmations: Option<usize>, pub confirmations: Option<usize>,
pub status: Result<()>, pub status: Result<()>,
pub err: Option<TransactionError>,
} }
#[derive(Debug, PartialEq, Serialize, Deserialize)] #[derive(Debug, PartialEq, Serialize, Deserialize)]
@ -74,7 +97,7 @@ pub struct RpcMessage {
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct TransactionWithStatusMeta { pub struct TransactionWithStatusMeta {
pub transaction: EncodedTransaction, pub transaction: EncodedTransaction,
pub meta: Option<TransactionStatusMeta>, pub meta: Option<RpcTransactionStatusMeta>,
} }
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]