瀏覽代碼

separates durable nonce and blockhash domains

AdvanceNonceAccount instruction updates nonce to blockhash. This makes it
possible that a durable transaction is executed twice both as a normal
transaction and a nonce transaction if it uses blockhash (as opposed to nonce)
for its recent_blockhash field.

The commit prevents this double execution by separating nonce and blockhash
domains; when advancing nonce account, blockhash is hashed with a fixed string.
As a result a blockhash cannot be a valid nonce value; and if transaction was
once executed as a normal transaction it cannot be re-executed as a durable
transaction again and vice-versa.
behzad nouri 3 年之前
父節點
當前提交
5ee157f43d

+ 1 - 1
account-decoder/src/parse_nonce.rs

@@ -20,7 +20,7 @@ pub fn parse_nonce(data: &[u8]) -> Result<UiNonceState, ParseAccountError> {
         )),
         )),
         State::Initialized(data) => Ok(UiNonceState::Initialized(UiNonceData {
         State::Initialized(data) => Ok(UiNonceState::Initialized(UiNonceData {
             authority: data.authority.to_string(),
             authority: data.authority.to_string(),
-            blockhash: data.blockhash.to_string(),
+            blockhash: data.blockhash().to_string(),
             fee_calculator: data.fee_calculator.into(),
             fee_calculator: data.fee_calculator.into(),
         })),
         })),
     }
     }

+ 24 - 12
cli/src/nonce.rs

@@ -333,10 +333,10 @@ pub fn check_nonce_account(
 ) -> Result<(), CliError> {
 ) -> Result<(), CliError> {
     match state_from_account(nonce_account)? {
     match state_from_account(nonce_account)? {
         State::Initialized(ref data) => {
         State::Initialized(ref data) => {
-            if &data.blockhash != nonce_hash {
+            if &data.blockhash() != nonce_hash {
                 Err(Error::InvalidHash {
                 Err(Error::InvalidHash {
                     provided: *nonce_hash,
                     provided: *nonce_hash,
-                    expected: data.blockhash,
+                    expected: data.blockhash(),
                 }
                 }
                 .into())
                 .into())
             } else if nonce_authority != &data.authority {
             } else if nonce_authority != &data.authority {
@@ -524,7 +524,7 @@ pub fn process_get_nonce(
         .and_then(|ref a| state_from_account(a))?
         .and_then(|ref a| state_from_account(a))?
     {
     {
         State::Uninitialized => Ok("Nonce account is uninitialized".to_string()),
         State::Uninitialized => Ok("Nonce account is uninitialized".to_string()),
-        State::Initialized(ref data) => Ok(format!("{:?}", data.blockhash)),
+        State::Initialized(ref data) => Ok(format!("{:?}", data.blockhash())),
     }
     }
 }
 }
 
 
@@ -598,7 +598,7 @@ pub fn process_show_nonce_account(
             ..CliNonceAccount::default()
             ..CliNonceAccount::default()
         };
         };
         if let Some(data) = data {
         if let Some(data) = data {
-            nonce_account.nonce = Some(data.blockhash.to_string());
+            nonce_account.nonce = Some(data.blockhash().to_string());
             nonce_account.lamports_per_signature = Some(data.fee_calculator.lamports_per_signature);
             nonce_account.lamports_per_signature = Some(data.fee_calculator.lamports_per_signature);
             nonce_account.authority = Some(data.authority.to_string());
             nonce_account.authority = Some(data.authority.to_string());
         }
         }
@@ -665,7 +665,11 @@ mod tests {
             account::Account,
             account::Account,
             account_utils::StateMut,
             account_utils::StateMut,
             hash::hash,
             hash::hash,
-            nonce::{self, state::Versions, State},
+            nonce::{
+                self,
+                state::{DurableNonce, Versions},
+                State,
+            },
             nonce_account,
             nonce_account,
             signature::{read_keypair_file, write_keypair, Keypair, Signer},
             signature::{read_keypair_file, write_keypair, Keypair, Signer},
             system_program,
             system_program,
@@ -925,11 +929,13 @@ mod tests {
 
 
     #[test]
     #[test]
     fn test_check_nonce_account() {
     fn test_check_nonce_account() {
-        let blockhash = Hash::default();
+        let durable_nonce =
+            DurableNonce::from_blockhash(&Hash::default(), /*separate_domains:*/ true);
+        let blockhash = *durable_nonce.as_hash();
         let nonce_pubkey = solana_sdk::pubkey::new_rand();
         let nonce_pubkey = solana_sdk::pubkey::new_rand();
         let data = Versions::new_current(State::Initialized(nonce::state::Data::new(
         let data = Versions::new_current(State::Initialized(nonce::state::Data::new(
             nonce_pubkey,
             nonce_pubkey,
-            blockhash,
+            durable_nonce,
             0,
             0,
         )));
         )));
         let valid = Account::new_data(1, &data, &system_program::ID);
         let valid = Account::new_data(1, &data, &system_program::ID);
@@ -949,9 +955,11 @@ mod tests {
             assert_eq!(err, Error::InvalidAccountData,);
             assert_eq!(err, Error::InvalidAccountData,);
         }
         }
 
 
+        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_current(State::Initialized(nonce::state::Data::new(
             nonce_pubkey,
             nonce_pubkey,
-            hash(b"invalid"),
+            invalid_durable_nonce,
             0,
             0,
         )));
         )));
         let invalid_hash = Account::new_data(1, &data, &system_program::ID).unwrap();
         let invalid_hash = Account::new_data(1, &data, &system_program::ID).unwrap();
@@ -962,7 +970,7 @@ mod tests {
                 err,
                 err,
                 Error::InvalidHash {
                 Error::InvalidHash {
                     provided: blockhash,
                     provided: blockhash,
-                    expected: hash(b"invalid"),
+                    expected: *invalid_durable_nonce.as_hash(),
                 }
                 }
             );
             );
         }
         }
@@ -970,7 +978,7 @@ mod tests {
         let new_nonce_authority = solana_sdk::pubkey::new_rand();
         let new_nonce_authority = solana_sdk::pubkey::new_rand();
         let data = Versions::new_current(State::Initialized(nonce::state::Data::new(
         let data = Versions::new_current(State::Initialized(nonce::state::Data::new(
             new_nonce_authority,
             new_nonce_authority,
-            blockhash,
+            durable_nonce,
             0,
             0,
         )));
         )));
         let invalid_authority = Account::new_data(1, &data, &system_program::ID);
         let invalid_authority = Account::new_data(1, &data, &system_program::ID);
@@ -1019,7 +1027,9 @@ mod tests {
         let mut nonce_account = nonce_account::create_account(1).into_inner();
         let mut nonce_account = nonce_account::create_account(1).into_inner();
         assert_eq!(state_from_account(&nonce_account), Ok(State::Uninitialized));
         assert_eq!(state_from_account(&nonce_account), Ok(State::Uninitialized));
 
 
-        let data = nonce::state::Data::new(Pubkey::new(&[1u8; 32]), Hash::new(&[42u8; 32]), 42);
+        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
         nonce_account
             .set_state(&Versions::new_current(State::Initialized(data.clone())))
             .set_state(&Versions::new_current(State::Initialized(data.clone())))
             .unwrap();
             .unwrap();
@@ -1048,7 +1058,9 @@ mod tests {
             Err(Error::InvalidStateForOperation)
             Err(Error::InvalidStateForOperation)
         );
         );
 
 
-        let data = nonce::state::Data::new(Pubkey::new(&[1u8; 32]), Hash::new(&[42u8; 32]), 42);
+        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
         nonce_account
             .set_state(&Versions::new_current(State::Initialized(data.clone())))
             .set_state(&Versions::new_current(State::Initialized(data.clone())))
             .unwrap();
             .unwrap();

+ 1 - 1
cli/tests/nonce.rs

@@ -325,7 +325,7 @@ fn test_create_account_with_seed() {
     )
     )
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .unwrap()
     .unwrap()
-    .blockhash;
+    .blockhash();
 
 
     // Test by creating transfer TX with nonce, fully offline
     // Test by creating transfer TX with nonce, fully offline
     let mut authority_config = CliConfig::recent_for_tests();
     let mut authority_config = CliConfig::recent_for_tests();

+ 9 - 9
cli/tests/stake.rs

@@ -539,7 +539,7 @@ fn test_nonced_stake_delegation_and_deactivation() {
     )
     )
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .unwrap()
     .unwrap()
-    .blockhash;
+    .blockhash();
 
 
     // Delegate stake
     // Delegate stake
     config.signers = vec![&config_keypair];
     config.signers = vec![&config_keypair];
@@ -569,7 +569,7 @@ fn test_nonced_stake_delegation_and_deactivation() {
     )
     )
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .unwrap()
     .unwrap()
