Skip to content

Commit

Permalink
getConfirmedBlock: add encoding optional parameter (#7756) (#7767)
Browse files Browse the repository at this point in the history
automerge
  • Loading branch information
mergify[bot] authored and solana-grimes committed Jan 13, 2020
1 parent db979b3 commit 8745034
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 74 deletions.
7 changes: 4 additions & 3 deletions book/src/api-reference/jsonrpc-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,16 +278,17 @@ Returns identity and transaction information about a confirmed block in the ledg
#### Parameters:

* `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:

The result field will be an object with the following fields:

* `blockhash` - the blockhash of this block
* `previousBlockhash` - the blockhash of this block's parent
* `blockhash` - the blockhash of this block, as base-58 encoded string
* `previousBlockhash` - the blockhash of this block's parent, as base-58 encoded string
* `parentSlot` - the slot index of this block's parent
* `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:
* `status` - Transaction status:
* `"Ok": null` - Transaction was successful
Expand Down
90 changes: 85 additions & 5 deletions client/src/rpc_request.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use bincode::serialize;
use jsonrpc_core::Result as JsonResult;
use serde_json::{json, Value};
use solana_sdk::{
clock::{Epoch, Slot},
hash::Hash,
message::MessageHeader,
transaction::{Result, Transaction},
};
use std::{collections::HashMap, error, fmt, io, net::SocketAddr};
Expand All @@ -21,13 +22,92 @@ pub struct Response<T> {
pub value: T,
}

#[derive(Debug, Default, PartialEq, Serialize, Deserialize)]
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcConfirmedBlock {
pub previous_blockhash: Hash,
pub blockhash: Hash,
pub previous_blockhash: String,
pub blockhash: String,
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)]
Expand Down
29 changes: 16 additions & 13 deletions core/src/banking_stage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1028,6 +1028,7 @@ mod tests {
};
use crossbeam_channel::unbounded;
use itertools::Itertools;
use solana_client::rpc_request::RpcEncodedTransaction;
use solana_ledger::{
blocktree::entries_to_test_shreds,
entry::{next_entry, Entry, EntrySlice},
Expand Down Expand Up @@ -1982,22 +1983,24 @@ mod tests {

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);

for (transaction, result) in confirmed_block.transactions.into_iter() {
if transaction.signatures[0] == success_signature {
assert_eq!(result.unwrap().status, Ok(()));
} else if transaction.signatures[0] == ix_error_signature {
assert_eq!(
result.unwrap().status,
Err(TransactionError::InstructionError(
0,
InstructionError::CustomError(1)
))
);
} else {
assert_eq!(result, None);
if let RpcEncodedTransaction::Json(transaction) = transaction {
if transaction.signatures[0] == success_signature.to_string() {
assert_eq!(result.unwrap().status, Ok(()));
} else if transaction.signatures[0] == ix_error_signature.to_string() {
assert_eq!(
result.unwrap().status,
Err(TransactionError::InstructionError(
0,
InstructionError::CustomError(1)
))
);
} else {
assert_eq!(result, None);
}
}
}
}
Expand Down
29 changes: 16 additions & 13 deletions core/src/replay_stage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1180,6 +1180,7 @@ pub(crate) mod tests {
transaction_status_service::TransactionStatusService,
};
use crossbeam_channel::unbounded;
use solana_client::rpc_request::RpcEncodedTransaction;
use solana_ledger::{
blocktree::make_slot_entries,
blocktree::{entries_to_test_shreds, BlocktreeError},
Expand Down Expand Up @@ -1982,22 +1983,24 @@ pub(crate) mod tests {
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);

for (transaction, result) in confirmed_block.transactions.into_iter() {
if transaction.signatures[0] == signatures[0] {
assert_eq!(result.unwrap().status, Ok(()));
} else if transaction.signatures[0] == signatures[1] {
assert_eq!(
result.unwrap().status,
Err(TransactionError::InstructionError(
0,
InstructionError::CustomError(1)
))
);
} else {
assert_eq!(result, None);
if let RpcEncodedTransaction::Json(transaction) = transaction {
if transaction.signatures[0] == signatures[0].to_string() {
assert_eq!(result.unwrap().status, Ok(()));
} else if transaction.signatures[0] == signatures[1].to_string() {
assert_eq!(
result.unwrap().status,
Err(TransactionError::InstructionError(
0,
InstructionError::CustomError(1)
))
);
} else {
assert_eq!(result, None);
}
}
}
}
Expand Down
77 changes: 60 additions & 17 deletions core/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ use jsonrpc_core::{Error, Metadata, Result};
use jsonrpc_derive::rpc;
use solana_client::rpc_request::{
Response, RpcConfirmedBlock, RpcContactInfo, RpcEpochInfo, RpcLeaderSchedule,
RpcResponseContext, RpcVersionInfo, RpcVoteAccountInfo, RpcVoteAccountStatus,
RpcResponseContext, RpcTransactionEncoding, RpcVersionInfo, RpcVoteAccountInfo,
RpcVoteAccountStatus,
};
use solana_faucet::faucet::request_airdrop_transaction;
use solana_ledger::{
Expand Down Expand Up @@ -312,8 +313,12 @@ impl JsonRpcRequestProcessor {
}
}

pub fn get_confirmed_block(&self, slot: Slot) -> Result<Option<RpcConfirmedBlock>> {
Ok(self.blocktree.get_confirmed_block(slot).ok())
pub fn get_confirmed_block(
&self,
slot: Slot,
encoding: Option<RpcTransactionEncoding>,
) -> Result<Option<RpcConfirmedBlock>> {
Ok(self.blocktree.get_confirmed_block(slot, encoding).ok())
}

pub fn get_confirmed_blocks(
Expand Down Expand Up @@ -562,6 +567,7 @@ pub trait RpcSol {
&self,
meta: Self::Metadata,
slot: Slot,
encoding: Option<RpcTransactionEncoding>,
) -> Result<Option<RpcConfirmedBlock>>;

#[rpc(meta, name = "getBlockTime")]
Expand Down Expand Up @@ -1031,11 +1037,12 @@ impl RpcSol for RpcSolImpl {
&self,
meta: Self::Metadata,
slot: Slot,
encoding: Option<RpcTransactionEncoding>,
) -> Result<Option<RpcConfirmedBlock>> {
meta.request_processor
.read()
.unwrap()
.get_confirmed_block(slot)
.get_confirmed_block(slot, encoding)
}

fn get_confirmed_blocks(
Expand Down Expand Up @@ -1063,7 +1070,9 @@ pub mod tests {
genesis_utils::{create_genesis_config, GenesisConfigInfo},
replay_stage::tests::create_test_transactions_and_populate_blocktree,
};
use bincode::deserialize;
use jsonrpc_core::{MetaIoHandler, Output, Response, Value};
use solana_client::rpc_request::RpcEncodedTransaction;
use solana_ledger::{
blocktree::entries_to_test_shreds, blocktree_processor::fill_blocktree_slot_with_ticks,
entry::next_entry_mut, get_tmp_ledger_path,
Expand Down Expand Up @@ -2008,6 +2017,36 @@ pub mod tests {

let req =
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 result: Value = serde_json::from_str(&res.expect("actual response"))
.expect("actual response deserialization");
Expand All @@ -2017,19 +2056,23 @@ pub mod tests {
assert_eq!(confirmed_block.transactions.len(), 3);

for (transaction, result) in confirmed_block.transactions.into_iter() {
if transaction.signatures[0] == confirmed_block_signatures[0] {
assert_eq!(transaction.message.recent_blockhash, blockhash);
assert_eq!(result.unwrap().status, Ok(()));
} else if transaction.signatures[0] == confirmed_block_signatures[1] {
assert_eq!(
result.unwrap().status,
Err(TransactionError::InstructionError(
0,
InstructionError::CustomError(1)
))
);
} else {
assert_eq!(result, None);
if let RpcEncodedTransaction::Binary(transaction) = transaction {
let decoded_transaction: Transaction =
deserialize(&bs58::decode(&transaction).into_vec().unwrap()).unwrap();
if decoded_transaction.signatures[0] == confirmed_block_signatures[0] {
assert_eq!(decoded_transaction.message.recent_blockhash, blockhash);
assert_eq!(result.unwrap().status, Ok(()));
} else if decoded_transaction.signatures[0] == confirmed_block_signatures[1] {
assert_eq!(
result.unwrap().status,
Err(TransactionError::InstructionError(
0,
InstructionError::CustomError(1)
))
);
} else {
assert_eq!(result, None);
}
}
}
}
Expand Down
Loading

0 comments on commit 8745034

Please sign in to comment.