Skip to content

Commit

Permalink
permanently disables durable transactions with chain blockhash domain
Browse files Browse the repository at this point in the history
#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 blockhash domain, and permanently disables durable transactions
for legacy nonces which are in blockhash domain.
  • Loading branch information
behzadnouri committed Jun 5, 2022
1 parent 73b7db4 commit 97b316b
Show file tree
Hide file tree
Showing 15 changed files with 369 additions and 285 deletions.
5 changes: 4 additions & 1 deletion account-decoder/src/parse_account_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
10 changes: 6 additions & 4 deletions account-decoder/src/parse_nonce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(),
Expand Down
56 changes: 35 additions & 21 deletions cli/src/nonce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand All @@ -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(
nonce_pubkey,
invalid_durable_nonce,
0,
)));
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()
Expand All @@ -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(
new_nonce_authority,
durable_nonce,
0,
)));
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()
Expand All @@ -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()
Expand All @@ -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());
Expand All @@ -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),
Expand All @@ -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),
Expand All @@ -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));
Expand Down
5 changes: 4 additions & 1 deletion client/src/blockhash_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
)
Expand Down
5 changes: 2 additions & 3 deletions client/src/nonce_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
13 changes: 6 additions & 7 deletions rpc/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5549,11 +5549,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();
Expand Down Expand Up @@ -5584,8 +5583,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(),
},
}]},
])),
Expand Down
6 changes: 4 additions & 2 deletions rpc/src/transaction_status_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
Loading

0 comments on commit 97b316b

Please sign in to comment.