-    .blockhash;
+    .blockhash();
 
 
     // Deactivate stake
     // Deactivate stake
     config.command = CliCommand::DeactivateStake {
     config.command = CliCommand::DeactivateStake {
@@ -838,7 +838,7 @@ fn test_stake_authorize() {
     )
     )
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .unwrap()
     .unwrap()
-    .blockhash;
+    .blockhash();
 
 
     // Nonced assignment of new online stake authority
     // Nonced assignment of new online stake authority
     let online_authority = Keypair::new();
     let online_authority = Keypair::new();
@@ -906,7 +906,7 @@ fn test_stake_authorize() {
     )
     )
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .unwrap()
     .unwrap()
-    .blockhash;
+    .blockhash();
     assert_ne!(nonce_hash, new_nonce_hash);
     assert_ne!(nonce_hash, new_nonce_hash);
 }
 }
 
 
@@ -1188,7 +1188,7 @@ fn test_stake_split() {
     )
     )
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .unwrap()
     .unwrap()
-    .blockhash;
+    .blockhash();
 
 
     // Nonced offline split
     // Nonced offline split
     let split_account = keypair_from_seed(&[2u8; 32]).unwrap();
     let split_account = keypair_from_seed(&[2u8; 32]).unwrap();
@@ -1458,7 +1458,7 @@ fn test_stake_set_lockup() {
     )
     )
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .unwrap()
     .unwrap()
-    .blockhash;
+    .blockhash();
 
 
     // Nonced offline set lockup
     // Nonced offline set lockup
     let lockup = LockupArgs {
     let lockup = LockupArgs {
@@ -1584,7 +1584,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
     )
     )
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .unwrap()
     .unwrap()
-    .blockhash;
+    .blockhash();
 
 
     // Create stake account offline
     // Create stake account offline
     let stake_keypair = keypair_from_seed(&[4u8; 32]).unwrap();
     let stake_keypair = keypair_from_seed(&[4u8; 32]).unwrap();
@@ -1645,7 +1645,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
     )
     )
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .unwrap()
     .unwrap()
-    .blockhash;
+    .blockhash();
 
 
     // Offline, nonced stake-withdraw
     // Offline, nonced stake-withdraw
     let recipient = keypair_from_seed(&[5u8; 32]).unwrap();
     let recipient = keypair_from_seed(&[5u8; 32]).unwrap();
@@ -1699,7 +1699,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
     )
     )
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .unwrap()
     .unwrap()
-    .blockhash;
+    .blockhash();
 
 
     // Create another stake account. This time with seed
     // Create another stake account. This time with seed
     let seed = "seedy";
     let seed = "seedy";

+ 3 - 3
cli/tests/transfer.rs

@@ -200,7 +200,7 @@ fn test_transfer() {
     )
     )
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .unwrap()
     .unwrap()
-    .blockhash;
+    .blockhash();
 
 
     // Nonced transfer
     // Nonced transfer
     config.signers = vec![&default_signer];
     config.signers = vec![&default_signer];
@@ -237,7 +237,7 @@ fn test_transfer() {
     )
     )
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .unwrap()
     .unwrap()
-    .blockhash;
+    .blockhash();
     assert_ne!(nonce_hash, new_nonce_hash);
     assert_ne!(nonce_hash, new_nonce_hash);
 
 
     // Assign nonce authority to offline
     // Assign nonce authority to offline
@@ -263,7 +263,7 @@ fn test_transfer() {
     )
     )
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .and_then(|ref a| nonce_utils::data_from_account(a))
     .unwrap()
     .unwrap()
-    .blockhash;
+    .blockhash();
 
 
     // Offline, nonced transfer
     // Offline, nonced transfer
     offline.signers = vec![&default_offline_signer];
     offline.signers = vec![&default_offline_signer];

+ 13 - 6
client/src/blockhash_query.rs

@@ -37,7 +37,7 @@ impl Source {
                 #[allow(clippy::redundant_closure)]
                 #[allow(clippy::redundant_closure)]
                 let data = nonce_utils::get_account_with_commitment(rpc_client, pubkey, commitment)
                 let data = nonce_utils::get_account_with_commitment(rpc_client, pubkey, commitment)
                     .and_then(|ref a| nonce_utils::data_from_account(a))?;
                     .and_then(|ref a| nonce_utils::data_from_account(a))?;
-                Ok((data.blockhash, data.fee_calculator))
+                Ok((data.blockhash(), data.fee_calculator))
             }
             }
         }
         }
     }
     }
@@ -64,7 +64,7 @@ impl Source {
                 let res = nonce_utils::get_account_with_commitment(rpc_client, pubkey, commitment)?;
                 let res = nonce_utils::get_account_with_commitment(rpc_client, pubkey, commitment)?;
                 let res = nonce_utils::data_from_account(&res)?;
                 let res = nonce_utils::data_from_account(&res)?;
                 Ok(Some(res)
                 Ok(Some(res)
-                    .filter(|d| d.blockhash == *blockhash)
+                    .filter(|d| d.blockhash() == *blockhash)
                     .map(|d| d.fee_calculator))
                     .map(|d| d.fee_calculator))
             }
             }
         }
         }
@@ -84,7 +84,7 @@ impl Source {
                 #[allow(clippy::redundant_closure)]
                 #[allow(clippy::redundant_closure)]
                 let data = nonce_utils::get_account_with_commitment(rpc_client, pubkey, commitment)
                 let data = nonce_utils::get_account_with_commitment(rpc_client, pubkey, commitment)
                     .and_then(|ref a| nonce_utils::data_from_account(a))?;
                     .and_then(|ref a| nonce_utils::data_from_account(a))?;
-                Ok(data.blockhash)
+                Ok(data.blockhash())
             }
             }
         }
         }
     }
     }
@@ -193,7 +193,12 @@ mod tests {
         clap::App,
         clap::App,
         serde_json::{self, json},
         serde_json::{self, json},
         solana_account_decoder::{UiAccount, UiAccountEncoding},
         solana_account_decoder::{UiAccount, UiAccountEncoding},
-        solana_sdk::{account::Account, hash::hash, nonce, system_program},
+        solana_sdk::{
+            account::Account,
+            hash::hash,
+            nonce::{self, state::DurableNonce},
+            system_program,
+        },
         std::collections::HashMap,
         std::collections::HashMap,
     };
     };
 
 
@@ -411,11 +416,13 @@ mod tests {
             .get_blockhash_and_fee_calculator(&rpc_client, CommitmentConfig::default())
             .get_blockhash_and_fee_calculator(&rpc_client, CommitmentConfig::default())
             .is_err());
             .is_err());
 
 
