Enable cluster tests (#3372)
* Cluster tests * stable! * fixup! stable! * fixup! fixup! stable! * fixup! fixup! fixup! stable! * fixup! fixup! fixup! fixup! stable! * fixed space * add getNumBlocksSinceSignatureConfirmation entry for the json rpc docs * Check in upcoming epochs for potential leadership slots in next_leader_slot()
This commit is contained in:
parent
402a733cd7
commit
148e08a8a5
|
@ -26,6 +26,7 @@ Methods
|
||||||
* [getBalance](#getbalance)
|
* [getBalance](#getbalance)
|
||||||
* [getRecentBlockhash](#getrecentblockhash)
|
* [getRecentBlockhash](#getrecentblockhash)
|
||||||
* [getSignatureStatus](#getsignaturestatus)
|
* [getSignatureStatus](#getsignaturestatus)
|
||||||
|
* [getNumBlocksSinceSignatureConfirmation](#getnumblockssincesignatureconfirmation)
|
||||||
* [getTransactionCount](#gettransactioncount)
|
* [getTransactionCount](#gettransactioncount)
|
||||||
* [requestAirdrop](#requestairdrop)
|
* [requestAirdrop](#requestairdrop)
|
||||||
* [sendTransaction](#sendtransaction)
|
* [sendTransaction](#sendtransaction)
|
||||||
|
@ -185,6 +186,27 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### getNumBlocksSinceSignatureConfirmation
|
||||||
|
Returns the current number of blocks since signature has been confirmed.
|
||||||
|
|
||||||
|
##### Parameters:
|
||||||
|
* `string` - Signature of Transaction to confirm, as base-58 encoded string
|
||||||
|
|
||||||
|
##### Results:
|
||||||
|
* `integer` - count, as unsigned 64-bit integer
|
||||||
|
|
||||||
|
##### Example:
|
||||||
|
```bash
|
||||||
|
// Request
|
||||||
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getNumBlocksSinceSignatureConfirmation", "params":["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"]}' http://localhost:8899
|
||||||
|
|
||||||
|
// Result
|
||||||
|
{"jsonrpc":"2.0","result":8,"id":1}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### getTransactionCount
|
### getTransactionCount
|
||||||
Returns the current Transaction count from the ledger
|
Returns the current Transaction count from the ledger
|
||||||
|
|
||||||
|
|
|
@ -438,6 +438,78 @@ impl RpcClient {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Poll the server to confirm a transaction.
|
||||||
|
pub fn poll_for_signature_confirmation(
|
||||||
|
&self,
|
||||||
|
signature: &Signature,
|
||||||
|
min_confirmed_blocks: usize,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
let mut now = Instant::now();
|
||||||
|
let mut confirmed_blocks = 0;
|
||||||
|
loop {
|
||||||
|
let response = self.get_num_blocks_since_signature_confirmation(signature);
|
||||||
|
match response {
|
||||||
|
Ok(count) => {
|
||||||
|
if confirmed_blocks != count {
|
||||||
|
info!(
|
||||||
|
"signature {} confirmed {} out of {}",
|
||||||
|
signature, count, min_confirmed_blocks
|
||||||
|
);
|
||||||
|
now = Instant::now();
|
||||||
|
confirmed_blocks = count;
|
||||||
|
}
|
||||||
|
if count >= min_confirmed_blocks {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
debug!("check_confirmations request failed: {:?}", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if now.elapsed().as_secs() > 15 {
|
||||||
|
// TODO: Return a better error.
|
||||||
|
return Err(io::Error::new(io::ErrorKind::Other, "signature not found"));
|
||||||
|
}
|
||||||
|
sleep(Duration::from_millis(250));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_num_blocks_since_signature_confirmation(
|
||||||
|
&self,
|
||||||
|
sig: &Signature,
|
||||||
|
) -> io::Result<usize> {
|
||||||
|
let params = json!([format!("{}", sig)]);
|
||||||
|
let response = self
|
||||||
|
.client
|
||||||
|
.send(
|
||||||
|
&RpcRequest::GetNumBlocksSinceSignatureConfirmation,
|
||||||
|
Some(params.clone()),
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
.map_err(|error| {
|
||||||
|
debug!(
|
||||||
|
"Response get_num_blocks_since_signature_confirmation: {}",
|
||||||
|
error
|
||||||
|
);
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"GetNumBlocksSinceSignatureConfirmation request failure",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
serde_json::from_value(response).map_err(|error| {
|
||||||
|
debug!(
|
||||||
|
"ParseError: get_num_blocks_since_signature_confirmation: {}",
|
||||||
|
error
|
||||||
|
);
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"GetNumBlocksSinceSignatureConfirmation parse failure",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn fullnode_exit(&self) -> io::Result<bool> {
|
pub fn fullnode_exit(&self) -> io::Result<bool> {
|
||||||
let response = self
|
let response = self
|
||||||
.client
|
.client
|
||||||
|
|
|
@ -18,6 +18,7 @@ pub enum RpcRequest {
|
||||||
GetStorageEntryHeight,
|
GetStorageEntryHeight,
|
||||||
GetStoragePubkeysForEntryHeight,
|
GetStoragePubkeysForEntryHeight,
|
||||||
FullnodeExit,
|
FullnodeExit,
|
||||||
|
GetNumBlocksSinceSignatureConfirmation,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RpcRequest {
|
impl RpcRequest {
|
||||||
|
@ -39,6 +40,9 @@ impl RpcRequest {
|
||||||
RpcRequest::GetStorageEntryHeight => "getStorageEntryHeight",
|
RpcRequest::GetStorageEntryHeight => "getStorageEntryHeight",
|
||||||
RpcRequest::GetStoragePubkeysForEntryHeight => "getStoragePubkeysForEntryHeight",
|
RpcRequest::GetStoragePubkeysForEntryHeight => "getStoragePubkeysForEntryHeight",
|
||||||
RpcRequest::FullnodeExit => "fullnodeExit",
|
RpcRequest::FullnodeExit => "fullnodeExit",
|
||||||
|
RpcRequest::GetNumBlocksSinceSignatureConfirmation => {
|
||||||
|
"getNumBlocksSinceSignatureConfirmation"
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let mut request = json!({
|
let mut request = json!({
|
||||||
"jsonrpc": jsonrpc,
|
"jsonrpc": jsonrpc,
|
||||||
|
|
|
@ -73,6 +73,36 @@ impl ThinClient {
|
||||||
Ok(transaction.signatures[0])
|
Ok(transaction.signatures[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retry a sending a signed Transaction to the server for processing.
|
||||||
|
pub fn retry_transfer_until_confirmed(
|
||||||
|
&self,
|
||||||
|
keypair: &Keypair,
|
||||||
|
transaction: &mut Transaction,
|
||||||
|
tries: usize,
|
||||||
|
min_confirmed_blocks: usize,
|
||||||
|
) -> io::Result<Signature> {
|
||||||
|
for x in 0..tries {
|
||||||
|
transaction.sign(&[keypair], self.get_recent_blockhash()?);
|
||||||
|
let mut buf = vec![0; transaction.serialized_size().unwrap() as usize];
|
||||||
|
let mut wr = std::io::Cursor::new(&mut buf[..]);
|
||||||
|
serialize_into(&mut wr, &transaction)
|
||||||
|
.expect("serialize Transaction in pub fn transfer_signed");
|
||||||
|
self.transactions_socket
|
||||||
|
.send_to(&buf[..], &self.transactions_addr)?;
|
||||||
|
if self
|
||||||
|
.poll_for_signature_confirmation(&transaction.signatures[0], min_confirmed_blocks)
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
return Ok(transaction.signatures[0]);
|
||||||
|
}
|
||||||
|
info!("{} tries failed transfer to {}", x, self.transactions_addr);
|
||||||
|
}
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"retry_transfer failed",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
/// Retry a sending a signed Transaction to the server for processing.
|
/// Retry a sending a signed Transaction to the server for processing.
|
||||||
pub fn retry_transfer(
|
pub fn retry_transfer(
|
||||||
&self,
|
&self,
|
||||||
|
@ -140,7 +170,18 @@ impl ThinClient {
|
||||||
pub fn poll_for_signature(&self, signature: &Signature) -> io::Result<()> {
|
pub fn poll_for_signature(&self, signature: &Signature) -> io::Result<()> {
|
||||||
self.rpc_client.poll_for_signature(signature)
|
self.rpc_client.poll_for_signature(signature)
|
||||||
}
|
}
|
||||||
|
/// Poll the server until the signature has been confirmed by at least `min_confirmed_blocks`
|
||||||
|
pub fn poll_for_signature_confirmation(
|
||||||
|
&self,
|
||||||
|
signature: &Signature,
|
||||||
|
min_confirmed_blocks: usize,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
self.rpc_client
|
||||||
|
.poll_for_signature_confirmation(signature, min_confirmed_blocks)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check a signature in the bank. This method blocks
|
||||||
|
/// until the server sends a response.
|
||||||
pub fn check_signature(&self, signature: &Signature) -> bool {
|
pub fn check_signature(&self, signature: &Signature) -> bool {
|
||||||
self.rpc_client.check_signature(signature)
|
self.rpc_client.check_signature(signature)
|
||||||
}
|
}
|
||||||
|
@ -148,6 +189,13 @@ impl ThinClient {
|
||||||
pub fn fullnode_exit(&self) -> io::Result<bool> {
|
pub fn fullnode_exit(&self) -> io::Result<bool> {
|
||||||
self.rpc_client.fullnode_exit()
|
self.rpc_client.fullnode_exit()
|
||||||
}
|
}
|
||||||
|
pub fn get_num_blocks_since_signature_confirmation(
|
||||||
|
&mut self,
|
||||||
|
sig: &Signature,
|
||||||
|
) -> io::Result<usize> {
|
||||||
|
self.rpc_client
|
||||||
|
.get_num_blocks_since_signature_confirmation(sig)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_client((rpc, tpu): (SocketAddr, SocketAddr), range: (u16, u16)) -> ThinClient {
|
pub fn create_client((rpc, tpu): (SocketAddr, SocketAddr), range: (u16, u16)) -> ThinClient {
|
||||||
|
|
|
@ -7,11 +7,14 @@ use crate::cluster_info::FULLNODE_PORT_RANGE;
|
||||||
use crate::contact_info::ContactInfo;
|
use crate::contact_info::ContactInfo;
|
||||||
use crate::entry::{Entry, EntrySlice};
|
use crate::entry::{Entry, EntrySlice};
|
||||||
use crate::gossip_service::discover;
|
use crate::gossip_service::discover;
|
||||||
|
use crate::locktower::VOTE_THRESHOLD_DEPTH;
|
||||||
use solana_client::thin_client::create_client;
|
use solana_client::thin_client::create_client;
|
||||||
use solana_sdk::hash::Hash;
|
use solana_sdk::hash::Hash;
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil, Signature};
|
use solana_sdk::signature::{Keypair, KeypairUtil, Signature};
|
||||||
use solana_sdk::system_transaction::SystemTransaction;
|
use solana_sdk::system_transaction::SystemTransaction;
|
||||||
use solana_sdk::timing::{DEFAULT_SLOTS_PER_EPOCH, DEFAULT_TICKS_PER_SLOT, NUM_TICKS_PER_SECOND};
|
use solana_sdk::timing::{
|
||||||
|
DEFAULT_TICKS_PER_SLOT, NUM_CONSECUTIVE_LEADER_SLOTS, NUM_TICKS_PER_SECOND,
|
||||||
|
};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
@ -40,12 +43,13 @@ pub fn spend_and_verify_all_nodes(
|
||||||
client.get_recent_blockhash().unwrap(),
|
client.get_recent_blockhash().unwrap(),
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
|
let confs = VOTE_THRESHOLD_DEPTH + 1;
|
||||||
let sig = client
|
let sig = client
|
||||||
.retry_transfer(&funding_keypair, &mut transaction, 5)
|
.retry_transfer_until_confirmed(&funding_keypair, &mut transaction, 5, confs)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
for validator in &cluster_nodes {
|
for validator in &cluster_nodes {
|
||||||
let client = create_client(validator.client_facing_addr(), FULLNODE_PORT_RANGE);
|
let client = create_client(validator.client_facing_addr(), FULLNODE_PORT_RANGE);
|
||||||
client.poll_for_signature(&sig).unwrap();
|
client.poll_for_signature_confirmation(&sig, confs).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,14 +131,18 @@ pub fn kill_entry_and_spend_and_verify_rest(
|
||||||
let cluster_nodes = discover(&entry_point_info.gossip, nodes).unwrap();
|
let cluster_nodes = discover(&entry_point_info.gossip, nodes).unwrap();
|
||||||
assert!(cluster_nodes.len() >= nodes);
|
assert!(cluster_nodes.len() >= nodes);
|
||||||
let client = create_client(entry_point_info.client_facing_addr(), FULLNODE_PORT_RANGE);
|
let client = create_client(entry_point_info.client_facing_addr(), FULLNODE_PORT_RANGE);
|
||||||
info!("sleeping for an epoch");
|
info!("sleeping for 2 leader fortnights");
|
||||||
sleep(Duration::from_millis(SLOT_MILLIS * DEFAULT_SLOTS_PER_EPOCH));
|
sleep(Duration::from_millis(
|
||||||
info!("done sleeping for an epoch");
|
SLOT_MILLIS * NUM_CONSECUTIVE_LEADER_SLOTS * 2,
|
||||||
|
));
|
||||||
|
info!("done sleeping for 2 fortnights");
|
||||||
info!("killing entry point");
|
info!("killing entry point");
|
||||||
assert!(client.fullnode_exit().unwrap());
|
assert!(client.fullnode_exit().unwrap());
|
||||||
info!("sleeping for a slot");
|
info!("sleeping for 2 leader fortnights");
|
||||||
sleep(Duration::from_millis(SLOT_MILLIS));
|
sleep(Duration::from_millis(
|
||||||
info!("done sleeping for a slot");
|
SLOT_MILLIS * NUM_CONSECUTIVE_LEADER_SLOTS,
|
||||||
|
));
|
||||||
|
info!("done sleeping for 2 fortnights");
|
||||||
for ingress_node in &cluster_nodes {
|
for ingress_node in &cluster_nodes {
|
||||||
if ingress_node.id == entry_point_info.id {
|
if ingress_node.id == entry_point_info.id {
|
||||||
continue;
|
continue;
|
||||||
|
@ -163,8 +171,15 @@ pub fn kill_entry_and_spend_and_verify_rest(
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let confs = VOTE_THRESHOLD_DEPTH + 1;
|
||||||
let sig = {
|
let sig = {
|
||||||
match client.retry_transfer(&funding_keypair, &mut transaction, 5) {
|
let sig = client.retry_transfer_until_confirmed(
|
||||||
|
&funding_keypair,
|
||||||
|
&mut transaction,
|
||||||
|
5,
|
||||||
|
confs,
|
||||||
|
);
|
||||||
|
match sig {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
result = Err(e);
|
result = Err(e);
|
||||||
continue;
|
continue;
|
||||||
|
@ -174,7 +189,7 @@ pub fn kill_entry_and_spend_and_verify_rest(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match poll_all_nodes_for_signature(&entry_point_info, &cluster_nodes, &sig) {
|
match poll_all_nodes_for_signature(&entry_point_info, &cluster_nodes, &sig, confs) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
result = Err(e);
|
result = Err(e);
|
||||||
}
|
}
|
||||||
|
@ -190,13 +205,14 @@ fn poll_all_nodes_for_signature(
|
||||||
entry_point_info: &ContactInfo,
|
entry_point_info: &ContactInfo,
|
||||||
cluster_nodes: &[ContactInfo],
|
cluster_nodes: &[ContactInfo],
|
||||||
sig: &Signature,
|
sig: &Signature,
|
||||||
|
confs: usize,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
for validator in cluster_nodes {
|
for validator in cluster_nodes {
|
||||||
if validator.id == entry_point_info.id {
|
if validator.id == entry_point_info.id {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let client = create_client(validator.client_facing_addr(), FULLNODE_PORT_RANGE);
|
let client = create_client(validator.client_facing_addr(), FULLNODE_PORT_RANGE);
|
||||||
client.poll_for_signature(&sig)?;
|
client.poll_for_signature_confirmation(&sig, confs)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -44,10 +44,9 @@ pub fn slot_leader_at(slot: u64, bank: &Bank) -> Option<Pubkey> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the next slot after the given current_slot that the given node will be leader
|
/// Return the next slot after the given current_slot that the given node will be leader
|
||||||
pub fn next_leader_slot(pubkey: &Pubkey, current_slot: u64, bank: &Bank) -> Option<u64> {
|
pub fn next_leader_slot(pubkey: &Pubkey, mut current_slot: u64, bank: &Bank) -> Option<u64> {
|
||||||
let (epoch, slot_index) = bank.get_epoch_and_slot_index(current_slot + 1);
|
let (mut epoch, mut start_index) = bank.get_epoch_and_slot_index(current_slot + 1);
|
||||||
|
while let Some(leader_schedule) = leader_schedule(epoch, bank) {
|
||||||
if let Some(leader_schedule) = leader_schedule(epoch, bank) {
|
|
||||||
// clippy thinks I should do this:
|
// clippy thinks I should do this:
|
||||||
// for (i, <item>) in leader_schedule
|
// for (i, <item>) in leader_schedule
|
||||||
// .iter()
|
// .iter()
|
||||||
|
@ -57,11 +56,15 @@ pub fn next_leader_slot(pubkey: &Pubkey, current_slot: u64, bank: &Bank) -> Opti
|
||||||
//
|
//
|
||||||
// but leader_schedule doesn't implement Iter...
|
// but leader_schedule doesn't implement Iter...
|
||||||
#[allow(clippy::needless_range_loop)]
|
#[allow(clippy::needless_range_loop)]
|
||||||
for i in slot_index..bank.get_slots_in_epoch(epoch) {
|
for i in start_index..bank.get_slots_in_epoch(epoch) {
|
||||||
|
current_slot += 1;
|
||||||
if *pubkey == leader_schedule[i] {
|
if *pubkey == leader_schedule[i] {
|
||||||
return Some(current_slot + 1 + (i - slot_index) as u64);
|
return Some(current_slot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
epoch += 1;
|
||||||
|
start_index = 0;
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -80,8 +83,10 @@ pub fn tick_height_to_slot(ticks_per_slot: u64, tick_height: u64) -> u64 {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::staking_utils;
|
use crate::staking_utils;
|
||||||
|
use crate::voting_keypair::tests::new_vote_account_with_delegate;
|
||||||
use solana_sdk::genesis_block::{GenesisBlock, BOOTSTRAP_LEADER_LAMPORTS};
|
use solana_sdk::genesis_block::{GenesisBlock, BOOTSTRAP_LEADER_LAMPORTS};
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_next_leader_slot() {
|
fn test_next_leader_slot() {
|
||||||
|
@ -117,6 +122,58 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_next_leader_slot_next_epoch() {
|
||||||
|
let pubkey = Keypair::new().pubkey();
|
||||||
|
let (mut genesis_block, mint_keypair) = GenesisBlock::new_with_leader(
|
||||||
|
2 * BOOTSTRAP_LEADER_LAMPORTS,
|
||||||
|
&pubkey,
|
||||||
|
BOOTSTRAP_LEADER_LAMPORTS,
|
||||||
|
);
|
||||||
|
genesis_block.epoch_warmup = false;
|
||||||
|
|
||||||
|
let bank = Bank::new(&genesis_block);
|
||||||
|
let delegate_id = Keypair::new().pubkey();
|
||||||
|
|
||||||
|
// Create new vote account
|
||||||
|
let new_voting_keypair = Keypair::new();
|
||||||
|
new_vote_account_with_delegate(
|
||||||
|
&mint_keypair,
|
||||||
|
&new_voting_keypair,
|
||||||
|
&delegate_id,
|
||||||
|
&bank,
|
||||||
|
BOOTSTRAP_LEADER_LAMPORTS,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Have to wait until the epoch at after the epoch stakes generated at genesis
|
||||||
|
// for the new votes to take effect.
|
||||||
|
let mut target_slot = 1;
|
||||||
|
let epoch = bank.get_stakers_epoch(0);
|
||||||
|
while bank.get_stakers_epoch(target_slot) == epoch {
|
||||||
|
target_slot += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let bank = Bank::new_from_parent(&Arc::new(bank), &Pubkey::default(), target_slot);
|
||||||
|
let mut expected_slot = 0;
|
||||||
|
let epoch = bank.get_stakers_epoch(target_slot);
|
||||||
|
for i in 0..epoch {
|
||||||
|
expected_slot += bank.get_slots_in_epoch(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
let schedule = leader_schedule(epoch, &bank).unwrap();
|
||||||
|
let mut index = 0;
|
||||||
|
while schedule[index] != delegate_id {
|
||||||
|
index += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
expected_slot += index;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
next_leader_slot(&delegate_id, 0, &bank),
|
||||||
|
Some(expected_slot)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_leader_schedule_via_bank() {
|
fn test_leader_schedule_via_bank() {
|
||||||
let pubkey = Keypair::new().pubkey();
|
let pubkey = Keypair::new().pubkey();
|
||||||
|
|
|
@ -162,7 +162,7 @@ impl ReplayStage {
|
||||||
.filter(|(b, stake_lockouts)| {
|
.filter(|(b, stake_lockouts)| {
|
||||||
let vote_threshold =
|
let vote_threshold =
|
||||||
locktower.check_vote_stake_threshold(b.slot(), &stake_lockouts);
|
locktower.check_vote_stake_threshold(b.slot(), &stake_lockouts);
|
||||||
trace!("bank vote_threshold: {} {}", b.slot(), vote_threshold);
|
debug!("bank vote_threshold: {} {}", b.slot(), vote_threshold);
|
||||||
vote_threshold
|
vote_threshold
|
||||||
})
|
})
|
||||||
.map(|(b, stake_lockouts)| {
|
.map(|(b, stake_lockouts)| {
|
||||||
|
|
|
@ -80,7 +80,20 @@ impl JsonRpcRequestProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_signature_status(&self, signature: Signature) -> Option<bank::Result<()>> {
|
pub fn get_signature_status(&self, signature: Signature) -> Option<bank::Result<()>> {
|
||||||
self.bank().get_signature_status(&signature)
|
self.get_signature_confirmation_status(signature)
|
||||||
|
.map(|x| x.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_signature_confirmations(&self, signature: Signature) -> Option<usize> {
|
||||||
|
self.get_signature_confirmation_status(signature)
|
||||||
|
.map(|x| x.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_signature_confirmation_status(
|
||||||
|
&self,
|
||||||
|
signature: Signature,
|
||||||
|
) -> Option<(usize, bank::Result<()>)> {
|
||||||
|
self.bank().get_signature_confirmation_status(&signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_transaction_count(&self) -> Result<u64> {
|
fn get_transaction_count(&self) -> Result<u64> {
|
||||||
|
@ -202,6 +215,20 @@ pub trait RpcSol {
|
||||||
|
|
||||||
#[rpc(meta, name = "fullnodeExit")]
|
#[rpc(meta, name = "fullnodeExit")]
|
||||||
fn fullnode_exit(&self, _: Self::Metadata) -> Result<bool>;
|
fn fullnode_exit(&self, _: Self::Metadata) -> Result<bool>;
|
||||||
|
|
||||||
|
#[rpc(meta, name = "getNumBlocksSinceSignatureConfirmation")]
|
||||||
|
fn get_num_blocks_since_signature_confirmation(
|
||||||
|
&self,
|
||||||
|
_: Self::Metadata,
|
||||||
|
_: String,
|
||||||
|
) -> Result<usize>;
|
||||||
|
|
||||||
|
#[rpc(meta, name = "getSignatureConfirmation")]
|
||||||
|
fn get_signature_confirmation(
|
||||||
|
&self,
|
||||||
|
_: Self::Metadata,
|
||||||
|
_: String,
|
||||||
|
) -> Result<(usize, RpcSignatureStatus)>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RpcSolImpl;
|
pub struct RpcSolImpl;
|
||||||
|
@ -239,19 +266,33 @@ impl RpcSol for RpcSolImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_signature_status(&self, meta: Self::Metadata, id: String) -> Result<RpcSignatureStatus> {
|
fn get_signature_status(&self, meta: Self::Metadata, id: String) -> Result<RpcSignatureStatus> {
|
||||||
info!("get_signature_status rpc request received: {:?}", id);
|
self.get_signature_confirmation(meta, id).map(|x| x.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_num_blocks_since_signature_confirmation(
|
||||||
|
&self,
|
||||||
|
meta: Self::Metadata,
|
||||||
|
id: String,
|
||||||
|
) -> Result<usize> {
|
||||||
|
self.get_signature_confirmation(meta, id).map(|x| x.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_signature_confirmation(
|
||||||
|
&self,
|
||||||
|
meta: Self::Metadata,
|
||||||
|
id: String,
|
||||||
|
) -> Result<(usize, RpcSignatureStatus)> {
|
||||||
|
info!("get_signature_confirmation rpc request received: {:?}", id);
|
||||||
let signature = verify_signature(&id)?;
|
let signature = verify_signature(&id)?;
|
||||||
let res = meta
|
let res = meta
|
||||||
.request_processor
|
.request_processor
|
||||||
.read()
|
.read()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get_signature_status(signature);
|
.get_signature_confirmation_status(signature);
|
||||||
|
|
||||||
let status = {
|
let status = {
|
||||||
if res.is_none() {
|
if let Some((count, res)) = res {
|
||||||
RpcSignatureStatus::SignatureNotFound
|
let res = match res {
|
||||||
} else {
|
|
||||||
match res.unwrap() {
|
|
||||||
Ok(_) => RpcSignatureStatus::Confirmed,
|
Ok(_) => RpcSignatureStatus::Confirmed,
|
||||||
Err(TransactionError::AccountInUse) => RpcSignatureStatus::AccountInUse,
|
Err(TransactionError::AccountInUse) => RpcSignatureStatus::AccountInUse,
|
||||||
Err(TransactionError::AccountLoadedTwice) => {
|
Err(TransactionError::AccountLoadedTwice) => {
|
||||||
|
@ -264,10 +305,16 @@ impl RpcSol for RpcSolImpl {
|
||||||
trace!("mapping {:?} to GenericFailure", err);
|
trace!("mapping {:?} to GenericFailure", err);
|
||||||
RpcSignatureStatus::GenericFailure
|
RpcSignatureStatus::GenericFailure
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
(count, res)
|
||||||
|
} else {
|
||||||
|
(0, RpcSignatureStatus::SignatureNotFound)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
info!("get_signature_status rpc request status: {:?}", status);
|
info!(
|
||||||
|
"get_signature_confirmation rpc request status: {:?}",
|
||||||
|
status
|
||||||
|
);
|
||||||
Ok(status)
|
Ok(status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,6 @@ fn test_spend_and_verify_all_nodes_1() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore] //TODO: confirmations are not useful: #3346
|
|
||||||
fn test_spend_and_verify_all_nodes_2() {
|
fn test_spend_and_verify_all_nodes_2() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
let num_nodes = 2;
|
let num_nodes = 2;
|
||||||
|
@ -34,7 +33,6 @@ fn test_spend_and_verify_all_nodes_2() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore] //TODO: confirmations are not useful: #3346
|
|
||||||
fn test_spend_and_verify_all_nodes_3() {
|
fn test_spend_and_verify_all_nodes_3() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
let num_nodes = 3;
|
let num_nodes = 3;
|
||||||
|
@ -65,34 +63,20 @@ fn test_fullnode_exit_2() {
|
||||||
cluster_tests::fullnode_exit(&local.entry_point_info, num_nodes);
|
cluster_tests::fullnode_exit(&local.entry_point_info, num_nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cluster needs a supermajority to remain, so the minimum size for this test is 4
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
fn test_leader_failure_4() {
|
||||||
fn test_leader_failure_2() {
|
solana_logger::setup();
|
||||||
let num_nodes = 2;
|
let num_nodes = 4;
|
||||||
let mut fullnode_config = FullnodeConfig::default();
|
let mut fullnode_config = FullnodeConfig::default();
|
||||||
fullnode_config.rpc_config.enable_fullnode_exit = true;
|
fullnode_config.rpc_config.enable_fullnode_exit = true;
|
||||||
let local = LocalCluster::new_with_config(&[100; 2], 10_000, &fullnode_config);
|
let local = LocalCluster::new_with_config(&[100; 4], 10_000, &fullnode_config);
|
||||||
cluster_tests::kill_entry_and_spend_and_verify_rest(
|
cluster_tests::kill_entry_and_spend_and_verify_rest(
|
||||||
&local.entry_point_info,
|
&local.entry_point_info,
|
||||||
&local.funding_keypair,
|
&local.funding_keypair,
|
||||||
num_nodes,
|
num_nodes,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[ignore]
|
|
||||||
fn test_leader_failure_3() {
|
|
||||||
let num_nodes = 3;
|
|
||||||
let mut fullnode_config = FullnodeConfig::default();
|
|
||||||
fullnode_config.rpc_config.enable_fullnode_exit = true;
|
|
||||||
let local = LocalCluster::new_with_config(&[100; 3], 10_000, &fullnode_config);
|
|
||||||
cluster_tests::kill_entry_and_spend_and_verify_rest(
|
|
||||||
&local.entry_point_info,
|
|
||||||
&local.funding_keypair,
|
|
||||||
num_nodes,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_two_unbalanced_stakes() {
|
fn test_two_unbalanced_stakes() {
|
||||||
let mut fullnode_config = FullnodeConfig::default();
|
let mut fullnode_config = FullnodeConfig::default();
|
||||||
|
|
|
@ -790,13 +790,21 @@ impl Bank {
|
||||||
self.accounts.transaction_count(self.accounts_id)
|
self.accounts.transaction_count(self.accounts_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_signature_status(&self, signature: &Signature) -> Option<Result<()>> {
|
pub fn get_signature_confirmation_status(
|
||||||
|
&self,
|
||||||
|
signature: &Signature,
|
||||||
|
) -> Option<(usize, Result<()>)> {
|
||||||
let parents = self.parents();
|
let parents = self.parents();
|
||||||
let mut caches = vec![self.status_cache.read().unwrap()];
|
let mut caches = vec![self.status_cache.read().unwrap()];
|
||||||
caches.extend(parents.iter().map(|b| b.status_cache.read().unwrap()));
|
caches.extend(parents.iter().map(|b| b.status_cache.read().unwrap()));
|
||||||
StatusCache::get_signature_status_all(&caches, signature)
|
StatusCache::get_signature_status_all(&caches, signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_signature_status(&self, signature: &Signature) -> Option<Result<()>> {
|
||||||
|
self.get_signature_confirmation_status(signature)
|
||||||
|
.map(|v| v.1)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn has_signature(&self, signature: &Signature) -> bool {
|
pub fn has_signature(&self, signature: &Signature) -> bool {
|
||||||
let parents = self.parents();
|
let parents = self.parents();
|
||||||
let mut caches = vec![self.status_cache.read().unwrap()];
|
let mut caches = vec![self.status_cache.read().unwrap()];
|
||||||
|
|
|
@ -192,13 +192,13 @@ impl<T: Clone> StatusCache<T> {
|
||||||
pub fn get_signature_status_all<U>(
|
pub fn get_signature_status_all<U>(
|
||||||
checkpoints: &[U],
|
checkpoints: &[U],
|
||||||
signature: &Signature,
|
signature: &Signature,
|
||||||
) -> Option<Result<(), T>>
|
) -> Option<(usize, Result<(), T>)>
|
||||||
where
|
where
|
||||||
U: Deref<Target = Self>,
|
U: Deref<Target = Self>,
|
||||||
{
|
{
|
||||||
for c in checkpoints {
|
for (i, c) in checkpoints.iter().enumerate() {
|
||||||
if let Some(status) = c.get_signature_status(signature) {
|
if let Some(status) = c.get_signature_status(signature) {
|
||||||
return Some(status);
|
return Some((i, status));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
@ -257,7 +257,7 @@ mod tests {
|
||||||
let checkpoints = [&second, &first];
|
let checkpoints = [&second, &first];
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
BankStatusCache::get_signature_status_all(&checkpoints, &sig),
|
BankStatusCache::get_signature_status_all(&checkpoints, &sig),
|
||||||
Some(Ok(())),
|
Some((1, Ok(()))),
|
||||||
);
|
);
|
||||||
assert!(StatusCache::has_signature_all(&checkpoints, &sig));
|
assert!(StatusCache::has_signature_all(&checkpoints, &sig));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue