|
|
|
@ -7,16 +7,19 @@ use indexmap::IndexMap;
|
|
|
|
|
use indicatif::{ProgressBar, ProgressStyle};
|
|
|
|
|
use pickledb::PickleDb;
|
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
use solana_banks_client::BanksClient;
|
|
|
|
|
use solana_client::{
|
|
|
|
|
client_error::{ClientError, Result as ClientResult},
|
|
|
|
|
rpc_client::RpcClient,
|
|
|
|
|
rpc_config::RpcSendTransactionConfig,
|
|
|
|
|
};
|
|
|
|
|
use solana_sdk::{
|
|
|
|
|
commitment_config::CommitmentLevel,
|
|
|
|
|
commitment_config::CommitmentConfig,
|
|
|
|
|
instruction::Instruction,
|
|
|
|
|
message::Message,
|
|
|
|
|
native_token::{lamports_to_sol, sol_to_lamports},
|
|
|
|
|
signature::{unique_signers, Signature, Signer},
|
|
|
|
|
system_instruction,
|
|
|
|
|
transaction::Transaction,
|
|
|
|
|
transport::{self, TransportError},
|
|
|
|
|
};
|
|
|
|
|
use solana_stake_program::{
|
|
|
|
|
stake_instruction::{self, LockupArgs},
|
|
|
|
@ -25,9 +28,9 @@ use solana_stake_program::{
|
|
|
|
|
use std::{
|
|
|
|
|
cmp::{self},
|
|
|
|
|
io,
|
|
|
|
|
thread::sleep,
|
|
|
|
|
time::Duration,
|
|
|
|
|
};
|
|
|
|
|
use tokio::time::sleep;
|
|
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
|
|
|
|
struct Allocation {
|
|
|
|
@ -78,7 +81,7 @@ pub enum Error {
|
|
|
|
|
#[error("PickleDb error")]
|
|
|
|
|
PickleDbError(#[from] pickledb::error::Error),
|
|
|
|
|
#[error("Transport error")]
|
|
|
|
|
TransportError(#[from] TransportError),
|
|
|
|
|
ClientError(#[from] ClientError),
|
|
|
|
|
#[error("Missing lockup authority")]
|
|
|
|
|
MissingLockupAuthority,
|
|
|
|
|
#[error("insufficient funds in {0:?}, requires {1} SOL")]
|
|
|
|
@ -128,16 +131,16 @@ fn apply_previous_transactions(
|
|
|
|
|
allocations.retain(|x| x.amount > 0.5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn transfer<S: Signer>(
|
|
|
|
|
client: &mut BanksClient,
|
|
|
|
|
fn transfer<S: Signer>(
|
|
|
|
|
client: &RpcClient,
|
|
|
|
|
lamports: u64,
|
|
|
|
|
sender_keypair: &S,
|
|
|
|
|
to_pubkey: &Pubkey,
|
|
|
|
|
) -> io::Result<Transaction> {
|
|
|
|
|
) -> ClientResult<Transaction> {
|
|
|
|
|
let create_instruction =
|
|
|
|
|
system_instruction::transfer(&sender_keypair.pubkey(), &to_pubkey, lamports);
|
|
|
|
|
let message = Message::new(&[create_instruction], Some(&sender_keypair.pubkey()));
|
|
|
|
|
let recent_blockhash = client.get_recent_blockhash().await?;
|
|
|
|
|
let (recent_blockhash, _fees) = client.get_recent_blockhash()?;
|
|
|
|
|
Ok(Transaction::new(
|
|
|
|
|
&[sender_keypair],
|
|
|
|
|
message,
|
|
|
|
@ -218,8 +221,8 @@ fn distribution_instructions(
|
|
|
|
|
instructions
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn distribute_allocations(
|
|
|
|
|
client: &mut BanksClient,
|
|
|
|
|
fn distribute_allocations(
|
|
|
|
|
client: &RpcClient,
|
|
|
|
|
db: &mut PickleDb,
|
|
|
|
|
allocations: &[Allocation],
|
|
|
|
|
args: &DistributeTokensArgs,
|
|
|
|
@ -252,7 +255,7 @@ async fn distribute_allocations(
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|message| message.header.num_required_signatures as usize)
|
|
|
|
|
.sum();
|
|
|
|
|
check_payer_balances(num_signatures, allocations, client, args).await?;
|
|
|
|
|
check_payer_balances(num_signatures, allocations, client, args)?;
|
|
|
|
|
|
|
|
|
|
for ((allocation, message), (new_stake_account_keypair, lockup_date)) in
|
|
|
|
|
allocations.iter().zip(messages).zip(stake_extras)
|
|
|
|
@ -273,13 +276,19 @@ async fn distribute_allocations(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
let signers = unique_signers(signers);
|
|
|
|
|
let result: transport::Result<(Transaction, u64)> = {
|
|
|
|
|
let result: ClientResult<(Transaction, u64)> = {
|
|
|
|
|
if args.dry_run {
|
|
|
|
|
Ok((Transaction::new_unsigned(message), std::u64::MAX))
|
|
|
|
|
} else {
|
|
|
|
|
let (_fee_calculator, blockhash, last_valid_slot) = client.get_fees().await?;
|
|
|
|
|
let (blockhash, _fee_calculator, last_valid_slot) = client
|
|
|
|
|
.get_recent_blockhash_with_commitment(CommitmentConfig::default())?
|
|
|
|
|
.value;
|
|
|
|
|
let transaction = Transaction::new(&signers, message, blockhash);
|
|
|
|
|
client.send_transaction(transaction.clone()).await?;
|
|
|
|
|
let config = RpcSendTransactionConfig {
|
|
|
|
|
skip_preflight: true,
|
|
|
|
|
..RpcSendTransactionConfig::default()
|
|
|
|
|
};
|
|
|
|
|
client.send_transaction_with_config(&transaction, config)?;
|
|
|
|
|
Ok((transaction, last_valid_slot))
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
@ -333,8 +342,8 @@ fn new_spinner_progress_bar() -> ProgressBar {
|
|
|
|
|
progress_bar
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn process_allocations(
|
|
|
|
|
client: &mut BanksClient,
|
|
|
|
|
pub fn process_allocations(
|
|
|
|
|
client: &RpcClient,
|
|
|
|
|
args: &DistributeTokensArgs,
|
|
|
|
|
) -> Result<Option<usize>, Error> {
|
|
|
|
|
let mut allocations: Vec<Allocation> = read_allocations(&args.input_csv, args.transfer_amount)?;
|
|
|
|
@ -349,7 +358,7 @@ pub async fn process_allocations(
|
|
|
|
|
let mut db = db::open_db(&args.transaction_db, args.dry_run)?;
|
|
|
|
|
|
|
|
|
|
// Start by finalizing any transactions from the previous run.
|
|
|
|
|
let confirmations = finalize_transactions(client, &mut db, args.dry_run).await?;
|
|
|
|
|
let confirmations = finalize_transactions(client, &mut db, args.dry_run)?;
|
|
|
|
|
|
|
|
|
|
let transaction_infos = db::read_transaction_infos(&db);
|
|
|
|
|
apply_previous_transactions(&mut allocations, &transaction_infos);
|
|
|
|
@ -382,9 +391,9 @@ pub async fn process_allocations(
|
|
|
|
|
.bold()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
distribute_allocations(client, &mut db, &allocations, args).await?;
|
|
|
|
|
distribute_allocations(client, &mut db, &allocations, args)?;
|
|
|
|
|
|
|
|
|
|
let opt_confirmations = finalize_transactions(client, &mut db, args.dry_run).await?;
|
|
|
|
|
let opt_confirmations = finalize_transactions(client, &mut db, args.dry_run)?;
|
|
|
|
|
|
|
|
|
|
if !args.dry_run {
|
|
|
|
|
if let Some(output_path) = &args.output_path {
|
|
|
|
@ -395,8 +404,8 @@ pub async fn process_allocations(
|
|
|
|
|
Ok(opt_confirmations)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn finalize_transactions(
|
|
|
|
|
client: &mut BanksClient,
|
|
|
|
|
fn finalize_transactions(
|
|
|
|
|
client: &RpcClient,
|
|
|
|
|
db: &mut PickleDb,
|
|
|
|
|
dry_run: bool,
|
|
|
|
|
) -> Result<Option<usize>, Error> {
|
|
|
|
@ -404,7 +413,7 @@ async fn finalize_transactions(
|
|
|
|
|
return Ok(None);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut opt_confirmations = update_finalized_transactions(client, db).await?;
|
|
|
|
|
let mut opt_confirmations = update_finalized_transactions(client, db)?;
|
|
|
|
|
|
|
|
|
|
let progress_bar = new_spinner_progress_bar();
|
|
|
|
|
|
|
|
|
@ -417,8 +426,8 @@ async fn finalize_transactions(
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Sleep for about 1 slot
|
|
|
|
|
sleep(Duration::from_millis(500)).await;
|
|
|
|
|
let opt_conf = update_finalized_transactions(client, db).await?;
|
|
|
|
|
sleep(Duration::from_millis(500));
|
|
|
|
|
let opt_conf = update_finalized_transactions(client, db)?;
|
|
|
|
|
opt_confirmations = opt_conf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -427,8 +436,8 @@ async fn finalize_transactions(
|
|
|
|
|
|
|
|
|
|
// Update the finalized bit on any transactions that are now rooted
|
|
|
|
|
// Return the lowest number of confirmations on the unfinalized transactions or None if all are finalized.
|
|
|
|
|
async fn update_finalized_transactions(
|
|
|
|
|
client: &mut BanksClient,
|
|
|
|
|
fn update_finalized_transactions(
|
|
|
|
|
client: &RpcClient,
|
|
|
|
|
db: &mut PickleDb,
|
|
|
|
|
) -> Result<Option<usize>, Error> {
|
|
|
|
|
let transaction_infos = db::read_transaction_infos(db);
|
|
|
|
@ -448,9 +457,9 @@ async fn update_finalized_transactions(
|
|
|
|
|
.filter(|sig| *sig != Signature::default()) // Filter out dry-run signatures
|
|
|
|
|
.collect();
|
|
|
|
|
let transaction_statuses = client
|
|
|
|
|
.get_transaction_statuses(unconfirmed_signatures)
|
|
|
|
|
.await?;
|
|
|
|
|
let root_slot = client.get_root_slot().await?;
|
|
|
|
|
.get_signature_statuses(&unconfirmed_signatures)?
|
|
|
|
|
.value;
|
|
|
|
|
let root_slot = client.get_slot()?;
|
|
|
|
|
|
|
|
|
|
let mut confirmations = None;
|
|
|
|
|
for ((transaction, last_valid_slot), opt_transaction_status) in unconfirmed_transactions
|
|
|
|
@ -475,15 +484,15 @@ async fn update_finalized_transactions(
|
|
|
|
|
Ok(confirmations)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn check_payer_balances(
|
|
|
|
|
fn check_payer_balances(
|
|
|
|
|
num_signatures: usize,
|
|
|
|
|
allocations: &[Allocation],
|
|
|
|
|
client: &mut BanksClient,
|
|
|
|
|
client: &RpcClient,
|
|
|
|
|
args: &DistributeTokensArgs,
|
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
|
let mut undistributed_tokens: f64 = allocations.iter().map(|x| x.amount).sum();
|
|
|
|
|
|
|
|
|
|
let (fee_calculator, _blockhash, _last_valid_slot) = client.get_fees().await?;
|
|
|
|
|
let (_blockhash, fee_calculator) = client.get_recent_blockhash()?;
|
|
|
|
|
let fees = fee_calculator
|
|
|
|
|
.lamports_per_signature
|
|
|
|
|
.checked_mul(num_signatures as u64)
|
|
|
|
@ -505,7 +514,7 @@ async fn check_payer_balances(
|
|
|
|
|
let allocation_lamports = sol_to_lamports(undistributed_tokens);
|
|
|
|
|
|
|
|
|
|
if let Some((unlocked_sol_source, total_unlocked_sol)) = unlocked_sol_source {
|
|
|
|
|
let staker_balance = client.get_balance(distribution_source).await?;
|
|
|
|
|
let staker_balance = client.get_balance(&distribution_source)?;
|
|
|
|
|
if staker_balance < allocation_lamports {
|
|
|
|
|
return Err(Error::InsufficientFunds(
|
|
|
|
|
vec![FundingSource::StakeAccount].into(),
|
|
|
|
@ -513,7 +522,7 @@ async fn check_payer_balances(
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
if args.fee_payer.pubkey() == unlocked_sol_source {
|
|
|
|
|
let balance = client.get_balance(args.fee_payer.pubkey()).await?;
|
|
|
|
|
let balance = client.get_balance(&args.fee_payer.pubkey())?;
|
|
|
|
|
if balance < fees + total_unlocked_sol {
|
|
|
|
|
return Err(Error::InsufficientFunds(
|
|
|
|
|
vec![FundingSource::SystemAccount, FundingSource::FeePayer].into(),
|
|
|
|
@ -521,14 +530,14 @@ async fn check_payer_balances(
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
let fee_payer_balance = client.get_balance(args.fee_payer.pubkey()).await?;
|
|
|
|
|
let fee_payer_balance = client.get_balance(&args.fee_payer.pubkey())?;
|
|
|
|
|
if fee_payer_balance < fees {
|
|
|
|
|
return Err(Error::InsufficientFunds(
|
|
|
|
|
vec![FundingSource::FeePayer].into(),
|
|
|
|
|
lamports_to_sol(fees),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
let unlocked_sol_balance = client.get_balance(unlocked_sol_source).await?;
|
|
|
|
|
let unlocked_sol_balance = client.get_balance(&unlocked_sol_source)?;
|
|
|
|
|
if unlocked_sol_balance < total_unlocked_sol {
|
|
|
|
|
return Err(Error::InsufficientFunds(
|
|
|
|
|
vec![FundingSource::SystemAccount].into(),
|
|
|
|
@ -537,7 +546,7 @@ async fn check_payer_balances(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if args.fee_payer.pubkey() == distribution_source {
|
|
|
|
|
let balance = client.get_balance(args.fee_payer.pubkey()).await?;
|
|
|
|
|
let balance = client.get_balance(&args.fee_payer.pubkey())?;
|
|
|
|
|
if balance < fees + allocation_lamports {
|
|
|
|
|
return Err(Error::InsufficientFunds(
|
|
|
|
|
vec![FundingSource::SystemAccount, FundingSource::FeePayer].into(),
|
|
|
|
@ -545,14 +554,14 @@ async fn check_payer_balances(
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
let fee_payer_balance = client.get_balance(args.fee_payer.pubkey()).await?;
|
|
|
|
|
let fee_payer_balance = client.get_balance(&args.fee_payer.pubkey())?;
|
|
|
|
|
if fee_payer_balance < fees {
|
|
|
|
|
return Err(Error::InsufficientFunds(
|
|
|
|
|
vec![FundingSource::FeePayer].into(),
|
|
|
|
|
lamports_to_sol(fees),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
let sender_balance = client.get_balance(distribution_source).await?;
|
|
|
|
|
let sender_balance = client.get_balance(&distribution_source)?;
|
|
|
|
|
if sender_balance < allocation_lamports {
|
|
|
|
|
return Err(Error::InsufficientFunds(
|
|
|
|
|
vec![FundingSource::SystemAccount].into(),
|
|
|
|
@ -563,10 +572,7 @@ async fn check_payer_balances(
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn process_balances(
|
|
|
|
|
client: &mut BanksClient,
|
|
|
|
|
args: &BalancesArgs,
|
|
|
|
|
) -> Result<(), csv::Error> {
|
|
|
|
|
pub fn process_balances(client: &RpcClient, args: &BalancesArgs) -> Result<(), csv::Error> {
|
|
|
|
|
let allocations: Vec<Allocation> = read_allocations(&args.input_csv, None)?;
|
|
|
|
|
let allocations = merge_allocations(&allocations);
|
|
|
|
|
|
|
|
|
@ -582,7 +588,7 @@ pub async fn process_balances(
|
|
|
|
|
for allocation in &allocations {
|
|
|
|
|
let address = allocation.recipient.parse().unwrap();
|
|
|
|
|
let expected = lamports_to_sol(sol_to_lamports(allocation.amount));
|
|
|
|
|
let actual = lamports_to_sol(client.get_balance(address).await.unwrap());
|
|
|
|
|
let actual = lamports_to_sol(client.get_balance(&address).unwrap());
|
|
|
|
|
println!(
|
|
|
|
|
"{:<44} {:>24.9} {:>24.9} {:>24.9}",
|
|
|
|
|
allocation.recipient,
|
|
|
|
@ -604,8 +610,8 @@ pub fn process_transaction_log(args: &TransactionLogArgs) -> Result<(), Error> {
|
|
|
|
|
use crate::db::check_output_file;
|
|
|
|
|
use solana_sdk::{pubkey::Pubkey, signature::Keypair};
|
|
|
|
|
use tempfile::{tempdir, NamedTempFile};
|
|
|
|
|
pub async fn test_process_distribute_tokens_with_client(
|
|
|
|
|
client: &mut BanksClient,
|
|
|
|
|
pub fn test_process_distribute_tokens_with_client(
|
|
|
|
|
client: &RpcClient,
|
|
|
|
|
sender_keypair: Keypair,
|
|
|
|
|
transfer_amount: Option<f64>,
|
|
|
|
|
) {
|
|
|
|
@ -616,17 +622,12 @@ pub async fn test_process_distribute_tokens_with_client(
|
|
|
|
|
&sender_keypair,
|
|
|
|
|
&fee_payer.pubkey(),
|
|
|
|
|
)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
client
|
|
|
|
|
.process_transaction_with_commitment(transaction, CommitmentLevel::Recent)
|
|
|
|
|
.await
|
|
|
|
|
.send_and_confirm_transaction_with_spinner(&transaction)
|
|
|
|
|
.unwrap();
|
|
|
|
|
assert_eq!(
|
|
|
|
|
client
|
|
|
|
|
.get_balance_with_commitment(fee_payer.pubkey(), CommitmentLevel::Recent)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap(),
|
|
|
|
|
client.get_balance(&fee_payer.pubkey()).unwrap(),
|
|
|
|
|
sol_to_lamports(1.0),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
@ -667,7 +668,7 @@ pub async fn test_process_distribute_tokens_with_client(
|
|
|
|
|
stake_args: None,
|
|
|
|
|
transfer_amount,
|
|
|
|
|
};
|
|
|
|
|
let confirmations = process_allocations(client, &args).await.unwrap();
|
|
|
|
|
let confirmations = process_allocations(client, &args).unwrap();
|
|
|
|
|
assert_eq!(confirmations, None);
|
|
|
|
|
|
|
|
|
|
let transaction_infos =
|
|
|
|
@ -680,15 +681,12 @@ pub async fn test_process_distribute_tokens_with_client(
|
|
|
|
|
expected_amount
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
client.get_balance(alice_pubkey).await.unwrap(),
|
|
|
|
|
expected_amount,
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(client.get_balance(&alice_pubkey).unwrap(), expected_amount,);
|
|
|
|
|
|
|
|
|
|
check_output_file(&output_path, &db::open_db(&transaction_db, true).unwrap());
|
|
|
|
|
|
|
|
|
|
// Now, run it again, and check there's no double-spend.
|
|
|
|
|
process_allocations(client, &args).await.unwrap();
|
|
|
|
|
process_allocations(client, &args).unwrap();
|
|
|
|
|
let transaction_infos =
|
|
|
|
|
db::read_transaction_infos(&db::open_db(&transaction_db, true).unwrap());
|
|
|
|
|
assert_eq!(transaction_infos.len(), 1);
|
|
|
|
@ -699,18 +697,12 @@ pub async fn test_process_distribute_tokens_with_client(
|
|
|
|
|
expected_amount
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
client.get_balance(alice_pubkey).await.unwrap(),
|
|
|
|
|
expected_amount,
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(client.get_balance(&alice_pubkey).unwrap(), expected_amount,);
|
|
|
|
|
|
|
|
|
|
check_output_file(&output_path, &db::open_db(&transaction_db, true).unwrap());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn test_process_distribute_stake_with_client(
|
|
|
|
|
client: &mut BanksClient,
|
|
|
|
|
sender_keypair: Keypair,
|
|
|
|
|
) {
|
|
|
|
|
pub fn test_process_distribute_stake_with_client(client: &RpcClient, sender_keypair: Keypair) {
|
|
|
|
|
let fee_payer = Keypair::new();
|
|
|
|
|
let transaction = transfer(
|
|
|
|
|
client,
|
|
|
|
@ -718,11 +710,9 @@ pub async fn test_process_distribute_stake_with_client(
|
|
|
|
|
&sender_keypair,
|
|
|
|
|
&fee_payer.pubkey(),
|
|
|
|
|
)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
client
|
|
|
|
|
.process_transaction_with_commitment(transaction, CommitmentLevel::Recent)
|
|
|
|
|
.await
|
|
|
|
|
.send_and_confirm_transaction_with_spinner(&transaction)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
let stake_account_keypair = Keypair::new();
|
|
|
|
@ -744,11 +734,10 @@ pub async fn test_process_distribute_stake_with_client(
|
|
|
|
|
);
|
|
|
|
|
let message = Message::new(&instructions, Some(&sender_keypair.pubkey()));
|
|
|
|
|
let signers = [&sender_keypair, &stake_account_keypair];
|
|
|
|
|
let blockhash = client.get_recent_blockhash().await.unwrap();
|
|
|
|
|
let (blockhash, _fees) = client.get_recent_blockhash().unwrap();
|
|
|
|
|
let transaction = Transaction::new(&signers, message, blockhash);
|
|
|
|
|
client
|
|
|
|
|
.process_transaction_with_commitment(transaction, CommitmentLevel::Recent)
|
|
|
|
|
.await
|
|
|
|
|
.send_and_confirm_transaction_with_spinner(&transaction)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
let alice_pubkey = solana_sdk::pubkey::new_rand();
|
|
|
|
@ -791,7 +780,7 @@ pub async fn test_process_distribute_stake_with_client(
|
|
|
|
|
sender_keypair: Box::new(sender_keypair),
|
|
|
|
|
transfer_amount: None,
|
|
|
|
|
};
|
|
|
|
|
let confirmations = process_allocations(client, &args).await.unwrap();
|
|
|
|
|
let confirmations = process_allocations(client, &args).unwrap();
|
|
|
|
|
assert_eq!(confirmations, None);
|
|
|
|
|
|
|
|
|
|
let transaction_infos =
|
|
|
|
@ -805,19 +794,19 @@ pub async fn test_process_distribute_stake_with_client(
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
client.get_balance(alice_pubkey).await.unwrap(),
|
|
|
|
|
client.get_balance(&alice_pubkey).unwrap(),
|
|
|
|
|
sol_to_lamports(1.0),
|
|
|
|
|
);
|
|
|
|
|
let new_stake_account_address = transaction_infos[0].new_stake_account_address.unwrap();
|
|
|
|
|
assert_eq!(
|
|
|
|
|
client.get_balance(new_stake_account_address).await.unwrap(),
|
|
|
|
|
client.get_balance(&new_stake_account_address).unwrap(),
|
|
|
|
|
expected_amount - sol_to_lamports(1.0),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
check_output_file(&output_path, &db::open_db(&transaction_db, true).unwrap());
|
|
|
|
|
|
|
|
|
|
// Now, run it again, and check there's no double-spend.
|
|
|
|
|
process_allocations(client, &args).await.unwrap();
|
|
|
|
|
process_allocations(client, &args).unwrap();
|
|
|
|
|
let transaction_infos =
|
|
|
|
|
db::read_transaction_infos(&db::open_db(&transaction_db, true).unwrap());
|
|
|
|
|
assert_eq!(transaction_infos.len(), 1);
|
|
|
|
@ -829,11 +818,11 @@ pub async fn test_process_distribute_stake_with_client(
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
client.get_balance(alice_pubkey).await.unwrap(),
|
|
|
|
|
client.get_balance(&alice_pubkey).unwrap(),
|
|
|
|
|
sol_to_lamports(1.0),
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
client.get_balance(new_stake_account_address).await.unwrap(),
|
|
|
|
|
client.get_balance(&new_stake_account_address).unwrap(),
|
|
|
|
|
expected_amount - sol_to_lamports(1.0),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
@ -843,55 +832,67 @@ pub async fn test_process_distribute_stake_with_client(
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
use solana_banks_client::start_client;
|
|
|
|
|
use solana_banks_server::banks_server::start_local_server;
|
|
|
|
|
use solana_runtime::{bank::Bank, bank_forks::BankForks};
|
|
|
|
|
use solana_client::rpc_client::get_rpc_request_str;
|
|
|
|
|
use solana_core::test_validator::TestValidator;
|
|
|
|
|
use solana_sdk::{
|
|
|
|
|
fee_calculator::FeeRateGovernor,
|
|
|
|
|
genesis_config::create_genesis_config,
|
|
|
|
|
clock::DEFAULT_MS_PER_SLOT,
|
|
|
|
|
signature::{read_keypair_file, write_keypair_file},
|
|
|
|
|
};
|
|
|
|
|
use solana_stake_program::stake_instruction::StakeInstruction;
|
|
|
|
|
use std::sync::{Arc, RwLock};
|
|
|
|
|
use tokio::runtime::Runtime;
|
|
|
|
|
use std::fs::remove_dir_all;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_process_token_allocations() {
|
|
|
|
|
let (genesis_config, sender_keypair) = create_genesis_config(sol_to_lamports(9_000_000.0));
|
|
|
|
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(Bank::new(&genesis_config))));
|
|
|
|
|
Runtime::new().unwrap().block_on(async {
|
|
|
|
|
let transport = start_local_server(&bank_forks).await;
|
|
|
|
|
let mut banks_client = start_client(transport).await.unwrap();
|
|
|
|
|
test_process_distribute_tokens_with_client(&mut banks_client, sender_keypair, None)
|
|
|
|
|
.await;
|
|
|
|
|
});
|
|
|
|
|
let TestValidator {
|
|
|
|
|
server,
|
|
|
|
|
leader_data,
|
|
|
|
|
alice,
|
|
|
|
|
ledger_path,
|
|
|
|
|
..
|
|
|
|
|
} = TestValidator::run();
|
|
|
|
|
let url = get_rpc_request_str(leader_data.rpc, false);
|
|
|
|
|
let client = RpcClient::new_with_commitment(url, CommitmentConfig::recent());
|
|
|
|
|
test_process_distribute_tokens_with_client(&client, alice, None);
|
|
|
|
|
|
|
|
|
|
// Explicit cleanup, otherwise "pure virtual method called" crash in Docker
|
|
|
|
|
server.close().unwrap();
|
|
|
|
|
remove_dir_all(ledger_path).unwrap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_process_transfer_amount_allocations() {
|
|
|
|
|
let (genesis_config, sender_keypair) = create_genesis_config(sol_to_lamports(9_000_000.0));
|
|
|
|
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(Bank::new(&genesis_config))));
|
|
|
|
|
Runtime::new().unwrap().block_on(async {
|
|
|
|
|
let transport = start_local_server(&bank_forks).await;
|
|
|
|
|
let mut banks_client = start_client(transport).await.unwrap();
|
|
|
|
|
test_process_distribute_tokens_with_client(
|
|
|
|
|
&mut banks_client,
|
|
|
|
|
sender_keypair,
|
|
|
|
|
Some(1.5),
|
|
|
|
|
)
|
|
|
|
|
.await;
|
|
|
|
|
});
|
|
|
|
|
let TestValidator {
|
|
|
|
|
server,
|
|
|
|
|
leader_data,
|
|
|
|
|
alice,
|
|
|
|
|
ledger_path,
|
|
|
|
|
..
|
|
|
|
|
} = TestValidator::run();
|
|
|
|
|
let url = get_rpc_request_str(leader_data.rpc, false);
|
|
|
|
|
let client = RpcClient::new_with_commitment(url, CommitmentConfig::recent());
|
|
|
|
|
test_process_distribute_tokens_with_client(&client, alice, Some(1.5));
|
|
|
|
|
|
|
|
|
|
// Explicit cleanup, otherwise "pure virtual method called" crash in Docker
|
|
|
|
|
server.close().unwrap();
|
|
|
|
|
remove_dir_all(ledger_path).unwrap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_process_stake_allocations() {
|
|
|
|
|
let (genesis_config, sender_keypair) = create_genesis_config(sol_to_lamports(9_000_000.0));
|
|
|
|
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(Bank::new(&genesis_config))));
|
|
|
|
|
Runtime::new().unwrap().block_on(async {
|
|
|
|
|
let transport = start_local_server(&bank_forks).await;
|
|
|
|
|
let mut banks_client = start_client(transport).await.unwrap();
|
|
|
|
|
test_process_distribute_stake_with_client(&mut banks_client, sender_keypair).await;
|
|
|
|
|
});
|
|
|
|
|
let TestValidator {
|
|
|
|
|
server,
|
|
|
|
|
leader_data,
|
|
|
|
|
alice,
|
|
|
|
|
ledger_path,
|
|
|
|
|
..
|
|
|
|
|
} = TestValidator::run();
|
|
|
|
|
let url = get_rpc_request_str(leader_data.rpc, false);
|
|
|
|
|
let client = RpcClient::new_with_commitment(url, CommitmentConfig::recent());
|
|
|
|
|
test_process_distribute_stake_with_client(&client, alice);
|
|
|
|
|
|
|
|
|
|
// Explicit cleanup, otherwise "pure virtual method called" crash in Docker
|
|
|
|
|
server.close().unwrap();
|
|
|
|
|
remove_dir_all(ledger_path).unwrap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
@ -1115,16 +1116,28 @@ mod tests {
|
|
|
|
|
fn test_check_payer_balances_distribute_tokens_single_payer() {
|
|
|
|
|
let fees = 10_000;
|
|
|
|
|
let fees_in_sol = lamports_to_sol(fees);
|
|
|
|
|
let (mut genesis_config, sender_keypair) =
|
|
|
|
|
create_genesis_config(sol_to_lamports(9_000_000.0));
|
|
|
|
|
genesis_config.fee_rate_governor = FeeRateGovernor::new(fees, 0);
|
|
|
|
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(Bank::new(&genesis_config))));
|
|
|
|
|
Runtime::new().unwrap().block_on(async {
|
|
|
|
|
let transport = start_local_server(&bank_forks).await;
|
|
|
|
|
let mut banks_client = start_client(transport).await.unwrap();
|
|
|
|
|
let TestValidator {
|
|
|
|
|
server,
|
|
|
|
|
leader_data,
|
|
|
|
|
alice,
|
|
|
|
|
ledger_path,
|
|
|
|
|
..
|
|
|
|
|
} = TestValidator::run_with_fees(fees);
|
|
|
|
|
let url = get_rpc_request_str(leader_data.rpc, false);
|
|
|
|
|
let client = RpcClient::new_with_commitment(url, CommitmentConfig::recent());
|
|
|
|
|
let sender_keypair_file = tmp_file_path("keypair_file", &alice.pubkey());
|
|
|
|
|
write_keypair_file(&alice, &sender_keypair_file).unwrap();
|
|
|
|
|
|
|
|
|
|
let sender_keypair_file = tmp_file_path("keypair_file", &sender_keypair.pubkey());
|
|
|
|
|
write_keypair_file(&sender_keypair, &sender_keypair_file).unwrap();
|
|
|
|
|
// This is a quick hack until TestValidator can be initialized with fees from block 0
|
|
|
|
|
while client
|
|
|
|
|
.get_recent_blockhash()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.1
|
|
|
|
|
.lamports_per_signature
|
|
|
|
|
== 0
|
|
|
|
|
{
|
|
|
|
|
sleep(Duration::from_millis(DEFAULT_MS_PER_SLOT));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let allocation_amount = 1000.0;
|
|
|
|
|
|
|
|
|
@ -1135,14 +1148,11 @@ mod tests {
|
|
|
|
|
&sender_keypair_file,
|
|
|
|
|
None,
|
|
|
|
|
);
|
|
|
|
|
check_payer_balances(1, &allocations, &mut banks_client, &args)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
check_payer_balances(1, &allocations, &client, &args).unwrap();
|
|
|
|
|
|
|
|
|
|
// Unfunded payer
|
|
|
|
|
let unfunded_payer = Keypair::new();
|
|
|
|
|
let unfunded_payer_keypair_file =
|
|
|
|
|
tmp_file_path("keypair_file", &unfunded_payer.pubkey());
|
|
|
|
|
let unfunded_payer_keypair_file = tmp_file_path("keypair_file", &unfunded_payer.pubkey());
|
|
|
|
|
write_keypair_file(&unfunded_payer, &unfunded_payer_keypair_file).unwrap();
|
|
|
|
|
args.sender_keypair = read_keypair_file(&unfunded_payer_keypair_file)
|
|
|
|
|
.unwrap()
|
|
|
|
@ -1151,9 +1161,7 @@ mod tests {
|
|
|
|
|
.unwrap()
|
|
|
|
|
.into();
|
|
|
|
|
|
|
|
|
|
let err_result = check_payer_balances(1, &allocations, &mut banks_client, &args)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap_err();
|
|
|
|
|
let err_result = check_payer_balances(1, &allocations, &client, &args).unwrap_err();
|
|
|
|
|
if let Error::InsufficientFunds(sources, amount) = err_result {
|
|
|
|
|
assert_eq!(
|
|
|
|
|
sources,
|
|
|
|
@ -1174,16 +1182,14 @@ mod tests {
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
let transaction = transfer(
|
|
|
|
|
&mut banks_client,
|
|
|
|
|
&client,
|
|
|
|
|
sol_to_lamports(allocation_amount),
|
|
|
|
|
&sender_keypair,
|
|
|
|
|
&alice,
|
|
|
|
|
&partially_funded_payer.pubkey(),
|
|
|
|
|
)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
banks_client
|
|
|
|
|
.process_transaction_with_commitment(transaction, CommitmentLevel::Recent)
|
|
|
|
|
.await
|
|
|
|
|
client
|
|
|
|
|
.send_and_confirm_transaction_with_spinner(&transaction)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
args.sender_keypair = read_keypair_file(&partially_funded_payer_keypair_file)
|
|
|
|
@ -1192,9 +1198,7 @@ mod tests {
|
|
|
|
|
args.fee_payer = read_keypair_file(&partially_funded_payer_keypair_file)
|
|
|
|
|
.unwrap()
|
|
|
|
|
.into();
|
|
|
|
|
let err_result = check_payer_balances(1, &allocations, &mut banks_client, &args)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap_err();
|
|
|
|
|
let err_result = check_payer_balances(1, &allocations, &client, &args).unwrap_err();
|
|
|
|
|
if let Error::InsufficientFunds(sources, amount) = err_result {
|
|
|
|
|
assert_eq!(
|
|
|
|
|
sources,
|
|
|
|
@ -1204,23 +1208,39 @@ mod tests {
|
|
|
|
|
} else {
|
|
|
|
|
panic!("check_payer_balances should have errored");
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Explicit cleanup, otherwise "pure virtual method called" crash in Docker
|
|
|
|
|
server.close().unwrap();
|
|
|
|
|
remove_dir_all(ledger_path).unwrap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_check_payer_balances_distribute_tokens_separate_payers() {
|
|
|
|
|
let fees = 10_000;
|
|
|
|
|
let fees_in_sol = lamports_to_sol(fees);
|
|
|
|
|
let (mut genesis_config, sender_keypair) =
|
|
|
|
|
create_genesis_config(sol_to_lamports(9_000_000.0));
|
|
|
|
|
genesis_config.fee_rate_governor = FeeRateGovernor::new(fees, 0);
|
|
|
|
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(Bank::new(&genesis_config))));
|
|
|
|
|
Runtime::new().unwrap().block_on(async {
|
|
|
|
|
let transport = start_local_server(&bank_forks).await;
|
|
|
|
|
let mut banks_client = start_client(transport).await.unwrap();
|
|
|
|
|
let TestValidator {
|
|
|
|
|
server,
|
|
|
|
|
leader_data,
|
|
|
|
|
alice,
|
|
|
|
|
ledger_path,
|
|
|
|
|
..
|
|
|
|
|
} = TestValidator::run_with_fees(fees);
|
|
|
|
|
let url = get_rpc_request_str(leader_data.rpc, false);
|
|
|
|
|
let client = RpcClient::new_with_commitment(url, CommitmentConfig::recent());
|
|
|
|
|
|
|
|
|
|
let sender_keypair_file = tmp_file_path("keypair_file", &sender_keypair.pubkey());
|
|
|
|
|
write_keypair_file(&sender_keypair, &sender_keypair_file).unwrap();
|
|
|
|
|
let sender_keypair_file = tmp_file_path("keypair_file", &alice.pubkey());
|
|
|
|
|
write_keypair_file(&alice, &sender_keypair_file).unwrap();
|
|
|
|
|
|
|
|
|
|
// This is a quick hack until TestValidator can be initialized with fees from block 0
|
|
|
|
|
while client
|
|
|
|
|
.get_recent_blockhash()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.1
|
|
|
|
|
.lamports_per_signature
|
|
|
|
|
== 0
|
|
|
|
|
{
|
|
|
|
|
sleep(Duration::from_millis(DEFAULT_MS_PER_SLOT));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let allocation_amount = 1000.0;
|
|
|
|
|
|
|
|
|
@ -1228,16 +1248,14 @@ mod tests {
|
|
|
|
|
let funded_payer_keypair_file = tmp_file_path("keypair_file", &funded_payer.pubkey());
|
|
|
|
|
write_keypair_file(&funded_payer, &funded_payer_keypair_file).unwrap();
|
|
|
|
|
let transaction = transfer(
|
|
|
|
|
&mut banks_client,
|
|
|
|
|
&client,
|
|
|
|
|
sol_to_lamports(allocation_amount),
|
|
|
|
|
&sender_keypair,
|
|
|
|
|
&alice,
|
|
|
|
|
&funded_payer.pubkey(),
|
|
|
|
|
)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
banks_client
|
|
|
|
|
.process_transaction_with_commitment(transaction, CommitmentLevel::Recent)
|
|
|
|
|
.await
|
|
|
|
|
client
|
|
|
|
|
.send_and_confirm_transaction_with_spinner(&transaction)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
// Fully funded payers
|
|
|
|
@ -1247,23 +1265,18 @@ mod tests {
|
|
|
|
|
&sender_keypair_file,
|
|
|
|
|
None,
|
|
|
|
|
);
|
|
|
|
|
check_payer_balances(1, &allocations, &mut banks_client, &args)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
check_payer_balances(1, &allocations, &client, &args).unwrap();
|
|
|
|
|
|
|
|
|
|
// Unfunded sender
|
|
|
|
|
let unfunded_payer = Keypair::new();
|
|
|
|
|
let unfunded_payer_keypair_file =
|
|
|
|
|
tmp_file_path("keypair_file", &unfunded_payer.pubkey());
|
|
|
|
|
let unfunded_payer_keypair_file = tmp_file_path("keypair_file", &unfunded_payer.pubkey());
|
|
|
|
|
write_keypair_file(&unfunded_payer, &unfunded_payer_keypair_file).unwrap();
|
|
|
|
|
args.sender_keypair = read_keypair_file(&unfunded_payer_keypair_file)
|
|
|
|
|
.unwrap()
|
|
|
|
|
.into();
|
|
|
|
|
args.fee_payer = read_keypair_file(&sender_keypair_file).unwrap().into();
|
|
|
|
|
|
|
|
|
|
let err_result = check_payer_balances(1, &allocations, &mut banks_client, &args)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap_err();
|
|
|
|
|
let err_result = check_payer_balances(1, &allocations, &client, &args).unwrap_err();
|
|
|
|
|
if let Error::InsufficientFunds(sources, amount) = err_result {
|
|
|
|
|
assert_eq!(sources, vec![FundingSource::SystemAccount].into());
|
|
|
|
|
assert!((amount - allocation_amount).abs() < f64::EPSILON);
|
|
|
|
@ -1277,23 +1290,24 @@ mod tests {
|
|
|
|
|
.unwrap()
|
|
|
|
|
.into();
|
|
|
|
|
|
|
|
|
|
let err_result = check_payer_balances(1, &allocations, &mut banks_client, &args)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap_err();
|
|
|
|
|
let err_result = check_payer_balances(1, &allocations, &client, &args).unwrap_err();
|
|
|
|
|
if let Error::InsufficientFunds(sources, amount) = err_result {
|
|
|
|
|
assert_eq!(sources, vec![FundingSource::FeePayer].into());
|
|
|
|
|
assert!((amount - fees_in_sol).abs() < f64::EPSILON);
|
|
|
|
|
} else {
|
|
|
|
|
panic!("check_payer_balances should have errored");
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Explicit cleanup, otherwise "pure virtual method called" crash in Docker
|
|
|
|
|
server.close().unwrap();
|
|
|
|
|
remove_dir_all(ledger_path).unwrap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn initialize_stake_account(
|
|
|
|
|
fn initialize_stake_account(
|
|
|
|
|
stake_account_amount: f64,
|
|
|
|
|
unlocked_sol: f64,
|
|
|
|
|
sender_keypair: &Keypair,
|
|
|
|
|
banks_client: &mut BanksClient,
|
|
|
|
|
client: &RpcClient,
|
|
|
|
|
) -> StakeArgs {
|
|
|
|
|
let stake_account_keypair = Keypair::new();
|
|
|
|
|
let stake_account_address = stake_account_keypair.pubkey();
|
|
|
|
@ -1314,11 +1328,10 @@ mod tests {
|
|
|
|
|
);
|
|
|
|
|
let message = Message::new(&instructions, Some(&sender_keypair.pubkey()));
|
|
|
|
|
let signers = [sender_keypair, &stake_account_keypair];
|
|
|
|
|
let blockhash = banks_client.get_recent_blockhash().await.unwrap();
|
|
|
|
|
let (blockhash, _fees) = client.get_recent_blockhash().unwrap();
|
|
|
|
|
let transaction = Transaction::new(&signers, message, blockhash);
|
|
|
|
|
banks_client
|
|
|
|
|
.process_transaction_with_commitment(transaction, CommitmentLevel::Recent)
|
|
|
|
|
.await
|
|
|
|
|
client
|
|
|
|
|
.send_and_confirm_transaction_with_spinner(&transaction)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
StakeArgs {
|
|
|
|
@ -1334,26 +1347,33 @@ mod tests {
|
|
|
|
|
fn test_check_payer_balances_distribute_stakes_single_payer() {
|
|
|
|
|
let fees = 10_000;
|
|
|
|
|
let fees_in_sol = lamports_to_sol(fees);
|
|
|
|
|
let (mut genesis_config, sender_keypair) =
|
|
|
|
|
create_genesis_config(sol_to_lamports(9_000_000.0));
|
|
|
|
|
genesis_config.fee_rate_governor = FeeRateGovernor::new(fees, 0);
|
|
|
|
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(Bank::new(&genesis_config))));
|
|
|
|
|
Runtime::new().unwrap().block_on(async {
|
|
|
|
|
let transport = start_local_server(&bank_forks).await;
|
|
|
|
|
let mut banks_client = start_client(transport).await.unwrap();
|
|
|
|
|
let TestValidator {
|
|
|
|
|
server,
|
|
|
|
|
leader_data,
|
|
|
|
|
alice,
|
|
|
|
|
ledger_path,
|
|
|
|
|
..
|
|
|
|
|
} = TestValidator::run_with_fees(fees);
|
|
|
|
|
let url = get_rpc_request_str(leader_data.rpc, false);
|
|
|
|
|
let client = RpcClient::new_with_commitment(url, CommitmentConfig::recent());
|
|
|
|
|
|
|
|
|
|
let sender_keypair_file = tmp_file_path("keypair_file", &sender_keypair.pubkey());
|
|
|
|
|
write_keypair_file(&sender_keypair, &sender_keypair_file).unwrap();
|
|
|
|
|
let sender_keypair_file = tmp_file_path("keypair_file", &alice.pubkey());
|
|
|
|
|
write_keypair_file(&alice, &sender_keypair_file).unwrap();
|
|
|
|
|
|
|
|
|
|
// This is a quick hack until TestValidator can be initialized with fees from block 0
|
|
|
|
|
while client
|
|
|
|
|
.get_recent_blockhash()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.1
|
|
|
|
|
.lamports_per_signature
|
|
|
|
|
== 0
|
|
|
|
|
{
|
|
|
|
|
sleep(Duration::from_millis(DEFAULT_MS_PER_SLOT));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let allocation_amount = 1000.0;
|
|
|
|
|
let unlocked_sol = 1.0;
|
|
|
|
|
let stake_args = initialize_stake_account(
|
|
|
|
|
allocation_amount,
|
|
|
|
|
unlocked_sol,
|
|
|
|
|
&sender_keypair,
|
|
|
|
|
&mut banks_client,
|
|
|
|
|
)
|
|
|
|
|
.await;
|
|
|
|
|
let stake_args = initialize_stake_account(allocation_amount, unlocked_sol, &alice, &client);
|
|
|
|
|
|
|
|
|
|
// Fully funded payer & stake account
|
|
|
|
|
let (allocations, mut args) = initialize_check_payer_balances_inputs(
|
|
|
|
@ -1362,9 +1382,7 @@ mod tests {
|
|
|
|
|
&sender_keypair_file,
|
|
|
|
|
Some(stake_args),
|
|
|
|
|
);
|
|
|
|
|
check_payer_balances(1, &allocations, &mut banks_client, &args)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
check_payer_balances(1, &allocations, &client, &args).unwrap();
|
|
|
|
|
|
|
|
|
|
// Underfunded stake-account
|
|
|
|
|
let expensive_allocation_amount = 5000.0;
|
|
|
|
@ -1374,22 +1392,17 @@ mod tests {
|
|
|
|
|
lockup_date: "".to_string(),
|
|
|
|
|
}];
|
|
|
|
|
let err_result =
|
|
|
|
|
check_payer_balances(1, &expensive_allocations, &mut banks_client, &args)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap_err();
|
|
|
|
|
check_payer_balances(1, &expensive_allocations, &client, &args).unwrap_err();
|
|
|
|
|
if let Error::InsufficientFunds(sources, amount) = err_result {
|
|
|
|
|
assert_eq!(sources, vec![FundingSource::StakeAccount].into());
|
|
|
|
|
assert!(
|
|
|
|
|
(amount - (expensive_allocation_amount - unlocked_sol)).abs() < f64::EPSILON
|
|
|
|
|
);
|
|
|
|
|
assert!((amount - (expensive_allocation_amount - unlocked_sol)).abs() < f64::EPSILON);
|
|
|
|
|
} else {
|
|
|
|
|
panic!("check_payer_balances should have errored");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Unfunded payer
|
|
|
|
|
let unfunded_payer = Keypair::new();
|
|
|
|
|
let unfunded_payer_keypair_file =
|
|
|
|
|
tmp_file_path("keypair_file", &unfunded_payer.pubkey());
|
|
|
|
|
let unfunded_payer_keypair_file = tmp_file_path("keypair_file", &unfunded_payer.pubkey());
|
|
|
|
|
write_keypair_file(&unfunded_payer, &unfunded_payer_keypair_file).unwrap();
|
|
|
|
|
args.sender_keypair = read_keypair_file(&unfunded_payer_keypair_file)
|
|
|
|
|
.unwrap()
|
|
|
|
@ -1398,9 +1411,7 @@ mod tests {
|
|
|
|
|
.unwrap()
|
|
|
|
|
.into();
|
|
|
|
|
|
|
|
|
|
let err_result = check_payer_balances(1, &allocations, &mut banks_client, &args)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap_err();
|
|
|
|
|
let err_result = check_payer_balances(1, &allocations, &client, &args).unwrap_err();
|
|
|
|
|
if let Error::InsufficientFunds(sources, amount) = err_result {
|
|
|
|
|
assert_eq!(
|
|
|
|
|
sources,
|
|
|
|
@ -1421,16 +1432,14 @@ mod tests {
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
let transaction = transfer(
|
|
|
|
|
&mut banks_client,
|
|
|
|
|
&client,
|
|
|
|
|
sol_to_lamports(unlocked_sol),
|
|
|
|
|
&sender_keypair,
|
|
|
|
|
&alice,
|
|
|
|
|
&partially_funded_payer.pubkey(),
|
|
|
|
|
)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
banks_client
|
|
|
|
|
.process_transaction_with_commitment(transaction, CommitmentLevel::Recent)
|
|
|
|
|
.await
|
|
|
|
|
client
|
|
|
|
|
.send_and_confirm_transaction_with_spinner(&transaction)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
args.sender_keypair = read_keypair_file(&partially_funded_payer_keypair_file)
|
|
|
|
@ -1439,9 +1448,7 @@ mod tests {
|
|
|
|
|
args.fee_payer = read_keypair_file(&partially_funded_payer_keypair_file)
|
|
|
|
|
.unwrap()
|
|
|
|
|
.into();
|
|
|
|
|
let err_result = check_payer_balances(1, &allocations, &mut banks_client, &args)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap_err();
|
|
|
|
|
let err_result = check_payer_balances(1, &allocations, &client, &args).unwrap_err();
|
|
|
|
|
if let Error::InsufficientFunds(sources, amount) = err_result {
|
|
|
|
|
assert_eq!(
|
|
|
|
|
sources,
|
|
|
|
@ -1451,48 +1458,56 @@ mod tests {
|
|
|
|
|
} else {
|
|
|
|
|
panic!("check_payer_balances should have errored");
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Explicit cleanup, otherwise "pure virtual method called" crash in Docker
|
|
|
|
|
server.close().unwrap();
|
|
|
|
|
remove_dir_all(ledger_path).unwrap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_check_payer_balances_distribute_stakes_separate_payers() {
|
|
|
|
|
let fees = 10_000;
|
|
|
|
|
let fees_in_sol = lamports_to_sol(fees);
|
|
|
|
|
let (mut genesis_config, sender_keypair) =
|
|
|
|
|
create_genesis_config(sol_to_lamports(9_000_000.0));
|
|
|
|
|
genesis_config.fee_rate_governor = FeeRateGovernor::new(fees, 0);
|
|
|
|
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(Bank::new(&genesis_config))));
|
|
|
|
|
Runtime::new().unwrap().block_on(async {
|
|
|
|
|
let transport = start_local_server(&bank_forks).await;
|
|
|
|
|
let mut banks_client = start_client(transport).await.unwrap();
|
|
|
|
|
let TestValidator {
|
|
|
|
|
server,
|
|
|
|
|
leader_data,
|
|
|
|
|
alice,
|
|
|
|
|
ledger_path,
|
|
|
|
|
..
|
|
|
|
|
} = TestValidator::run_with_fees(fees);
|
|
|
|
|
let url = get_rpc_request_str(leader_data.rpc, false);
|
|
|
|
|
let client = RpcClient::new_with_commitment(url, CommitmentConfig::recent());
|
|
|
|
|
|
|
|
|
|
let sender_keypair_file = tmp_file_path("keypair_file", &sender_keypair.pubkey());
|
|
|
|
|
write_keypair_file(&sender_keypair, &sender_keypair_file).unwrap();
|
|
|
|
|
let sender_keypair_file = tmp_file_path("keypair_file", &alice.pubkey());
|
|
|
|
|
write_keypair_file(&alice, &sender_keypair_file).unwrap();
|
|
|
|
|
|
|
|
|
|
// This is a quick hack until TestValidator can be initialized with fees from block 0
|
|
|
|
|
while client
|
|
|
|
|
.get_recent_blockhash()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.1
|
|
|
|
|
.lamports_per_signature
|
|
|
|
|
== 0
|
|
|
|
|
{
|
|
|
|
|
sleep(Duration::from_millis(DEFAULT_MS_PER_SLOT));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let allocation_amount = 1000.0;
|
|
|
|
|
let unlocked_sol = 1.0;
|
|
|
|
|
let stake_args = initialize_stake_account(
|
|
|
|
|
allocation_amount,
|
|
|
|
|
unlocked_sol,
|
|
|
|
|
&sender_keypair,
|
|
|
|
|
&mut banks_client,
|
|
|
|
|
)
|
|
|
|
|
.await;
|
|
|
|
|
let stake_args = initialize_stake_account(allocation_amount, unlocked_sol, &alice, &client);
|
|
|
|
|
|
|
|
|
|
let funded_payer = Keypair::new();
|
|
|
|
|
let funded_payer_keypair_file = tmp_file_path("keypair_file", &funded_payer.pubkey());
|
|
|
|
|
write_keypair_file(&funded_payer, &funded_payer_keypair_file).unwrap();
|
|
|
|
|
let transaction = transfer(
|
|
|
|
|
&mut banks_client,
|
|
|
|
|
&client,
|
|
|
|
|
sol_to_lamports(unlocked_sol),
|
|
|
|
|
&sender_keypair,
|
|
|
|
|
&alice,
|
|
|
|
|
&funded_payer.pubkey(),
|
|
|
|
|
)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
banks_client
|
|
|
|
|
.process_transaction_with_commitment(transaction, CommitmentLevel::Recent)
|
|
|
|
|
.await
|
|
|
|
|
client
|
|
|
|
|
.send_and_confirm_transaction_with_spinner(&transaction)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
// Fully funded payers
|
|
|
|
@ -1502,23 +1517,18 @@ mod tests {
|
|
|
|
|
&sender_keypair_file,
|
|
|
|
|
Some(stake_args),
|
|
|
|
|
);
|
|
|
|
|
check_payer_balances(1, &allocations, &mut banks_client, &args)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
check_payer_balances(1, &allocations, &client, &args).unwrap();
|
|
|
|
|
|
|
|
|
|
// Unfunded sender
|
|
|
|
|
let unfunded_payer = Keypair::new();
|
|
|
|
|
let unfunded_payer_keypair_file =
|
|
|
|
|
tmp_file_path("keypair_file", &unfunded_payer.pubkey());
|
|
|
|
|
let unfunded_payer_keypair_file = tmp_file_path("keypair_file", &unfunded_payer.pubkey());
|
|
|
|
|
write_keypair_file(&unfunded_payer, &unfunded_payer_keypair_file).unwrap();
|
|
|
|
|
args.sender_keypair = read_keypair_file(&unfunded_payer_keypair_file)
|
|
|
|
|
.unwrap()
|
|
|
|
|
.into();
|
|
|
|
|
args.fee_payer = read_keypair_file(&sender_keypair_file).unwrap().into();
|
|
|
|
|
|
|
|
|
|
let err_result = check_payer_balances(1, &allocations, &mut banks_client, &args)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap_err();
|
|
|
|
|
let err_result = check_payer_balances(1, &allocations, &client, &args).unwrap_err();
|
|
|
|
|
if let Error::InsufficientFunds(sources, amount) = err_result {
|
|
|
|
|
assert_eq!(sources, vec![FundingSource::SystemAccount].into());
|
|
|
|
|
assert!((amount - unlocked_sol).abs() < f64::EPSILON);
|
|
|
|
@ -1532,15 +1542,16 @@ mod tests {
|
|
|
|
|
.unwrap()
|
|
|
|
|
.into();
|
|
|
|
|
|
|
|
|
|
let err_result = check_payer_balances(1, &allocations, &mut banks_client, &args)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap_err();
|
|
|
|
|
let err_result = check_payer_balances(1, &allocations, &client, &args).unwrap_err();
|
|
|
|
|
if let Error::InsufficientFunds(sources, amount) = err_result {
|
|
|
|
|
assert_eq!(sources, vec![FundingSource::FeePayer].into());
|
|
|
|
|
assert!((amount - fees_in_sol).abs() < f64::EPSILON);
|
|
|
|
|
} else {
|
|
|
|
|
panic!("check_payer_balances should have errored");
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Explicit cleanup, otherwise "pure virtual method called" crash in Docker
|
|
|
|
|
server.close().unwrap();
|
|
|
|
|
remove_dir_all(ledger_path).unwrap();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|