-        let nonce_blockhash = Hash::new(&[2u8; 32]);
+        let durable_nonce =
+            DurableNonce::from_blockhash(&Hash::new(&[2u8; 32]), /*separate_domains:*/ true);
+        let nonce_blockhash = *durable_nonce.as_hash();
         let nonce_fee_calc = FeeCalculator::new(4242);
         let nonce_fee_calc = FeeCalculator::new(4242);
         let data = nonce::state::Data {
         let data = nonce::state::Data {
             authority: Pubkey::new(&[3u8; 32]),
             authority: Pubkey::new(&[3u8; 32]),
-            blockhash: nonce_blockhash,
+            durable_nonce,
             fee_calculator: nonce_fee_calc,
             fee_calculator: nonce_fee_calc,
         };
         };
         let nonce_account = Account::new_data_with_space(
         let nonce_account = Account::new_data_with_space(

+ 1 - 1
client/src/nonce_utils.rs

@@ -199,7 +199,7 @@ pub fn state_from_account<T: ReadableAccount + StateMut<Versions>>(
 ///     // network's latest blockhash.
 ///     // network's latest blockhash.
 ///     let nonce_account = client.get_account(nonce_account_pubkey)?;
 ///     let nonce_account = client.get_account(nonce_account_pubkey)?;
 ///     let nonce_data = nonce_utils::data_from_account(&nonce_account)?;
 ///     let nonce_data = nonce_utils::data_from_account(&nonce_account)?;
-///     let blockhash = nonce_data.blockhash;
+///     let blockhash = nonce_data.blockhash();
 ///
 ///
 ///     tx.try_sign(&[payer], blockhash)?;
 ///     tx.try_sign(&[payer], blockhash)?;
 ///
 ///

+ 3 - 2
rpc/src/rpc.rs

@@ -4570,7 +4570,8 @@ pub mod tests {
             hash::{hash, Hash},
             hash::{hash, Hash},
             instruction::InstructionError,
             instruction::InstructionError,
             message::{v0, v0::MessageAddressTableLookup, MessageHeader, VersionedMessage},
             message::{v0, v0::MessageAddressTableLookup, MessageHeader, VersionedMessage},
-            nonce, rpc_port,
+            nonce::{self, state::DurableNonce},
+            rpc_port,
             signature::{Keypair, Signer},
             signature::{Keypair, Signer},
             slot_hashes::SlotHashes,
             slot_hashes::SlotHashes,
             system_program, system_transaction,
             system_program, system_transaction,
@@ -5550,7 +5551,7 @@ pub mod tests {
                     42,
                     42,
                     &nonce::state::Versions::new_current(nonce::State::new_initialized(
                     &nonce::state::Versions::new_current(nonce::State::new_initialized(
                         &authority,
                         &authority,
-                        &Hash::default(),
+                        DurableNonce::default(),
                         1000,
                         1000,
                     )),
                     )),
                     &system_program::id(),
                     &system_program::id(),

+ 5 - 2
rpc/src/transaction_status_service.rs

@@ -223,7 +223,8 @@ pub(crate) mod tests {
             hash::Hash,
             hash::Hash,
             instruction::CompiledInstruction,
             instruction::CompiledInstruction,
             message::{Message, MessageHeader, SanitizedMessage},
             message::{Message, MessageHeader, SanitizedMessage},
-            nonce, nonce_account,
+            nonce::{self, state::DurableNonce},
+            nonce_account,
             pubkey::Pubkey,
             pubkey::Pubkey,
             signature::{Keypair, Signature, Signer},
             signature::{Keypair, Signature, Signer},
             system_transaction,
             system_transaction,
@@ -323,7 +324,9 @@ pub(crate) mod tests {
         let pubkey = Pubkey::new_unique();
         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).into_inner();
-        let data = nonce::state::Data::new(Pubkey::new(&[1u8; 32]), Hash::new(&[42u8; 32]), 42);
+        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
         nonce_account
             .set_state(&nonce::state::Versions::new_current(
             .set_state(&nonce::state::Versions::new_current(
                 nonce::State::Initialized(data),
                 nonce::State::Initialized(data),

+ 40 - 27
runtime/src/accounts.rs

@@ -40,7 +40,10 @@ use {
             SanitizedMessage,
             SanitizedMessage,
         },
         },
         native_loader,
         native_loader,
-        nonce::{state::Versions as NonceVersions, State as NonceState},
+        nonce::{
+            state::{DurableNonce, Versions as NonceVersions},
+            State as NonceState,
+        },
         pubkey::Pubkey,
         pubkey::Pubkey,
         slot_hashes::SlotHashes,
         slot_hashes::SlotHashes,
         system_program,
         system_program,
@@ -1193,7 +1196,7 @@ impl Accounts {
         res: &'a [TransactionExecutionResult],
         res: &'a [TransactionExecutionResult],
         loaded: &'a mut [TransactionLoadResult],
         loaded: &'a mut [TransactionLoadResult],
         rent_collector: &RentCollector,
         rent_collector: &RentCollector,
-        blockhash: &Hash,
+        durable_nonce: &DurableNonce,
         lamports_per_signature: u64,
         lamports_per_signature: u64,
         leave_nonce_on_success: bool,
         leave_nonce_on_success: bool,
     ) {
     ) {
@@ -1202,7 +1205,7 @@ impl Accounts {
             res,
             res,
             loaded,
             loaded,
             rent_collector,
             rent_collector,
-            blockhash,
+            durable_nonce,
             lamports_per_signature,
             lamports_per_signature,
             leave_nonce_on_success,
             leave_nonce_on_success,
         );
         );
@@ -1225,7 +1228,7 @@ impl Accounts {
         execution_results: &'a [TransactionExecutionResult],
         execution_results: &'a [TransactionExecutionResult],
         load_results: &'a mut [TransactionLoadResult],
         load_results: &'a mut [TransactionLoadResult],
         rent_collector: &RentCollector,
         rent_collector: &RentCollector,
-        blockhash: &Hash,
+        durable_nonce: &DurableNonce,
         lamports_per_signature: u64,
         lamports_per_signature: u64,
         leave_nonce_on_success: bool,
         leave_nonce_on_success: bool,
     ) -> Vec<(&'a Pubkey, &'a AccountSharedData)> {
     ) -> Vec<(&'a Pubkey, &'a AccountSharedData)> {
@@ -1279,7 +1282,7 @@ impl Accounts {
                         execution_status,
                         execution_status,
                         is_fee_payer,
                         is_fee_payer,
                         maybe_nonce,
                         maybe_nonce,
-                        blockhash,
+                        durable_nonce,
                         lamports_per_signature,
                         lamports_per_signature,
                     );
                     );
 
 
@@ -1306,13 +1309,13 @@ impl Accounts {
     }
     }
 }
 }
 
 
-pub fn prepare_if_nonce_account<'a>(
+fn prepare_if_nonce_account<'a>(
     address: &Pubkey,
     address: &Pubkey,
     account: &mut AccountSharedData,
     account: &mut AccountSharedData,
     execution_result: &Result<()>,
     execution_result: &Result<()>,
     is_fee_payer: bool,
     is_fee_payer: bool,
     maybe_nonce: Option<(&'a NonceFull, bool)>,
     maybe_nonce: Option<(&'a NonceFull, bool)>,
-    blockhash: &Hash,
+    durable_nonce: &DurableNonce,
     lamports_per_signature: u64,
     lamports_per_signature: u64,
 ) -> bool {
 ) -> bool {
     if let Some((nonce, rollback)) = maybe_nonce {
     if let Some((nonce, rollback)) = maybe_nonce {
@@ -1338,7 +1341,7 @@ pub fn prepare_if_nonce_account<'a>(
                 account
                 account
                     .set_state(&NonceVersions::new_current(NonceState::new_initialized(
                     .set_state(&NonceVersions::new_current(NonceState::new_initialized(
                         &data.authority,
                         &data.authority,
-                        blockhash,
+                        *durable_nonce,
                         lamports_per_signature,
                         lamports_per_signature,
                     )))
                     )))
                     .unwrap();
                     .unwrap();
@@ -3025,7 +3028,7 @@ mod tests {
             &execution_results,
             &execution_results,
             loaded.as_mut_slice(),
             loaded.as_mut_slice(),
             &rent_collector,
             &rent_collector,
-            &Hash::default(),
+            &DurableNonce::default(),
             0,
             0,
             true, // leave_nonce_on_success
             true, // leave_nonce_on_success
         );
         );
@@ -3171,7 +3174,7 @@ mod tests {
         Pubkey,
         Pubkey,
         AccountSharedData,
         AccountSharedData,
         AccountSharedData,
         AccountSharedData,
-        Hash,
+        DurableNonce,
         u64,
         u64,
         Option<AccountSharedData>,
         Option<AccountSharedData>,
     ) {
     ) {
@@ -3184,7 +3187,7 @@ mod tests {
             Pubkey::default(),
             Pubkey::default(),
             pre_account,
             pre_account,
             account,
             account,
-            Hash::new(&[1u8; 32]),
+            DurableNonce::from_blockhash(&Hash::new(&[1u8; 32]), /*separate_domains:*/ true),
             1234,
             1234,
             None,
             None,
         )
         )
@@ -3196,7 +3199,7 @@ mod tests {
         tx_result: &Result<()>,
         tx_result: &Result<()>,
         is_fee_payer: bool,
         is_fee_payer: bool,
         maybe_nonce: Option<(&NonceFull, bool)>,
         maybe_nonce: Option<(&NonceFull, bool)>,
-        blockhash: &Hash,
+        durable_nonce: &DurableNonce,
         lamports_per_signature: u64,
         lamports_per_signature: u64,
         expect_account: &AccountSharedData,
         expect_account: &AccountSharedData,
     ) -> bool {
     ) -> bool {
@@ -3216,7 +3219,7 @@ mod tests {
             tx_result,
             tx_result,
             is_fee_payer,
             is_fee_payer,
             maybe_nonce,
             maybe_nonce,
-            blockhash,
+            durable_nonce,
             lamports_per_signature,
             lamports_per_signature,
         );
         );
         assert_eq!(expect_account, account);
         assert_eq!(expect_account, account);
@@ -3370,7 +3373,7 @@ mod tests {
             )),
             )),
             false,
             false,
             Some((&nonce, true)),
             Some((&nonce, true)),
-            &Hash::default(),
+            &DurableNonce::default(),
             1,
             1,
             &post_fee_payer_account.clone(),
             &post_fee_payer_account.clone(),
         ));
         ));
@@ -3381,7 +3384,7 @@ mod tests {
             &Ok(()),
             &Ok(()),
             true,
             true,
             Some((&nonce, true)),
             Some((&nonce, true)),
-            &Hash::default(),
+            &DurableNonce::default(),
             1,
             1,
             &post_fee_payer_account.clone(),
             &post_fee_payer_account.clone(),
         ));
         ));
