Nonce accounts must be writeable (#21260)

* Nonce accounts must be writeable

* feedback

* feedback
This commit is contained in:
Jack May
2021-11-16 15:01:00 -08:00
committed by GitHub
parent 8e0068ca6a
commit cb0bb5bd1e
7 changed files with 194 additions and 43 deletions

View File

@ -237,6 +237,10 @@ pub mod reject_deployment_of_unresolved_syscalls {
solana_sdk::declare_id!("DqniU3MfvdpU3yhmNF1RKeaM5TZQELZuyFGosASRVUoy");
}
pub mod nonce_must_be_writable {
solana_sdk::declare_id!("BiCU7M5w8ZCMykVSyhZ7Q3m2SWoR2qrEQ86ERcDX77ME");
}
lazy_static! {
/// Map of feature identifiers to user-visible description
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
@ -291,6 +295,7 @@ lazy_static! {
(disable_fee_calculator::id(), "deprecate fee calculator"),
(add_compute_budget_program::id(), "Add compute_budget_program"),
(reject_deployment_of_unresolved_syscalls::id(), "Reject deployment of programs with unresolved syscall symbols"),
(nonce_must_be_writable::id(), "nonce must be writable"),
/*************** ADD NEW FEATURES HERE ***************/
]
.iter()

View File

@ -502,14 +502,23 @@ pub fn uses_durable_nonce(tx: &Transaction) -> Option<&CompiledInstruction> {
message
.instructions
.get(NONCED_TX_MARKER_IX_INDEX as usize)
.filter(|maybe_ix| {
let prog_id_idx = maybe_ix.program_id_index as usize;
match message.account_keys.get(prog_id_idx) {
Some(program_id) => system_program::check_id(program_id),
_ => false,
}
} && matches!(limited_deserialize(&maybe_ix.data), Ok(SystemInstruction::AdvanceNonceAccount))
)
.filter(|instruction| {
// Is system program
matches!(
message.account_keys.get(instruction.program_id_index as usize),
Some(program_id) if system_program::check_id(program_id)
)
// Is a nonce advance instruction
&& matches!(
limited_deserialize(&instruction.data),
Ok(SystemInstruction::AdvanceNonceAccount)
)
// Nonce account is writable
&& matches!(
instruction.accounts.get(0),
Some(index) if message.is_writable(*index as usize, true)
)
})
}
#[deprecated]
@ -532,7 +541,7 @@ mod tests {
hash::hash,
instruction::AccountMeta,
signature::{Keypair, Presigner, Signer},
system_instruction,
system_instruction, sysvar,
};
use bincode::{deserialize, serialize, serialized_size};
use std::mem::size_of;
@ -993,6 +1002,32 @@ mod tests {
assert!(uses_durable_nonce(&tx).is_none());
}
#[test]
fn tx_uses_ro_nonce_account() {
let from_keypair = Keypair::new();
let from_pubkey = from_keypair.pubkey();
let nonce_keypair = Keypair::new();
let nonce_pubkey = nonce_keypair.pubkey();
let account_metas = vec![
AccountMeta::new_readonly(nonce_pubkey, false),
#[allow(deprecated)]
AccountMeta::new_readonly(sysvar::recent_blockhashes::id(), false),
AccountMeta::new_readonly(nonce_pubkey, true),
];
let nonce_instruction = Instruction::new_with_bincode(
system_program::id(),
&system_instruction::SystemInstruction::AdvanceNonceAccount,
account_metas,
);
let tx = Transaction::new_signed_with_payer(
&[nonce_instruction],
Some(&from_pubkey),
&[&from_keypair, &nonce_keypair],
Hash::default(),
);
assert!(uses_durable_nonce(&tx).is_none());
}
#[test]
fn tx_uses_nonce_wrong_first_nonce_ix_fail() {
let from_keypair = Keypair::new();

View File

@ -161,7 +161,7 @@ impl SanitizedTransaction {
}
/// If the transaction uses a durable nonce, return the pubkey of the nonce account
pub fn get_durable_nonce(&self) -> Option<&Pubkey> {
pub fn get_durable_nonce(&self, nonce_must_be_writable: bool) -> Option<&Pubkey> {
self.message
.instructions()
.get(NONCED_TX_MARKER_IX_INDEX as usize)
@ -180,7 +180,11 @@ impl SanitizedTransaction {
.and_then(|ix| {
ix.accounts.get(0).and_then(|idx| {
let idx = *idx as usize;
self.message.get_account_key(idx)
if nonce_must_be_writable && !self.message.is_writable(idx, true) {
None
} else {
self.message.get_account_key(idx)
}
})
})
}