Nonce updates (#13799)
* runtime: Add `FeeCalculator` resolution method to `HashAgeKind` * runtime: Plumb fee-collected accounts for failed nonce tx rollback * runtime: Use fee-collected nonce/fee account for nonced TX error rollback * runtime: Add test for failed nonced TX accounts rollback * Fee payer test * fixup: replace nonce account when it pays the fee * fixup: nonce fee-payer collect test * fixup: fixup: clippy/fmt for replace... * runtime: Test for `HashAgeKind::fee_calculator()` * Clippy Co-authored-by: Trent Nelson <trent@solana.com>
This commit is contained in:
parent
42421e77a9
commit
b70abdc645
|
@ -1,11 +1,7 @@
|
|||
use crossbeam_channel::{Receiver, RecvTimeoutError};
|
||||
use itertools::izip;
|
||||
use solana_ledger::{blockstore::Blockstore, blockstore_processor::TransactionStatusBatch};
|
||||
use solana_runtime::{
|
||||
bank::{Bank, HashAgeKind},
|
||||
transaction_utils::OrderedIterator,
|
||||
};
|
||||
use solana_sdk::nonce_account;
|
||||
use solana_runtime::{bank::Bank, transaction_utils::OrderedIterator};
|
||||
use solana_transaction_status::{InnerInstructions, TransactionStatusMeta};
|
||||
use std::{
|
||||
sync::{
|
||||
|
@ -76,13 +72,12 @@ impl TransactionStatusService {
|
|||
transaction_logs
|
||||
) {
|
||||
if Bank::can_commit(&status) && !transaction.signatures.is_empty() {
|
||||
let fee_calculator = match hash_age_kind {
|
||||
Some(HashAgeKind::DurableNonce(_, account)) => {
|
||||
nonce_account::fee_calculator_of(&account)
|
||||
}
|
||||
_ => bank.get_fee_calculator(&transaction.message().recent_blockhash),
|
||||
}
|
||||
.expect("FeeCalculator must exist");
|
||||
let fee_calculator = hash_age_kind
|
||||
.and_then(|hash_age_kind| hash_age_kind.fee_calculator())
|
||||
.unwrap_or_else(|| {
|
||||
bank.get_fee_calculator(&transaction.message().recent_blockhash)
|
||||
})
|
||||
.expect("FeeCalculator must exist");
|
||||
let fee = fee_calculator.calculate_fee(transaction.message());
|
||||
let (writable_keys, readonly_keys) =
|
||||
transaction.message.get_account_keys_by_lock_type();
|
||||
|
|
|
@ -22,7 +22,7 @@ use solana_sdk::{
|
|||
genesis_config::ClusterType,
|
||||
hash::Hash,
|
||||
message::Message,
|
||||
native_loader, nonce, nonce_account,
|
||||
native_loader, nonce,
|
||||
pubkey::Pubkey,
|
||||
transaction::Result,
|
||||
transaction::{Transaction, TransactionError},
|
||||
|
@ -316,14 +316,14 @@ impl Accounts {
|
|||
.zip(lock_results.into_iter())
|
||||
.map(|etx| match etx {
|
||||
((_, tx), (Ok(()), hash_age_kind)) => {
|
||||
let fee_calculator = match hash_age_kind.as_ref() {
|
||||
Some(HashAgeKind::DurableNonce(_, account)) => {
|
||||
nonce_account::fee_calculator_of(account)
|
||||
}
|
||||
_ => hash_queue
|
||||
.get_fee_calculator(&tx.message().recent_blockhash)
|
||||
.cloned(),
|
||||
};
|
||||
let fee_calculator = hash_age_kind
|
||||
.as_ref()
|
||||
.and_then(|hash_age_kind| hash_age_kind.fee_calculator())
|
||||
.unwrap_or_else(|| {
|
||||
hash_queue
|
||||
.get_fee_calculator(&tx.message().recent_blockhash)
|
||||
.cloned()
|
||||
});
|
||||
let fee = if let Some(fee_calculator) = fee_calculator {
|
||||
fee_calculator.calculate_fee_with_config(tx.message(), &fee_config)
|
||||
} else {
|
||||
|
@ -357,6 +357,47 @@ impl Accounts {
|
|||
Err(e) => return (Err(e), hash_age_kind),
|
||||
};
|
||||
|
||||
// Update hash_age_kind with fee-subtracted accounts
|
||||
let hash_age_kind = if let Some(hash_age_kind) = hash_age_kind {
|
||||
match hash_age_kind {
|
||||
HashAgeKind::Extant => Some(HashAgeKind::Extant),
|
||||
HashAgeKind::DurableNoncePartial(pubkey, account) => {
|
||||
let fee_payer = tx
|
||||
.message()
|
||||
.account_keys
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(i, k)| Self::is_non_loader_key(tx.message(), k, *i))
|
||||
.map(|(i, k)| (*k, accounts[i].clone()));
|
||||
if let Some((fee_pubkey, fee_account)) = fee_payer {
|
||||
if fee_pubkey == pubkey {
|
||||
Some(HashAgeKind::DurableNonceFull(
|
||||
pubkey,
|
||||
fee_account,
|
||||
None,
|
||||
))
|
||||
} else {
|
||||
Some(HashAgeKind::DurableNonceFull(
|
||||
pubkey,
|
||||
account,
|
||||
Some(fee_account),
|
||||
))
|
||||
}
|
||||
} else {
|
||||
return (
|
||||
Err(TransactionError::AccountNotFound),
|
||||
Some(HashAgeKind::DurableNoncePartial(pubkey, account)),
|
||||
);
|
||||
}
|
||||
}
|
||||
HashAgeKind::DurableNonceFull(_, _, _) => {
|
||||
panic!("update: unexpected HashAgeKind variant")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
(Ok((accounts, loaders, rents)), hash_age_kind)
|
||||
}
|
||||
(_, (Err(e), hash_age_kind)) => (Err(e), hash_age_kind),
|
||||
|
@ -800,11 +841,16 @@ impl Accounts {
|
|||
}
|
||||
let (res, hash_age_kind) = &res[i];
|
||||
let maybe_nonce = match (res, hash_age_kind) {
|
||||
(Ok(_), Some(HashAgeKind::DurableNonce(pubkey, acc))) => Some((pubkey, acc)),
|
||||
(Ok(_), Some(HashAgeKind::DurableNonceFull(pubkey, acc, maybe_fee_account))) => {
|
||||
Some((pubkey, acc, maybe_fee_account))
|
||||
}
|
||||
(
|
||||
Err(TransactionError::InstructionError(_, _)),
|
||||
Some(HashAgeKind::DurableNonce(pubkey, acc)),
|
||||
) => Some((pubkey, acc)),
|
||||
Some(HashAgeKind::DurableNonceFull(pubkey, acc, maybe_fee_account)),
|
||||
) => Some((pubkey, acc, maybe_fee_account)),
|
||||
(_, Some(HashAgeKind::DurableNoncePartial(_, _))) => {
|
||||
panic!("collect: unexpected HashAgeKind variant")
|
||||
}
|
||||
(Ok(_), _hash_age_kind) => None,
|
||||
(Err(_), _hash_age_kind) => continue,
|
||||
};
|
||||
|
@ -818,7 +864,7 @@ impl Accounts {
|
|||
.zip(acc.0.iter_mut())
|
||||
.filter(|((i, key), _account)| Self::is_non_loader_key(message, key, *i))
|
||||
{
|
||||
prepare_if_nonce_account(
|
||||
let is_nonce_account = prepare_if_nonce_account(
|
||||
account,
|
||||
key,
|
||||
res,
|
||||
|
@ -826,7 +872,24 @@ impl Accounts {
|
|||
last_blockhash_with_fee_calculator,
|
||||
fix_recent_blockhashes_sysvar_delay,
|
||||
);
|
||||
if message.is_writable(i) {
|
||||
let is_fee_payer = i == 0;
|
||||
if message.is_writable(i)
|
||||
&& (res.is_ok()
|
||||
|| (maybe_nonce.is_some() && (is_nonce_account || is_fee_payer)))
|
||||
{
|
||||
if res.is_err() {
|
||||
match (is_nonce_account, is_fee_payer, maybe_nonce) {
|
||||
// nonce is fee-payer, state updated in `prepare_if_nonce_account()`
|
||||
(true, true, Some((_, _, None))) => (),
|
||||
// nonce not fee-payer, state updated in `prepare_if_nonce_account()`
|
||||
(true, false, Some((_, _, Some(_)))) => (),
|
||||
// not nonce, but fee-payer. rollback to cached state
|
||||
(false, true, Some((_, _, Some(fee_payer_account)))) => {
|
||||
*account = fee_payer_account.clone();
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
if account.rent_epoch == 0 {
|
||||
acc.2 += rent_collector.collect_from_created_account(
|
||||
&key,
|
||||
|
@ -846,11 +909,11 @@ pub fn prepare_if_nonce_account(
|
|||
account: &mut Account,
|
||||
account_pubkey: &Pubkey,
|
||||
tx_result: &Result<()>,
|
||||
maybe_nonce: Option<(&Pubkey, &Account)>,
|
||||
maybe_nonce: Option<(&Pubkey, &Account, &Option<Account>)>,
|
||||
last_blockhash_with_fee_calculator: &(Hash, FeeCalculator),
|
||||
fix_recent_blockhashes_sysvar_delay: bool,
|
||||
) {
|
||||
if let Some((nonce_key, nonce_acc)) = maybe_nonce {
|
||||
) -> bool {
|
||||
if let Some((nonce_key, nonce_acc, _maybe_fee_account)) = maybe_nonce {
|
||||
if account_pubkey == nonce_key {
|
||||
let overwrite = if tx_result.is_err() {
|
||||
// Nonce TX failed with an InstructionError. Roll back
|
||||
|
@ -878,8 +941,10 @@ pub fn prepare_if_nonce_account(
|
|||
account.set_state(&new_data).unwrap();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn create_test_accounts(
|
||||
|
@ -918,10 +983,10 @@ mod tests {
|
|||
hash::Hash,
|
||||
instruction::{CompiledInstruction, InstructionError},
|
||||
message::Message,
|
||||
nonce,
|
||||
nonce, nonce_account,
|
||||
rent::Rent,
|
||||
signature::{Keypair, Signer},
|
||||
system_program,
|
||||
signature::{keypair_from_seed, Keypair, Signer},
|
||||
system_instruction, system_program,
|
||||
};
|
||||
use std::{
|
||||
sync::atomic::{AtomicBool, AtomicU64, Ordering},
|
||||
|
@ -1919,8 +1984,14 @@ mod tests {
|
|||
assert!(loaded_accounts[0].0.is_err());
|
||||
}
|
||||
|
||||
fn create_accounts_prepare_if_nonce_account() -> (Pubkey, Account, Account, Hash, FeeCalculator)
|
||||
{
|
||||
fn create_accounts_prepare_if_nonce_account() -> (
|
||||
Pubkey,
|
||||
Account,
|
||||
Account,
|
||||
Hash,
|
||||
FeeCalculator,
|
||||
Option<Account>,
|
||||
) {
|
||||
let data = nonce::state::Versions::new_current(nonce::State::Initialized(
|
||||
nonce::state::Data::default(),
|
||||
));
|
||||
|
@ -1937,6 +2008,7 @@ mod tests {
|
|||
FeeCalculator {
|
||||
lamports_per_signature: 1234,
|
||||
},
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1944,18 +2016,20 @@ mod tests {
|
|||
account: &mut Account,
|
||||
account_pubkey: &Pubkey,
|
||||
tx_result: &Result<()>,
|
||||
maybe_nonce: Option<(&Pubkey, &Account)>,
|
||||
maybe_nonce: Option<(&Pubkey, &Account, &Option<Account>)>,
|
||||
last_blockhash_with_fee_calculator: &(Hash, FeeCalculator),
|
||||
expect_account: &Account,
|
||||
) -> bool {
|
||||
// Verify expect_account's relationship
|
||||
match maybe_nonce {
|
||||
Some((nonce_pubkey, _nonce_account))
|
||||
Some((nonce_pubkey, _nonce_account, _maybe_fee_account))
|
||||
if nonce_pubkey == account_pubkey && tx_result.is_ok() =>
|
||||
{
|
||||
assert_eq!(expect_account, account) // Account update occurs in system_instruction_processor
|
||||
}
|
||||
Some((nonce_pubkey, nonce_account)) if nonce_pubkey == account_pubkey => {
|
||||
Some((nonce_pubkey, nonce_account, _maybe_fee_account))
|
||||
if nonce_pubkey == account_pubkey =>
|
||||
{
|
||||
assert_ne!(expect_account, nonce_account)
|
||||
}
|
||||
_ => assert_eq!(expect_account, account),
|
||||
|
@ -1980,6 +2054,7 @@ mod tests {
|
|||
mut post_account,
|
||||
last_blockhash,
|
||||
last_fee_calculator,
|
||||
maybe_fee_account,
|
||||
) = create_accounts_prepare_if_nonce_account();
|
||||
let post_account_pubkey = pre_account_pubkey;
|
||||
|
||||
|
@ -1993,7 +2068,7 @@ mod tests {
|
|||
&mut post_account,
|
||||
&post_account_pubkey,
|
||||
&Ok(()),
|
||||
Some((&pre_account_pubkey, &pre_account)),
|
||||
Some((&pre_account_pubkey, &pre_account, &maybe_fee_account)),
|
||||
&(last_blockhash, last_fee_calculator),
|
||||
&expect_account,
|
||||
));
|
||||
|
@ -2001,8 +2076,14 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_prepare_if_nonce_account_not_nonce_tx() {
|
||||
let (pre_account_pubkey, _pre_account, _post_account, last_blockhash, last_fee_calculator) =
|
||||
create_accounts_prepare_if_nonce_account();
|
||||
let (
|
||||
pre_account_pubkey,
|
||||
_pre_account,
|
||||
_post_account,
|
||||
last_blockhash,
|
||||
last_fee_calculator,
|
||||
_maybe_fee_account,
|
||||
) = create_accounts_prepare_if_nonce_account();
|
||||
let post_account_pubkey = pre_account_pubkey;
|
||||
|
||||
let mut post_account = Account::default();
|
||||
|
@ -2025,6 +2106,7 @@ mod tests {
|
|||
mut post_account,
|
||||
last_blockhash,
|
||||
last_fee_calculator,
|
||||
maybe_fee_account,
|
||||
) = create_accounts_prepare_if_nonce_account();
|
||||
|
||||
let expect_account = post_account.clone();
|
||||
|
@ -2033,7 +2115,7 @@ mod tests {
|
|||
&mut post_account,
|
||||
&Pubkey::new(&[1u8; 32]),
|
||||
&Ok(()),
|
||||
Some((&pre_account_pubkey, &pre_account)),
|
||||
Some((&pre_account_pubkey, &pre_account, &maybe_fee_account)),
|
||||
&(last_blockhash, last_fee_calculator),
|
||||
&expect_account,
|
||||
));
|
||||
|
@ -2047,6 +2129,7 @@ mod tests {
|
|||
mut post_account,
|
||||
last_blockhash,
|
||||
last_fee_calculator,
|
||||
maybe_fee_account,
|
||||
) = create_accounts_prepare_if_nonce_account();
|
||||
let post_account_pubkey = pre_account_pubkey;
|
||||
|
||||
|
@ -2068,9 +2151,211 @@ mod tests {
|
|||
0,
|
||||
InstructionError::InvalidArgument,
|
||||
)),
|
||||
Some((&pre_account_pubkey, &pre_account)),
|
||||
Some((&pre_account_pubkey, &pre_account, &maybe_fee_account)),
|
||||
&(last_blockhash, last_fee_calculator),
|
||||
&expect_account,
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nonced_failure_accounts_rollback_from_pays() {
|
||||
let rent_collector = RentCollector::default();
|
||||
|
||||
let nonce_address = Pubkey::new_unique();
|
||||
let nonce_authority = keypair_from_seed(&[0; 32]).unwrap();
|
||||
let from = keypair_from_seed(&[1; 32]).unwrap();
|
||||
let from_address = from.pubkey();
|
||||
let to_address = Pubkey::new_unique();
|
||||
let instructions = vec![
|
||||
system_instruction::advance_nonce_account(&nonce_address, &nonce_authority.pubkey()),
|
||||
system_instruction::transfer(&from_address, &to_address, 42),
|
||||
];
|
||||
let message = Message::new(&instructions, Some(&from_address));
|
||||
let blockhash = Hash::new_unique();
|
||||
let tx = Transaction::new(&[&nonce_authority, &from], message, blockhash);
|
||||
|
||||
let txs = vec![tx];
|
||||
|
||||
let nonce_state =
|
||||
nonce::state::Versions::new_current(nonce::State::Initialized(nonce::state::Data {
|
||||
authority: nonce_authority.pubkey(),
|
||||
blockhash,
|
||||
fee_calculator: FeeCalculator::default(),
|
||||
}));
|
||||
let nonce_account_pre = Account::new_data(42, &nonce_state, &system_program::id()).unwrap();
|
||||
let from_account_pre = Account::new(4242, 0, &Pubkey::default());
|
||||
|
||||
let hash_age_kind = Some(HashAgeKind::DurableNonceFull(
|
||||
nonce_address,
|
||||
nonce_account_pre.clone(),
|
||||
Some(from_account_pre.clone()),
|
||||
));
|
||||
let loaders = vec![(
|
||||
Err(TransactionError::InstructionError(
|
||||
1,
|
||||
InstructionError::InvalidArgument,
|
||||
)),
|
||||
hash_age_kind.clone(),
|
||||
)];
|
||||
|
||||
let nonce_state =
|
||||
nonce::state::Versions::new_current(nonce::State::Initialized(nonce::state::Data {
|
||||
authority: nonce_authority.pubkey(),
|
||||
blockhash: Hash::new_unique(),
|
||||
fee_calculator: FeeCalculator::default(),
|
||||
}));
|
||||
let nonce_account_post =
|
||||
Account::new_data(43, &nonce_state, &system_program::id()).unwrap();
|
||||
|
||||
let from_account_post = Account::new(4199, 0, &Pubkey::default());
|
||||
let to_account = Account::new(2, 0, &Pubkey::default());
|
||||
let nonce_authority_account = Account::new(3, 0, &Pubkey::default());
|
||||
let recent_blockhashes_sysvar_account = Account::new(4, 0, &Pubkey::default());
|
||||
|
||||
let transaction_accounts = vec![
|
||||
from_account_post,
|
||||
nonce_authority_account,
|
||||
nonce_account_post,
|
||||
to_account,
|
||||
recent_blockhashes_sysvar_account,
|
||||
];
|
||||
let transaction_loaders = vec![];
|
||||
let transaction_rent = 0;
|
||||
let loaded = (
|
||||
Ok((transaction_accounts, transaction_loaders, transaction_rent)),
|
||||
hash_age_kind,
|
||||
);
|
||||
|
||||
let mut loaded = vec![loaded];
|
||||
|
||||
let next_blockhash = Hash::new_unique();
|
||||
let accounts = Accounts::new(Vec::new(), &ClusterType::Development);
|
||||
let collected_accounts = accounts.collect_accounts_to_store(
|
||||
&txs,
|
||||
None,
|
||||
&loaders,
|
||||
&mut loaded,
|
||||
&rent_collector,
|
||||
&(next_blockhash, FeeCalculator::default()),
|
||||
true,
|
||||
true,
|
||||
);
|
||||
assert_eq!(collected_accounts.len(), 2);
|
||||
assert_eq!(
|
||||
collected_accounts
|
||||
.iter()
|
||||
.find(|(pubkey, _account)| *pubkey == &from_address)
|
||||
.map(|(_pubkey, account)| *account)
|
||||
.cloned()
|
||||
.unwrap(),
|
||||
from_account_pre,
|
||||
);
|
||||
let collected_nonce_account = collected_accounts
|
||||
.iter()
|
||||
.find(|(pubkey, _account)| *pubkey == &nonce_address)
|
||||
.map(|(_pubkey, account)| *account)
|
||||
.cloned()
|
||||
.unwrap();
|
||||
assert_eq!(collected_nonce_account.lamports, nonce_account_pre.lamports,);
|
||||
assert!(nonce_account::verify_nonce_account(
|
||||
&collected_nonce_account,
|
||||
&next_blockhash
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nonced_failure_accounts_rollback_nonce_pays() {
|
||||
let rent_collector = RentCollector::default();
|
||||
|
||||
let nonce_authority = keypair_from_seed(&[0; 32]).unwrap();
|
||||
let nonce_address = nonce_authority.pubkey();
|
||||
let from = keypair_from_seed(&[1; 32]).unwrap();
|
||||
let from_address = from.pubkey();
|
||||
let to_address = Pubkey::new_unique();
|
||||
let instructions = vec![
|
||||
system_instruction::advance_nonce_account(&nonce_address, &nonce_authority.pubkey()),
|
||||
system_instruction::transfer(&from_address, &to_address, 42),
|
||||
];
|
||||
let message = Message::new(&instructions, Some(&nonce_address));
|
||||
let blockhash = Hash::new_unique();
|
||||
let tx = Transaction::new(&[&nonce_authority, &from], message, blockhash);
|
||||
|
||||
let txs = vec![tx];
|
||||
|
||||
let nonce_state =
|
||||
nonce::state::Versions::new_current(nonce::State::Initialized(nonce::state::Data {
|
||||
authority: nonce_authority.pubkey(),
|
||||
blockhash,
|
||||
fee_calculator: FeeCalculator::default(),
|
||||
}));
|
||||
let nonce_account_pre = Account::new_data(42, &nonce_state, &system_program::id()).unwrap();
|
||||
|
||||
let hash_age_kind = Some(HashAgeKind::DurableNonceFull(
|
||||
nonce_address,
|
||||
nonce_account_pre.clone(),
|
||||
None,
|
||||
));
|
||||
let loaders = vec![(
|
||||
Err(TransactionError::InstructionError(
|
||||
1,
|
||||
InstructionError::InvalidArgument,
|
||||
)),
|
||||
hash_age_kind.clone(),
|
||||
)];
|
||||
|
||||
let nonce_state =
|
||||
nonce::state::Versions::new_current(nonce::State::Initialized(nonce::state::Data {
|
||||
authority: nonce_authority.pubkey(),
|
||||
blockhash: Hash::new_unique(),
|
||||
fee_calculator: FeeCalculator::default(),
|
||||
}));
|
||||
let nonce_account_post =
|
||||
Account::new_data(43, &nonce_state, &system_program::id()).unwrap();
|
||||
|
||||
let from_account_post = Account::new(4200, 0, &Pubkey::default());
|
||||
let to_account = Account::new(2, 0, &Pubkey::default());
|
||||
let nonce_authority_account = Account::new(3, 0, &Pubkey::default());
|
||||
let recent_blockhashes_sysvar_account = Account::new(4, 0, &Pubkey::default());
|
||||
|
||||
let transaction_accounts = vec![
|
||||
from_account_post,
|
||||
nonce_authority_account,
|
||||
nonce_account_post,
|
||||
to_account,
|
||||
recent_blockhashes_sysvar_account,
|
||||
];
|
||||
let transaction_loaders = vec![];
|
||||
let transaction_rent = 0;
|
||||
let loaded = (
|
||||
Ok((transaction_accounts, transaction_loaders, transaction_rent)),
|
||||
hash_age_kind,
|
||||
);
|
||||
|
||||
let mut loaded = vec![loaded];
|
||||
|
||||
let next_blockhash = Hash::new_unique();
|
||||
let accounts = Accounts::new(Vec::new(), &ClusterType::Development);
|
||||
let collected_accounts = accounts.collect_accounts_to_store(
|
||||
&txs,
|
||||
None,
|
||||
&loaders,
|
||||
&mut loaded,
|
||||
&rent_collector,
|
||||
&(next_blockhash, FeeCalculator::default()),
|
||||
true,
|
||||
true,
|
||||
);
|
||||
assert_eq!(collected_accounts.len(), 1);
|
||||
let collected_nonce_account = collected_accounts
|
||||
.iter()
|
||||
.find(|(pubkey, _account)| *pubkey == &nonce_address)
|
||||
.map(|(_pubkey, account)| *account)
|
||||
.cloned()
|
||||
.unwrap();
|
||||
assert_eq!(collected_nonce_account.lamports, nonce_account_pre.lamports);
|
||||
assert!(nonce_account::verify_nonce_account(
|
||||
&collected_nonce_account,
|
||||
&next_blockhash
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -407,12 +407,29 @@ pub type TransactionLogMessages = Vec<String>;
|
|||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum HashAgeKind {
|
||||
Extant,
|
||||
DurableNonce(Pubkey, Account),
|
||||
DurableNoncePartial(Pubkey, Account),
|
||||
DurableNonceFull(Pubkey, Account, Option<Account>),
|
||||
}
|
||||
|
||||
impl HashAgeKind {
|
||||
pub fn is_durable_nonce(&self) -> bool {
|
||||
matches!(self, HashAgeKind::DurableNonce(_, _))
|
||||
match self {
|
||||
Self::Extant => false,
|
||||
Self::DurableNoncePartial(_, _) => true,
|
||||
Self::DurableNonceFull(_, _, _) => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fee_calculator(&self) -> Option<Option<FeeCalculator>> {
|
||||
match self {
|
||||
Self::Extant => None,
|
||||
Self::DurableNoncePartial(_, account) => {
|
||||
Some(nonce_account::fee_calculator_of(account))
|
||||
}
|
||||
Self::DurableNonceFull(_, account, _) => {
|
||||
Some(nonce_account::fee_calculator_of(account))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2183,7 +2200,7 @@ impl Bank {
|
|||
if hash_age == Some(true) {
|
||||
(Ok(()), Some(HashAgeKind::Extant))
|
||||
} else if let Some((pubkey, acc)) = self.check_tx_durable_nonce(&tx) {
|
||||
(Ok(()), Some(HashAgeKind::DurableNonce(pubkey, acc)))
|
||||
(Ok(()), Some(HashAgeKind::DurableNoncePartial(pubkey, acc)))
|
||||
} else if hash_age == Some(false) {
|
||||
error_counters.blockhash_too_old += 1;
|
||||
(Err(TransactionError::BlockhashNotFound), None)
|
||||
|
@ -2722,17 +2739,18 @@ impl Bank {
|
|||
let results = OrderedIterator::new(txs, iteration_order)
|
||||
.zip(executed.iter())
|
||||
.map(|((_, tx), (res, hash_age_kind))| {
|
||||
let (fee_calculator, is_durable_nonce) = match hash_age_kind {
|
||||
Some(HashAgeKind::DurableNonce(_, account)) => {
|
||||
(nonce_account::fee_calculator_of(account), true)
|
||||
}
|
||||
_ => (
|
||||
hash_queue
|
||||
.get_fee_calculator(&tx.message().recent_blockhash)
|
||||
.cloned(),
|
||||
false,
|
||||
),
|
||||
};
|
||||
let (fee_calculator, is_durable_nonce) = hash_age_kind
|
||||
.as_ref()
|
||||
.and_then(|hash_age_kind| hash_age_kind.fee_calculator())
|
||||
.map(|maybe_fee_calculator| (maybe_fee_calculator, true))
|
||||
.unwrap_or_else(|| {
|
||||
(
|
||||
hash_queue
|
||||
.get_fee_calculator(&tx.message().recent_blockhash)
|
||||
.cloned(),
|
||||
false,
|
||||
)
|
||||
});
|
||||
let fee_calculator = fee_calculator.ok_or(TransactionError::BlockhashNotFound)?;
|
||||
|
||||
let fee = fee_calculator.calculate_fee_with_config(tx.message(), &fee_config);
|
||||
|
@ -4406,11 +4424,58 @@ pub(crate) mod tests {
|
|||
#[test]
|
||||
fn test_hash_age_kind_is_durable_nonce() {
|
||||
assert!(
|
||||
HashAgeKind::DurableNonce(Pubkey::default(), Account::default()).is_durable_nonce()
|
||||
HashAgeKind::DurableNoncePartial(Pubkey::default(), Account::default())
|
||||
.is_durable_nonce()
|
||||
);
|
||||
assert!(
|
||||
HashAgeKind::DurableNonceFull(Pubkey::default(), Account::default(), None)
|
||||
.is_durable_nonce()
|
||||
);
|
||||
assert!(HashAgeKind::DurableNonceFull(
|
||||
Pubkey::default(),
|
||||
Account::default(),
|
||||
Some(Account::default())
|
||||
)
|
||||
.is_durable_nonce());
|
||||
assert!(!HashAgeKind::Extant.is_durable_nonce());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_age_kind_fee_calculator() {
|
||||
let state =
|
||||
nonce::state::Versions::new_current(nonce::State::Initialized(nonce::state::Data {
|
||||
authority: Pubkey::default(),
|
||||
blockhash: Hash::new_unique(),
|
||||
fee_calculator: FeeCalculator::default(),
|
||||
}));
|
||||
let account = Account::new_data(42, &state, &system_program::id()).unwrap();
|
||||
|
||||
assert_eq!(HashAgeKind::Extant.fee_calculator(), None);
|
||||
assert_eq!(
|
||||
HashAgeKind::DurableNoncePartial(Pubkey::default(), account.clone()).fee_calculator(),
|
||||
Some(Some(FeeCalculator::default()))
|
||||
);
|
||||
assert_eq!(
|
||||
HashAgeKind::DurableNoncePartial(Pubkey::default(), Account::default())
|
||||
.fee_calculator(),
|
||||
Some(None)
|
||||
);
|
||||
assert_eq!(
|
||||
HashAgeKind::DurableNonceFull(Pubkey::default(), account, Some(Account::default()))
|
||||
.fee_calculator(),
|
||||
Some(Some(FeeCalculator::default()))
|
||||
);
|
||||
assert_eq!(
|
||||
HashAgeKind::DurableNonceFull(
|
||||
Pubkey::default(),
|
||||
Account::default(),
|
||||
Some(Account::default())
|
||||
)
|
||||
.fee_calculator(),
|
||||
Some(None)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bank_unix_timestamp_from_genesis() {
|
||||
let (genesis_config, _mint_keypair) = create_genesis_config(1);
|
||||
|
@ -8598,6 +8663,50 @@ pub(crate) mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nonce_payer() {
|
||||
solana_logger::setup();
|
||||
let (mut bank, _mint_keypair, custodian_keypair, nonce_keypair) =
|
||||
setup_nonce_with_bank(10_000_000, |_| {}, 5_000_000, 250_000, None).unwrap();
|
||||
let alice_keypair = Keypair::new();
|
||||
let alice_pubkey = alice_keypair.pubkey();
|
||||
let custodian_pubkey = custodian_keypair.pubkey();
|
||||
let nonce_pubkey = nonce_keypair.pubkey();
|
||||
|
||||
warn!("alice: {}", alice_pubkey);
|
||||
warn!("custodian: {}", custodian_pubkey);
|
||||
warn!("nonce: {}", nonce_pubkey);
|
||||
warn!("nonce account: {:?}", bank.get_account(&nonce_pubkey));
|
||||
warn!("cust: {:?}", bank.get_account(&custodian_pubkey));
|
||||
let nonce_hash = get_nonce_account(&bank, &nonce_pubkey).unwrap();
|
||||
|
||||
for _ in 0..MAX_RECENT_BLOCKHASHES + 1 {
|
||||
goto_end_of_slot(Arc::get_mut(&mut bank).unwrap());
|
||||
bank = Arc::new(new_from_parent(&bank));
|
||||
}
|
||||
|
||||
let durable_tx = Transaction::new_signed_with_payer(
|
||||
&[
|
||||
system_instruction::advance_nonce_account(&nonce_pubkey, &nonce_pubkey),
|
||||
system_instruction::transfer(&custodian_pubkey, &alice_pubkey, 100_000_000),
|
||||
],
|
||||
Some(&nonce_pubkey),
|
||||
&[&custodian_keypair, &nonce_keypair],
|
||||
nonce_hash,
|
||||
);
|
||||
warn!("{:?}", durable_tx);
|
||||
assert_eq!(
|
||||
bank.process_transaction(&durable_tx),
|
||||
Err(TransactionError::InstructionError(
|
||||
1,
|
||||
system_instruction::SystemError::ResultWithNegativeLamports.into(),
|
||||
))
|
||||
);
|
||||
/* Check fee charged and nonce has advanced */
|
||||
assert_eq!(bank.get_balance(&nonce_pubkey), 240_000);
|
||||
assert_ne!(nonce_hash, get_nonce_account(&bank, &nonce_pubkey).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nonce_fee_calculator_updates() {
|
||||
let (mut genesis_config, mint_keypair) = create_genesis_config(1_000_000);
|
||||
|
|
Loading…
Reference in New Issue