program-test now generates new blockhashes for test usage

This commit is contained in:
Michael Vines
2020-11-11 17:08:32 -08:00
parent 2374664e95
commit fd68f8ba2e
3 changed files with 69 additions and 9 deletions

2
Cargo.lock generated
View File

@ -4580,6 +4580,7 @@ dependencies = [
name = "solana-program-test" name = "solana-program-test"
version = "1.4.7" version = "1.4.7"
dependencies = [ dependencies = [
"async-trait",
"base64 0.12.3", "base64 0.12.3",
"chrono", "chrono",
"chrono-humanize", "chrono-humanize",
@ -4591,6 +4592,7 @@ dependencies = [
"solana-program 1.4.7", "solana-program 1.4.7",
"solana-runtime", "solana-runtime",
"solana-sdk", "solana-sdk",
"tokio 0.3.2",
] ]
[[package]] [[package]]

View File

@ -8,6 +8,7 @@ repository = "https://github.com/solana-labs/solana"
version = "1.4.7" version = "1.4.7"
[dependencies] [dependencies]
async-trait = "0.1.36"
base64 = "0.12.3" base64 = "0.12.3"
chrono = "0.4.19" chrono = "0.4.19"
chrono-humanize = "0.1.1" chrono-humanize = "0.1.1"
@ -19,3 +20,4 @@ solana-logger = { path = "../logger", version = "1.4.7" }
solana-program = { path = "../sdk/program", version = "1.4.7" } solana-program = { path = "../sdk/program", version = "1.4.7" }
solana-runtime = { path = "../runtime", version = "1.4.7" } solana-runtime = { path = "../runtime", version = "1.4.7" }
solana-sdk = { path = "../sdk", version = "1.4.7" } solana-sdk = { path = "../sdk", version = "1.4.7" }
tokio = { version = "0.3", features = ["full"] }

View File

@ -1,13 +1,15 @@
//! The solana-program-test provides a BanksClient-based test framework BPF programs //! The solana-program-test provides a BanksClient-based test framework BPF programs
use async_trait::async_trait;
use chrono_humanize::{Accuracy, HumanTime, Tense}; use chrono_humanize::{Accuracy, HumanTime, Tense};
use log::*; use log::*;
use solana_banks_client::start_client; use solana_banks_client::start_client;
use solana_banks_server::banks_server::start_local_server; use solana_banks_server::banks_server::start_local_server;
use solana_program::{ use solana_program::{
account_info::AccountInfo, entrypoint::ProgramResult, hash::Hash, instruction::Instruction, account_info::AccountInfo, entrypoint::ProgramResult, fee_calculator::FeeCalculator,
instruction::InstructionError, message::Message, native_token::sol_to_lamports, hash::Hash, instruction::Instruction, instruction::InstructionError, message::Message,
program_error::ProgramError, program_stubs, pubkey::Pubkey, rent::Rent, native_token::sol_to_lamports, program_error::ProgramError, program_stubs, pubkey::Pubkey,
rent::Rent,
}; };
use solana_runtime::{ use solana_runtime::{
bank::{Bank, Builtin}, bank::{Bank, Builtin},
@ -26,10 +28,11 @@ use std::{
collections::HashMap, collections::HashMap,
convert::TryFrom, convert::TryFrom,
fs::File, fs::File,
io::Read, io::{self, Read},
path::{Path, PathBuf}, path::{Path, PathBuf},
rc::Rc, rc::Rc,
sync::{Arc, RwLock}, sync::{Arc, RwLock},
time::{Duration, Instant},
}; };
// Export types so test clients can limit their solana crate dependencies // Export types so test clients can limit their solana crate dependencies
@ -579,6 +582,7 @@ impl ProgramTest {
let payer = gci.mint_keypair; let payer = gci.mint_keypair;
debug!("Payer address: {}", payer.pubkey()); debug!("Payer address: {}", payer.pubkey());
debug!("Genesis config: {}", genesis_config); debug!("Genesis config: {}", genesis_config);
let target_tick_duration = genesis_config.poh_config.target_tick_duration;
let mut bank = Bank::new(&genesis_config); let mut bank = Bank::new(&genesis_config);
@ -628,10 +632,12 @@ impl ProgramTest {
// non-zero fee calculator // non-zero fee calculator
let last_blockhash = bank.last_blockhash(); let last_blockhash = bank.last_blockhash();
while last_blockhash == bank.last_blockhash() { while last_blockhash == bank.last_blockhash() {
bank.register_tick(&Hash::default()); bank.register_tick(&Hash::new_unique());
} }
let last_blockhash = bank.last_blockhash();
// Make sure the new last_blockhash now requires a fee
assert_ne!( assert_ne!(
bank.get_fee_calculator(&bank.last_blockhash()) bank.get_fee_calculator(&last_blockhash)
.expect("fee_calculator") .expect("fee_calculator")
.lamports_per_signature, .lamports_per_signature,
0 0
@ -639,11 +645,61 @@ impl ProgramTest {
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank))); let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
let transport = start_local_server(&bank_forks).await; let transport = start_local_server(&bank_forks).await;
let mut banks_client = start_client(transport) let banks_client = start_client(transport)
.await .await
.unwrap_or_else(|err| panic!("Failed to start banks client: {}", err)); .unwrap_or_else(|err| panic!("Failed to start banks client: {}", err));
let recent_blockhash = banks_client.get_recent_blockhash().await.unwrap(); // Run a simulated PohService to provide the client with new blockhashes. New blockhashes
(banks_client, payer, recent_blockhash) // are required when sending multiple otherwise identical transactions in series from a
// test
tokio::spawn(async move {
loop {
bank_forks
.read()
.unwrap()
.working_bank()
.register_tick(&Hash::new_unique());
tokio::time::sleep(target_tick_duration).await;
}
});
(banks_client, payer, last_blockhash)
}
}
#[async_trait]
pub trait ProgramTestBanksClientExt {
async fn get_new_blockhash(&mut self, blockhash: &Hash) -> io::Result<(Hash, FeeCalculator)>;
}
#[async_trait]
impl ProgramTestBanksClientExt for BanksClient {
/// Get a new blockhash, similar in spirit to RpcClient::get_new_blockhash()
///
/// This probably should eventually be moved into BanksClient proper in some form
async fn get_new_blockhash(&mut self, blockhash: &Hash) -> io::Result<(Hash, FeeCalculator)> {
let mut num_retries = 0;
let start = Instant::now();
while start.elapsed().as_secs() < 5 {
if let Ok((fee_calculator, new_blockhash, _slot)) = self.get_fees().await {
if new_blockhash != *blockhash {
return Ok((new_blockhash, fee_calculator));
}
}
debug!("Got same blockhash ({:?}), will retry...", blockhash);
tokio::time::sleep(Duration::from_millis(200)).await;
num_retries += 1;
}
Err(io::Error::new(
io::ErrorKind::Other,
format!(
"Unable to get new blockhash after {}ms (retried {} times), stuck at {}",
start.elapsed().as_millis(),
num_retries,
blockhash
),
))
} }
} }