diff --git a/Cargo.lock b/Cargo.lock index ed292c5fbf..35c382e8a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3898,6 +3898,7 @@ dependencies = [ "solana-logger 1.1.0", "solana-net-utils 1.1.0", "solana-sdk 1.1.0", + "solana-transaction-status 1.1.0", "solana-vote-program 1.1.0", "thiserror 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", "tungstenite 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3972,6 +3973,7 @@ dependencies = [ "solana-storage-program 1.1.0", "solana-streamer 1.1.0", "solana-sys-tuner 1.1.0", + "solana-transaction-status 1.1.0", "solana-vote-program 1.1.0", "solana-vote-signer 1.1.0", "systemstat 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4205,7 +4207,6 @@ dependencies = [ "serde_bytes 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "solana-budget-program 1.1.0", - "solana-client 1.1.0", "solana-genesis-programs 1.1.0", "solana-logger 1.1.0", "solana-measure 1.1.0", @@ -4217,6 +4218,7 @@ dependencies = [ "solana-runtime 1.1.0", "solana-sdk 1.1.0", "solana-stake-program 1.1.0", + "solana-transaction-status 1.1.0", "solana-vote-program 1.1.0", "symlink 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "tar 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4645,6 +4647,17 @@ dependencies = [ "users 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "solana-transaction-status" +version = "1.1.0" +dependencies = [ + "bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bs58 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", + "solana-sdk 1.1.0", +] + [[package]] name = "solana-upload-perf" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index 9b5be03613..a1a869f59f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,7 @@ members = [ "sdk-c", "scripts", "sys-tuner", + "transaction-status", "upload-perf", "net-utils", "vote-signer", diff --git a/client/Cargo.toml b/client/Cargo.toml index 3fee7e96db..139d791c89 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -19,6 +19,7 @@ reqwest = { version = "0.10.4", default-features = false, features = ["blocking" serde = "1.0.105" serde_derive = "1.0.103" serde_json = "1.0.48" +solana-transaction-status = { path = "../transaction-status", version = "1.1.0" } solana-net-utils = { path = "../net-utils", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.0" } solana-vote-program = { path = "../programs/vote", version = "1.1.0" } diff --git a/client/src/mock_rpc_client_request.rs b/client/src/mock_rpc_client_request.rs index f442ae6319..91d3b6754d 100644 --- a/client/src/mock_rpc_client_request.rs +++ b/client/src/mock_rpc_client_request.rs @@ -2,7 +2,7 @@ use crate::{ client_error::Result, generic_rpc_client_request::GenericRpcClientRequest, rpc_request::RpcRequest, - rpc_response::{Response, RpcResponseContext, RpcTransactionStatus}, + rpc_response::{Response, RpcResponseContext}, }; use serde_json::{Number, Value}; use solana_sdk::{ @@ -10,6 +10,7 @@ use solana_sdk::{ instruction::InstructionError, transaction::{self, TransactionError}, }; +use solana_transaction_status::TransactionStatus; use std::{collections::HashMap, sync::RwLock}; pub const PUBKEY: &str = "7RoSF9fUmdphVCpabEoefH81WwrW7orsWonXWqTXkKV8"; @@ -100,7 +101,7 @@ impl GenericRpcClientRequest for MockRpcClientRequest { let status = if self.url == "sig_not_found" { None } else { - Some(RpcTransactionStatus { status, slot: 1 }) + Some(TransactionStatus { status, slot: 1 }) }; serde_json::to_value(vec![status])? } diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index 60aa7eb300..7776bbfdb4 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -23,6 +23,7 @@ use solana_sdk::{ signers::Signers, transaction::{self, Transaction, TransactionError}, }; +use solana_transaction_status::{ConfirmedBlock, TransactionEncoding, TransactionStatus}; use solana_vote_program::vote_state::MAX_LOCKOUT_HISTORY; use std::{ error, @@ -119,7 +120,7 @@ impl RpcClient { json!([[signature.to_string()], commitment_config]), 5, )?; - let result: Vec> = + let result: Vec> = serde_json::from_value(signature_status).unwrap(); Ok(result[0].clone().map(|status_meta| status_meta.status)) } @@ -185,15 +186,15 @@ impl RpcClient { .map_err(|err| ClientError::new_with_command(err.into(), "GetClusterNodes")) } - pub fn get_confirmed_block(&self, slot: Slot) -> ClientResult { - self.get_confirmed_block_with_encoding(slot, RpcTransactionEncoding::Json) + pub fn get_confirmed_block(&self, slot: Slot) -> ClientResult { + self.get_confirmed_block_with_encoding(slot, TransactionEncoding::Json) } pub fn get_confirmed_block_with_encoding( &self, slot: Slot, - encoding: RpcTransactionEncoding, - ) -> ClientResult { + encoding: TransactionEncoding, + ) -> ClientResult { let response = self .client .send(&RpcRequest::GetConfirmedBlock, json!([slot, encoding]), 0) diff --git a/client/src/rpc_response.rs b/client/src/rpc_response.rs index 65de9459a5..389cee91f0 100644 --- a/client/src/rpc_response.rs +++ b/client/src/rpc_response.rs @@ -1,12 +1,10 @@ use crate::{client_error, rpc_request::RpcError}; -use bincode::serialize; use solana_sdk::{ account::Account, clock::{Epoch, Slot}, fee_calculator::{FeeCalculator, FeeRateGovernor}, - message::MessageHeader, pubkey::Pubkey, - transaction::{Result, Transaction}, + transaction::Result, }; use std::{collections::HashMap, net::SocketAddr, str::FromStr}; @@ -30,126 +28,6 @@ pub struct RpcBlockCommitment { pub total_stake: u64, } -#[derive(Debug, PartialEq, Serialize, Deserialize)] -pub struct RpcReward { - pub pubkey: String, - pub lamports: i64, -} - -pub type RpcRewards = Vec; - -#[derive(Debug, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct RpcConfirmedBlock { - pub previous_blockhash: String, - pub blockhash: String, - pub parent_slot: Slot, - pub transactions: Vec, - pub rewards: RpcRewards, -} - -#[derive(Debug, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct RpcTransactionWithStatusMeta { - pub transaction: RpcEncodedTransaction, - pub meta: Option, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -#[serde(rename_all = "camelCase")] -pub enum RpcTransactionEncoding { - Binary, - Json, -} - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", untagged)] -pub enum RpcEncodedTransaction { - Binary(String), - Json(RpcTransaction), -} - -impl RpcEncodedTransaction { - pub fn encode(transaction: Transaction, encoding: RpcTransactionEncoding) -> Self { - if encoding == RpcTransactionEncoding::Json { - RpcEncodedTransaction::Json(RpcTransaction { - signatures: transaction - .signatures - .iter() - .map(|sig| sig.to_string()) - .collect(), - message: RpcMessage { - header: transaction.message.header, - account_keys: transaction - .message - .account_keys - .iter() - .map(|pubkey| pubkey.to_string()) - .collect(), - recent_blockhash: transaction.message.recent_blockhash.to_string(), - instructions: transaction - .message - .instructions - .iter() - .map(|instruction| RpcCompiledInstruction { - program_id_index: instruction.program_id_index, - accounts: instruction.accounts.clone(), - data: bs58::encode(instruction.data.clone()).into_string(), - }) - .collect(), - }, - }) - } else { - RpcEncodedTransaction::Binary( - bs58::encode(serialize(&transaction).unwrap()).into_string(), - ) - } - } -} - -/// A duplicate representation of a Transaction for pretty JSON serialization -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct RpcTransaction { - pub signatures: Vec, - pub message: RpcMessage, -} - -/// A duplicate representation of a Message for pretty JSON serialization -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct RpcMessage { - pub header: MessageHeader, - pub account_keys: Vec, - pub recent_blockhash: String, - pub instructions: Vec, -} - -/// A duplicate representation of a Message for pretty JSON serialization -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct RpcCompiledInstruction { - pub program_id_index: u8, - pub accounts: Vec, - pub data: String, -} - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct RpcTransactionStatusMeta { - pub status: Result<()>, - pub fee: u64, - pub pre_balances: Vec, - pub post_balances: Vec, -} - -#[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 { diff --git a/core/Cargo.toml b/core/Cargo.toml index 93d634d57b..4d5d222338 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -44,6 +44,7 @@ serde_json = "1.0.48" solana-budget-program = { path = "../programs/budget", version = "1.1.0" } solana-clap-utils = { path = "../clap-utils", version = "1.1.0" } solana-client = { path = "../client", version = "1.1.0" } +solana-transaction-status = { path = "../transaction-status", version = "1.1.0" } solana-faucet = { path = "../faucet", version = "1.1.0" } ed25519-dalek = "=1.0.0-pre.1" solana-ledger = { path = "../ledger", version = "1.1.0" } diff --git a/core/src/banking_stage.rs b/core/src/banking_stage.rs index 1fd7a2255c..e44859515a 100644 --- a/core/src/banking_stage.rs +++ b/core/src/banking_stage.rs @@ -1017,7 +1017,6 @@ mod tests { }; use crossbeam_channel::unbounded; use itertools::Itertools; - use solana_client::rpc_response::{RpcEncodedTransaction, RpcTransactionWithStatusMeta}; use solana_ledger::{ blockstore::entries_to_test_shreds, entry::{next_entry, Entry, EntrySlice}, @@ -1032,6 +1031,7 @@ mod tests { system_transaction, transaction::TransactionError, }; + use solana_transaction_status::{EncodedTransaction, TransactionWithStatusMeta}; use std::{sync::atomic::Ordering, thread::sleep}; #[test] @@ -1977,10 +1977,10 @@ mod tests { let confirmed_block = blockstore.get_confirmed_block(bank.slot(), None).unwrap(); assert_eq!(confirmed_block.transactions.len(), 3); - for RpcTransactionWithStatusMeta { transaction, meta } in + for TransactionWithStatusMeta { transaction, meta } in confirmed_block.transactions.into_iter() { - if let RpcEncodedTransaction::Json(transaction) = transaction { + if let EncodedTransaction::Json(transaction) = transaction { if transaction.signatures[0] == success_signature.to_string() { assert_eq!(meta.unwrap().status, Ok(())); } else if transaction.signatures[0] == ix_error_signature.to_string() { diff --git a/core/src/replay_stage.rs b/core/src/replay_stage.rs index c8564b1d1d..2c631c1248 100644 --- a/core/src/replay_stage.rs +++ b/core/src/replay_stage.rs @@ -1281,7 +1281,6 @@ pub(crate) mod tests { transaction_status_service::TransactionStatusService, }; use crossbeam_channel::unbounded; - use solana_client::rpc_response::{RpcEncodedTransaction, RpcTransactionWithStatusMeta}; use solana_ledger::{ blockstore::make_slot_entries, blockstore::{entries_to_test_shreds, BlockstoreError}, @@ -1306,6 +1305,7 @@ pub(crate) mod tests { transaction::TransactionError, }; use solana_stake_program::stake_state; + use solana_transaction_status::{EncodedTransaction, TransactionWithStatusMeta}; use solana_vote_program::{ vote_state::{self, Vote, VoteState, VoteStateVersions}, vote_transaction, @@ -2137,10 +2137,10 @@ pub(crate) mod tests { let confirmed_block = blockstore.get_confirmed_block(slot, None).unwrap(); assert_eq!(confirmed_block.transactions.len(), 3); - for RpcTransactionWithStatusMeta { transaction, meta } in + for TransactionWithStatusMeta { transaction, meta } in confirmed_block.transactions.into_iter() { - if let RpcEncodedTransaction::Json(transaction) = transaction { + if let EncodedTransaction::Json(transaction) = transaction { if transaction.signatures[0] == signatures[0].to_string() { assert_eq!(meta.unwrap().status, Ok(())); } else if transaction.signatures[0] == signatures[1].to_string() { diff --git a/core/src/rewards_recorder_service.rs b/core/src/rewards_recorder_service.rs index 525709052e..18fdbcc542 100644 --- a/core/src/rewards_recorder_service.rs +++ b/core/src/rewards_recorder_service.rs @@ -1,7 +1,7 @@ use crossbeam_channel::{Receiver, RecvTimeoutError, Sender}; -use solana_client::rpc_response::RpcReward; use solana_ledger::blockstore::Blockstore; use solana_sdk::{clock::Slot, pubkey::Pubkey}; +use solana_transaction_status::Reward; use std::{ sync::{ atomic::{AtomicBool, Ordering}, @@ -49,7 +49,7 @@ impl RewardsRecorderService { let (slot, rewards) = rewards_receiver.recv_timeout(Duration::from_secs(1))?; let rpc_rewards = rewards .into_iter() - .map(|(pubkey, lamports)| RpcReward { + .map(|(pubkey, lamports)| Reward { pubkey: pubkey.to_string(), lamports, }) diff --git a/core/src/rpc.rs b/core/src/rpc.rs index 79559ec52b..24588f2917 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -25,6 +25,7 @@ use solana_sdk::{ timing::slot_duration_from_slots_per_year, transaction::Transaction, }; +use solana_transaction_status::{ConfirmedBlock, TransactionEncoding, TransactionStatus}; use solana_vote_program::vote_state::{VoteState, MAX_LOCKOUT_HISTORY}; use std::{ collections::HashMap, @@ -366,8 +367,8 @@ impl JsonRpcRequestProcessor { pub fn get_confirmed_block( &self, slot: Slot, - encoding: Option, - ) -> Result> { + encoding: Option, + ) -> Result> { if self.config.enable_rpc_transaction_history { Ok(self.blockstore.get_confirmed_block(slot, encoding).ok()) } else { @@ -421,14 +422,14 @@ impl JsonRpcRequestProcessor { &self, signatures: Vec, commitment: Option, - ) -> Result>> { - let mut statuses: Vec> = vec![]; + ) -> Result>> { + let mut statuses: Vec> = vec![]; let bank = self.bank(commitment); for signature in signatures { let status = bank.get_signature_confirmation_status(&signature).map( - |SignatureConfirmationStatus { slot, status, .. }| RpcTransactionStatus { + |SignatureConfirmationStatus { slot, status, .. }| TransactionStatus { slot, status, }, @@ -565,7 +566,7 @@ pub trait RpcSol { meta: Self::Metadata, signature_strs: Vec, commitment: Option, - ) -> Result>>; + ) -> Result>>; #[rpc(meta, name = "getSlot")] fn get_slot(&self, meta: Self::Metadata, commitment: Option) -> Result; @@ -662,8 +663,8 @@ pub trait RpcSol { &self, meta: Self::Metadata, slot: Slot, - encoding: Option, - ) -> Result>; + encoding: Option, + ) -> Result>; #[rpc(meta, name = "getBlockTime")] fn get_block_time(&self, meta: Self::Metadata, slot: Slot) -> Result>; @@ -911,7 +912,7 @@ impl RpcSol for RpcSolImpl { meta: Self::Metadata, signature_strs: Vec, commitment: Option, - ) -> Result>> { + ) -> Result>> { let mut signatures: Vec = vec![]; for signature_str in signature_strs { signatures.push(verify_signature(&signature_str)?); @@ -1182,8 +1183,8 @@ impl RpcSol for RpcSolImpl { &self, meta: Self::Metadata, slot: Slot, - encoding: Option, - ) -> Result> { + encoding: Option, + ) -> Result> { meta.request_processor .read() .unwrap() @@ -1216,7 +1217,6 @@ pub mod tests { }; use bincode::deserialize; use jsonrpc_core::{MetaIoHandler, Output, Response, Value}; - use solana_client::rpc_response::{RpcEncodedTransaction, RpcTransactionWithStatusMeta}; use solana_ledger::{ blockstore::entries_to_test_shreds, blockstore_processor::fill_blockstore_slot_with_ticks, @@ -1233,6 +1233,7 @@ pub mod tests { system_transaction, transaction::{self, TransactionError}, }; + use solana_transaction_status::{EncodedTransaction, TransactionWithStatusMeta}; use solana_vote_program::{ vote_instruction, vote_state::{Vote, VoteInit, MAX_LOCKOUT_HISTORY}, @@ -1815,9 +1816,8 @@ pub mod tests { let res = io.handle_request_sync(&req, meta.clone()); let expected_res: transaction::Result<()> = Ok(()); let json: Value = serde_json::from_str(&res.unwrap()).unwrap(); - let result: Vec> = - serde_json::from_value(json["result"].clone()) - .expect("actual response deserialization"); + let result: Vec> = 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 @@ -1828,9 +1828,8 @@ pub mod tests { ); let res = io.handle_request_sync(&req, meta.clone()); let json: Value = serde_json::from_str(&res.unwrap()).unwrap(); - let result: Vec> = - serde_json::from_value(json["result"].clone()) - .expect("actual response deserialization"); + let result: Vec> = serde_json::from_value(json["result"].clone()) + .expect("actual response deserialization"); assert!(result[0].is_none()); // Test getSignatureStatus request on a TransactionError @@ -1844,9 +1843,8 @@ pub mod tests { InstructionError::CustomError(1), )); let json: Value = serde_json::from_str(&res.unwrap()).unwrap(); - let result: Vec> = - serde_json::from_value(json["result"].clone()) - .expect("actual response deserialization"); + let result: Vec> = serde_json::from_value(json["result"].clone()) + .expect("actual response deserialization"); assert_eq!(expected_res, result[0].as_ref().unwrap().status); } @@ -2286,15 +2284,15 @@ pub mod tests { let res = io.handle_request_sync(&req, meta.clone()); let result: Value = serde_json::from_str(&res.expect("actual response")) .expect("actual response deserialization"); - let confirmed_block: Option = + let confirmed_block: Option = serde_json::from_value(result["result"].clone()).unwrap(); let confirmed_block = confirmed_block.unwrap(); assert_eq!(confirmed_block.transactions.len(), 3); - for RpcTransactionWithStatusMeta { transaction, meta } in + for TransactionWithStatusMeta { transaction, meta } in confirmed_block.transactions.into_iter() { - if let RpcEncodedTransaction::Json(transaction) = transaction { + if let EncodedTransaction::Json(transaction) = transaction { if transaction.signatures[0] == confirmed_block_signatures[0].to_string() { assert_eq!(transaction.message.recent_blockhash, blockhash.to_string()); assert_eq!(meta.unwrap().status, Ok(())); @@ -2318,15 +2316,15 @@ pub mod tests { let res = io.handle_request_sync(&req, meta); let result: Value = serde_json::from_str(&res.expect("actual response")) .expect("actual response deserialization"); - let confirmed_block: Option = + let confirmed_block: Option = serde_json::from_value(result["result"].clone()).unwrap(); let confirmed_block = confirmed_block.unwrap(); assert_eq!(confirmed_block.transactions.len(), 3); - for RpcTransactionWithStatusMeta { transaction, meta } in + for TransactionWithStatusMeta { transaction, meta } in confirmed_block.transactions.into_iter() { - if let RpcEncodedTransaction::Binary(transaction) = transaction { + if let EncodedTransaction::Binary(transaction) = transaction { let decoded_transaction: Transaction = deserialize(&bs58::decode(&transaction).into_vec().unwrap()).unwrap(); if decoded_transaction.signatures[0] == confirmed_block_signatures[0] { diff --git a/core/src/transaction_status_service.rs b/core/src/transaction_status_service.rs index b541be2e12..3baef68a18 100644 --- a/core/src/transaction_status_service.rs +++ b/core/src/transaction_status_service.rs @@ -1,10 +1,10 @@ use crossbeam_channel::{Receiver, RecvTimeoutError}; -use solana_client::rpc_response::RpcTransactionStatusMeta; use solana_ledger::{blockstore::Blockstore, blockstore_processor::TransactionStatusBatch}; use solana_runtime::{ bank::{Bank, HashAgeKind}, nonce_utils, }; +use solana_transaction_status::TransactionStatusMeta; use std::{ sync::{ atomic::{AtomicBool, Ordering}, @@ -73,7 +73,7 @@ impl TransactionStatusService { blockstore .write_transaction_status( (slot, transaction.signatures[0]), - &RpcTransactionStatusMeta { + &TransactionStatusMeta { status, fee, pre_balances, diff --git a/ledger/Cargo.toml b/ledger/Cargo.toml index 10b3d23b8d..4ad9294516 100644 --- a/ledger/Cargo.toml +++ b/ledger/Cargo.toml @@ -28,7 +28,7 @@ reed-solomon-erasure = { package = "solana-reed-solomon-erasure", version = "4.0 regex = "1.3.6" serde = "1.0.105" serde_bytes = "0.11.3" -solana-client = { path = "../client", version = "1.1.0" } +solana-transaction-status = { path = "../transaction-status", version = "1.1.0" } solana-genesis-programs = { path = "../genesis-programs", version = "1.1.0" } solana-logger = { path = "../logger", version = "1.1.0" } solana-measure = { path = "../measure", version = "1.1.0" } diff --git a/ledger/src/blockstore.rs b/ledger/src/blockstore.rs index a215379ce4..8f797bc949 100644 --- a/ledger/src/blockstore.rs +++ b/ledger/src/blockstore.rs @@ -22,10 +22,6 @@ use rayon::{ ThreadPool, }; use rocksdb::DBRawIterator; -use solana_client::rpc_response::{ - RpcConfirmedBlock, RpcEncodedTransaction, RpcRewards, RpcTransactionEncoding, - RpcTransactionStatusMeta, RpcTransactionWithStatusMeta, -}; use solana_measure::measure::Measure; use solana_metrics::{datapoint_debug, datapoint_error}; use solana_rayon_threadlimit::get_thread_count; @@ -40,6 +36,10 @@ use solana_sdk::{ timing::timestamp, transaction::Transaction, }; +use solana_transaction_status::{ + ConfirmedBlock, EncodedTransaction, Rewards, TransactionEncoding, TransactionStatusMeta, + TransactionWithStatusMeta, +}; use solana_vote_program::{vote_instruction::VoteInstruction, vote_state::TIMESTAMP_SLOT_INTERVAL}; use std::{ cell::RefCell, @@ -1430,15 +1430,15 @@ impl Blockstore { pub fn get_confirmed_block( &self, slot: Slot, - encoding: Option, - ) -> Result { + encoding: Option, + ) -> Result { let lowest_cleanup_slot = self.lowest_cleanup_slot.read().unwrap(); // lowest_cleanup_slot is the last slot that was not cleaned up by // LedgerCleanupService if *lowest_cleanup_slot > slot { return Err(BlockstoreError::SlotCleanedUp); } - let encoding = encoding.unwrap_or(RpcTransactionEncoding::Json); + let encoding = encoding.unwrap_or(TransactionEncoding::Json); if self.is_root(slot) { let slot_meta_cf = self.db.column::(); let slot_meta = match slot_meta_cf.get(slot)? { @@ -1467,7 +1467,7 @@ impl Blockstore { let rewards = self.rewards_cf.get(slot)?.unwrap_or_else(|| vec![]); - let block = RpcConfirmedBlock { + let block = ConfirmedBlock { previous_blockhash: previous_blockhash.to_string(), blockhash: blockhash.to_string(), parent_slot: slot_meta.parent_slot, @@ -1487,15 +1487,14 @@ impl Blockstore { fn map_transactions_to_statuses<'a>( &self, slot: Slot, - encoding: RpcTransactionEncoding, + encoding: TransactionEncoding, iterator: impl Iterator + 'a, - ) -> Vec { + ) -> Vec { iterator .map(|transaction| { let signature = transaction.signatures[0]; - let encoded_transaction = - RpcEncodedTransaction::encode(transaction, encoding.clone()); - RpcTransactionWithStatusMeta { + let encoded_transaction = EncodedTransaction::encode(transaction, encoding.clone()); + TransactionWithStatusMeta { transaction: encoded_transaction, meta: self .transaction_status_cf @@ -1509,23 +1508,23 @@ impl Blockstore { pub fn read_transaction_status( &self, index: (Slot, Signature), - ) -> Result> { + ) -> Result> { self.transaction_status_cf.get(index) } pub fn write_transaction_status( &self, index: (Slot, Signature), - status: &RpcTransactionStatusMeta, + status: &TransactionStatusMeta, ) -> Result<()> { self.transaction_status_cf.put(index, status) } - pub fn read_rewards(&self, index: Slot) -> Result> { + pub fn read_rewards(&self, index: Slot) -> Result> { self.rewards_cf.get(index) } - pub fn write_rewards(&self, index: Slot, rewards: RpcRewards) -> Result<()> { + pub fn write_rewards(&self, index: Slot, rewards: Rewards) -> Result<()> { self.rewards_cf.put(index, &rewards) } @@ -4848,7 +4847,7 @@ pub mod tests { .put_meta_bytes(slot - 1, &serialize(&parent_meta).unwrap()) .unwrap(); - let expected_transactions: Vec<(Transaction, Option)> = entries + let expected_transactions: Vec<(Transaction, Option)> = entries .iter() .cloned() .filter(|entry| !entry.is_tick()) @@ -4865,7 +4864,7 @@ pub mod tests { .transaction_status_cf .put( (slot, signature), - &RpcTransactionStatusMeta { + &TransactionStatusMeta { status: Ok(()), fee: 42, pre_balances: pre_balances.clone(), @@ -4877,7 +4876,7 @@ pub mod tests { .transaction_status_cf .put( (slot + 1, signature), - &RpcTransactionStatusMeta { + &TransactionStatusMeta { status: Ok(()), fee: 42, pre_balances: pre_balances.clone(), @@ -4887,7 +4886,7 @@ pub mod tests { .unwrap(); ( transaction, - Some(RpcTransactionStatusMeta { + Some(TransactionStatusMeta { status: Ok(()), fee: 42, pre_balances, @@ -4904,12 +4903,12 @@ pub mod tests { let confirmed_block = ledger.get_confirmed_block(slot, None).unwrap(); assert_eq!(confirmed_block.transactions.len(), 100); - let expected_block = RpcConfirmedBlock { + let expected_block = ConfirmedBlock { transactions: expected_transactions .iter() .cloned() - .map(|(tx, meta)| RpcTransactionWithStatusMeta { - transaction: RpcEncodedTransaction::encode(tx, RpcTransactionEncoding::Json), + .map(|(tx, meta)| TransactionWithStatusMeta { + transaction: EncodedTransaction::encode(tx, TransactionEncoding::Json), meta, }) .collect(), @@ -4925,12 +4924,12 @@ pub mod tests { let confirmed_block = ledger.get_confirmed_block(slot + 1, None).unwrap(); assert_eq!(confirmed_block.transactions.len(), 100); - let expected_block = RpcConfirmedBlock { + let expected_block = ConfirmedBlock { transactions: expected_transactions .iter() .cloned() - .map(|(tx, meta)| RpcTransactionWithStatusMeta { - transaction: RpcEncodedTransaction::encode(tx, RpcTransactionEncoding::Json), + .map(|(tx, meta)| TransactionWithStatusMeta { + transaction: EncodedTransaction::encode(tx, TransactionEncoding::Json), meta, }) .collect(), @@ -5150,7 +5149,7 @@ pub mod tests { assert!(transaction_status_cf .put( (0, Signature::default()), - &RpcTransactionStatusMeta { + &TransactionStatusMeta { status: solana_sdk::transaction::Result::<()>::Err( TransactionError::AccountNotFound ), @@ -5162,7 +5161,7 @@ pub mod tests { .is_ok()); // result found - let RpcTransactionStatusMeta { + let TransactionStatusMeta { status, fee, pre_balances, @@ -5180,7 +5179,7 @@ pub mod tests { assert!(transaction_status_cf .put( (9, Signature::default()), - &RpcTransactionStatusMeta { + &TransactionStatusMeta { status: solana_sdk::transaction::Result::<()>::Ok(()), fee: 9u64, pre_balances: pre_balances_vec.clone(), @@ -5190,7 +5189,7 @@ pub mod tests { .is_ok()); // result found - let RpcTransactionStatusMeta { + let TransactionStatusMeta { status, fee, pre_balances, @@ -5245,7 +5244,7 @@ pub mod tests { transaction_status_cf .put( (slot, transaction.signatures[0]), - &RpcTransactionStatusMeta { + &TransactionStatusMeta { status: solana_sdk::transaction::Result::<()>::Err( TransactionError::AccountNotFound, ), @@ -5268,7 +5267,7 @@ pub mod tests { let map = blockstore.map_transactions_to_statuses( slot, - RpcTransactionEncoding::Json, + TransactionEncoding::Json, transactions.into_iter(), ); assert_eq!(map.len(), 5); diff --git a/ledger/src/blockstore_db.rs b/ledger/src/blockstore_db.rs index 9ac9266940..5f29f40012 100644 --- a/ledger/src/blockstore_db.rs +++ b/ledger/src/blockstore_db.rs @@ -10,8 +10,8 @@ use rocksdb::{ }; use serde::de::DeserializeOwned; use serde::Serialize; -use solana_client::rpc_response::{RpcRewards, RpcTransactionStatusMeta}; use solana_sdk::{clock::Slot, signature::Signature}; +use solana_transaction_status::{Rewards, TransactionStatusMeta}; use std::{collections::HashMap, fs, marker::PhantomData, path::Path, sync::Arc}; use thiserror::Error; @@ -269,7 +269,7 @@ pub trait TypedColumn: Column { } impl TypedColumn for columns::TransactionStatus { - type Type = RpcTransactionStatusMeta; + type Type = TransactionStatusMeta; } pub trait SlotColumn {} @@ -330,7 +330,7 @@ impl ColumnName for columns::Rewards { const NAME: &'static str = REWARDS_CF; } impl TypedColumn for columns::Rewards { - type Type = RpcRewards; + type Type = Rewards; } impl Column for columns::ShredCode { diff --git a/transaction-status/Cargo.toml b/transaction-status/Cargo.toml new file mode 100644 index 0000000000..f3d53a9987 --- /dev/null +++ b/transaction-status/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "solana-transaction-status" +version = "1.1.0" +description = "Solana transaction status types" +authors = ["Solana Maintainers "] +repository = "https://github.com/solana-labs/solana" +homepage = "https://solana.com/" +license = "Apache-2.0" +edition = "2018" + +[dependencies] +bincode = "1.2.1" +bs58 = "0.3.0" +solana-sdk = { path = "../sdk", version = "1.1.0" } +serde = "1.0.105" +serde_derive = "1.0.103" diff --git a/transaction-status/src/lib.rs b/transaction-status/src/lib.rs new file mode 100644 index 0000000000..2bd4faa5bb --- /dev/null +++ b/transaction-status/src/lib.rs @@ -0,0 +1,127 @@ +#[macro_use] +extern crate serde_derive; + +use bincode::serialize; +use solana_sdk::{ + clock::Slot, + message::MessageHeader, + transaction::{Result, Transaction}, +}; + +/// A duplicate representation of a Message for pretty JSON serialization +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcCompiledInstruction { + pub program_id_index: u8, + pub accounts: Vec, + pub data: String, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TransactionStatusMeta { + pub status: Result<()>, + pub fee: u64, + pub pre_balances: Vec, + pub post_balances: Vec, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TransactionStatus { + pub slot: Slot, + pub status: Result<()>, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub struct Reward { + pub pubkey: String, + pub lamports: i64, +} + +pub type Rewards = Vec; + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ConfirmedBlock { + pub previous_blockhash: String, + pub blockhash: String, + pub parent_slot: Slot, + pub transactions: Vec, + pub rewards: Rewards, +} + +/// A duplicate representation of a Transaction for pretty JSON serialization +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcTransaction { + pub signatures: Vec, + pub message: RpcMessage, +} + +/// A duplicate representation of a Message for pretty JSON serialization +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcMessage { + pub header: MessageHeader, + pub account_keys: Vec, + pub recent_blockhash: String, + pub instructions: Vec, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TransactionWithStatusMeta { + pub transaction: EncodedTransaction, + pub meta: Option, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[serde(rename_all = "camelCase")] +pub enum TransactionEncoding { + Binary, + Json, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase", untagged)] +pub enum EncodedTransaction { + Binary(String), + Json(RpcTransaction), +} + +impl EncodedTransaction { + pub fn encode(transaction: Transaction, encoding: TransactionEncoding) -> Self { + if encoding == TransactionEncoding::Json { + EncodedTransaction::Json(RpcTransaction { + signatures: transaction + .signatures + .iter() + .map(|sig| sig.to_string()) + .collect(), + message: RpcMessage { + header: transaction.message.header, + account_keys: transaction + .message + .account_keys + .iter() + .map(|pubkey| pubkey.to_string()) + .collect(), + recent_blockhash: transaction.message.recent_blockhash.to_string(), + instructions: transaction + .message + .instructions + .iter() + .map(|instruction| RpcCompiledInstruction { + program_id_index: instruction.program_id_index, + accounts: instruction.accounts.clone(), + data: bs58::encode(instruction.data.clone()).into_string(), + }) + .collect(), + }, + }) + } else { + EncodedTransaction::Binary(bs58::encode(serialize(&transaction).unwrap()).into_string()) + } + } +}