@@ -3395,7 +3398,7 @@ mod tests {
             )),
             )),
             true,
             true,
             None,
             None,
-            &Hash::default(),
+            &DurableNonce::default(),
             1,
             1,
             &post_fee_payer_account.clone(),
             &post_fee_payer_account.clone(),
         ));
         ));
@@ -3409,7 +3412,7 @@ mod tests {
             )),
             )),
             true,
             true,
             Some((&nonce, true)),
             Some((&nonce, true)),
-            &Hash::default(),
+            &DurableNonce::default(),
             1,
             1,
             &pre_fee_payer_account,
             &pre_fee_payer_account,
         ));
         ));
@@ -3424,8 +3427,10 @@ mod tests {
         let from = keypair_from_seed(&[1; 32]).unwrap();
         let from = keypair_from_seed(&[1; 32]).unwrap();
         let from_address = from.pubkey();
         let from_address = from.pubkey();
         let to_address = Pubkey::new_unique();
         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(
         let nonce_state = NonceVersions::new_current(NonceState::Initialized(
-            nonce::state::Data::new(nonce_authority.pubkey(), Hash::new_unique(), 0),
+            nonce::state::Data::new(nonce_authority.pubkey(), durable_nonce, 0),
         ));
         ));
         let nonce_account_post =
         let nonce_account_post =
             AccountSharedData::new_data(43, &nonce_state, &system_program::id()).unwrap();
             AccountSharedData::new_data(43, &nonce_state, &system_program::id()).unwrap();
@@ -3449,8 +3454,10 @@ mod tests {
         ];
         ];
         let tx = new_sanitized_tx(&[&nonce_authority, &from], message, blockhash);
         let tx = new_sanitized_tx(&[&nonce_authority, &from], message, blockhash);
 
 
+        let durable_nonce =
+            DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true);
         let nonce_state = NonceVersions::new_current(NonceState::Initialized(
         let nonce_state = NonceVersions::new_current(NonceState::Initialized(
-            nonce::state::Data::new(nonce_authority.pubkey(), Hash::new_unique(), 0),
+            nonce::state::Data::new(nonce_authority.pubkey(), durable_nonce, 0),
         ));
         ));
         let nonce_account_pre =
         let nonce_account_pre =
             AccountSharedData::new_data(42, &nonce_state, &system_program::id()).unwrap();
             AccountSharedData::new_data(42, &nonce_state, &system_program::id()).unwrap();
@@ -3474,7 +3481,8 @@ mod tests {
 
 
         let mut loaded = vec![loaded];
         let mut loaded = vec![loaded];
 
 
-        let next_blockhash = Hash::new_unique();
+        let durable_nonce =
+            DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true);
         let accounts = Accounts::new_with_config_for_tests(
         let accounts = Accounts::new_with_config_for_tests(
             Vec::new(),
             Vec::new(),
             &ClusterType::Development,
             &ClusterType::Development,
@@ -3495,7 +3503,7 @@ mod tests {
             &execution_results,
             &execution_results,
             loaded.as_mut_slice(),
             loaded.as_mut_slice(),
             &rent_collector,
             &rent_collector,
-            &next_blockhash,
+            &durable_nonce,
             0,
             0,
             true, // leave_nonce_on_success
             true, // leave_nonce_on_success
         );
         );
@@ -3521,7 +3529,7 @@ mod tests {
         );
         );
         assert!(nonce_account::verify_nonce_account(
         assert!(nonce_account::verify_nonce_account(
             &collected_nonce_account,
             &collected_nonce_account,
-            &next_blockhash
+            durable_nonce.as_hash(),
         ));
         ));
     }
     }
 
 
@@ -3534,8 +3542,10 @@ mod tests {
         let from = keypair_from_seed(&[1; 32]).unwrap();
         let from = keypair_from_seed(&[1; 32]).unwrap();
         let from_address = from.pubkey();
         let from_address = from.pubkey();
         let to_address = Pubkey::new_unique();
         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(
         let nonce_state = NonceVersions::new_current(NonceState::Initialized(
-            nonce::state::Data::new(nonce_authority.pubkey(), Hash::new_unique(), 0),
+            nonce::state::Data::new(nonce_authority.pubkey(), durable_nonce, 0),
         ));
         ));
         let nonce_account_post =
         let nonce_account_post =
             AccountSharedData::new_data(43, &nonce_state, &system_program::id()).unwrap();
             AccountSharedData::new_data(43, &nonce_state, &system_program::id()).unwrap();
