From c5b076ec7ef3653c79442bf4bd341330ac0eb363 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Wed, 18 Dec 2019 16:51:47 -0700 Subject: [PATCH] Add getConfirmedBlocks rpc method (#7550) automerge --- book/src/api-reference/jsonrpc-api.md | 26 ++++++ core/src/rpc.rs | 126 +++++++++++++++++++++++++- 2 files changed, 148 insertions(+), 4 deletions(-) diff --git a/book/src/api-reference/jsonrpc-api.md b/book/src/api-reference/jsonrpc-api.md index 6de00c200b..20c7ed2998 100644 --- a/book/src/api-reference/jsonrpc-api.md +++ b/book/src/api-reference/jsonrpc-api.md @@ -21,6 +21,7 @@ To interact with a Solana node inside a JavaScript application, use the [solana- * [getBlockTime](jsonrpc-api.md#getblocktime) * [getClusterNodes](jsonrpc-api.md#getclusternodes) * [getConfirmedBlock](jsonrpc-api.md#getconfirmedblock) +* [getConfirmedBlocks](jsonrpc-api.md#getconfirmedblocks) * [getEpochInfo](jsonrpc-api.md#getepochinfo) * [getEpochSchedule](jsonrpc-api.md#getepochschedule) * [getGenesisHash](jsonrpc-api.md#getgenesishash) @@ -297,6 +298,31 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"m {"jsonrpc":"2.0","result":{"blockhash":[165,245,120,183,32,205,89,222,249,114,229,49,250,231,149,122,156,232,181,83,238,194,157,153,7,213,180,54,177,6,25,101],"parentSlot":429,"previousBlockhash":[21,108,181,90,139,241,212,203,45,78,232,29,161,31,159,188,110,82,81,11,250,74,47,140,188,28,23,96,251,164,208,166],"transactions":[[{"message":{"accountKeys":[[5],[219,181,202,40,52,148,34,136,186,59,137,160,250,225,234,17,244,160,88,116,24,176,30,227,68,11,199,38,141,68,131,228],[233,48,179,56,91,40,254,206,53,48,196,176,119,248,158,109,121,77,11,69,108,160,128,27,228,122,146,249,53,184,68,87],[6,167,213,23,25,47,10,175,198,242,101,227,251,119,204,122,218,130,197,41,208,190,59,19,110,45,0,85,32,0,0,0],[6,167,213,23,24,199,116,201,40,86,99,152,105,29,94,182,139,94,184,163,155,75,109,92,115,85,91,33,0,0,0,0],[7,97,72,29,53,116,116,187,124,77,118,36,235,211,189,179,216,53,94,115,209,16,67,252,13,163,83,128,0,0,0,0]],"header":{"numReadonlySignedAccounts":0,"numReadonlyUnsignedAccounts":3,"numRequiredSignatures":2},"instructions":[[1],{"accounts":[[3],1,2,3],"data":[[52],2,0,0,0,1,0,0,0,0,0,0,0,173,1,0,0,0,0,0,0,86,55,9,248,142,238,135,114,103,83,247,124,67,68,163,233,55,41,59,129,64,50,110,221,234,234,27,213,205,193,219,50],"program_id_index":4}],"recentBlockhash":[21,108,181,90,139,241,212,203,45,78,232,29,161,31,159,188,110,82,81,11,250,74,47,140,188,28,23,96,251,164,208,166]},"signatures":[[2],[119,9,95,108,35,95,7,1,69,101,65,45,5,204,61,114,172,88,123,238,32,201,135,229,57,50,13,21,106,216,129,183,238,43,37,101,148,81,56,232,88,136,80,65,46,189,39,106,94,13,238,54,186,48,118,186,0,62,121,122,172,171,66,5],[78,40,77,250,10,93,6,157,48,173,100,40,251,9,7,218,7,184,43,169,76,240,254,34,235,48,41,175,119,126,75,107,106,248,45,161,119,48,174,213,57,69,111,225,245,60,148,73,124,82,53,6,203,126,120,180,111,169,89,64,29,23,237,13]]},{"fee":100000,"status":{"Ok":null},"preBalances":[499998337500,15298080,1,1,1],"postBalances":[499998237500,15298080,1,1,1]}]]},"id":1} ``` +### getConfirmedBlocks + +Returns a list of confirmed blocks + +#### Parameters: + +* `integer` - start_slot, as u64 integer +* `integer` - (optional) end_slot, as u64 integer + +#### Results: + +The result field will be an array of u64 integers listing confirmed blocks +between start_slot and either end_slot, if provided, or latest confirmed block, +inclusive. + +#### Example: + +```bash +// Request +curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedBlocks","params":[5, 10]}' localhost:8899 + +// Result +{"jsonrpc":"2.0","result":[5,6,7,8,9,10],"id":1} +``` + ### getEpochInfo Returns information about the current epoch diff --git a/core/src/rpc.rs b/core/src/rpc.rs index 5d2ab77650..c2bace99b8 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -16,7 +16,9 @@ use solana_client::rpc_request::{ RpcResponseContext, RpcVersionInfo, RpcVoteAccountInfo, RpcVoteAccountStatus, }; use solana_faucet::faucet::request_airdrop_transaction; -use solana_ledger::{bank_forks::BankForks, blocktree::Blocktree}; +use solana_ledger::{ + bank_forks::BankForks, blocktree::Blocktree, rooted_slot_iterator::RootedSlotIterator, +}; use solana_runtime::bank::Bank; use solana_sdk::{ account::Account, @@ -314,6 +316,29 @@ impl JsonRpcRequestProcessor { Ok(self.blocktree.get_confirmed_block(slot).ok()) } + pub fn get_confirmed_blocks( + &self, + start_slot: Slot, + end_slot: Option, + ) -> Result> { + let end_slot = end_slot.unwrap_or_else(|| self.bank(None).slot()); + if end_slot < start_slot { + return Ok(vec![]); + } + + let start_slot = (start_slot..end_slot).find(|&slot| self.blocktree.is_root(slot)); + if let Some(start_slot) = start_slot { + let mut slots: Vec = RootedSlotIterator::new(start_slot, &self.blocktree) + .unwrap() + .map(|(slot, _)| slot) + .collect(); + slots.retain(|&x| x <= end_slot); + Ok(slots) + } else { + Ok(vec![]) + } + } + pub fn get_block_time(&self, slot: Slot) -> Result> { // This calculation currently assumes that bank.slots_per_year will remain unchanged after // genesis (ie. that this bank's slot_per_year will be applicable to any rooted slot being @@ -541,6 +566,14 @@ pub trait RpcSol { #[rpc(meta, name = "getBlockTime")] fn get_block_time(&self, meta: Self::Metadata, slot: Slot) -> Result>; + + #[rpc(meta, name = "getConfirmedBlocks")] + fn get_confirmed_blocks( + &self, + meta: Self::Metadata, + start_slot: Slot, + end_slot: Option, + ) -> Result>; } pub struct RpcSolImpl; @@ -1005,6 +1038,18 @@ impl RpcSol for RpcSolImpl { .get_confirmed_block(slot) } + fn get_confirmed_blocks( + &self, + meta: Self::Metadata, + start_slot: Slot, + end_slot: Option, + ) -> Result> { + meta.request_processor + .read() + .unwrap() + .get_confirmed_blocks(start_slot, end_slot) + } + fn get_block_time(&self, meta: Self::Metadata, slot: Slot) -> Result> { meta.request_processor.read().unwrap().get_block_time(slot) } @@ -1020,7 +1065,8 @@ pub mod tests { }; use jsonrpc_core::{MetaIoHandler, Output, Response, Value}; use solana_ledger::{ - blocktree::entries_to_test_shreds, entry::next_entry_mut, get_tmp_ledger_path, + blocktree::entries_to_test_shreds, blocktree_processor::fill_blocktree_slot_with_ticks, + entry::next_entry_mut, get_tmp_ledger_path, }; use solana_sdk::{ fee_calculator::DEFAULT_BURN_PERCENT, @@ -1057,7 +1103,7 @@ pub mod tests { } fn start_rpc_handler_with_tx(pubkey: &Pubkey) -> RpcHandler { - start_rpc_handler_with_tx_and_blocktree(pubkey, vec![1], 0) + start_rpc_handler_with_tx_and_blocktree(pubkey, vec![], 0) } fn start_rpc_handler_with_tx_and_blocktree( @@ -1118,7 +1164,31 @@ pub mod tests { 0, ); blocktree.insert_shreds(shreds, None, false).unwrap(); - blocktree.set_roots(&blocktree_roots).unwrap(); + blocktree.set_roots(&[1]).unwrap(); + + let mut roots = blocktree_roots.clone(); + if !roots.is_empty() { + roots.retain(|&x| x > 1); + let mut parent_bank = bank; + for (i, root) in roots.iter().enumerate() { + let new_bank = + Bank::new_from_parent(&parent_bank, parent_bank.collector_id(), *root); + parent_bank = bank_forks.write().unwrap().insert(new_bank); + parent_bank.squash(); + bank_forks.write().unwrap().set_root(*root, &None); + let parent = if i > 0 { roots[i - 1] } else { 1 }; + fill_blocktree_slot_with_ticks(&blocktree, 5, *root, parent, Hash::default()); + } + blocktree.set_roots(&roots).unwrap(); + let new_bank = Bank::new_from_parent( + &parent_bank, + parent_bank.collector_id(), + roots.iter().max().unwrap() + 1, + ); + bank_forks.write().unwrap().insert(new_bank); + } + + let bank = bank_forks.read().unwrap().working_bank(); let leader_pubkey = *bank.collector_id(); let exit = Arc::new(AtomicBool::new(false)); @@ -1964,6 +2034,54 @@ pub mod tests { } } + #[test] + fn test_get_confirmed_blocks() { + let bob_pubkey = Pubkey::new_rand(); + let roots = vec![0, 1, 3, 4, 8]; + let RpcHandler { io, meta, .. } = + start_rpc_handler_with_tx_and_blocktree(&bob_pubkey, roots.clone(), 0); + + let req = + format!(r#"{{"jsonrpc":"2.0","id":1,"method":"getConfirmedBlocks","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_blocks: Vec = serde_json::from_value(result["result"].clone()).unwrap(); + assert_eq!(confirmed_blocks, roots); + + let req = + format!(r#"{{"jsonrpc":"2.0","id":1,"method":"getConfirmedBlocks","params":[2]}}"#); + 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_blocks: Vec = serde_json::from_value(result["result"].clone()).unwrap(); + assert_eq!(confirmed_blocks, vec![3, 4, 8]); + + let req = + format!(r#"{{"jsonrpc":"2.0","id":1,"method":"getConfirmedBlocks","params":[0, 4]}}"#); + 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_blocks: Vec = serde_json::from_value(result["result"].clone()).unwrap(); + assert_eq!(confirmed_blocks, vec![0, 1, 3, 4]); + + let req = + format!(r#"{{"jsonrpc":"2.0","id":1,"method":"getConfirmedBlocks","params":[0, 7]}}"#); + 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_blocks: Vec = serde_json::from_value(result["result"].clone()).unwrap(); + assert_eq!(confirmed_blocks, vec![0, 1, 3, 4]); + + let req = + format!(r#"{{"jsonrpc":"2.0","id":1,"method":"getConfirmedBlocks","params":[9, 11]}}"#); + 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_blocks: Vec = serde_json::from_value(result["result"].clone()).unwrap(); + assert_eq!(confirmed_blocks, Vec::::new()); + } + #[test] fn test_get_block_time() { let bob_pubkey = Pubkey::new_rand();