@ -278,16 +278,17 @@ Returns identity and transaction information about a confirmed block in the ledg
|
|||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `integer` - slot, as u64 integer
|
* `integer` - slot, as u64 integer
|
||||||
|
* `string` - (optional) encoding for each returned Transaction, either "json" or "binary". If not provided, the default encoding is JSON.
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
The result field will be an object with the following fields:
|
The result field will be an object with the following fields:
|
||||||
|
|
||||||
* `blockhash` - the blockhash of this block
|
* `blockhash` - the blockhash of this block, as base-58 encoded string
|
||||||
* `previousBlockhash` - the blockhash of this block's parent
|
* `previousBlockhash` - the blockhash of this block's parent, as base-58 encoded string
|
||||||
* `parentSlot` - the slot index of this block's parent
|
* `parentSlot` - the slot index of this block's parent
|
||||||
* `transactions` - an array of tuples containing:
|
* `transactions` - an array of tuples containing:
|
||||||
* [Transaction](transaction-api.md) object, in JSON format
|
* [Transaction](transaction-api.md) object, either in JSON format or base-58 encoded binary data, depending on encoding parameter
|
||||||
* Transaction status object, containing:
|
* Transaction status object, containing:
|
||||||
* `status` - Transaction status:
|
* `status` - Transaction status:
|
||||||
* `"Ok": null` - Transaction was successful
|
* `"Ok": null` - Transaction was successful
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
use bincode::serialize;
|
||||||
use jsonrpc_core::Result as JsonResult;
|
use jsonrpc_core::Result as JsonResult;
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
clock::{Epoch, Slot},
|
clock::{Epoch, Slot},
|
||||||
hash::Hash,
|
message::MessageHeader,
|
||||||
transaction::{Result, Transaction},
|
transaction::{Result, Transaction},
|
||||||
};
|
};
|
||||||
use std::{collections::HashMap, error, fmt, io, net::SocketAddr};
|
use std::{collections::HashMap, error, fmt, io, net::SocketAddr};
|
||||||
@ -21,13 +22,92 @@ pub struct Response<T> {
|
|||||||
pub value: T,
|
pub value: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct RpcConfirmedBlock {
|
pub struct RpcConfirmedBlock {
|
||||||
pub previous_blockhash: Hash,
|
pub previous_blockhash: String,
|
||||||
pub blockhash: Hash,
|
pub blockhash: String,
|
||||||
pub parent_slot: Slot,
|
pub parent_slot: Slot,
|
||||||
pub transactions: Vec<(Transaction, Option<RpcTransactionStatus>)>,
|
pub transactions: Vec<(RpcEncodedTransaction, Option<RpcTransactionStatus>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<String>,
|
||||||
|
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<String>,
|
||||||
|
pub recent_blockhash: String,
|
||||||
|
pub instructions: Vec<RpcCompiledInstruction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<u8>,
|
||||||
|
pub data: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
@ -1020,6 +1020,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
use crossbeam_channel::unbounded;
|
use crossbeam_channel::unbounded;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use solana_client::rpc_request::RpcEncodedTransaction;
|
||||||
use solana_ledger::{
|
use solana_ledger::{
|
||||||
blocktree::entries_to_test_shreds,
|
blocktree::entries_to_test_shreds,
|
||||||
entry::{next_entry, Entry, EntrySlice},
|
entry::{next_entry, Entry, EntrySlice},
|
||||||
@ -1971,22 +1972,24 @@ mod tests {
|
|||||||
|
|
||||||
transaction_status_service.join().unwrap();
|
transaction_status_service.join().unwrap();
|
||||||
|
|
||||||
let confirmed_block = blocktree.get_confirmed_block(bank.slot()).unwrap();
|
let confirmed_block = blocktree.get_confirmed_block(bank.slot(), None).unwrap();
|
||||||
assert_eq!(confirmed_block.transactions.len(), 3);
|
assert_eq!(confirmed_block.transactions.len(), 3);
|
||||||
|
|
||||||
for (transaction, result) in confirmed_block.transactions.into_iter() {
|
for (transaction, result) in confirmed_block.transactions.into_iter() {
|
||||||
if transaction.signatures[0] == success_signature {
|
if let RpcEncodedTransaction::Json(transaction) = transaction {
|
||||||
assert_eq!(result.unwrap().status, Ok(()));
|
if transaction.signatures[0] == success_signature.to_string() {
|
||||||
} else if transaction.signatures[0] == ix_error_signature {
|
assert_eq!(result.unwrap().status, Ok(()));
|
||||||
assert_eq!(
|
} else if transaction.signatures[0] == ix_error_signature.to_string() {
|
||||||
result.unwrap().status,
|
assert_eq!(
|
||||||
Err(TransactionError::InstructionError(
|
result.unwrap().status,
|
||||||
0,
|
Err(TransactionError::InstructionError(
|
||||||
InstructionError::CustomError(1)
|
0,
|
||||||
))
|
InstructionError::CustomError(1)
|
||||||
);
|
))
|
||||||
} else {
|
);
|
||||||
assert_eq!(result, None);
|
} else {
|
||||||
|
assert_eq!(result, None);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1186,6 +1186,7 @@ pub(crate) mod tests {
|
|||||||
transaction_status_service::TransactionStatusService,
|
transaction_status_service::TransactionStatusService,
|
||||||
};
|
};
|
||||||
use crossbeam_channel::unbounded;
|
use crossbeam_channel::unbounded;
|
||||||
|
use solana_client::rpc_request::RpcEncodedTransaction;
|
||||||
use solana_ledger::{
|
use solana_ledger::{
|
||||||
blocktree::make_slot_entries,
|
blocktree::make_slot_entries,
|
||||||
blocktree::{entries_to_test_shreds, BlocktreeError},
|
blocktree::{entries_to_test_shreds, BlocktreeError},
|
||||||
@ -1988,22 +1989,24 @@ pub(crate) mod tests {
|
|||||||
blocktree.clone(),
|
blocktree.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let confirmed_block = blocktree.get_confirmed_block(slot).unwrap();
|
let confirmed_block = blocktree.get_confirmed_block(slot, None).unwrap();
|
||||||
assert_eq!(confirmed_block.transactions.len(), 3);
|
assert_eq!(confirmed_block.transactions.len(), 3);
|
||||||
|
|
||||||
for (transaction, result) in confirmed_block.transactions.into_iter() {
|
for (transaction, result) in confirmed_block.transactions.into_iter() {
|
||||||
if transaction.signatures[0] == signatures[0] {
|
if let RpcEncodedTransaction::Json(transaction) = transaction {
|
||||||
assert_eq!(result.unwrap().status, Ok(()));
|
if transaction.signatures[0] == signatures[0].to_string() {
|
||||||
} else if transaction.signatures[0] == signatures[1] {
|
assert_eq!(result.unwrap().status, Ok(()));
|
||||||
assert_eq!(
|
} else if transaction.signatures[0] == signatures[1].to_string() {
|
||||||
result.unwrap().status,
|
assert_eq!(
|
||||||
Err(TransactionError::InstructionError(
|
result.unwrap().status,
|
||||||
0,
|
Err(TransactionError::InstructionError(
|
||||||
InstructionError::CustomError(1)
|
0,
|
||||||
))
|
InstructionError::CustomError(1)
|
||||||
);
|
))
|
||||||
} else {
|
);
|
||||||
assert_eq!(result, None);
|
} else {
|
||||||
|
assert_eq!(result, None);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,8 @@ use jsonrpc_core::{Error, Metadata, Result};
|
|||||||
use jsonrpc_derive::rpc;
|
use jsonrpc_derive::rpc;
|
||||||
use solana_client::rpc_request::{
|
use solana_client::rpc_request::{
|
||||||
Response, RpcConfirmedBlock, RpcContactInfo, RpcEpochInfo, RpcLeaderSchedule,
|
Response, RpcConfirmedBlock, RpcContactInfo, RpcEpochInfo, RpcLeaderSchedule,
|
||||||
RpcResponseContext, RpcVersionInfo, RpcVoteAccountInfo, RpcVoteAccountStatus,
|
RpcResponseContext, RpcTransactionEncoding, RpcVersionInfo, RpcVoteAccountInfo,
|
||||||
|
RpcVoteAccountStatus,
|
||||||
};
|
};
|
||||||
use solana_faucet::faucet::request_airdrop_transaction;
|
use solana_faucet::faucet::request_airdrop_transaction;
|
||||||
use solana_ledger::{
|
use solana_ledger::{
|
||||||
@ -312,8 +313,12 @@ impl JsonRpcRequestProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_confirmed_block(&self, slot: Slot) -> Result<Option<RpcConfirmedBlock>> {
|
pub fn get_confirmed_block(
|
||||||
Ok(self.blocktree.get_confirmed_block(slot).ok())
|
&self,
|
||||||
|
slot: Slot,
|
||||||
|
encoding: Option<RpcTransactionEncoding>,
|
||||||
|
) -> Result<Option<RpcConfirmedBlock>> {
|
||||||
|
Ok(self.blocktree.get_confirmed_block(slot, encoding).ok())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_confirmed_blocks(
|
pub fn get_confirmed_blocks(
|
||||||
@ -562,6 +567,7 @@ pub trait RpcSol {
|
|||||||
&self,
|
&self,
|
||||||
meta: Self::Metadata,
|
meta: Self::Metadata,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
|
encoding: Option<RpcTransactionEncoding>,
|
||||||
) -> Result<Option<RpcConfirmedBlock>>;
|
) -> Result<Option<RpcConfirmedBlock>>;
|
||||||
|
|
||||||
#[rpc(meta, name = "getBlockTime")]
|
#[rpc(meta, name = "getBlockTime")]
|
||||||
@ -1031,11 +1037,12 @@ impl RpcSol for RpcSolImpl {
|
|||||||
&self,
|
&self,
|
||||||
meta: Self::Metadata,
|
meta: Self::Metadata,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
|
encoding: Option<RpcTransactionEncoding>,
|
||||||
) -> Result<Option<RpcConfirmedBlock>> {
|
) -> Result<Option<RpcConfirmedBlock>> {
|
||||||
meta.request_processor
|
meta.request_processor
|
||||||
.read()
|
.read()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get_confirmed_block(slot)
|
.get_confirmed_block(slot, encoding)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_confirmed_blocks(
|
fn get_confirmed_blocks(
|
||||||
@ -1063,7 +1070,9 @@ pub mod tests {
|
|||||||
genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
||||||
replay_stage::tests::create_test_transactions_and_populate_blocktree,
|
replay_stage::tests::create_test_transactions_and_populate_blocktree,
|
||||||
};
|
};
|
||||||
|
use bincode::deserialize;
|
||||||
use jsonrpc_core::{MetaIoHandler, Output, Response, Value};
|
use jsonrpc_core::{MetaIoHandler, Output, Response, Value};
|
||||||
|
use solana_client::rpc_request::RpcEncodedTransaction;
|
||||||
use solana_ledger::{
|
use solana_ledger::{
|
||||||
blocktree::entries_to_test_shreds, blocktree_processor::fill_blocktree_slot_with_ticks,
|
blocktree::entries_to_test_shreds, blocktree_processor::fill_blocktree_slot_with_ticks,
|
||||||
entry::next_entry_mut, get_tmp_ledger_path,
|
entry::next_entry_mut, get_tmp_ledger_path,
|
||||||
@ -2008,6 +2017,36 @@ pub mod tests {
|
|||||||
|
|
||||||
let req =
|
let req =
|
||||||
format!(r#"{{"jsonrpc":"2.0","id":1,"method":"getConfirmedBlock","params":[0]}}"#);
|
format!(r#"{{"jsonrpc":"2.0","id":1,"method":"getConfirmedBlock","params":[0]}}"#);
|
||||||
|
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<RpcConfirmedBlock> =
|
||||||
|
serde_json::from_value(result["result"].clone()).unwrap();
|
||||||
|
let confirmed_block = confirmed_block.unwrap();
|
||||||
|
assert_eq!(confirmed_block.transactions.len(), 3);
|
||||||
|
|
||||||
|
for (transaction, result) in confirmed_block.transactions.into_iter() {
|
||||||
|
if let RpcEncodedTransaction::Json(transaction) = transaction {
|
||||||
|
if transaction.signatures[0] == confirmed_block_signatures[0].to_string() {
|
||||||
|
assert_eq!(transaction.message.recent_blockhash, blockhash.to_string());
|
||||||
|
assert_eq!(result.unwrap().status, Ok(()));
|
||||||
|
} else if transaction.signatures[0] == confirmed_block_signatures[1].to_string() {
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap().status,
|
||||||
|
Err(TransactionError::InstructionError(
|
||||||
|
0,
|
||||||
|
InstructionError::CustomError(1)
|
||||||
|
))
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
assert_eq!(result, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let req = format!(
|
||||||
|
r#"{{"jsonrpc":"2.0","id":1,"method":"getConfirmedBlock","params":[0, "binary"]}}"#
|
||||||
|
);
|
||||||
let res = io.handle_request_sync(&req, meta);
|
let res = io.handle_request_sync(&req, meta);
|
||||||
let result: Value = serde_json::from_str(&res.expect("actual response"))
|
let result: Value = serde_json::from_str(&res.expect("actual response"))
|
||||||
.expect("actual response deserialization");
|
.expect("actual response deserialization");
|
||||||
@ -2017,19 +2056,23 @@ pub mod tests {
|
|||||||
assert_eq!(confirmed_block.transactions.len(), 3);
|
assert_eq!(confirmed_block.transactions.len(), 3);
|
||||||
|
|
||||||
for (transaction, result) in confirmed_block.transactions.into_iter() {
|
for (transaction, result) in confirmed_block.transactions.into_iter() {
|
||||||
if transaction.signatures[0] == confirmed_block_signatures[0] {
|
if let RpcEncodedTransaction::Binary(transaction) = transaction {
|
||||||
assert_eq!(transaction.message.recent_blockhash, blockhash);
|
let decoded_transaction: Transaction =
|
||||||
assert_eq!(result.unwrap().status, Ok(()));
|
deserialize(&bs58::decode(&transaction).into_vec().unwrap()).unwrap();
|
||||||
} else if transaction.signatures[0] == confirmed_block_signatures[1] {
|
if decoded_transaction.signatures[0] == confirmed_block_signatures[0] {
|
||||||
assert_eq!(
|
assert_eq!(decoded_transaction.message.recent_blockhash, blockhash);
|
||||||
result.unwrap().status,
|
assert_eq!(result.unwrap().status, Ok(()));
|
||||||
Err(TransactionError::InstructionError(
|
} else if decoded_transaction.signatures[0] == confirmed_block_signatures[1] {
|
||||||
0,
|
assert_eq!(
|
||||||
InstructionError::CustomError(1)
|
result.unwrap().status,
|
||||||
))
|
Err(TransactionError::InstructionError(
|
||||||
);
|
0,
|
||||||
} else {
|
InstructionError::CustomError(1)
|
||||||
assert_eq!(result, None);
|
))
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
assert_eq!(result, None);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,9 @@ use rayon::{
|
|||||||
ThreadPool,
|
ThreadPool,
|
||||||
};
|
};
|
||||||
use rocksdb::DBRawIterator;
|
use rocksdb::DBRawIterator;
|
||||||
use solana_client::rpc_request::{RpcConfirmedBlock, RpcTransactionStatus};
|
use solana_client::rpc_request::{
|
||||||
|
RpcConfirmedBlock, RpcEncodedTransaction, RpcTransactionEncoding, RpcTransactionStatus,
|
||||||
|
};
|
||||||
use solana_measure::measure::Measure;
|
use solana_measure::measure::Measure;
|
||||||
use solana_metrics::{datapoint_debug, datapoint_error};
|
use solana_metrics::{datapoint_debug, datapoint_error};
|
||||||
use solana_rayon_threadlimit::get_thread_count;
|
use solana_rayon_threadlimit::get_thread_count;
|
||||||
@ -1324,7 +1326,12 @@ impl Blocktree {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_confirmed_block(&self, slot: Slot) -> Result<RpcConfirmedBlock> {
|
pub fn get_confirmed_block(
|
||||||
|
&self,
|
||||||
|
slot: Slot,
|
||||||
|
encoding: Option<RpcTransactionEncoding>,
|
||||||
|
) -> Result<RpcConfirmedBlock> {
|
||||||
|
let encoding = encoding.unwrap_or(RpcTransactionEncoding::Json);
|
||||||
if self.is_root(slot) {
|
if self.is_root(slot) {
|
||||||
let slot_meta_cf = self.db.column::<cf::SlotMeta>();
|
let slot_meta_cf = self.db.column::<cf::SlotMeta>();
|
||||||
let slot_meta = slot_meta_cf
|
let slot_meta = slot_meta_cf
|
||||||
@ -1344,13 +1351,18 @@ impl Blocktree {
|
|||||||
Hash::default()
|
Hash::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let blockhash = get_last_hash(slot_entries.iter())
|
||||||
|
.unwrap_or_else(|| panic!("Rooted slot {:?} must have blockhash", slot));
|
||||||
|
|
||||||
let block = RpcConfirmedBlock {
|
let block = RpcConfirmedBlock {
|
||||||
previous_blockhash,
|
previous_blockhash: previous_blockhash.to_string(),
|
||||||
blockhash: get_last_hash(slot_entries.iter())
|
blockhash: blockhash.to_string(),
|
||||||
.unwrap_or_else(|| panic!("Rooted slot {:?} must have blockhash", slot)),
|
|
||||||
parent_slot: slot_meta.parent_slot,
|
parent_slot: slot_meta.parent_slot,
|
||||||
transactions: self
|
transactions: self.map_transactions_to_statuses(
|
||||||
.map_transactions_to_statuses(slot, slot_transaction_iterator),
|
slot,
|
||||||
|
encoding,
|
||||||
|
slot_transaction_iterator,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
return Ok(block);
|
return Ok(block);
|
||||||
}
|
}
|
||||||
@ -1361,13 +1373,16 @@ impl Blocktree {
|
|||||||
fn map_transactions_to_statuses<'a>(
|
fn map_transactions_to_statuses<'a>(
|
||||||
&self,
|
&self,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
|
encoding: RpcTransactionEncoding,
|
||||||
iterator: impl Iterator<Item = Transaction> + 'a,
|
iterator: impl Iterator<Item = Transaction> + 'a,
|
||||||
) -> Vec<(Transaction, Option<RpcTransactionStatus>)> {
|
) -> Vec<(RpcEncodedTransaction, Option<RpcTransactionStatus>)> {
|
||||||
iterator
|
iterator
|
||||||
.map(|transaction| {
|
.map(|transaction| {
|
||||||
let signature = transaction.signatures[0];
|
let signature = transaction.signatures[0];
|
||||||
|
let encoded_transaction =
|
||||||
|
RpcEncodedTransaction::encode(transaction, encoding.clone());
|
||||||
(
|
(
|
||||||
transaction,
|
encoded_transaction,
|
||||||
self.transaction_status_cf
|
self.transaction_status_cf
|
||||||
.get((slot, signature))
|
.get((slot, signature))
|
||||||
.expect("Expect database get to succeed"),
|
.expect("Expect database get to succeed"),
|
||||||
@ -4634,31 +4649,52 @@ pub mod tests {
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Even if marked as root, a slot that is empty of entries should return an error
|
// Even if marked as root, a slot that is empty of entries should return an error
|
||||||
let confirmed_block_err = ledger.get_confirmed_block(slot - 1).unwrap_err();
|
let confirmed_block_err = ledger.get_confirmed_block(slot - 1, None).unwrap_err();
|
||||||
assert_matches!(confirmed_block_err, BlocktreeError::SlotNotRooted);
|
assert_matches!(confirmed_block_err, BlocktreeError::SlotNotRooted);
|
||||||
|
|
||||||
let confirmed_block = ledger.get_confirmed_block(slot).unwrap();
|
let confirmed_block = ledger.get_confirmed_block(slot, None).unwrap();
|
||||||
assert_eq!(confirmed_block.transactions.len(), 100);
|
assert_eq!(confirmed_block.transactions.len(), 100);
|
||||||
|
|
||||||
let mut expected_block = RpcConfirmedBlock::default();
|
let expected_block = RpcConfirmedBlock {
|
||||||
expected_block.transactions = expected_transactions.clone();
|
transactions: expected_transactions
|
||||||
expected_block.parent_slot = slot - 1;
|
.iter()
|
||||||
expected_block.blockhash = blockhash;
|
.cloned()
|
||||||
|
.map(|(tx, status)| {
|
||||||
|
(
|
||||||
|
RpcEncodedTransaction::encode(tx, RpcTransactionEncoding::Json),
|
||||||
|
status,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
parent_slot: slot - 1,
|
||||||
|
blockhash: blockhash.to_string(),
|
||||||
|
previous_blockhash: Hash::default().to_string(),
|
||||||
|
};
|
||||||
// The previous_blockhash of `expected_block` is default because its parent slot is a
|
// The previous_blockhash of `expected_block` is default because its parent slot is a
|
||||||
// root, but empty of entries. This is special handling for snapshot root slots.
|
// root, but empty of entries. This is special handling for snapshot root slots.
|
||||||
assert_eq!(confirmed_block, expected_block);
|
assert_eq!(confirmed_block, expected_block);
|
||||||
|
|
||||||
let confirmed_block = ledger.get_confirmed_block(slot + 1).unwrap();
|
let confirmed_block = ledger.get_confirmed_block(slot + 1, None).unwrap();
|
||||||
assert_eq!(confirmed_block.transactions.len(), 100);
|
assert_eq!(confirmed_block.transactions.len(), 100);
|
||||||
|
|
||||||
let mut expected_block = RpcConfirmedBlock::default();
|
let expected_block = RpcConfirmedBlock {
|
||||||
expected_block.transactions = expected_transactions;
|
transactions: expected_transactions
|
||||||
expected_block.parent_slot = slot;
|
.iter()
|
||||||
expected_block.previous_blockhash = blockhash;
|
.cloned()
|
||||||
expected_block.blockhash = blockhash;
|
.map(|(tx, status)| {
|
||||||
|
(
|
||||||
|
RpcEncodedTransaction::encode(tx, RpcTransactionEncoding::Json),
|
||||||
|
status,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
parent_slot: slot,
|
||||||
|
blockhash: blockhash.to_string(),
|
||||||
|
previous_blockhash: blockhash.to_string(),
|
||||||
|
};
|
||||||
assert_eq!(confirmed_block, expected_block);
|
assert_eq!(confirmed_block, expected_block);
|
||||||
|
|
||||||
let not_root = ledger.get_confirmed_block(slot + 2).unwrap_err();
|
let not_root = ledger.get_confirmed_block(slot + 2, None).unwrap_err();
|
||||||
assert_matches!(not_root, BlocktreeError::SlotNotRooted);
|
assert_matches!(not_root, BlocktreeError::SlotNotRooted);
|
||||||
|
|
||||||
drop(ledger);
|
drop(ledger);
|
||||||
@ -4875,7 +4911,11 @@ pub mod tests {
|
|||||||
vec![CompiledInstruction::new(1, &(), vec![0])],
|
vec![CompiledInstruction::new(1, &(), vec![0])],
|
||||||
));
|
));
|
||||||
|
|
||||||
let map = blocktree.map_transactions_to_statuses(slot, transactions.into_iter());
|
let map = blocktree.map_transactions_to_statuses(
|
||||||
|
slot,
|
||||||
|
RpcTransactionEncoding::Json,
|
||||||
|
transactions.into_iter(),
|
||||||
|
);
|
||||||
assert_eq!(map.len(), 5);
|
assert_eq!(map.len(), 5);
|
||||||
for x in 0..4 {
|
for x in 0..4 {
|
||||||
assert_eq!(map[x].1.as_ref().unwrap().fee, x as u64);
|
assert_eq!(map[x].1.as_ref().unwrap().fee, x as u64);
|
||||||
|
Reference in New Issue
Block a user