@@ -3559,8 +3569,10 @@ mod tests {
         ];
         ];
         let tx = new_sanitized_tx(&[&nonce_authority, &from], message, blockhash);
         let tx = new_sanitized_tx(&[&nonce_authority, &from], message, blockhash);
 
 
+        let durable_nonce =
+            DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true);
         let nonce_state = NonceVersions::new_current(NonceState::Initialized(
         let nonce_state = NonceVersions::new_current(NonceState::Initialized(
-            nonce::state::Data::new(nonce_authority.pubkey(), Hash::new_unique(), 0),
+            nonce::state::Data::new(nonce_authority.pubkey(), durable_nonce, 0),
         ));
         ));
         let nonce_account_pre =
         let nonce_account_pre =
             AccountSharedData::new_data(42, &nonce_state, &system_program::id()).unwrap();
             AccountSharedData::new_data(42, &nonce_state, &system_program::id()).unwrap();
@@ -3583,7 +3595,8 @@ mod tests {
 
 
         let mut loaded = vec![loaded];
         let mut loaded = vec![loaded];
 
 
-        let next_blockhash = Hash::new_unique();
+        let durable_nonce =
+            DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true);
         let accounts = Accounts::new_with_config_for_tests(
         let accounts = Accounts::new_with_config_for_tests(
             Vec::new(),
             Vec::new(),
             &ClusterType::Development,
             &ClusterType::Development,
@@ -3604,7 +3617,7 @@ mod tests {
             &execution_results,
             &execution_results,
             loaded.as_mut_slice(),
             loaded.as_mut_slice(),
             &rent_collector,
             &rent_collector,
-            &next_blockhash,
+            &durable_nonce,
             0,
             0,
             true, // leave_nonce_on_success
             true, // leave_nonce_on_success
         );
         );
@@ -3621,7 +3634,7 @@ mod tests {
         );
         );
         assert!(nonce_account::verify_nonce_account(
         assert!(nonce_account::verify_nonce_account(
             &collected_nonce_account,
             &collected_nonce_account,
-            &next_blockhash
+            durable_nonce.as_hash(),
         ));
         ));
     }
     }
 
 

+ 22 - 17
runtime/src/bank.rs

@@ -124,7 +124,8 @@ use {
         message::{AccountKeys, SanitizedMessage},
         message::{AccountKeys, SanitizedMessage},
         native_loader,
         native_loader,
         native_token::sol_to_lamports,
         native_token::sol_to_lamports,
-        nonce, nonce_account,
+        nonce::{self, state::DurableNonce},
+        nonce_account,
         packet::PACKET_DATA_SIZE,
         packet::PACKET_DATA_SIZE,
         precompiles::get_precompiles,
         precompiles::get_precompiles,
         pubkey::Pubkey,
         pubkey::Pubkey,
@@ -4909,13 +4910,19 @@ impl Bank {
         }
         }
 
 
         let mut write_time = Measure::start("write_time");
         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)
+        };
         self.rc.accounts.store_cached(
         self.rc.accounts.store_cached(
             self.slot(),
             self.slot(),
             sanitized_txs,
             sanitized_txs,
             &execution_results,
             &execution_results,
             loaded_txs,
             loaded_txs,
             &self.rent_collector,
             &self.rent_collector,
-            &last_blockhash,
+            &durable_nonce,
             lamports_per_signature,
             lamports_per_signature,
             self.leave_nonce_on_success(),
             self.leave_nonce_on_success(),
         );
         );
@@ -7622,14 +7629,12 @@ pub(crate) mod tests {
         let from_address = from.pubkey();
         let from_address = from.pubkey();
         let to_address = Pubkey::new_unique();
         let to_address = Pubkey::new_unique();
 
 
+        let durable_nonce =
+            DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true);
         let nonce_account = AccountSharedData::new_data(
         let nonce_account = AccountSharedData::new_data(
             43,
             43,
             &nonce::state::Versions::new_current(nonce::State::Initialized(
             &nonce::state::Versions::new_current(nonce::State::Initialized(
-                nonce::state::Data::new(
-                    Pubkey::default(),
-                    Hash::new_unique(),
-                    lamports_per_signature,
-                ),
+                nonce::state::Data::new(Pubkey::default(), durable_nonce, lamports_per_signature),
             )),
             )),
             &system_program::id(),
             &system_program::id(),
         )
         )
@@ -12347,7 +12352,7 @@ pub(crate) mod tests {
             let state =
             let state =
                 StateMut::<nonce::state::Versions>::state(&acc).map(|v| v.convert_to_current());
                 StateMut::<nonce::state::Versions>::state(&acc).map(|v| v.convert_to_current());
             match state {
             match state {
-                Ok(nonce::State::Initialized(ref data)) => Some(data.blockhash),
+                Ok(nonce::State::Initialized(ref data)) => Some(data.blockhash()),
                 _ => None,
                 _ => None,
             }
             }
         })
         })
