permanently disables durable nonces with chain blockhash domain (#25788)
https://github.com/solana-labs/solana/pull/25744 separated durable nonce and blockhash domains, which will stop double execution going forward. However it is possible that a durable transaction has *already* been executed once as a normal transaction and it is now a valid durable transaction. #25744 cannot stop such transactions to be re-executed until the nonce accounts are advanced. This commit adds a new nonce version indicating that the nonce is moved out of the blockhash domain, and permanently disables durable transactions for legacy nonces which are in the blockhash domain.
This commit is contained in:
parent
e5f36aa371
commit
3c1ce3cc93
|
@ -145,7 +145,10 @@ mod test {
|
|||
assert_eq!(parsed.program, "vote".to_string());
|
||||
assert_eq!(parsed.space, VoteState::size_of() as u64);
|
||||
|
||||
let nonce_data = Versions::new_current(State::Initialized(Data::default()));
|
||||
let nonce_data = Versions::new(
|
||||
State::Initialized(Data::default()),
|
||||
true, // separate_domains
|
||||
);
|
||||
let nonce_account_data = bincode::serialize(&nonce_data).unwrap();
|
||||
let parsed = parse_account_data(
|
||||
&account_pubkey,
|
||||
|
|
|
@ -7,10 +7,9 @@ use {
|
|||
};
|
||||
|
||||
pub fn parse_nonce(data: &[u8]) -> Result<UiNonceState, ParseAccountError> {
|
||||
let nonce_state: Versions = bincode::deserialize(data)
|
||||
let nonce_versions: Versions = bincode::deserialize(data)
|
||||
.map_err(|_| ParseAccountError::from(InstructionError::InvalidAccountData))?;
|
||||
let nonce_state = nonce_state.convert_to_current();
|
||||
match nonce_state {
|
||||
match nonce_versions.state() {
|
||||
// This prevents parsing an allocated System-owned account with empty data of any non-zero
|
||||
// length as `uninitialized` nonce. An empty account of the wrong length can never be
|
||||
// initialized as a nonce account, and an empty account of the correct length may not be an
|
||||
|
@ -58,7 +57,10 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_parse_nonce() {
|
||||
let nonce_data = Versions::new_current(State::Initialized(Data::default()));
|
||||
let nonce_data = Versions::new(
|
||||
State::Initialized(Data::default()),
|
||||
true, // separate_domains
|
||||
);
|
||||
let nonce_account_data = bincode::serialize(&nonce_data).unwrap();
|
||||
assert_eq!(
|
||||
parse_nonce(&nonce_account_data).unwrap(),
|
||||
|
|
|
@ -933,11 +933,10 @@ mod tests {
|
|||
DurableNonce::from_blockhash(&Hash::default(), /*separate_domains:*/ true);
|
||||
let blockhash = *durable_nonce.as_hash();
|
||||
let nonce_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let data = Versions::new_current(State::Initialized(nonce::state::Data::new(
|
||||
nonce_pubkey,
|
||||
durable_nonce,
|
||||
0,
|
||||
)));
|
||||
let data = Versions::new(
|
||||
State::Initialized(nonce::state::Data::new(nonce_pubkey, durable_nonce, 0)),
|
||||
true, // separate_domains
|
||||
);
|
||||
let valid = Account::new_data(1, &data, &system_program::ID);
|
||||
assert!(check_nonce_account(&valid.unwrap(), &nonce_pubkey, &blockhash).is_ok());
|
||||
|
||||
|
@ -957,11 +956,14 @@ mod tests {
|
|||
|
||||
let invalid_durable_nonce =
|
||||
DurableNonce::from_blockhash(&hash(b"invalid"), /*separate_domains:*/ true);
|
||||
let data = Versions::new_current(State::Initialized(nonce::state::Data::new(
|
||||
let data = Versions::new(
|
||||
State::Initialized(nonce::state::Data::new(
|
||||
nonce_pubkey,
|
||||
invalid_durable_nonce,
|
||||
0,
|
||||
)));
|
||||
)),
|
||||
true, // separate_domains
|
||||
);
|
||||
let invalid_hash = Account::new_data(1, &data, &system_program::ID).unwrap();
|
||||
if let CliError::InvalidNonce(err) =
|
||||
check_nonce_account(&invalid_hash, &nonce_pubkey, &blockhash).unwrap_err()
|
||||
|
@ -976,11 +978,14 @@ mod tests {
|
|||
}
|
||||
|
||||
let new_nonce_authority = solana_sdk::pubkey::new_rand();
|
||||
let data = Versions::new_current(State::Initialized(nonce::state::Data::new(
|
||||
let data = Versions::new(
|
||||
State::Initialized(nonce::state::Data::new(
|
||||
new_nonce_authority,
|
||||
durable_nonce,
|
||||
0,
|
||||
)));
|
||||
)),
|
||||
true, // separate_domains
|
||||
);
|
||||
let invalid_authority = Account::new_data(1, &data, &system_program::ID);
|
||||
if let CliError::InvalidNonce(err) =
|
||||
check_nonce_account(&invalid_authority.unwrap(), &nonce_pubkey, &blockhash).unwrap_err()
|
||||
|
@ -994,7 +999,7 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
let data = Versions::new_current(State::Uninitialized);
|
||||
let data = Versions::new(State::Uninitialized, /*separate_domains:*/ true);
|
||||
let invalid_state = Account::new_data(1, &data, &system_program::ID);
|
||||
if let CliError::InvalidNonce(err) =
|
||||
check_nonce_account(&invalid_state.unwrap(), &nonce_pubkey, &blockhash).unwrap_err()
|
||||
|
@ -1005,7 +1010,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_account_identity_ok() {
|
||||
let nonce_account = nonce_account::create_account(1).into_inner();
|
||||
let nonce_account =
|
||||
nonce_account::create_account(1, /*separate_domains:*/ true).into_inner();
|
||||
assert_eq!(account_identity_ok(&nonce_account), Ok(()));
|
||||
|
||||
let system_account = Account::new(1, 0, &system_program::id());
|
||||
|
@ -1024,14 +1030,18 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_state_from_account() {
|
||||
let mut nonce_account = nonce_account::create_account(1).into_inner();
|
||||
let mut nonce_account =
|
||||
nonce_account::create_account(1, /*separate_domains:*/ true).into_inner();
|
||||
assert_eq!(state_from_account(&nonce_account), Ok(State::Uninitialized));
|
||||
|
||||
let durable_nonce =
|
||||
DurableNonce::from_blockhash(&Hash::new(&[42u8; 32]), /*separate_domains:*/ true);
|
||||
let data = nonce::state::Data::new(Pubkey::new(&[1u8; 32]), durable_nonce, 42);
|
||||
nonce_account
|
||||
.set_state(&Versions::new_current(State::Initialized(data.clone())))
|
||||
.set_state(&Versions::new(
|
||||
State::Initialized(data.clone()),
|
||||
true, // separate_domains
|
||||
))
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
state_from_account(&nonce_account),
|
||||
|
@ -1047,7 +1057,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_data_from_helpers() {
|
||||
let mut nonce_account = nonce_account::create_account(1).into_inner();
|
||||
let mut nonce_account =
|
||||
nonce_account::create_account(1, /*separate_domains:*/ true).into_inner();
|
||||
let state = state_from_account(&nonce_account).unwrap();
|
||||
assert_eq!(
|
||||
data_from_state(&state),
|
||||
|
@ -1062,7 +1073,10 @@ mod tests {
|
|||
DurableNonce::from_blockhash(&Hash::new(&[42u8; 32]), /*separate_domains:*/ true);
|
||||
let data = nonce::state::Data::new(Pubkey::new(&[1u8; 32]), durable_nonce, 42);
|
||||
nonce_account
|
||||
.set_state(&Versions::new_current(State::Initialized(data.clone())))
|
||||
.set_state(&Versions::new(
|
||||
State::Initialized(data.clone()),
|
||||
true, // separate_domains
|
||||
))
|
||||
.unwrap();
|
||||
let state = state_from_account(&nonce_account).unwrap();
|
||||
assert_eq!(data_from_state(&state), Ok(&data));
|
||||
|
|
|
@ -427,7 +427,10 @@ mod tests {
|
|||
};
|
||||
let nonce_account = Account::new_data_with_space(
|
||||
42,
|
||||
&nonce::state::Versions::new_current(nonce::State::Initialized(data)),
|
||||
&nonce::state::Versions::new(
|
||||
nonce::State::Initialized(data),
|
||||
true, // separate_domains
|
||||
),
|
||||
nonce::State::size(),
|
||||
&system_program::id(),
|
||||
)
|
||||
|
|
|
@ -129,9 +129,8 @@ pub fn state_from_account<T: ReadableAccount + StateMut<Versions>>(
|
|||
account: &T,
|
||||
) -> Result<State, Error> {
|
||||
account_identity_ok(account)?;
|
||||
StateMut::<Versions>::state(account)
|
||||
.map_err(|_| Error::InvalidAccountData)
|
||||
.map(|v| v.convert_to_current())
|
||||
let versions = StateMut::<Versions>::state(account).map_err(|_| Error::InvalidAccountData)?;
|
||||
Ok(State::from(versions))
|
||||
}
|
||||
|
||||
/// Deserialize the state data of a durable transaction nonce account.
|
||||
|
|
|
@ -5573,11 +5573,10 @@ pub mod tests {
|
|||
let authority = Pubkey::new_unique();
|
||||
let account = AccountSharedData::new_data(
|
||||
42,
|
||||
&nonce::state::Versions::new_current(nonce::State::new_initialized(
|
||||
&authority,
|
||||
DurableNonce::default(),
|
||||
1000,
|
||||
)),
|
||||
&nonce::state::Versions::new(
|
||||
nonce::State::new_initialized(&authority, DurableNonce::default(), 1000),
|
||||
true, // separate_domains
|
||||
),
|
||||
&system_program::id(),
|
||||
)
|
||||
.unwrap();
|
||||
|
@ -5608,8 +5607,8 @@ pub mod tests {
|
|||
system_program::id().to_string(),
|
||||
{"filters": [{
|
||||
"memcmp": {
|
||||
"offset": 0,
|
||||
"bytes": bs58::encode(vec![1, 0, 0, 0]).into_string(),
|
||||
"offset": 4,
|
||||
"bytes": bs58::encode(vec![0, 0, 0, 0]).into_string(),
|
||||
},
|
||||
}]},
|
||||
])),
|
||||
|
|
|
@ -323,13 +323,15 @@ pub(crate) mod tests {
|
|||
let expected_transaction = transaction.clone();
|
||||
let pubkey = Pubkey::new_unique();
|
||||
|
||||
let mut nonce_account = nonce_account::create_account(1).into_inner();
|
||||
let mut nonce_account =
|
||||
nonce_account::create_account(1, /*separate_domains:*/ true).into_inner();
|
||||
let durable_nonce =
|
||||
DurableNonce::from_blockhash(&Hash::new(&[42u8; 32]), /*separate_domains:*/ true);
|
||||
let data = nonce::state::Data::new(Pubkey::new(&[1u8; 32]), durable_nonce, 42);
|
||||
nonce_account
|
||||
.set_state(&nonce::state::Versions::new_current(
|
||||
.set_state(&nonce::state::Versions::new(
|
||||
nonce::State::Initialized(data),
|
||||
true, // separate_domains
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
|
|
|
@ -1196,7 +1196,7 @@ impl Accounts {
|
|||
res: &'a [TransactionExecutionResult],
|
||||
loaded: &'a mut [TransactionLoadResult],
|
||||
rent_collector: &RentCollector,
|
||||
durable_nonce: &DurableNonce,
|
||||
durable_nonce: &(DurableNonce, /*separate_domains:*/ bool),
|
||||
lamports_per_signature: u64,
|
||||
leave_nonce_on_success: bool,
|
||||
) {
|
||||
|
@ -1228,7 +1228,7 @@ impl Accounts {
|
|||
execution_results: &'a [TransactionExecutionResult],
|
||||
load_results: &'a mut [TransactionLoadResult],
|
||||
rent_collector: &RentCollector,
|
||||
durable_nonce: &DurableNonce,
|
||||
durable_nonce: &(DurableNonce, /*separate_domains:*/ bool),
|
||||
lamports_per_signature: u64,
|
||||
leave_nonce_on_success: bool,
|
||||
) -> Vec<(&'a Pubkey, &'a AccountSharedData)> {
|
||||
|
@ -1315,7 +1315,7 @@ fn prepare_if_nonce_account<'a>(
|
|||
execution_result: &Result<()>,
|
||||
is_fee_payer: bool,
|
||||
maybe_nonce: Option<(&'a NonceFull, bool)>,
|
||||
durable_nonce: &DurableNonce,
|
||||
&(durable_nonce, separate_domains): &(DurableNonce, bool),
|
||||
lamports_per_signature: u64,
|
||||
) -> bool {
|
||||
if let Some((nonce, rollback)) = maybe_nonce {
|
||||
|
@ -1334,17 +1334,15 @@ fn prepare_if_nonce_account<'a>(
|
|||
//
|
||||
// Since we know we are dealing with a valid nonce account,
|
||||
// unwrap is safe here
|
||||
let state = StateMut::<NonceVersions>::state(nonce.account())
|
||||
.unwrap()
|
||||
.convert_to_current();
|
||||
if let NonceState::Initialized(ref data) = state {
|
||||
account
|
||||
.set_state(&NonceVersions::new_current(NonceState::new_initialized(
|
||||
let nonce_versions = StateMut::<NonceVersions>::state(nonce.account()).unwrap();
|
||||
if let NonceState::Initialized(ref data) = nonce_versions.state() {
|
||||
let nonce_state = NonceState::new_initialized(
|
||||
&data.authority,
|
||||
*durable_nonce,
|
||||
durable_nonce,
|
||||
lamports_per_signature,
|
||||
)))
|
||||
.unwrap();
|
||||
);
|
||||
let nonce_versions = NonceVersions::new(nonce_state, separate_domains);
|
||||
account.set_state(&nonce_versions).unwrap();
|
||||
}
|
||||
true
|
||||
} else {
|
||||
|
@ -1402,6 +1400,7 @@ mod tests {
|
|||
bank::{DurableNonceFee, TransactionExecutionDetails},
|
||||
rent_collector::RentCollector,
|
||||
},
|
||||
assert_matches::assert_matches,
|
||||
solana_address_lookup_table_program::state::LookupTableMeta,
|
||||
solana_program_runtime::invoke_context::Executors,
|
||||
solana_sdk::{
|
||||
|
@ -1725,7 +1724,10 @@ mod tests {
|
|||
nonce.pubkey(),
|
||||
AccountSharedData::new_data(
|
||||
min_balance * 2,
|
||||
&NonceVersions::new_current(NonceState::Initialized(nonce::state::Data::default())),
|
||||
&NonceVersions::new(
|
||||
NonceState::Initialized(nonce::state::Data::default()),
|
||||
true, // separate_domains
|
||||
),
|
||||
&system_program::id(),
|
||||
)
|
||||
.unwrap(),
|
||||
|
@ -3028,7 +3030,7 @@ mod tests {
|
|||
&execution_results,
|
||||
loaded.as_mut_slice(),
|
||||
&rent_collector,
|
||||
&DurableNonce::default(),
|
||||
&(DurableNonce::default(), /*separate_domains:*/ true),
|
||||
0,
|
||||
true, // leave_nonce_on_success
|
||||
);
|
||||
|
@ -3174,20 +3176,24 @@ mod tests {
|
|||
Pubkey,
|
||||
AccountSharedData,
|
||||
AccountSharedData,
|
||||
DurableNonce,
|
||||
(DurableNonce, /*separate_domains:*/ bool),
|
||||
u64,
|
||||
Option<AccountSharedData>,
|
||||
) {
|
||||
let data =
|
||||
NonceVersions::new_current(NonceState::Initialized(nonce::state::Data::default()));
|
||||
let data = NonceVersions::new(
|
||||
NonceState::Initialized(nonce::state::Data::default()),
|
||||
true, // separate_domains
|
||||
);
|
||||
let account = AccountSharedData::new_data(42, &data, &system_program::id()).unwrap();
|
||||
let mut pre_account = account.clone();
|
||||
pre_account.set_lamports(43);
|
||||
let durable_nonce =
|
||||
DurableNonce::from_blockhash(&Hash::new(&[1u8; 32]), /*separate_domains:*/ true);
|
||||
(
|
||||
Pubkey::default(),
|
||||
pre_account,
|
||||
account,
|
||||
DurableNonce::from_blockhash(&Hash::new(&[1u8; 32]), /*separate_domains:*/ true),
|
||||
(durable_nonce, /*separate_domains:*/ true),
|
||||
1234,
|
||||
None,
|
||||
)
|
||||
|
@ -3199,7 +3205,7 @@ mod tests {
|
|||
tx_result: &Result<()>,
|
||||
is_fee_payer: bool,
|
||||
maybe_nonce: Option<(&NonceFull, bool)>,
|
||||
durable_nonce: &DurableNonce,
|
||||
durable_nonce: &(DurableNonce, /*separate_domains:*/ bool),
|
||||
lamports_per_signature: u64,
|
||||
expect_account: &AccountSharedData,
|
||||
) -> bool {
|
||||
|
@ -3245,9 +3251,14 @@ mod tests {
|
|||
|
||||
let mut expect_account = pre_account;
|
||||
expect_account
|
||||
.set_state(&NonceVersions::new_current(NonceState::Initialized(
|
||||
nonce::state::Data::new(Pubkey::default(), blockhash, lamports_per_signature),
|
||||
)))
|
||||
.set_state(&NonceVersions::new(
|
||||
NonceState::Initialized(nonce::state::Data::new(
|
||||
Pubkey::default(),
|
||||
blockhash.0,
|
||||
lamports_per_signature,
|
||||
)),
|
||||
true, // separate_domains
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
assert!(run_prepare_if_nonce_account_test(
|
||||
|
@ -3331,9 +3342,14 @@ mod tests {
|
|||
let nonce = NonceFull::new(pre_account_address, pre_account, maybe_fee_payer_account);
|
||||
|
||||
expect_account
|
||||
.set_state(&NonceVersions::new_current(NonceState::Initialized(
|
||||
nonce::state::Data::new(Pubkey::default(), blockhash, lamports_per_signature),
|
||||
)))
|
||||
.set_state(&NonceVersions::new(
|
||||
NonceState::Initialized(nonce::state::Data::new(
|
||||
Pubkey::default(),
|
||||
blockhash.0,
|
||||
lamports_per_signature,
|
||||
)),
|
||||
true, // separate_domains
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
assert!(run_prepare_if_nonce_account_test(
|
||||
|
@ -3373,7 +3389,7 @@ mod tests {
|
|||
)),
|
||||
false,
|
||||
Some((&nonce, true)),
|
||||
&DurableNonce::default(),
|
||||
&(DurableNonce::default(), /*separate_domains:*/ true),
|
||||
1,
|
||||
&post_fee_payer_account.clone(),
|
||||
));
|
||||
|
@ -3384,7 +3400,7 @@ mod tests {
|
|||
&Ok(()),
|
||||
true,
|
||||
Some((&nonce, true)),
|
||||
&DurableNonce::default(),
|
||||
&(DurableNonce::default(), /*separate_domains:*/ true),
|
||||
1,
|
||||
&post_fee_payer_account.clone(),
|
||||
));
|
||||
|
@ -3398,7 +3414,7 @@ mod tests {
|
|||
)),
|
||||
true,
|
||||
None,
|
||||
&DurableNonce::default(),
|
||||
&(DurableNonce::default(), /*separate_domains:*/ true),
|
||||
1,
|
||||
&post_fee_payer_account.clone(),
|
||||
));
|
||||
|
@ -3412,7 +3428,7 @@ mod tests {
|
|||
)),
|
||||
true,
|
||||
Some((&nonce, true)),
|
||||
&DurableNonce::default(),
|
||||
&(DurableNonce::default(), /*separate_domains:*/ true),
|
||||
1,
|
||||
&pre_fee_payer_account,
|
||||
));
|
||||
|
@ -3429,9 +3445,14 @@ mod tests {
|
|||
let to_address = Pubkey::new_unique();
|
||||
let durable_nonce =
|
||||
DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true);
|
||||
let nonce_state = NonceVersions::new_current(NonceState::Initialized(
|
||||
nonce::state::Data::new(nonce_authority.pubkey(), durable_nonce, 0),
|
||||
));
|
||||
let nonce_state = NonceVersions::new(
|
||||
NonceState::Initialized(nonce::state::Data::new(
|
||||
nonce_authority.pubkey(),
|
||||
durable_nonce,
|
||||
0,
|
||||
)),
|
||||
true, // separate_domains
|
||||
);
|
||||
let nonce_account_post =
|
||||
AccountSharedData::new_data(43, &nonce_state, &system_program::id()).unwrap();
|
||||
let from_account_post = AccountSharedData::new(4199, 0, &Pubkey::default());
|
||||
|
@ -3456,9 +3477,14 @@ mod tests {
|
|||
|
||||
let durable_nonce =
|
||||
DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true);
|
||||
let nonce_state = NonceVersions::new_current(NonceState::Initialized(
|
||||
nonce::state::Data::new(nonce_authority.pubkey(), durable_nonce, 0),
|
||||
));
|
||||
let nonce_state = NonceVersions::new(
|
||||
NonceState::Initialized(nonce::state::Data::new(
|
||||
nonce_authority.pubkey(),
|
||||
durable_nonce,
|
||||
0,
|
||||
)),
|
||||
true, // separate_domains
|
||||
);
|
||||
let nonce_account_pre =
|
||||
AccountSharedData::new_data(42, &nonce_state, &system_program::id()).unwrap();
|
||||
let from_account_pre = AccountSharedData::new(4242, 0, &Pubkey::default());
|
||||
|
@ -3503,7 +3529,7 @@ mod tests {
|
|||
&execution_results,
|
||||
loaded.as_mut_slice(),
|
||||
&rent_collector,
|
||||
&durable_nonce,
|
||||
&(durable_nonce, /*separate_domains:*/ true),
|
||||
0,
|
||||
true, // leave_nonce_on_success
|
||||
);
|
||||
|
@ -3527,11 +3553,14 @@ mod tests {
|
|||
collected_nonce_account.lamports(),
|
||||
nonce_account_pre.lamports(),
|
||||
);
|
||||
assert!(nonce_account::verify_nonce_account(
|
||||
assert_matches!(
|
||||
nonce_account::verify_nonce_account(
|
||||
&collected_nonce_account,
|
||||
durable_nonce.as_hash(),
|
||||
)
|
||||
.is_some());
|
||||
true, // separate_domins
|
||||
),
|
||||
Some(_)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -3545,9 +3574,14 @@ mod tests {
|
|||
let to_address = Pubkey::new_unique();
|
||||
let durable_nonce =
|
||||
DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true);
|
||||
let nonce_state = NonceVersions::new_current(NonceState::Initialized(
|
||||
nonce::state::Data::new(nonce_authority.pubkey(), durable_nonce, 0),
|
||||
));
|
||||
let nonce_state = NonceVersions::new(
|
||||
NonceState::Initialized(nonce::state::Data::new(
|
||||
nonce_authority.pubkey(),
|
||||
durable_nonce,
|
||||
0,
|
||||
)),
|
||||
true, // separate_domains
|
||||
);
|
||||
let nonce_account_post =
|
||||
AccountSharedData::new_data(43, &nonce_state, &system_program::id()).unwrap();
|
||||
let from_account_post = AccountSharedData::new(4200, 0, &Pubkey::default());
|
||||
|
@ -3572,9 +3606,14 @@ mod tests {
|
|||
|
||||
let durable_nonce =
|
||||
DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true);
|
||||
let nonce_state = NonceVersions::new_current(NonceState::Initialized(
|
||||
nonce::state::Data::new(nonce_authority.pubkey(), durable_nonce, 0),
|
||||
));
|
||||
let nonce_state = NonceVersions::new(
|
||||
NonceState::Initialized(nonce::state::Data::new(
|
||||
nonce_authority.pubkey(),
|
||||
durable_nonce,
|
||||
0,
|
||||
)),
|
||||
true, // separate_domains
|
||||
);
|
||||
let nonce_account_pre =
|
||||
AccountSharedData::new_data(42, &nonce_state, &system_program::id()).unwrap();
|
||||
|
||||
|
@ -3618,7 +3657,7 @@ mod tests {
|
|||
&execution_results,
|
||||
loaded.as_mut_slice(),
|
||||
&rent_collector,
|
||||
&durable_nonce,
|
||||
&(durable_nonce, /*separate_domains:*/ true),
|
||||
0,
|
||||
true, // leave_nonce_on_success
|
||||
);
|
||||
|
@ -3633,11 +3672,14 @@ mod tests {
|
|||
collected_nonce_account.lamports(),
|
||||
nonce_account_pre.lamports()
|
||||
);
|
||||
assert!(nonce_account::verify_nonce_account(
|
||||
assert_matches!(
|
||||
nonce_account::verify_nonce_account(
|
||||
&collected_nonce_account,
|
||||
durable_nonce.as_hash(),
|
||||
)
|
||||
.is_some());
|
||||
true, // separate_domins
|
||||
),
|
||||
Some(_)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -4021,6 +4021,11 @@ impl Bank {
|
|||
self.rc.accounts.accounts_db.set_shrink_paths(paths);
|
||||
}
|
||||
|
||||
pub fn separate_nonce_from_blockhash(&self) -> bool {
|
||||
self.feature_set
|
||||
.is_active(&feature_set::separate_nonce_from_blockhash::id())
|
||||
}
|
||||
|
||||
fn check_age<'a>(
|
||||
&self,
|
||||
txs: impl Iterator<Item = &'a SanitizedTransaction>,
|
||||
|
@ -4028,9 +4033,7 @@ impl Bank {
|
|||
max_age: usize,
|
||||
error_counters: &mut TransactionErrorMetrics,
|
||||
) -> Vec<TransactionCheckResult> {
|
||||
let separate_nonce_from_blockhash = self
|
||||
.feature_set
|
||||
.is_active(&feature_set::separate_nonce_from_blockhash::id());
|
||||
let separate_nonce_from_blockhash = self.separate_nonce_from_blockhash();
|
||||
let enable_durable_nonce = separate_nonce_from_blockhash
|
||||
&& self
|
||||
.feature_set
|
||||
|
@ -4112,8 +4115,11 @@ impl Bank {
|
|||
let nonce_address =
|
||||
message.get_durable_nonce(self.feature_set.is_active(&nonce_must_be_writable::id()))?;
|
||||
let nonce_account = self.get_account_with_fixed_root(nonce_address)?;
|
||||
let nonce_data =
|
||||
nonce_account::verify_nonce_account(&nonce_account, message.recent_blockhash())?;
|
||||
let nonce_data = nonce_account::verify_nonce_account(
|
||||
&nonce_account,
|
||||
message.recent_blockhash(),
|
||||
self.separate_nonce_from_blockhash(),
|
||||
)?;
|
||||
|
||||
if self
|
||||
.feature_set
|
||||
|
@ -4911,10 +4917,10 @@ impl Bank {
|
|||
|
||||
let mut write_time = Measure::start("write_time");
|
||||
let durable_nonce = {
|
||||
let separate_nonce_from_blockhash = self
|
||||
.feature_set
|
||||
.is_active(&feature_set::separate_nonce_from_blockhash::id());
|
||||
DurableNonce::from_blockhash(&last_blockhash, separate_nonce_from_blockhash)
|
||||
let separate_nonce_from_blockhash = self.separate_nonce_from_blockhash();
|
||||
let durable_nonce =
|
||||
DurableNonce::from_blockhash(&last_blockhash, separate_nonce_from_blockhash);
|
||||
(durable_nonce, separate_nonce_from_blockhash)
|
||||
};
|
||||
self.rc.accounts.store_cached(
|
||||
self.slot(),
|
||||
|
@ -7724,9 +7730,14 @@ pub(crate) mod tests {
|
|||
DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true);
|
||||
let nonce_account = AccountSharedData::new_data(
|
||||
43,
|
||||
&nonce::state::Versions::new_current(nonce::State::Initialized(
|
||||
nonce::state::Data::new(Pubkey::default(), durable_nonce, lamports_per_signature),
|
||||
&nonce::state::Versions::new(
|
||||
nonce::State::Initialized(nonce::state::Data::new(
|
||||
Pubkey::default(),
|
||||
durable_nonce,
|
||||
lamports_per_signature,
|
||||
)),
|
||||
true, // separate_domains
|
||||
),
|
||||
&system_program::id(),
|
||||
)
|
||||
.unwrap();
|
||||
|
@ -10249,9 +10260,10 @@ pub(crate) mod tests {
|
|||
let nonce = Keypair::new();
|
||||
let nonce_account = AccountSharedData::new_data(
|
||||
min_balance + 42,
|
||||
&nonce::state::Versions::new_current(nonce::State::Initialized(
|
||||
nonce::state::Data::default(),
|
||||
)),
|
||||
&nonce::state::Versions::new(
|
||||
nonce::State::Initialized(nonce::state::Data::default()),
|
||||
true, // separate_domains
|
||||
),
|
||||
&system_program::id(),
|
||||
)
|
||||
.unwrap();
|
||||
|
@ -12439,14 +12451,12 @@ pub(crate) mod tests {
|
|||
}
|
||||
|
||||
fn get_nonce_blockhash(bank: &Bank, nonce_pubkey: &Pubkey) -> Option<Hash> {
|
||||
bank.get_account(nonce_pubkey).and_then(|acc| {
|
||||
let state =
|
||||
StateMut::<nonce::state::Versions>::state(&acc).map(|v| v.convert_to_current());
|
||||
match state {
|
||||
Ok(nonce::State::Initialized(ref data)) => Some(data.blockhash()),
|
||||
let account = bank.get_account(nonce_pubkey)?;
|
||||
let nonce_versions = StateMut::<nonce::state::Versions>::state(&account);
|
||||
match nonce_versions.ok()?.state() {
|
||||
nonce::State::Initialized(ref data) => Some(data.blockhash()),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn nonce_setup(
|
||||
|
@ -12689,9 +12699,10 @@ pub(crate) mod tests {
|
|||
let nonce = Keypair::new();
|
||||
let nonce_account = AccountSharedData::new_data(
|
||||
42_424_242,
|
||||
&nonce::state::Versions::new_current(nonce::State::Initialized(
|
||||
nonce::state::Data::default(),
|
||||
)),
|
||||
&nonce::state::Versions::new(
|
||||
nonce::State::Initialized(nonce::state::Data::default()),
|
||||
true, // separate_domains
|
||||
),
|
||||
&system_program::id(),
|
||||
)
|
||||
.unwrap();
|
||||
|
@ -12721,9 +12732,14 @@ pub(crate) mod tests {
|
|||
DurableNonce::from_blockhash(&bank.last_blockhash(), true /* separate domains */);
|
||||
let nonce_account = AccountSharedData::new_data(
|
||||
42_424_242,
|
||||
&nonce::state::Versions::new_current(nonce::State::Initialized(
|
||||
nonce::state::Data::new(nonce_authority, durable_nonce, 5000),
|
||||
&nonce::state::Versions::new(
|
||||
nonce::State::Initialized(nonce::state::Data::new(
|
||||
nonce_authority,
|
||||
durable_nonce,
|
||||
5000,
|
||||
)),
|
||||
true, // separate_domains
|
||||
),
|
||||
&system_program::id(),
|
||||
)
|
||||
.unwrap();
|
||||
|
@ -13202,10 +13218,9 @@ pub(crate) mod tests {
|
|||
let (stored_nonce_hash, stored_fee_calculator) = bank
|
||||
.get_account(&nonce_pubkey)
|
||||
.and_then(|acc| {
|
||||
let state =
|
||||
StateMut::<nonce::state::Versions>::state(&acc).map(|v| v.convert_to_current());
|
||||
match state {
|
||||
Ok(nonce::State::Initialized(ref data)) => {
|
||||
let nonce_versions = StateMut::<nonce::state::Versions>::state(&acc);
|
||||
match nonce_versions.ok()?.state() {
|
||||
nonce::State::Initialized(ref data) => {
|
||||
Some((data.blockhash(), data.fee_calculator))
|
||||
}
|
||||
_ => None,
|
||||
|
@ -13239,10 +13254,9 @@ pub(crate) mod tests {
|
|||
let (nonce_hash, fee_calculator) = bank
|
||||
.get_account(&nonce_pubkey)
|
||||
.and_then(|acc| {
|
||||
let state =
|
||||
StateMut::<nonce::state::Versions>::state(&acc).map(|v| v.convert_to_current());
|
||||
match state {
|
||||
Ok(nonce::State::Initialized(ref data)) => {
|
||||
let nonce_versions = StateMut::<nonce::state::Versions>::state(&acc);
|
||||
match nonce_versions.ok()?.state() {
|
||||
nonce::State::Initialized(ref data) => {
|
||||
Some((data.blockhash(), data.fee_calculator))
|
||||
}
|
||||
_ => None,
|
||||
|
@ -13272,10 +13286,9 @@ pub(crate) mod tests {
|
|||
let (stored_nonce_hash, stored_fee_calculator) = bank
|
||||
.get_account(&nonce_pubkey)
|
||||
.and_then(|acc| {
|
||||
let state =
|
||||
StateMut::<nonce::state::Versions>::state(&acc).map(|v| v.convert_to_current());
|
||||
match state {
|
||||
Ok(nonce::State::Initialized(ref data)) => {
|
||||
let nonce_versions = StateMut::<nonce::state::Versions>::state(&acc);
|
||||
match nonce_versions.ok()?.state() {
|
||||
nonce::State::Initialized(ref data) => {
|
||||
Some((data.blockhash(), data.fee_calculator))
|
||||
}
|
||||
_ => None,
|
||||
|
@ -13309,10 +13322,9 @@ pub(crate) mod tests {
|
|||
let (nonce_hash, fee_calculator) = bank
|
||||
.get_account(&nonce_pubkey)
|
||||
.and_then(|acc| {
|
||||
let state =
|
||||
StateMut::<nonce::state::Versions>::state(&acc).map(|v| v.convert_to_current());
|
||||
match state {
|
||||
Ok(nonce::State::Initialized(ref data)) => {
|
||||
let nonce_versions = StateMut::<nonce::state::Versions>::state(&acc);
|
||||
match nonce_versions.ok()?.state() {
|
||||
nonce::State::Initialized(ref data) => {
|
||||
Some((data.blockhash(), data.fee_calculator))
|
||||
}
|
||||
_ => None,
|
||||
|
|
|
@ -16,11 +16,13 @@ use {
|
|||
std::collections::HashSet,
|
||||
};
|
||||
|
||||
fn get_durable_nonce(invoke_context: &InvokeContext) -> DurableNonce {
|
||||
fn get_durable_nonce(invoke_context: &InvokeContext) -> (DurableNonce, /*separate_domains:*/ bool) {
|
||||
let separate_nonce_from_blockhash = invoke_context
|
||||
.feature_set
|
||||
.is_active(&feature_set::separate_nonce_from_blockhash::id());
|
||||
DurableNonce::from_blockhash(&invoke_context.blockhash, separate_nonce_from_blockhash)
|
||||
let durable_nonce =
|
||||
DurableNonce::from_blockhash(&invoke_context.blockhash, separate_nonce_from_blockhash);
|
||||
(durable_nonce, separate_nonce_from_blockhash)
|
||||
}
|
||||
|
||||
pub fn advance_nonce_account(
|
||||
|
@ -46,7 +48,7 @@ pub fn advance_nonce_account(
|
|||
}
|
||||
|
||||
let state: Versions = account.get_state()?;
|
||||
match state.convert_to_current() {
|
||||
match state.state() {
|
||||
State::Initialized(data) => {
|
||||
if !signers.contains(&data.authority) {
|
||||
ic_msg!(
|
||||
|
@ -56,7 +58,7 @@ pub fn advance_nonce_account(
|
|||
);
|
||||
return Err(InstructionError::MissingRequiredSignature);
|
||||
}
|
||||
let next_durable_nonce = get_durable_nonce(invoke_context);
|
||||
let (next_durable_nonce, separate_domains) = get_durable_nonce(invoke_context);
|
||||
if data.durable_nonce == next_durable_nonce {
|
||||
ic_msg!(
|
||||
invoke_context,
|
||||
|
@ -73,9 +75,12 @@ pub fn advance_nonce_account(
|
|||
next_durable_nonce,
|
||||
invoke_context.lamports_per_signature,
|
||||
);
|
||||
account.set_state(&Versions::new_current(State::Initialized(new_data)))
|
||||
account.set_state(&Versions::new(
|
||||
State::Initialized(new_data),
|
||||
separate_domains,
|
||||
))
|
||||
}
|
||||
_ => {
|
||||
State::Uninitialized => {
|
||||
ic_msg!(
|
||||
invoke_context,
|
||||
"Advance nonce account: Account {} state is invalid",
|
||||
|
@ -119,7 +124,7 @@ pub fn withdraw_nonce_account(
|
|||
}
|
||||
|
||||
let state: Versions = from.get_state()?;
|
||||
let signer = match state.convert_to_current() {
|
||||
let signer = match state.state() {
|
||||
State::Uninitialized => {
|
||||
if lamports > from.get_lamports() {
|
||||
ic_msg!(
|
||||
|
@ -134,7 +139,8 @@ pub fn withdraw_nonce_account(
|
|||
}
|
||||
State::Initialized(ref data) => {
|
||||
if lamports == from.get_lamports() {
|
||||
if data.durable_nonce == get_durable_nonce(invoke_context) {
|
||||
let (durable_nonce, separate_domains) = get_durable_nonce(invoke_context);
|
||||
if data.durable_nonce == durable_nonce {
|
||||
ic_msg!(
|
||||
invoke_context,
|
||||
"Withdraw nonce account: nonce can only advance once per slot"
|
||||
|
@ -144,7 +150,7 @@ pub fn withdraw_nonce_account(
|
|||
merge_nonce_error_into_system_error,
|
||||
));
|
||||
}
|
||||
from.set_state(&Versions::new_current(State::Uninitialized))?;
|
||||
from.set_state(&Versions::new(State::Uninitialized, separate_domains))?;
|
||||
} else {
|
||||
let min_balance = rent.minimum_balance(from.get_data().len());
|
||||
let amount = checked_add(lamports, min_balance)?;
|
||||
|
@ -204,8 +210,7 @@ pub fn initialize_nonce_account(
|
|||
return Err(InstructionError::InvalidArgument);
|
||||
}
|
||||
|
||||
let state: Versions = account.get_state()?;
|
||||
match state.convert_to_current() {
|
||||
match account.get_state::<Versions>()?.state() {
|
||||
State::Uninitialized => {
|
||||
let min_balance = rent.minimum_balance(account.get_data().len());
|
||||
if account.get_lamports() < min_balance {
|
||||
|
@ -217,14 +222,16 @@ pub fn initialize_nonce_account(
|
|||
);
|
||||
return Err(InstructionError::InsufficientFunds);
|
||||
}
|
||||
let (durable_nonce, separate_domains) = get_durable_nonce(invoke_context);
|
||||
let data = nonce::state::Data::new(
|
||||
*nonce_authority,
|
||||
get_durable_nonce(invoke_context),
|
||||
durable_nonce,
|
||||
invoke_context.lamports_per_signature,
|
||||
);
|
||||
account.set_state(&Versions::new_current(State::Initialized(data)))
|
||||
let state = State::Initialized(data);
|
||||
account.set_state(&Versions::new(state, separate_domains))
|
||||
}
|
||||
_ => {
|
||||
State::Initialized(_) => {
|
||||
ic_msg!(
|
||||
invoke_context,
|
||||
"Initialize nonce account: Account {} state is invalid",
|
||||
|
@ -262,7 +269,8 @@ pub fn authorize_nonce_account(
|
|||
}
|
||||
|
||||
let state: Versions = account.get_state()?;
|
||||
match state.convert_to_current() {
|
||||
let separate_domains = state.separate_domains();
|
||||
match state.state() {
|
||||
State::Initialized(data) => {
|
||||
if !signers.contains(&data.authority) {
|
||||
ic_msg!(
|
||||
|
@ -277,9 +285,12 @@ pub fn authorize_nonce_account(
|
|||
data.durable_nonce,
|
||||
data.get_lamports_per_signature(),
|
||||
);
|
||||
account.set_state(&Versions::new_current(State::Initialized(new_data)))
|
||||
account.set_state(&Versions::new(
|
||||
State::Initialized(new_data),
|
||||
separate_domains,
|
||||
))
|
||||
}
|
||||
_ => {
|
||||
State::Uninitialized => {
|
||||
ic_msg!(
|
||||
invoke_context,
|
||||
"Authorize nonce account: Account {} state is invalid",
|
||||
|
@ -297,6 +308,7 @@ pub fn authorize_nonce_account(
|
|||
mod test {
|
||||
use {
|
||||
super::*,
|
||||
assert_matches::assert_matches,
|
||||
solana_program_runtime::invoke_context::InvokeContext,
|
||||
solana_sdk::{
|
||||
account::AccountSharedData,
|
||||
|
@ -334,9 +346,12 @@ mod test {
|
|||
let accounts = vec![
|
||||
(
|
||||
Pubkey::new_unique(),
|
||||
create_account(from_lamports).into_inner(),
|
||||
create_account(from_lamports, /*separate_domains:*/ true).into_inner(),
|
||||
),
|
||||
(
|
||||
Pubkey::new_unique(),
|
||||
create_account(42, /*separate_domains:*/ true).into_inner(),
|
||||
),
|
||||
(Pubkey::new_unique(), create_account(42).into_inner()),
|
||||
(system_program::id(), AccountSharedData::default()),
|
||||
];
|
||||
let $instruction_accounts = vec![
|
||||
|
@ -390,52 +405,40 @@ mod test {
|
|||
};
|
||||
let mut signers = HashSet::new();
|
||||
signers.insert(*nonce_account.get_key());
|
||||
let state = nonce_account
|
||||
.get_state::<Versions>()
|
||||
.unwrap()
|
||||
.convert_to_current();
|
||||
let versions = nonce_account.get_state::<Versions>().unwrap();
|
||||
// New is in Uninitialzed state
|
||||
assert_eq!(state, State::Uninitialized);
|
||||
assert_eq!(versions.state(), &State::Uninitialized);
|
||||
set_invoke_context_blockhash!(invoke_context, 95);
|
||||
let authorized = *nonce_account.get_key();
|
||||
initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context).unwrap();
|
||||
let state = nonce_account
|
||||
.get_state::<Versions>()
|
||||
.unwrap()
|
||||
.convert_to_current();
|
||||
let versions = nonce_account.get_state::<Versions>().unwrap();
|
||||
let data = nonce::state::Data::new(
|
||||
data.authority,
|
||||
get_durable_nonce(&invoke_context),
|
||||
get_durable_nonce(&invoke_context).0,
|
||||
invoke_context.lamports_per_signature,
|
||||
);
|
||||
// First nonce instruction drives state from Uninitialized to Initialized
|
||||
assert_eq!(state, State::Initialized(data.clone()));
|
||||
assert_eq!(versions.state(), &State::Initialized(data.clone()));
|
||||
set_invoke_context_blockhash!(invoke_context, 63);
|
||||
advance_nonce_account(&mut nonce_account, &signers, &invoke_context).unwrap();
|
||||
let state = nonce_account
|
||||
.get_state::<Versions>()
|
||||
.unwrap()
|
||||
.convert_to_current();
|
||||
let versions = nonce_account.get_state::<Versions>().unwrap();
|
||||
let data = nonce::state::Data::new(
|
||||
data.authority,
|
||||
get_durable_nonce(&invoke_context),
|
||||
get_durable_nonce(&invoke_context).0,
|
||||
invoke_context.lamports_per_signature,
|
||||
);
|
||||
// Second nonce instruction consumes and replaces stored nonce
|
||||
assert_eq!(state, State::Initialized(data.clone()));
|
||||
assert_eq!(versions.state(), &State::Initialized(data.clone()));
|
||||
set_invoke_context_blockhash!(invoke_context, 31);
|
||||
advance_nonce_account(&mut nonce_account, &signers, &invoke_context).unwrap();
|
||||
let state = nonce_account
|
||||
.get_state::<Versions>()
|
||||
.unwrap()
|
||||
.convert_to_current();
|
||||
let versions = nonce_account.get_state::<Versions>().unwrap();
|
||||
let data = nonce::state::Data::new(
|
||||
data.authority,
|
||||
get_durable_nonce(&invoke_context),
|
||||
get_durable_nonce(&invoke_context).0,
|
||||
invoke_context.lamports_per_signature,
|
||||
);
|
||||
// Third nonce instruction for fun and profit
|
||||
assert_eq!(state, State::Initialized(data));
|
||||
assert_eq!(versions.state(), &State::Initialized(data));
|
||||
|
||||
set_invoke_context_blockhash!(invoke_context, 0);
|
||||
let to_account = instruction_context
|
||||
|
@ -467,12 +470,9 @@ mod test {
|
|||
assert_eq!(nonce_account.get_lamports(), expect_nonce_lamports);
|
||||
// Account balance goes to `to`
|
||||
assert_eq!(to_account.get_lamports(), expect_to_lamports);
|
||||
let state = nonce_account
|
||||
.get_state::<Versions>()
|
||||
.unwrap()
|
||||
.convert_to_current();
|
||||
let versions = nonce_account.get_state::<Versions>().unwrap();
|
||||
// Empty balance deinitializes data
|
||||
assert_eq!(state, State::Uninitialized);
|
||||
assert_eq!(versions.state(), &State::Uninitialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -490,16 +490,13 @@ mod test {
|
|||
set_invoke_context_blockhash!(invoke_context, 31);
|
||||
let authority = *nonce_account.get_key();
|
||||
initialize_nonce_account(&mut nonce_account, &authority, &rent, &invoke_context).unwrap();
|
||||
let state = nonce_account
|
||||
.get_state::<Versions>()
|
||||
.unwrap()
|
||||
.convert_to_current();
|
||||
let versions = nonce_account.get_state::<Versions>().unwrap();
|
||||
let data = nonce::state::Data::new(
|
||||
authority,
|
||||
get_durable_nonce(&invoke_context),
|
||||
get_durable_nonce(&invoke_context).0,
|
||||
invoke_context.lamports_per_signature,
|
||||
);
|
||||
assert_eq!(state, State::Initialized(data));
|
||||
assert_eq!(versions.state(), &State::Initialized(data));
|
||||
// Nonce account did not sign
|
||||
let signers = HashSet::new();
|
||||
set_invoke_context_blockhash!(invoke_context, 0);
|
||||
|
@ -613,11 +610,8 @@ mod test {
|
|||
let to_account = instruction_context
|
||||
.try_borrow_instruction_account(transaction_context, WITHDRAW_TO_ACCOUNT_INDEX)
|
||||
.unwrap();
|
||||
let state = nonce_account
|
||||
.get_state::<Versions>()
|
||||
.unwrap()
|
||||
.convert_to_current();
|
||||
assert_eq!(state, State::Uninitialized);
|
||||
let versions = nonce_account.get_state::<Versions>().unwrap();
|
||||
assert_eq!(versions.state(), &State::Uninitialized);
|
||||
let mut signers = HashSet::new();
|
||||
signers.insert(*nonce_account.get_key());
|
||||
set_invoke_context_blockhash!(invoke_context, 0);
|
||||
|
@ -643,11 +637,8 @@ mod test {
|
|||
let to_account = instruction_context
|
||||
.try_borrow_instruction_account(transaction_context, WITHDRAW_TO_ACCOUNT_INDEX)
|
||||
.unwrap();
|
||||
let state = nonce_account
|
||||
.get_state::<Versions>()
|
||||
.unwrap()
|
||||
.convert_to_current();
|
||||
assert_eq!(state, State::Uninitialized);
|
||||
let versions = nonce_account.get_state::<Versions>().unwrap();
|
||||
assert_eq!(versions.state(), &State::Uninitialized);
|
||||
assert_eq!(nonce_account.get_lamports(), expect_from_lamports);
|
||||
assert_eq!(to_account.get_lamports(), expect_to_lamports);
|
||||
}
|
||||
|
@ -667,11 +658,8 @@ mod test {
|
|||
let to_account = instruction_context
|
||||
.try_borrow_instruction_account(transaction_context, WITHDRAW_TO_ACCOUNT_INDEX)
|
||||
.unwrap();
|
||||
let state = nonce_account
|
||||
.get_state::<Versions>()
|
||||
.unwrap()
|
||||
.convert_to_current();
|
||||
assert_eq!(state, State::Uninitialized);
|
||||
let versions = nonce_account.get_state::<Versions>().unwrap();
|
||||
assert_eq!(versions.state(), &State::Uninitialized);
|
||||
let signers = HashSet::new();
|
||||
set_invoke_context_blockhash!(invoke_context, 0);
|
||||
let withdraw_lamports = nonce_account.get_lamports();
|
||||
|
@ -702,11 +690,8 @@ mod test {
|
|||
let nonce_account = instruction_context
|
||||
.try_borrow_instruction_account(transaction_context, NONCE_ACCOUNT_INDEX)
|
||||
.unwrap();
|
||||
let state = nonce_account
|
||||
.get_state::<Versions>()
|
||||
.unwrap()
|
||||
.convert_to_current();
|
||||
assert_eq!(state, State::Uninitialized);
|
||||
let versions = nonce_account.get_state::<Versions>().unwrap();
|
||||
assert_eq!(versions.state(), &State::Uninitialized);
|
||||
let mut signers = HashSet::new();
|
||||
signers.insert(*nonce_account.get_key());
|
||||
set_invoke_context_blockhash!(invoke_context, 0);
|
||||
|
@ -765,11 +750,8 @@ mod test {
|
|||
let to_account = instruction_context
|
||||
.try_borrow_instruction_account(transaction_context, WITHDRAW_TO_ACCOUNT_INDEX)
|
||||
.unwrap();
|
||||
let state = nonce_account
|
||||
.get_state::<Versions>()
|
||||
.unwrap()
|
||||
.convert_to_current();
|
||||
assert_eq!(state, State::Uninitialized);
|
||||
let versions = nonce_account.get_state::<Versions>().unwrap();
|
||||
assert_eq!(versions.state(), &State::Uninitialized);
|
||||
assert_eq!(nonce_account.get_lamports(), from_expect_lamports);
|
||||
assert_eq!(to_account.get_lamports(), to_expect_lamports);
|
||||
let withdraw_lamports = nonce_account.get_lamports();
|
||||
|
@ -794,11 +776,8 @@ mod test {
|
|||
let to_account = instruction_context
|
||||
.try_borrow_instruction_account(transaction_context, WITHDRAW_TO_ACCOUNT_INDEX)
|
||||
.unwrap();
|
||||
let state = nonce_account
|
||||
.get_state::<Versions>()
|
||||
.unwrap()
|
||||
.convert_to_current();
|
||||
assert_eq!(state, State::Uninitialized);
|
||||
let versions = nonce_account.get_state::<Versions>().unwrap();
|
||||
assert_eq!(versions.state(), &State::Uninitialized);
|
||||
assert_eq!(nonce_account.get_lamports(), from_expect_lamports);
|
||||
assert_eq!(to_account.get_lamports(), to_expect_lamports);
|
||||
}
|
||||
|
@ -823,16 +802,13 @@ mod test {
|
|||
set_invoke_context_blockhash!(invoke_context, 31);
|
||||
let authority = *nonce_account.get_key();
|
||||
initialize_nonce_account(&mut nonce_account, &authority, &rent, &invoke_context).unwrap();
|
||||
let state = nonce_account
|
||||
.get_state::<Versions>()
|
||||
.unwrap()
|
||||
.convert_to_current();
|
||||
let versions = nonce_account.get_state::<Versions>().unwrap();
|
||||
let data = nonce::state::Data::new(
|
||||
authority,
|
||||
get_durable_nonce(&invoke_context),
|
||||
get_durable_nonce(&invoke_context).0,
|
||||
invoke_context.lamports_per_signature,
|
||||
);
|
||||
assert_eq!(state, State::Initialized(data.clone()));
|
||||
assert_eq!(versions.state(), &State::Initialized(data.clone()));
|
||||
let withdraw_lamports = 42;
|
||||
let from_expect_lamports = nonce_account.get_lamports() - withdraw_lamports;
|
||||
let to_expect_lamports = to_account.get_lamports() + withdraw_lamports;
|
||||
|
@ -855,16 +831,13 @@ mod test {
|
|||
let to_account = instruction_context
|
||||
.try_borrow_instruction_account(transaction_context, WITHDRAW_TO_ACCOUNT_INDEX)
|
||||
.unwrap();
|
||||
let state = nonce_account
|
||||
.get_state::<Versions>()
|
||||
.unwrap()
|
||||
.convert_to_current();
|
||||
let versions = nonce_account.get_state::<Versions>().unwrap();
|
||||
let data = nonce::state::Data::new(
|
||||
data.authority,
|
||||
get_durable_nonce(&invoke_context),
|
||||
get_durable_nonce(&invoke_context).0,
|
||||
invoke_context.lamports_per_signature,
|
||||
);
|
||||
assert_eq!(state, State::Initialized(data));
|
||||
assert_eq!(versions.state(), &State::Initialized(data));
|
||||
assert_eq!(nonce_account.get_lamports(), from_expect_lamports);
|
||||
assert_eq!(to_account.get_lamports(), to_expect_lamports);
|
||||
set_invoke_context_blockhash!(invoke_context, 0);
|
||||
|
@ -890,11 +863,8 @@ mod test {
|
|||
let to_account = instruction_context
|
||||
.try_borrow_instruction_account(transaction_context, WITHDRAW_TO_ACCOUNT_INDEX)
|
||||
.unwrap();
|
||||
let state = nonce_account
|
||||
.get_state::<Versions>()
|
||||
.unwrap()
|
||||
.convert_to_current();
|
||||
assert_eq!(state, State::Uninitialized);
|
||||
let versions = nonce_account.get_state::<Versions>().unwrap();
|
||||
assert_eq!(versions.state(), &State::Uninitialized);
|
||||
assert_eq!(nonce_account.get_lamports(), from_expect_lamports);
|
||||
assert_eq!(to_account.get_lamports(), to_expect_lamports);
|
||||
}
|
||||
|
@ -1046,11 +1016,8 @@ mod test {
|
|||
let mut nonce_account = instruction_context
|
||||
.try_borrow_instruction_account(transaction_context, NONCE_ACCOUNT_INDEX)
|
||||
.unwrap();
|
||||
let state = nonce_account
|
||||
.get_state::<Versions>()
|
||||
.unwrap()
|
||||
.convert_to_current();
|
||||
assert_eq!(state, State::Uninitialized);
|
||||
let versions = nonce_account.get_state::<Versions>().unwrap();
|
||||
assert_eq!(versions.state(), &State::Uninitialized);
|
||||
let mut signers = HashSet::new();
|
||||
signers.insert(*nonce_account.get_key());
|
||||
set_invoke_context_blockhash!(invoke_context, 0);
|
||||
|
@ -1059,15 +1026,12 @@ mod test {
|
|||
initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context);
|
||||
let data = nonce::state::Data::new(
|
||||
authorized,
|
||||
get_durable_nonce(&invoke_context),
|
||||
get_durable_nonce(&invoke_context).0,
|
||||
invoke_context.lamports_per_signature,
|
||||
);
|
||||
assert_eq!(result, Ok(()));
|
||||
let state = nonce_account
|
||||
.get_state::<Versions>()
|
||||
.unwrap()
|
||||
.convert_to_current();
|
||||
assert_eq!(state, State::Initialized(data));
|
||||
let versions = nonce_account.get_state::<Versions>().unwrap();
|
||||
assert_eq!(versions.state(), &State::Initialized(data));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1131,15 +1095,12 @@ mod test {
|
|||
let authority = Pubkey::default();
|
||||
let data = nonce::state::Data::new(
|
||||
authority,
|
||||
get_durable_nonce(&invoke_context),
|
||||
get_durable_nonce(&invoke_context).0,
|
||||
invoke_context.lamports_per_signature,
|
||||
);
|
||||
authorize_nonce_account(&mut nonce_account, &authority, &signers, &invoke_context).unwrap();
|
||||
let state = nonce_account
|
||||
.get_state::<Versions>()
|
||||
.unwrap()
|
||||
.convert_to_current();
|
||||
assert_eq!(state, State::Initialized(data));
|
||||
let versions = nonce_account.get_state::<Versions>().unwrap();
|
||||
assert_eq!(versions.state(), &State::Initialized(data));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1201,21 +1162,24 @@ mod test {
|
|||
.unwrap();
|
||||
let mut signers = HashSet::new();
|
||||
signers.insert(nonce_account.get_key());
|
||||
let state: State = nonce_account.get_state().unwrap();
|
||||
let versions: Versions = nonce_account.get_state().unwrap();
|
||||
// New is in Uninitialzed state
|
||||
assert_eq!(state, State::Uninitialized);
|
||||
assert_eq!(versions.state(), &State::Uninitialized);
|
||||
set_invoke_context_blockhash!(invoke_context, 0);
|
||||
let authorized = *nonce_account.get_key();
|
||||
initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context).unwrap();
|
||||
drop(nonce_account);
|
||||
assert!(verify_nonce_account(
|
||||
assert_matches!(
|
||||
verify_nonce_account(
|
||||
&transaction_context
|
||||
.get_account_at_index(NONCE_ACCOUNT_INDEX)
|
||||
.unwrap()
|
||||
.borrow(),
|
||||
get_durable_nonce(&invoke_context).as_hash(),
|
||||
)
|
||||
.is_some());
|
||||
get_durable_nonce(&invoke_context).0.as_hash(),
|
||||
true, // separate_domins
|
||||
),
|
||||
Some(_)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1227,14 +1191,17 @@ mod test {
|
|||
_instruction_context,
|
||||
instruction_accounts
|
||||
);
|
||||
assert!(verify_nonce_account(
|
||||
assert_eq!(
|
||||
verify_nonce_account(
|
||||
&transaction_context
|
||||
.get_account_at_index(NONCE_ACCOUNT_INDEX)
|
||||
.unwrap()
|
||||
.borrow(),
|
||||
&Hash::default()
|
||||
)
|
||||
.is_none());
|
||||
&Hash::default(),
|
||||
true, // separate_domins
|
||||
),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1251,9 +1218,9 @@ mod test {
|
|||
.unwrap();
|
||||
let mut signers = HashSet::new();
|
||||
signers.insert(nonce_account.get_key());
|
||||
let state: State = nonce_account.get_state().unwrap();
|
||||
let versions: Versions = nonce_account.get_state().unwrap();
|
||||
// New is in Uninitialzed state
|
||||
assert_eq!(state, State::Uninitialized);
|
||||
assert_eq!(versions.state(), &State::Uninitialized);
|
||||
set_invoke_context_blockhash!(invoke_context, 0);
|
||||
let authorized = *nonce_account.get_key();
|
||||
initialize_nonce_account(
|
||||
|
@ -1265,13 +1232,16 @@ mod test {
|
|||
.unwrap();
|
||||
set_invoke_context_blockhash!(invoke_context, 1);
|
||||
drop(nonce_account);
|
||||
assert!(verify_nonce_account(
|
||||
assert_eq!(
|
||||
verify_nonce_account(
|
||||
&transaction_context
|
||||
.get_account_at_index(NONCE_ACCOUNT_INDEX)
|
||||
.unwrap()
|
||||
.borrow(),
|
||||
get_durable_nonce(&invoke_context).as_hash(),
|
||||
)
|
||||
.is_none());
|
||||
get_durable_nonce(&invoke_context).0.as_hash(),
|
||||
true, // separate_domins
|
||||
),
|
||||
None
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -549,11 +549,10 @@ pub fn get_system_account_kind(account: &AccountSharedData) -> Option<SystemAcco
|
|||
if account.data().is_empty() {
|
||||
Some(SystemAccountKind::System)
|
||||
} else if account.data().len() == nonce::State::size() {
|
||||
match account.state().ok()? {
|
||||
nonce::state::Versions::Current(state) => match *state {
|
||||
let nonce_versions: nonce::state::Versions = account.state().ok()?;
|
||||
match nonce_versions.state() {
|
||||
nonce::State::Uninitialized => None,
|
||||
nonce::State::Initialized(_) => Some(SystemAccountKind::Nonce),
|
||||
_ => None,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
None
|
||||
|
@ -1191,9 +1190,10 @@ mod tests {
|
|||
let nonce = Pubkey::new_unique();
|
||||
let nonce_account = AccountSharedData::new_data(
|
||||
42,
|
||||
&nonce::state::Versions::new_current(nonce::State::Initialized(
|
||||
nonce::state::Data::default(),
|
||||
)),
|
||||
&nonce::state::Versions::new(
|
||||
nonce::State::Initialized(nonce::state::Data::default()),
|
||||
true, // separate_domains
|
||||
),
|
||||
&system_program::id(),
|
||||
)
|
||||
.unwrap();
|
||||
|
@ -1482,10 +1482,13 @@ mod tests {
|
|||
let from = Pubkey::new_unique();
|
||||
let from_account = AccountSharedData::new_data(
|
||||
100,
|
||||
&nonce::state::Versions::new_current(nonce::State::Initialized(nonce::state::Data {
|
||||
&nonce::state::Versions::new(
|
||||
nonce::State::Initialized(nonce::state::Data {
|
||||
authority: from,
|
||||
..nonce::state::Data::default()
|
||||
})),
|
||||
}),
|
||||
true, // separate_domains
|
||||
),
|
||||
&system_program::id(),
|
||||
)
|
||||
.unwrap();
|
||||
|
@ -1764,7 +1767,8 @@ mod tests {
|
|||
#[test]
|
||||
fn test_process_nonce_ix_ok() {
|
||||
let nonce_address = Pubkey::new_unique();
|
||||
let nonce_account = nonce_account::create_account(1_000_000).into_inner();
|
||||
let nonce_account =
|
||||
nonce_account::create_account(1_000_000, /*separate_domains:*/ true).into_inner();
|
||||
#[allow(deprecated)]
|
||||
let blockhash_id = sysvar::recent_blockhashes::id();
|
||||
let accounts = process_instruction(
|
||||
|
@ -1871,7 +1875,8 @@ mod tests {
|
|||
#[test]
|
||||
fn test_process_withdraw_ix_ok() {
|
||||
let nonce_address = Pubkey::new_unique();
|
||||
let nonce_account = nonce_account::create_account(1_000_000).into_inner();
|
||||
let nonce_account =
|
||||
nonce_account::create_account(1_000_000, /*separate_domains:*/ true).into_inner();
|
||||
let pubkey = Pubkey::new_unique();
|
||||
#[allow(deprecated)]
|
||||
let blockhash_id = sysvar::recent_blockhashes::id();
|
||||
|
@ -1924,7 +1929,8 @@ mod tests {
|
|||
#[test]
|
||||
fn test_process_initialize_ix_only_nonce_acc_fail() {
|
||||
let nonce_address = Pubkey::new_unique();
|
||||
let nonce_account = nonce_account::create_account(1_000_000).into_inner();
|
||||
let nonce_account =
|
||||
nonce_account::create_account(1_000_000, /*separate_domains:*/ true).into_inner();
|
||||
process_instruction(
|
||||
&serialize(&SystemInstruction::InitializeNonceAccount(nonce_address)).unwrap(),
|
||||
vec![(nonce_address, nonce_account)],
|
||||
|
@ -1941,7 +1947,8 @@ mod tests {
|
|||
#[test]
|
||||
fn test_process_initialize_ix_ok() {
|
||||
let nonce_address = Pubkey::new_unique();
|
||||
let nonce_account = nonce_account::create_account(1_000_000).into_inner();
|
||||
let nonce_account =
|
||||
nonce_account::create_account(1_000_000, /*separate_domains:*/ true).into_inner();
|
||||
#[allow(deprecated)]
|
||||
let blockhash_id = sysvar::recent_blockhashes::id();
|
||||
process_instruction(
|
||||
|
@ -1976,7 +1983,8 @@ mod tests {
|
|||
#[test]
|
||||
fn test_process_authorize_ix_ok() {
|
||||
let nonce_address = Pubkey::new_unique();
|
||||
let nonce_account = nonce_account::create_account(1_000_000).into_inner();
|
||||
let nonce_account =
|
||||
nonce_account::create_account(1_000_000, /*separate_domains:*/ true).into_inner();
|
||||
#[allow(deprecated)]
|
||||
let blockhash_id = sysvar::recent_blockhashes::id();
|
||||
let accounts = process_instruction(
|
||||
|
@ -2045,9 +2053,10 @@ mod tests {
|
|||
fn test_get_system_account_kind_nonce_ok() {
|
||||
let nonce_account = AccountSharedData::new_data(
|
||||
42,
|
||||
&nonce::state::Versions::new_current(nonce::State::Initialized(
|
||||
nonce::state::Data::default(),
|
||||
)),
|
||||
&nonce::state::Versions::new(
|
||||
nonce::State::Initialized(nonce::state::Data::default()),
|
||||
true, // separate_domains
|
||||
),
|
||||
&system_program::id(),
|
||||
)
|
||||
.unwrap();
|
||||
|
@ -2060,7 +2069,9 @@ mod tests {
|
|||
#[test]
|
||||
fn test_get_system_account_kind_uninitialized_nonce_account_fail() {
|
||||
assert_eq!(
|
||||
get_system_account_kind(&nonce_account::create_account(42).borrow()),
|
||||
get_system_account_kind(
|
||||
&nonce_account::create_account(42, /*separate_domains:*/ true).borrow()
|
||||
),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
@ -2076,9 +2087,10 @@ mod tests {
|
|||
fn test_get_system_account_kind_nonsystem_owner_with_nonce_data_fail() {
|
||||
let nonce_account = AccountSharedData::new_data(
|
||||
42,
|
||||
&nonce::state::Versions::new_current(nonce::State::Initialized(
|
||||
nonce::state::Data::default(),
|
||||
)),
|
||||
&nonce::state::Versions::new(
|
||||
nonce::State::Initialized(nonce::state::Data::default()),
|
||||
true, // separate_domains
|
||||
),
|
||||
&Pubkey::new_unique(),
|
||||
)
|
||||
.unwrap();
|
||||
|
@ -2088,7 +2100,8 @@ mod tests {
|
|||
#[test]
|
||||
fn test_nonce_initialize_with_empty_recent_blockhashes_fail() {
|
||||
let nonce_address = Pubkey::new_unique();
|
||||
let nonce_account = nonce_account::create_account(1_000_000).into_inner();
|
||||
let nonce_account =
|
||||
nonce_account::create_account(1_000_000, /*separate_domains:*/ true).into_inner();
|
||||
#[allow(deprecated)]
|
||||
let blockhash_id = sysvar::recent_blockhashes::id();
|
||||
#[allow(deprecated)]
|
||||
|
@ -2128,7 +2141,8 @@ mod tests {
|
|||
#[test]
|
||||
fn test_nonce_advance_with_empty_recent_blockhashes_fail() {
|
||||
let nonce_address = Pubkey::new_unique();
|
||||
let nonce_account = nonce_account::create_account(1_000_000).into_inner();
|
||||
let nonce_account =
|
||||
nonce_account::create_account(1_000_000, /*separate_domains:*/ true).into_inner();
|
||||
#[allow(deprecated)]
|
||||
let blockhash_id = sysvar::recent_blockhashes::id();
|
||||
let accounts = process_instruction(
|
||||
|
|
|
@ -110,7 +110,10 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_nonce_state_size() {
|
||||
let data = Versions::new_current(State::Initialized(Data::default()));
|
||||
let data = Versions::new(
|
||||
State::Initialized(Data::default()),
|
||||
true, // separate_domains
|
||||
);
|
||||
let size = bincode::serialized_size(&data).unwrap();
|
||||
assert_eq!(State::size() as u64, size);
|
||||
}
|
||||
|
|
|
@ -2,21 +2,178 @@
|
|||
|
||||
mod current;
|
||||
pub use current::{Data, DurableNonce, State};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use {
|
||||
crate::hash::Hash,
|
||||
serde_derive::{Deserialize, Serialize},
|
||||
};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||
pub enum Versions {
|
||||
Legacy(Box<State>),
|
||||
/// Current variants have durable nonce and blockhash domains separated.
|
||||
Current(Box<State>),
|
||||
}
|
||||
|
||||
impl Versions {
|
||||
pub fn new_current(state: State) -> Self {
|
||||
pub fn new(state: State, separate_domains: bool) -> Self {
|
||||
if separate_domains {
|
||||
Self::Current(Box::new(state))
|
||||
} else {
|
||||
Self::Legacy(Box::new(state))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert_to_current(self) -> State {
|
||||
pub fn state(&self) -> &State {
|
||||
match self {
|
||||
Self::Current(state) => *state,
|
||||
Self::Legacy(state) => state,
|
||||
Self::Current(state) => state,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the durable nonce is not in the blockhash domain.
|
||||
pub fn separate_domains(&self) -> bool {
|
||||
match self {
|
||||
Self::Legacy(_) => false,
|
||||
Self::Current(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the recent_blockhash field in Transaction verifies, and
|
||||
/// returns nonce account data if so.
|
||||
pub fn verify_recent_blockhash(
|
||||
&self,
|
||||
recent_blockhash: &Hash, // Transaction.message.recent_blockhash
|
||||
separate_domains: bool,
|
||||
) -> Option<&Data> {
|
||||
let state = match self {
|
||||
Self::Legacy(state) => {
|
||||
if separate_domains {
|
||||
// Legacy durable nonces are invalid and should not
|
||||
// allow durable transactions.
|
||||
return None;
|
||||
} else {
|
||||
state
|
||||
}
|
||||
}
|
||||
Self::Current(state) => state,
|
||||
};
|
||||
match **state {
|
||||
State::Uninitialized => None,
|
||||
State::Initialized(ref data) => (recent_blockhash == &data.blockhash()).then(|| data),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Versions> for State {
|
||||
fn from(versions: Versions) -> Self {
|
||||
match versions {
|
||||
Versions::Legacy(state) => *state,
|
||||
Versions::Current(state) => *state,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use {
|
||||
super::*,
|
||||
crate::{fee_calculator::FeeCalculator, pubkey::Pubkey},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_verify_recent_blockhash() {
|
||||
let blockhash = Hash::from([171; 32]);
|
||||
let versions = Versions::Legacy(Box::new(State::Uninitialized));
|
||||
for separate_domains in [false, true] {
|
||||
assert_eq!(
|
||||
versions.verify_recent_blockhash(&blockhash, separate_domains),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
versions.verify_recent_blockhash(&Hash::default(), separate_domains),
|
||||
None
|
||||
);
|
||||
}
|
||||
let versions = Versions::Current(Box::new(State::Uninitialized));
|
||||
for separate_domains in [false, true] {
|
||||
assert_eq!(
|
||||
versions.verify_recent_blockhash(&blockhash, separate_domains),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
versions.verify_recent_blockhash(&Hash::default(), separate_domains),
|
||||
None
|
||||
);
|
||||
}
|
||||
let durable_nonce =
|
||||
DurableNonce::from_blockhash(&blockhash, /*separate_domains:*/ false);
|
||||
let data = Data {
|
||||
authority: Pubkey::new_unique(),
|
||||
durable_nonce,
|
||||
fee_calculator: FeeCalculator {
|
||||
lamports_per_signature: 2718,
|
||||
},
|
||||
};
|
||||
let versions = Versions::Legacy(Box::new(State::Initialized(data.clone())));
|
||||
let separate_domains = false;
|
||||
assert_eq!(
|
||||
versions.verify_recent_blockhash(&Hash::default(), separate_domains),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
versions.verify_recent_blockhash(&blockhash, separate_domains),
|
||||
Some(&data)
|
||||
);
|
||||
assert_eq!(
|
||||
versions.verify_recent_blockhash(&data.blockhash(), separate_domains),
|
||||
Some(&data)
|
||||
);
|
||||
assert_eq!(
|
||||
versions.verify_recent_blockhash(durable_nonce.as_hash(), separate_domains),
|
||||
Some(&data)
|
||||
);
|
||||
let separate_domains = true;
|
||||
assert_eq!(
|
||||
versions.verify_recent_blockhash(&Hash::default(), separate_domains),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
versions.verify_recent_blockhash(&blockhash, separate_domains),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
versions.verify_recent_blockhash(&data.blockhash(), separate_domains),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
versions.verify_recent_blockhash(durable_nonce.as_hash(), separate_domains),
|
||||
None
|
||||
);
|
||||
let durable_nonce =
|
||||
DurableNonce::from_blockhash(&blockhash, /*separate_domains:*/ true);
|
||||
assert_ne!(data.durable_nonce, durable_nonce);
|
||||
let data = Data {
|
||||
durable_nonce,
|
||||
..data
|
||||
};
|
||||
let versions = Versions::Current(Box::new(State::Initialized(data.clone())));
|
||||
for separate_domains in [false, true] {
|
||||
assert_eq!(
|
||||
versions.verify_recent_blockhash(&blockhash, separate_domains),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
versions.verify_recent_blockhash(&Hash::default(), separate_domains),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
versions.verify_recent_blockhash(&data.blockhash(), separate_domains),
|
||||
Some(&data)
|
||||
);
|
||||
assert_eq!(
|
||||
versions.verify_recent_blockhash(durable_nonce.as_hash(), separate_domains),
|
||||
Some(&data)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,11 +11,11 @@ use {
|
|||
std::cell::RefCell,
|
||||
};
|
||||
|
||||
pub fn create_account(lamports: u64) -> RefCell<AccountSharedData> {
|
||||
pub fn create_account(lamports: u64, separate_domains: bool) -> RefCell<AccountSharedData> {
|
||||
RefCell::new(
|
||||
AccountSharedData::new_data_with_space(
|
||||
lamports,
|
||||
&Versions::new_current(State::Uninitialized),
|
||||
&Versions::new(State::Uninitialized, separate_domains),
|
||||
State::size(),
|
||||
&crate::system_program::id(),
|
||||
)
|
||||
|
@ -23,30 +23,41 @@ pub fn create_account(lamports: u64) -> RefCell<AccountSharedData> {
|
|||
)
|
||||
}
|
||||
|
||||
// TODO: Consider changing argument from Hash to DurableNonce.
|
||||
pub fn verify_nonce_account(acc: &AccountSharedData, hash: &Hash) -> Option<Data> {
|
||||
if acc.owner() != &crate::system_program::id() {
|
||||
return None;
|
||||
}
|
||||
match StateMut::<Versions>::state(acc).map(|v| v.convert_to_current()) {
|
||||
Ok(State::Initialized(data)) => (hash == &data.blockhash()).then(|| data),
|
||||
_ => None,
|
||||
}
|
||||
/// Checks if the recent_blockhash field in Transaction verifies, and returns
|
||||
/// nonce account data if so.
|
||||
pub fn verify_nonce_account(
|
||||
account: &AccountSharedData,
|
||||
recent_blockhash: &Hash, // Transaction.message.recent_blockhash
|
||||
separate_domains: bool,
|
||||
) -> Option<Data> {
|
||||
(account.owner() == &crate::system_program::id())
|
||||
.then(|| {
|
||||
StateMut::<Versions>::state(account)
|
||||
.ok()?
|
||||
.verify_recent_blockhash(recent_blockhash, separate_domains)
|
||||
.cloned()
|
||||
})
|
||||
.flatten()
|
||||
}
|
||||
|
||||
pub fn lamports_per_signature_of(account: &AccountSharedData) -> Option<u64> {
|
||||
let state = StateMut::<Versions>::state(account)
|
||||
.ok()?
|
||||
.convert_to_current();
|
||||
match state {
|
||||
match StateMut::<Versions>::state(account).ok()?.state() {
|
||||
State::Initialized(data) => Some(data.fee_calculator.lamports_per_signature),
|
||||
_ => None,
|
||||
State::Uninitialized => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use {super::*, crate::pubkey::Pubkey};
|
||||
use {
|
||||
super::*,
|
||||
crate::{
|
||||
fee_calculator::FeeCalculator,
|
||||
nonce::state::{Data, DurableNonce},
|
||||
pubkey::Pubkey,
|
||||
system_program,
|
||||
},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_verify_bad_account_owner_fails() {
|
||||
|
@ -54,11 +65,126 @@ mod tests {
|
|||
assert_ne!(program_id, crate::system_program::id());
|
||||
let account = AccountSharedData::new_data_with_space(
|
||||
42,
|
||||
&Versions::new_current(State::Uninitialized),
|
||||
&Versions::new(State::Uninitialized, /*separate_domains:*/ true),
|
||||
State::size(),
|
||||
&program_id,
|
||||
)
|
||||
.expect("nonce_account");
|
||||
assert!(verify_nonce_account(&account, &Hash::default()).is_none());
|
||||
for separate_domains in [false, true] {
|
||||
assert_eq!(
|
||||
verify_nonce_account(&account, &Hash::default(), separate_domains),
|
||||
None
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn new_nonce_account(versions: Versions) -> AccountSharedData {
|
||||
AccountSharedData::new_data(
|
||||
1_000_000, // lamports
|
||||
&versions, // state
|
||||
&system_program::id(), // owner
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_nonce_account() {
|
||||
let blockhash = Hash::from([171; 32]);
|
||||
let versions = Versions::Legacy(Box::new(State::Uninitialized));
|
||||
let account = new_nonce_account(versions);
|
||||
for separate_domains in [false, true] {
|
||||
assert_eq!(
|
||||
verify_nonce_account(&account, &blockhash, separate_domains),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
verify_nonce_account(&account, &Hash::default(), separate_domains),
|
||||
None
|
||||
);
|
||||
}
|
||||
let versions = Versions::Current(Box::new(State::Uninitialized));
|
||||
let account = new_nonce_account(versions);
|
||||
for separate_domains in [false, true] {
|
||||
assert_eq!(
|
||||
verify_nonce_account(&account, &blockhash, separate_domains),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
verify_nonce_account(&account, &Hash::default(), separate_domains),
|
||||
None
|
||||
);
|
||||
}
|
||||
let durable_nonce =
|
||||
DurableNonce::from_blockhash(&blockhash, /*separate_domains:*/ false);
|
||||
let data = Data {
|
||||
authority: Pubkey::new_unique(),
|
||||
durable_nonce,
|
||||
fee_calculator: FeeCalculator {
|
||||
lamports_per_signature: 2718,
|
||||
},
|
||||
};
|
||||
let versions = Versions::Legacy(Box::new(State::Initialized(data.clone())));
|
||||
let account = new_nonce_account(versions);
|
||||
let separate_domains = false;
|
||||
assert_eq!(
|
||||
verify_nonce_account(&account, &blockhash, separate_domains),
|
||||
Some(data.clone())
|
||||
);
|
||||
assert_eq!(
|
||||
verify_nonce_account(&account, &Hash::default(), separate_domains),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
verify_nonce_account(&account, &data.blockhash(), separate_domains),
|
||||
Some(data.clone())
|
||||
);
|
||||
assert_eq!(
|
||||
verify_nonce_account(&account, durable_nonce.as_hash(), separate_domains),
|
||||
Some(data.clone())
|
||||
);
|
||||
let separate_domains = true;
|
||||
assert_eq!(
|
||||
verify_nonce_account(&account, &blockhash, separate_domains),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
verify_nonce_account(&account, &Hash::default(), separate_domains),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
verify_nonce_account(&account, &data.blockhash(), separate_domains),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
verify_nonce_account(&account, durable_nonce.as_hash(), separate_domains),
|
||||
None
|
||||
);
|
||||
let durable_nonce =
|
||||
DurableNonce::from_blockhash(&blockhash, /*separate_domains:*/ true);
|
||||
assert_ne!(data.durable_nonce, durable_nonce);
|
||||
let data = Data {
|
||||
durable_nonce,
|
||||
..data
|
||||
};
|
||||
let versions = Versions::Current(Box::new(State::Initialized(data.clone())));
|
||||
let account = new_nonce_account(versions);
|
||||
for separate_domains in [false, true] {
|
||||
assert_eq!(
|
||||
verify_nonce_account(&account, &blockhash, separate_domains),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
verify_nonce_account(&account, &Hash::default(), separate_domains),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
verify_nonce_account(&account, &data.blockhash(), separate_domains),
|
||||
Some(data.clone())
|
||||
);
|
||||
assert_eq!(
|
||||
verify_nonce_account(&account, durable_nonce.as_hash(), separate_domains),
|
||||
Some(data.clone())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -605,10 +605,12 @@ impl SendTransactionService {
|
|||
.last_sent_time
|
||||
.map(|last| now.duration_since(last) >= retry_rate)
|
||||
.unwrap_or(false);
|
||||
if nonce_account::verify_nonce_account(&nonce_account, &durable_nonce).is_none()
|
||||
&& signature_status.is_none()
|
||||
&& expired
|
||||
{
|
||||
let verify_nonce_account = nonce_account::verify_nonce_account(
|
||||
&nonce_account,
|
||||
&durable_nonce,
|
||||
working_bank.separate_nonce_from_blockhash(),
|
||||
);
|
||||
if verify_nonce_account.is_none() && signature_status.is_none() && expired {
|
||||
info!("Dropping expired durable-nonce transaction: {}", signature);
|
||||
result.expired += 1;
|
||||
stats.expired_transactions.fetch_add(1, Ordering::Relaxed);
|
||||
|
@ -1094,9 +1096,14 @@ mod test {
|
|||
let nonce_address = Pubkey::new_unique();
|
||||
let durable_nonce =
|
||||
DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true);
|
||||
let nonce_state = nonce::state::Versions::new_current(nonce::State::Initialized(
|
||||
nonce::state::Data::new(Pubkey::default(), durable_nonce, 42),
|
||||
));
|
||||
let nonce_state = nonce::state::Versions::new(
|
||||
nonce::State::Initialized(nonce::state::Data::new(
|
||||
Pubkey::default(),
|
||||
durable_nonce,
|
||||
42,
|
||||
)),
|
||||
true, // separate_domains
|
||||
);
|
||||
let nonce_account =
|
||||
AccountSharedData::new_data(43, &nonce_state, &system_program::id()).unwrap();
|
||||
root_bank.store_account(&nonce_address, &nonce_account);
|
||||
|
@ -1346,9 +1353,14 @@ mod test {
|
|||
}
|
||||
let new_durable_nonce =
|
||||
DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true);
|
||||
let new_nonce_state = nonce::state::Versions::new_current(nonce::State::Initialized(
|
||||
nonce::state::Data::new(Pubkey::default(), new_durable_nonce, 42),
|
||||
));
|
||||
let new_nonce_state = nonce::state::Versions::new(
|
||||
nonce::State::Initialized(nonce::state::Data::new(
|
||||
Pubkey::default(),
|
||||
new_durable_nonce,
|
||||
42,
|
||||
)),
|
||||
true, // separate_domains
|
||||
);
|
||||
let nonce_account =
|
||||
AccountSharedData::new_data(43, &new_nonce_state, &system_program::id()).unwrap();
|
||||
working_bank.store_account(&nonce_address, &nonce_account);
|
||||
|
|
Loading…
Reference in New Issue