@@ -13049,7 +13054,7 @@ pub(crate) mod tests {
                     StateMut::<nonce::state::Versions>::state(&acc).map(|v| v.convert_to_current());
                     StateMut::<nonce::state::Versions>::state(&acc).map(|v| v.convert_to_current());
                 match state {
                 match state {
                     Ok(nonce::State::Initialized(ref data)) => {
                     Ok(nonce::State::Initialized(ref data)) => {
-                        Some((data.blockhash, data.fee_calculator))
+                        Some((data.blockhash(), data.fee_calculator))
                     }
                     }
                     _ => None,
                     _ => None,
                 }
                 }
@@ -13086,7 +13091,7 @@ pub(crate) mod tests {
                     StateMut::<nonce::state::Versions>::state(&acc).map(|v| v.convert_to_current());
                     StateMut::<nonce::state::Versions>::state(&acc).map(|v| v.convert_to_current());
                 match state {
                 match state {
                     Ok(nonce::State::Initialized(ref data)) => {
                     Ok(nonce::State::Initialized(ref data)) => {
-                        Some((data.blockhash, data.fee_calculator))
+                        Some((data.blockhash(), data.fee_calculator))
                     }
                     }
                     _ => None,
                     _ => None,
                 }
                 }
@@ -13119,7 +13124,7 @@ pub(crate) mod tests {
                     StateMut::<nonce::state::Versions>::state(&acc).map(|v| v.convert_to_current());
                     StateMut::<nonce::state::Versions>::state(&acc).map(|v| v.convert_to_current());
                 match state {
                 match state {
                     Ok(nonce::State::Initialized(ref data)) => {
                     Ok(nonce::State::Initialized(ref data)) => {
-                        Some((data.blockhash, data.fee_calculator))
+                        Some((data.blockhash(), data.fee_calculator))
                     }
                     }
                     _ => None,
                     _ => None,
                 }
                 }
@@ -13156,7 +13161,7 @@ pub(crate) mod tests {
                     StateMut::<nonce::state::Versions>::state(&acc).map(|v| v.convert_to_current());
                     StateMut::<nonce::state::Versions>::state(&acc).map(|v| v.convert_to_current());
                 match state {
                 match state {
                     Ok(nonce::State::Initialized(ref data)) => {
                     Ok(nonce::State::Initialized(ref data)) => {
-                        Some((data.blockhash, data.fee_calculator))
+                        Some((data.blockhash(), data.fee_calculator))
                     }
                     }
                     _ => None,
                     _ => None,
                 }
                 }
@@ -13198,13 +13203,13 @@ pub(crate) mod tests {
             &[&custodian_keypair, &nonce_keypair],
             &[&custodian_keypair, &nonce_keypair],
             nonce_hash,
             nonce_hash,
         );
         );
-        // Caught by the system program because the tx hash is valid
+        // SanitizedMessage::get_durable_nonce returns None because nonce
+        // account is not writable. Durable nonce and blockhash domains are
+        // separate, so the recent_blockhash (== durable nonce) in the
+        // transaction is not found in the hash queue.
         assert_eq!(
         assert_eq!(
             bank.process_transaction(&tx),
             bank.process_transaction(&tx),
-            Err(TransactionError::InstructionError(
-                0,
-                InstructionError::InvalidArgument
-            ))
+            Err(TransactionError::BlockhashNotFound),
         );
         );
         // Kick nonce hash off the blockhash_queue
         // Kick nonce hash off the blockhash_queue
         for _ in 0..MAX_RECENT_BLOCKHASHES + 1 {
         for _ in 0..MAX_RECENT_BLOCKHASHES + 1 {

+ 28 - 17
runtime/src/nonce_keyed_account.rs

@@ -3,7 +3,11 @@ use {
     solana_sdk::{
     solana_sdk::{
         feature_set::{self, nonce_must_be_writable},
         feature_set::{self, nonce_must_be_writable},
         instruction::{checked_add, InstructionError},
         instruction::{checked_add, InstructionError},
-        nonce::{self, state::Versions, State},
+        nonce::{
+            self,
+            state::{DurableNonce, Versions},
+            State,
+        },
         pubkey::Pubkey,
         pubkey::Pubkey,
         system_instruction::{nonce_to_instruction_error, NonceError},
         system_instruction::{nonce_to_instruction_error, NonceError},
         sysvar::rent::Rent,
         sysvar::rent::Rent,
@@ -12,6 +16,13 @@ use {
     std::collections::HashSet,
     std::collections::HashSet,
 };
 };
 
 
+fn get_durable_nonce(invoke_context: &InvokeContext) -> DurableNonce {
+    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)
+}
+
 pub fn advance_nonce_account(
 pub fn advance_nonce_account(
     account: &mut BorrowedAccount,
     account: &mut BorrowedAccount,
     signers: &HashSet<Pubkey>,
     signers: &HashSet<Pubkey>,
@@ -45,8 +56,8 @@ pub fn advance_nonce_account(
                 );
                 );
                 return Err(InstructionError::MissingRequiredSignature);
                 return Err(InstructionError::MissingRequiredSignature);
             }
             }
-            let recent_blockhash = invoke_context.blockhash;
-            if data.blockhash == recent_blockhash {
+            let next_durable_nonce = get_durable_nonce(invoke_context);
+            if data.durable_nonce == next_durable_nonce {
                 ic_msg!(
                 ic_msg!(
                     invoke_context,
                     invoke_context,
                     "Advance nonce account: nonce can only advance once per slot"
                     "Advance nonce account: nonce can only advance once per slot"
@@ -59,7 +70,7 @@ pub fn advance_nonce_account(
 
 
             let new_data = nonce::state::Data::new(
             let new_data = nonce::state::Data::new(
                 data.authority,
                 data.authority,
-                recent_blockhash,
+                next_durable_nonce,
                 invoke_context.lamports_per_signature,
                 invoke_context.lamports_per_signature,
             );
             );
             account.set_state(&Versions::new_current(State::Initialized(new_data)))
             account.set_state(&Versions::new_current(State::Initialized(new_data)))
@@ -123,7 +134,7 @@ pub fn withdraw_nonce_account(
         }
         }
         State::Initialized(ref data) => {
         State::Initialized(ref data) => {
             if lamports == from.get_lamports() {
             if lamports == from.get_lamports() {
-                if data.blockhash == invoke_context.blockhash {
+                if data.durable_nonce == get_durable_nonce(invoke_context) {
                     ic_msg!(
                     ic_msg!(
                         invoke_context,
                         invoke_context,
                         "Withdraw nonce account: nonce can only advance once per slot"
                         "Withdraw nonce account: nonce can only advance once per slot"
@@ -208,7 +219,7 @@ pub fn initialize_nonce_account(
             }
             }
             let data = nonce::state::Data::new(
             let data = nonce::state::Data::new(
                 *nonce_authority,
                 *nonce_authority,
-                invoke_context.blockhash,
+                get_durable_nonce(invoke_context),
                 invoke_context.lamports_per_signature,
                 invoke_context.lamports_per_signature,
             );
             );
             account.set_state(&Versions::new_current(State::Initialized(data)))
             account.set_state(&Versions::new_current(State::Initialized(data)))
@@ -263,7 +274,7 @@ pub fn authorize_nonce_account(
             }
             }
             let new_data = nonce::state::Data::new(
             let new_data = nonce::state::Data::new(
                 *nonce_authority,
                 *nonce_authority,
-                data.blockhash,
+                data.durable_nonce,
                 data.get_lamports_per_signature(),
                 data.get_lamports_per_signature(),
             );
             );
             account.set_state(&Versions::new_current(State::Initialized(new_data)))
             account.set_state(&Versions::new_current(State::Initialized(new_data)))
@@ -394,7 +405,7 @@ mod test {
             .convert_to_current();
             .convert_to_current();
         let data = nonce::state::Data::new(
         let data = nonce::state::Data::new(
             data.authority,
             data.authority,
-            invoke_context.blockhash,
+            get_durable_nonce(&invoke_context),
             invoke_context.lamports_per_signature,
             invoke_context.lamports_per_signature,
         );
         );
         // First nonce instruction drives state from Uninitialized to Initialized
         // First nonce instruction drives state from Uninitialized to Initialized
@@ -407,7 +418,7 @@ mod test {
             .convert_to_current();
             .convert_to_current();
         let data = nonce::state::Data::new(
         let data = nonce::state::Data::new(
             data.authority,
             data.authority,
-            invoke_context.blockhash,
+            get_durable_nonce(&invoke_context),
             invoke_context.lamports_per_signature,
             invoke_context.lamports_per_signature,
         );
         );
         // Second nonce instruction consumes and replaces stored nonce
         // Second nonce instruction consumes and replaces stored nonce
@@ -420,7 +431,7 @@ mod test {
             .convert_to_current();
             .convert_to_current();
         let data = nonce::state::Data::new(
         let data = nonce::state::Data::new(
             data.authority,
             data.authority,
-            invoke_context.blockhash,
+            get_durable_nonce(&invoke_context),
             invoke_context.lamports_per_signature,
             invoke_context.lamports_per_signature,
         );
         );
         // Third nonce instruction for fun and profit
         // Third nonce instruction for fun and profit
@@ -485,7 +496,7 @@ mod test {
             .convert_to_current();
             .convert_to_current();
         let data = nonce::state::Data::new(
         let data = nonce::state::Data::new(
             authority,
             authority,
-            invoke_context.blockhash,
+            get_durable_nonce(&invoke_context),
             invoke_context.lamports_per_signature,
             invoke_context.lamports_per_signature,
         );
         );
         assert_eq!(state, State::Initialized(data));
         assert_eq!(state, State::Initialized(data));
@@ -818,7 +829,7 @@ mod test {
             .convert_to_current();
             .convert_to_current();
         let data = nonce::state::Data::new(
         let data = nonce::state::Data::new(
             authority,
             authority,
-            invoke_context.blockhash,
+            get_durable_nonce(&invoke_context),
             invoke_context.lamports_per_signature,
             invoke_context.lamports_per_signature,
         );
         );
         assert_eq!(state, State::Initialized(data.clone()));
         assert_eq!(state, State::Initialized(data.clone()));
@@ -850,7 +861,7 @@ mod test {
             .convert_to_current();
             .convert_to_current();
         let data = nonce::state::Data::new(
         let data = nonce::state::Data::new(
             data.authority,
             data.authority,
-            invoke_context.blockhash,
+            get_durable_nonce(&invoke_context),
             invoke_context.lamports_per_signature,
             invoke_context.lamports_per_signature,
         );
         );
         assert_eq!(state, State::Initialized(data));
         assert_eq!(state, State::Initialized(data));
@@ -1048,7 +1059,7 @@ mod test {
             initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context);
             initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context);
         let data = nonce::state::Data::new(
         let data = nonce::state::Data::new(
             authorized,
             authorized,
-            invoke_context.blockhash,
+            get_durable_nonce(&invoke_context),
             invoke_context.lamports_per_signature,
             invoke_context.lamports_per_signature,
         );
         );
         assert_eq!(result, Ok(()));
         assert_eq!(result, Ok(()));
@@ -1120,7 +1131,7 @@ mod test {
         let authority = Pubkey::default();
         let authority = Pubkey::default();
         let data = nonce::state::Data::new(
         let data = nonce::state::Data::new(
             authority,
             authority,
-            invoke_context.blockhash,
+            get_durable_nonce(&invoke_context),
             invoke_context.lamports_per_signature,
             invoke_context.lamports_per_signature,
         );
         );
         authorize_nonce_account(&mut nonce_account, &authority, &signers, &invoke_context).unwrap();
         authorize_nonce_account(&mut nonce_account, &authority, &signers, &invoke_context).unwrap();
@@ -1202,7 +1213,7 @@ mod test {
                 .get_account_at_index(NONCE_ACCOUNT_INDEX)
                 .get_account_at_index(NONCE_ACCOUNT_INDEX)
                 .unwrap()
                 .unwrap()
                 .borrow(),
                 .borrow(),
-            &invoke_context.blockhash,
+            get_durable_nonce(&invoke_context).as_hash(),
         ));
         ));
     }
     }
 
 
@@ -1257,7 +1268,7 @@ mod test {
                 .get_account_at_index(NONCE_ACCOUNT_INDEX)
                 .get_account_at_index(NONCE_ACCOUNT_INDEX)
                 .unwrap()
                 .unwrap()
                 .borrow(),
                 .borrow(),
-            &invoke_context.blockhash,
+            get_durable_nonce(&invoke_context).as_hash(),
         ));
         ));
     }
     }
 }
 }

+ 7 - 3
sdk/program/src/example_mocks.rs

@@ -24,9 +24,9 @@ pub mod solana_client {
     pub mod nonce_utils {
     pub mod nonce_utils {
         use {
         use {
             super::super::solana_sdk::{
             super::super::solana_sdk::{
-                account::ReadableAccount, account_utils::StateMut, hash::Hash, pubkey::Pubkey,
+                account::ReadableAccount, account_utils::StateMut, pubkey::Pubkey,
             },
             },
-            crate::nonce::state::{Data, Versions},
+            crate::nonce::state::{Data, DurableNonce, Versions},
         };
         };
 
 
         #[derive(thiserror::Error, Debug)]
         #[derive(thiserror::Error, Debug)]
@@ -36,7 +36,11 @@ pub mod solana_client {
         pub fn data_from_account<T: ReadableAccount + StateMut<Versions>>(
         pub fn data_from_account<T: ReadableAccount + StateMut<Versions>>(
             _account: &T,
             _account: &T,
         ) -> Result<Data, Error> {
         ) -> Result<Data, Error> {
-            Ok(Data::new(Pubkey::new_unique(), Hash::default(), 5000))
+            Ok(Data::new(
+                Pubkey::new_unique(),
+                DurableNonce::default(),
+                5000,
+            ))
         }
         }
     }
     }
 
 

+ 42 - 7
sdk/program/src/nonce/state/current.rs

@@ -1,8 +1,17 @@
 use {
 use {
-    crate::{fee_calculator::FeeCalculator, hash::Hash, pubkey::Pubkey},
+    crate::{
+        fee_calculator::FeeCalculator,
+        hash::{hashv, Hash},
+        pubkey::Pubkey,
+    },
     serde_derive::{Deserialize, Serialize},
     serde_derive::{Deserialize, Serialize},
 };
 };
 
 
+const DURABLE_NONCE_HASH_PREFIX: &[u8] = "DURABLE_NONCE".as_bytes();
+
+#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
+pub struct DurableNonce(Hash);
+
 /// Initialized data of a durable transaction nonce account.
 /// Initialized data of a durable transaction nonce account.
 ///
 ///
 /// This is stored within [`State`] for initialized nonce accounts.
 /// This is stored within [`State`] for initialized nonce accounts.
@@ -10,28 +19,54 @@ use {
 pub struct Data {
 pub struct Data {
     /// Address of the account that signs transactions using the nonce account.
     /// Address of the account that signs transactions using the nonce account.
     pub authority: Pubkey,
     pub authority: Pubkey,
-    /// A valid previous blockhash.
-    pub blockhash: Hash,
+    /// Durable nonce value derived from a valid previous blockhash.
+    pub durable_nonce: DurableNonce,
     /// The fee calculator associated with the blockhash.
     /// The fee calculator associated with the blockhash.
     pub fee_calculator: FeeCalculator,
     pub fee_calculator: FeeCalculator,
 }
 }
 
 
 impl Data {
 impl Data {
     /// Create new durable transaction nonce data.
     /// Create new durable transaction nonce data.
-    pub fn new(authority: Pubkey, blockhash: Hash, lamports_per_signature: u64) -> Self {
+    pub fn new(
+        authority: Pubkey,
+        durable_nonce: DurableNonce,
+        lamports_per_signature: u64,
+    ) -> Self {
         Data {
         Data {
             authority,
             authority,
-            blockhash,
+            durable_nonce,
             fee_calculator: FeeCalculator::new(lamports_per_signature),
             fee_calculator: FeeCalculator::new(lamports_per_signature),
         }
         }
     }
     }
 
 
+    /// Hash value used as recent_blockhash field in Transactions.
+    /// Named blockhash for legacy reasons, but durable nonce and blockhash
+    /// have separate domains.
+    pub fn blockhash(&self) -> Hash {
+        self.durable_nonce.0
+    }
+
     /// Get the cost per signature for the next transaction to use this nonce.
     /// Get the cost per signature for the next transaction to use this nonce.
     pub fn get_lamports_per_signature(&self) -> u64 {
     pub fn get_lamports_per_signature(&self) -> u64 {
         self.fee_calculator.lamports_per_signature
         self.fee_calculator.lamports_per_signature
     }
     }
 }
 }
 
 
+impl DurableNonce {
+    pub fn from_blockhash(blockhash: &Hash, separate_domains: bool) -> Self {
+        Self(if separate_domains {
+            hashv(&[DURABLE_NONCE_HASH_PREFIX, blockhash.as_ref()])
+        } else {
+            *blockhash
+        })
+    }
+
+    /// Hash value used as recent_blockhash field in Transactions.
+    pub fn as_hash(&self) -> &Hash {
+        &self.0
+    }
+}
+
 /// The state of a durable transaction nonce account.
 /// The state of a durable transaction nonce account.
 ///
 ///
 /// When created in memory with [`State::default`] or when deserialized from an
 /// When created in memory with [`State::default`] or when deserialized from an
@@ -52,10 +87,10 @@ impl State {
     /// Create new durable transaction nonce state.
     /// Create new durable transaction nonce state.
     pub fn new_initialized(
     pub fn new_initialized(
         authority: &Pubkey,
         authority: &Pubkey,
-        blockhash: &Hash,
+        durable_nonce: DurableNonce,
         lamports_per_signature: u64,
         lamports_per_signature: u64,
     ) -> Self {
     ) -> Self {
-        Self::Initialized(Data::new(*authority, *blockhash, lamports_per_signature))
+        Self::Initialized(Data::new(*authority, durable_nonce, lamports_per_signature))
     }
     }
 
 
     /// Get the serialized size of the nonce state.
     /// Get the serialized size of the nonce state.

+ 1 - 1
sdk/program/src/nonce/state/mod.rs

@@ -1,7 +1,7 @@
 //! State for durable transaction nonces.
 //! State for durable transaction nonces.
 
 
 mod current;
 mod current;
-pub use current::{Data, State};
+pub use current::{Data, DurableNonce, State};
 use serde_derive::{Deserialize, Serialize};
 use serde_derive::{Deserialize, Serialize};
 
 
 #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
 #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]

+ 1 - 1
sdk/program/src/system_instruction.rs

@@ -736,7 +736,7 @@ pub fn create_nonce_account(
 ///     # });
 ///     # });
 ///     let nonce_account = client.get_account(nonce_account_pubkey)?;
 ///     let nonce_account = client.get_account(nonce_account_pubkey)?;
 ///     let nonce_data = nonce_utils::data_from_account(&nonce_account)?;
 ///     let nonce_data = nonce_utils::data_from_account(&nonce_account)?;
-///     let blockhash = nonce_data.blockhash;
+///     let blockhash = nonce_data.blockhash();
 ///
 ///
 ///     tx.try_sign(&[payer], blockhash)?;
 ///     tx.try_sign(&[payer], blockhash)?;
 ///
 ///

+ 5 - 0
sdk/src/feature_set.rs

@@ -416,6 +416,10 @@ pub mod warp_timestamp_with_a_vengeance {
     solana_sdk::declare_id!("3BX6SBeEBibHaVQXywdkcgyUk6evfYZkHdztXiDtEpFS");
     solana_sdk::declare_id!("3BX6SBeEBibHaVQXywdkcgyUk6evfYZkHdztXiDtEpFS");
 }
 }
 
 
+pub mod separate_nonce_from_blockhash {
+    solana_sdk::declare_id!("Gea3ZkK2N4pHuVZVxWcnAtS6UEDdyumdYt4pFcKjA3ar");
+}
+
 lazy_static! {
 lazy_static! {
     /// Map of feature identifiers to user-visible description
     /// Map of feature identifiers to user-visible description
     pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
     pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
@@ -513,6 +517,7 @@ lazy_static! {
         (include_account_index_in_rent_error::id(), "include account index in rent tx error #25190"),
         (include_account_index_in_rent_error::id(), "include account index in rent tx error #25190"),
         (add_shred_type_to_shred_seed::id(), "add shred-type to shred seed #25556"),
         (add_shred_type_to_shred_seed::id(), "add shred-type to shred seed #25556"),
         (warp_timestamp_with_a_vengeance::id(), "warp timestamp again, adjust bounding to 150% slow #25666"),
         (warp_timestamp_with_a_vengeance::id(), "warp timestamp again, adjust bounding to 150% slow #25666"),
+        (separate_nonce_from_blockhash::id(), "separate durable nonce and blockhash domains #25744"),
         /*************** ADD NEW FEATURES HERE ***************/
         /*************** ADD NEW FEATURES HERE ***************/
     ]
     ]
     .iter()
     .iter()

+ 2 - 1
sdk/src/nonce_account.rs

@@ -20,12 +20,13 @@ 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) -> bool {
 pub fn verify_nonce_account(acc: &AccountSharedData, hash: &Hash) -> bool {
     if acc.owner() != &crate::system_program::id() {
     if acc.owner() != &crate::system_program::id() {
         return false;
         return false;
     }
     }
     match StateMut::<Versions>::state(acc).map(|v| v.convert_to_current()) {
     match StateMut::<Versions>::state(acc).map(|v| v.convert_to_current()) {
-        Ok(State::Initialized(ref data)) => *hash == data.blockhash,
+        Ok(State::Initialized(ref data)) => hash == &data.blockhash(),
         _ => false,
         _ => false,
     }
     }
 }
 }

+ 13 - 7
send-transaction-service/src/send_transaction_service.rs

@@ -764,8 +764,12 @@ mod test {
         crate::tpu_info::NullTpuInfo,
         crate::tpu_info::NullTpuInfo,
         crossbeam_channel::unbounded,
         crossbeam_channel::unbounded,
         solana_sdk::{
         solana_sdk::{
-            account::AccountSharedData, genesis_config::create_genesis_config, nonce,
-            pubkey::Pubkey, signature::Signer, system_program, system_transaction,
+            account::AccountSharedData,
+            genesis_config::create_genesis_config,
+            nonce::{self, state::DurableNonce},
+            pubkey::Pubkey,
+            signature::Signer,
+            system_program, system_transaction,
         },
         },
         std::ops::Sub,
         std::ops::Sub,
     };
     };
@@ -1068,7 +1072,8 @@ mod test {
             .unwrap();
             .unwrap();
 
 
         let nonce_address = Pubkey::new_unique();
         let nonce_address = Pubkey::new_unique();
-        let durable_nonce = Hash::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(
         let nonce_state = nonce::state::Versions::new_current(nonce::State::Initialized(
             nonce::state::Data::new(Pubkey::default(), durable_nonce, 42),
             nonce::state::Data::new(Pubkey::default(), durable_nonce, 42),
         ));
         ));
@@ -1101,7 +1106,7 @@ mod test {
                 rooted_signature,
                 rooted_signature,
                 vec![],
                 vec![],
                 last_valid_block_height,
                 last_valid_block_height,
-                Some((nonce_address, durable_nonce)),
+                Some((nonce_address, *durable_nonce.as_hash())),
                 None,
                 None,
                 Some(Instant::now()),
                 Some(Instant::now()),
             ),
             ),
@@ -1192,7 +1197,7 @@ mod test {
                 Signature::default(),
                 Signature::default(),
                 vec![],
                 vec![],
                 root_bank.block_height() - 1,
                 root_bank.block_height() - 1,
-                Some((nonce_address, durable_nonce)),
+                Some((nonce_address, *durable_nonce.as_hash())),
                 None,
                 None,
                 Some(Instant::now()),
                 Some(Instant::now()),
             ),
             ),
@@ -1284,7 +1289,7 @@ mod test {
                 Signature::default(),
                 Signature::default(),
                 vec![],
                 vec![],
                 last_valid_block_height,
                 last_valid_block_height,
-                Some((nonce_address, durable_nonce)),
+                Some((nonce_address, *durable_nonce.as_hash())),
                 None,
                 None,
                 Some(Instant::now().sub(Duration::from_millis(4000))),
                 Some(Instant::now().sub(Duration::from_millis(4000))),
             ),
             ),
@@ -1311,7 +1316,8 @@ mod test {
         for mut transaction in transactions.values_mut() {
         for mut transaction in transactions.values_mut() {
             transaction.last_sent_time = Some(Instant::now().sub(Duration::from_millis(4000)));
             transaction.last_sent_time = Some(Instant::now().sub(Duration::from_millis(4000)));
         }
         }
-        let new_durable_nonce = Hash::new_unique();
+        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(
         let new_nonce_state = nonce::state::Versions::new_current(nonce::State::Initialized(
             nonce::state::Data::new(Pubkey::default(), new_durable_nonce, 42),
             nonce::state::Data::new(Pubkey::default(), new_durable_nonce, 42),
         ));
         ));