From 2251aa2809ceedd49eaaa226d4e162baac2fb561 Mon Sep 17 00:00:00 2001 From: ivandzen Date: Fri, 10 Jun 2022 20:24:35 +0300 Subject: [PATCH] Report transaction signature on update account (#25726) There's no direct linkage between account-update events and transaction-events Issue is linked with geyser plugin interface Currently, there's no straightforward way to determine what notify_transaction call is related to particular update_account call. So, there's no simple way to determine what transaction caused what changes in accounts. This issue is especially valuable in case when several transactions modifies single account in the same slot. Add txn_signature field to ReplicaTransactionInfo structure This additional field will contain the signature of transaction which caused this account update event. This modification is not bringing significant overhead because all necessary information for such linkage already available inside Accounts::collect_accounts_to_store method --- .../src/geyser_plugin_interface.rs | 34 +++++ .../src/accounts_update_notifier.rs | 30 +++-- runtime/src/accounts.rs | 33 +++-- runtime/src/accounts_db.rs | 125 ++++++++++++------ .../src/accounts_db/geyser_plugin_utils.rs | 15 ++- .../src/accounts_update_notifier_interface.rs | 10 +- 6 files changed, 179 insertions(+), 68 deletions(-) diff --git a/geyser-plugin-interface/src/geyser_plugin_interface.rs b/geyser-plugin-interface/src/geyser_plugin_interface.rs index 37653eaa4..8dd9bdc54 100644 --- a/geyser-plugin-interface/src/geyser_plugin_interface.rs +++ b/geyser-plugin-interface/src/geyser_plugin_interface.rs @@ -38,12 +38,46 @@ pub struct ReplicaAccountInfo<'a> { pub write_version: u64, } +#[derive(Debug, Clone, PartialEq, Eq)] +/// Information about an account being updated +/// (extended with transaction signature doing this update) +pub struct ReplicaAccountInfoV2<'a> { + /// The Pubkey for the account + pub pubkey: &'a [u8], + + /// The lamports for the account + pub lamports: u64, + + /// The Pubkey of the owner program account + pub owner: &'a [u8], + + /// This account's data contains a loaded program (and is now read-only) + pub executable: bool, + + /// The epoch at which this account will next owe rent + pub rent_epoch: u64, + + /// The data held in this account. + pub data: &'a [u8], + + /// A global monotonically increasing atomic number, which can be used + /// to tell the order of the account update. For example, when an + /// account is updated in the same slot multiple times, the update + /// with higher write_version should supersede the one with lower + /// write_version. + pub write_version: u64, + + /// First signature of the transaction caused this account modification + pub txn_signature: Option<&'a Signature>, +} + /// A wrapper to future-proof ReplicaAccountInfo handling. /// If there were a change to the structure of ReplicaAccountInfo, /// there would be new enum entry for the newer version, forcing /// plugin implementations to handle the change. pub enum ReplicaAccountInfoVersions<'a> { V0_0_1(&'a ReplicaAccountInfo<'a>), + V0_0_2(&'a ReplicaAccountInfoV2<'a>), } /// Information about a transaction diff --git a/geyser-plugin-manager/src/accounts_update_notifier.rs b/geyser-plugin-manager/src/accounts_update_notifier.rs index 678ea1434..b774966d7 100644 --- a/geyser-plugin-manager/src/accounts_update_notifier.rs +++ b/geyser-plugin-manager/src/accounts_update_notifier.rs @@ -3,7 +3,7 @@ use { crate::geyser_plugin_manager::GeyserPluginManager, log::*, solana_geyser_plugin_interface::geyser_plugin_interface::{ - ReplicaAccountInfo, ReplicaAccountInfoVersions, + ReplicaAccountInfoV2, ReplicaAccountInfoVersions, }, solana_measure::measure::Measure, solana_metrics::*, @@ -14,6 +14,7 @@ use { solana_sdk::{ account::{AccountSharedData, ReadableAccount}, clock::Slot, + signature::Signature, }, std::sync::{Arc, RwLock}, }; @@ -23,8 +24,16 @@ pub(crate) struct AccountsUpdateNotifierImpl { } impl AccountsUpdateNotifierInterface for AccountsUpdateNotifierImpl { - fn notify_account_update(&self, slot: Slot, meta: &StoredMeta, account: &AccountSharedData) { - if let Some(account_info) = self.accountinfo_from_shared_account_data(meta, account) { + fn notify_account_update( + &self, + slot: Slot, + meta: &StoredMeta, + account: &AccountSharedData, + txn_signature: &Option<&Signature>, + ) { + if let Some(account_info) = + self.accountinfo_from_shared_account_data(meta, account, txn_signature) + { self.notify_plugins_of_account_update(account_info, slot, false); } } @@ -97,8 +106,9 @@ impl AccountsUpdateNotifierImpl { &self, meta: &'a StoredMeta, account: &'a AccountSharedData, - ) -> Option> { - Some(ReplicaAccountInfo { + txn_signature: &'a Option<&'a Signature>, + ) -> Option> { + Some(ReplicaAccountInfoV2 { pubkey: meta.pubkey.as_ref(), lamports: account.lamports(), owner: account.owner().as_ref(), @@ -106,14 +116,15 @@ impl AccountsUpdateNotifierImpl { rent_epoch: account.rent_epoch(), data: account.data(), write_version: meta.write_version, + txn_signature: *txn_signature, }) } fn accountinfo_from_stored_account_meta<'a>( &self, stored_account_meta: &'a StoredAccountMeta, - ) -> Option> { - Some(ReplicaAccountInfo { + ) -> Option> { + Some(ReplicaAccountInfoV2 { pubkey: stored_account_meta.meta.pubkey.as_ref(), lamports: stored_account_meta.account_meta.lamports, owner: stored_account_meta.account_meta.owner.as_ref(), @@ -121,12 +132,13 @@ impl AccountsUpdateNotifierImpl { rent_epoch: stored_account_meta.account_meta.rent_epoch, data: stored_account_meta.data, write_version: stored_account_meta.meta.write_version, + txn_signature: None, }) } fn notify_plugins_of_account_update( &self, - account: ReplicaAccountInfo, + account: ReplicaAccountInfoV2, slot: Slot, is_startup: bool, ) { @@ -139,7 +151,7 @@ impl AccountsUpdateNotifierImpl { for plugin in plugin_manager.plugins.iter_mut() { let mut measure = Measure::start("geyser-plugin-update-account"); match plugin.update_account( - ReplicaAccountInfoVersions::V0_0_1(&account), + ReplicaAccountInfoVersions::V0_0_2(&account), slot, is_startup, ) { diff --git a/runtime/src/accounts.rs b/runtime/src/accounts.rs index 7fb052c57..81823e623 100644 --- a/runtime/src/accounts.rs +++ b/runtime/src/accounts.rs @@ -45,6 +45,7 @@ use { State as NonceState, }, pubkey::Pubkey, + signature::Signature, slot_hashes::SlotHashes, system_program, sysvar::{self, epoch_schedule::EpochSchedule, instructions::construct_instructions_data}, @@ -1200,7 +1201,7 @@ impl Accounts { lamports_per_signature: u64, leave_nonce_on_success: bool, ) { - let accounts_to_store = self.collect_accounts_to_store( + let (accounts_to_store, txn_signatures) = self.collect_accounts_to_store( txs, res, loaded, @@ -1209,11 +1210,12 @@ impl Accounts { lamports_per_signature, leave_nonce_on_success, ); - self.accounts_db.store_cached(slot, &accounts_to_store); + self.accounts_db + .store_cached(slot, &accounts_to_store, Some(&txn_signatures)); } pub fn store_accounts_cached(&self, slot: Slot, accounts: &[(&Pubkey, &AccountSharedData)]) { - self.accounts_db.store_cached(slot, accounts) + self.accounts_db.store_cached(slot, accounts, None) } /// Add a slot to root. Root slots cannot be purged @@ -1231,8 +1233,12 @@ impl Accounts { durable_nonce: &(DurableNonce, /*separate_domains:*/ bool), lamports_per_signature: u64, leave_nonce_on_success: bool, - ) -> Vec<(&'a Pubkey, &'a AccountSharedData)> { + ) -> ( + Vec<(&'a Pubkey, &'a AccountSharedData)>, + Vec>, + ) { let mut accounts = Vec::with_capacity(load_results.len()); + let mut signatures = Vec::with_capacity(load_results.len()); for (i, ((tx_load_result, nonce), tx)) in load_results.iter_mut().zip(txs).enumerate() { if tx_load_result.is_err() { // Don't store any accounts if tx failed to load @@ -1301,11 +1307,12 @@ impl Accounts { // Add to the accounts to store accounts.push((&*address, &*account)); + signatures.push(Some(tx.signature())); } } } } - accounts + (accounts, signatures) } } @@ -2971,6 +2978,7 @@ mod tests { (message.account_keys[1], account2.clone()), ]; let tx0 = new_sanitized_tx(&[&keypair0], message, Hash::default()); + let tx0_sign = *tx0.signature(); let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])]; let message = Message::new_with_compiled_instructions( @@ -2986,6 +2994,7 @@ mod tests { (message.account_keys[1], account2), ]; let tx1 = new_sanitized_tx(&[&keypair1], message, Hash::default()); + let tx1_sign = *tx1.signature(); let loaded0 = ( Ok(LoadedTransaction { @@ -3025,7 +3034,7 @@ mod tests { } let txs = vec![tx0, tx1]; let execution_results = vec![new_execution_result(Ok(()), None); 2]; - let collected_accounts = accounts.collect_accounts_to_store( + let (collected_accounts, txn_signatures) = accounts.collect_accounts_to_store( &txs, &execution_results, loaded.as_mut_slice(), @@ -3042,6 +3051,14 @@ mod tests { .iter() .any(|(pubkey, _account)| *pubkey == &keypair1.pubkey())); + assert_eq!(txn_signatures.len(), 2); + assert!(txn_signatures + .iter() + .any(|signature| signature.unwrap().to_string().eq(&tx0_sign.to_string()))); + assert!(txn_signatures + .iter() + .any(|signature| signature.unwrap().to_string().eq(&tx1_sign.to_string()))); + // Ensure readonly_lock reflects lock assert_eq!( *accounts @@ -3524,7 +3541,7 @@ mod tests { )), nonce.as_ref(), )]; - let collected_accounts = accounts.collect_accounts_to_store( + let (collected_accounts, _) = accounts.collect_accounts_to_store( &txs, &execution_results, loaded.as_mut_slice(), @@ -3652,7 +3669,7 @@ mod tests { )), nonce.as_ref(), )]; - let collected_accounts = accounts.collect_accounts_to_store( + let (collected_accounts, _) = accounts.collect_accounts_to_store( &txs, &execution_results, loaded.as_mut_slice(), diff --git a/runtime/src/accounts_db.rs b/runtime/src/accounts_db.rs index b894d3c5e..c35899182 100644 --- a/runtime/src/accounts_db.rs +++ b/runtime/src/accounts_db.rs @@ -74,6 +74,7 @@ use { hash::Hash, pubkey::Pubkey, rent::Rent, + signature::Signature, timing::AtomicInterval, }, std::{ @@ -5774,11 +5775,12 @@ impl AccountsDb { } } - fn write_accounts_to_cache( + fn write_accounts_to_cache<'a>( &self, slot: Slot, hashes: Option<&[impl Borrow]>, accounts_and_meta_to_store: &[(StoredMeta, Option<&impl ReadableAccount>)], + txn_signatures_iter: Box> + 'a>, ) -> Vec { let len = accounts_and_meta_to_store.len(); let hashes = hashes.map(|hashes| { @@ -5788,8 +5790,9 @@ impl AccountsDb { accounts_and_meta_to_store .iter() + .zip(txn_signatures_iter) .enumerate() - .map(|(i, (meta, account))| { + .map(|(i, ((meta, account), signature))| { let hash = hashes.map(|hashes| hashes[i].borrow()); let account = account @@ -5801,7 +5804,7 @@ impl AccountsDb { account.lamports(), ); - self.notify_account_at_accounts_update(slot, meta, &account); + self.notify_account_at_accounts_update(slot, meta, &account, signature); let cached_account = self.accounts_cache.store(slot, &meta.pubkey, account, hash); // hash this account in the bg @@ -5828,6 +5831,7 @@ impl AccountsDb { storage_finder: F, mut write_version_producer: P, is_cached_store: bool, + txn_signatures: Option<&'a [Option<&'a Signature>]>, ) -> Vec { let mut calc_stored_meta_time = Measure::start("calc_stored_meta"); let slot = accounts.target_slot(); @@ -5858,7 +5862,18 @@ impl AccountsDb { .fetch_add(calc_stored_meta_time.as_us(), Ordering::Relaxed); if self.caching_enabled && is_cached_store { - self.write_accounts_to_cache(slot, hashes, &accounts_and_meta_to_store) + let signature_iter: Box>> = + match txn_signatures { + Some(txn_signatures) => { + assert_eq!(txn_signatures.len(), accounts_and_meta_to_store.len()); + Box::new(txn_signatures.iter()) + } + None => { + Box::new(std::iter::repeat(&None).take(accounts_and_meta_to_store.len())) + } + }; + + self.write_accounts_to_cache(slot, hashes, &accounts_and_meta_to_store, signature_iter) } else { match hashes { Some(hashes) => self.write_accounts_to_storage( @@ -7359,20 +7374,26 @@ impl AccountsDb { .fetch_add(measure.as_us(), Ordering::Relaxed); } - pub fn store_cached(&self, slot: Slot, accounts: &[(&Pubkey, &AccountSharedData)]) { - self.store((slot, accounts), self.caching_enabled); + pub fn store_cached( + &self, + slot: Slot, + accounts: &[(&Pubkey, &AccountSharedData)], + txn_signatures: Option<&[Option<&Signature>]>, + ) { + self.store((slot, accounts), self.caching_enabled, txn_signatures); } /// Store the account update. /// only called by tests pub fn store_uncached(&self, slot: Slot, accounts: &[(&Pubkey, &AccountSharedData)]) { - self.store((slot, accounts), false) + self.store((slot, accounts), false, None); } fn store<'a, T: ReadableAccount + Sync + ZeroLamport>( &self, accounts: impl StorableAccounts<'a, T>, is_cached_store: bool, + txn_signatures: Option<&'a [Option<&'a Signature>]>, ) { // If all transactions in a batch are errored, // it's possible to get a store with no accounts. @@ -7402,7 +7423,7 @@ impl AccountsDb { } // we use default hashes for now since the same account may be stored to the cache multiple times - self.store_accounts_unfrozen(accounts, None, is_cached_store); + self.store_accounts_unfrozen(accounts, None, is_cached_store, txn_signatures); self.report_store_timings(); } @@ -7529,6 +7550,7 @@ impl AccountsDb { accounts: impl StorableAccounts<'a, T>, hashes: Option<&[&Hash]>, is_cached_store: bool, + txn_signatures: Option<&'a [Option<&'a Signature>]>, ) { // This path comes from a store to a non-frozen slot. // If a store is dead here, then a newer update for @@ -7545,6 +7567,7 @@ impl AccountsDb { None::>>, is_cached_store, reset_accounts, + txn_signatures, ); } @@ -7567,6 +7590,7 @@ impl AccountsDb { write_version_producer, is_cached_store, reset_accounts, + None, ) } @@ -7578,6 +7602,7 @@ impl AccountsDb { write_version_producer: Option>>, is_cached_store: bool, reset_accounts: bool, + txn_signatures: Option<&'b [Option<&'b Signature>]>, ) -> StoreAccountsTiming { let storage_finder = Box::new(move |slot, size| { storage @@ -7605,6 +7630,7 @@ impl AccountsDb { storage_finder, write_version_producer, is_cached_store, + txn_signatures, ); store_accounts_time.stop(); self.stats @@ -9708,7 +9734,7 @@ pub mod tests { let account0 = AccountSharedData::new(1, 0, &key); let ancestors = vec![(unrooted_slot, 1)].into_iter().collect(); if is_cached { - db.store_cached(unrooted_slot, &[(&key, &account0)]); + db.store_cached(unrooted_slot, &[(&key, &account0)], None); } else { db.store_uncached(unrooted_slot, &[(&key, &account0)]); } @@ -11267,6 +11293,7 @@ pub mod tests { (some_slot, &[(&key, &account)][..]), Some(&[&Hash::default()]), false, + None, ); db.add_root(some_slot); let check_hash = true; @@ -11505,7 +11532,7 @@ pub mod tests { } // provide bogus account hashes let some_hash = Hash::new(&[0xca; HASH_BYTES]); - db.store_accounts_unfrozen((some_slot, accounts), Some(&[&some_hash]), false); + db.store_accounts_unfrozen((some_slot, accounts), Some(&[&some_hash]), false, None); db.add_root(some_slot); assert_matches!( db.verify_bank_hash_and_lamports( @@ -12676,13 +12703,13 @@ pub mod tests { let account = AccountSharedData::new(1, 16 * 4096, &Pubkey::default()); let pubkey1 = solana_sdk::pubkey::new_rand(); - accounts.store_cached(0, &[(&pubkey1, &account)]); + accounts.store_cached(0, &[(&pubkey1, &account)], None); let pubkey2 = solana_sdk::pubkey::new_rand(); - accounts.store_cached(0, &[(&pubkey2, &account)]); + accounts.store_cached(0, &[(&pubkey2, &account)], None); let zero_account = AccountSharedData::new(0, 1, &Pubkey::default()); - accounts.store_cached(1, &[(&pubkey1, &zero_account)]); + accounts.store_cached(1, &[(&pubkey1, &zero_account)], None); // Add root 0 and flush separately accounts.get_accounts_delta_hash(0); @@ -12854,7 +12881,7 @@ pub mod tests { let key = Pubkey::default(); let account0 = AccountSharedData::new(1, 0, &key); let slot = 0; - db.store_cached(slot, &[(&key, &account0)]); + db.store_cached(slot, &[(&key, &account0)], None); // Load with no ancestors and no root will return nothing assert!(db @@ -12887,7 +12914,7 @@ pub mod tests { let key = Pubkey::default(); let account0 = AccountSharedData::new(1, 0, &key); let slot = 0; - db.store_cached(slot, &[(&key, &account0)]); + db.store_cached(slot, &[(&key, &account0)], None); db.mark_slot_frozen(slot); // No root was added yet, requires an ancestor to find @@ -12920,9 +12947,9 @@ pub mod tests { let unrooted_key = solana_sdk::pubkey::new_rand(); let key5 = solana_sdk::pubkey::new_rand(); let key6 = solana_sdk::pubkey::new_rand(); - db.store_cached(unrooted_slot, &[(&unrooted_key, &account0)]); - db.store_cached(root5, &[(&key5, &account0)]); - db.store_cached(root6, &[(&key6, &account0)]); + db.store_cached(unrooted_slot, &[(&unrooted_key, &account0)], None); + db.store_cached(root5, &[(&key5, &account0)], None); + db.store_cached(root6, &[(&key6, &account0)], None); for slot in &[unrooted_slot, root5, root6] { db.mark_slot_frozen(*slot); } @@ -12985,7 +13012,7 @@ pub mod tests { let num_slots = 2 * max_cache_slots(); for i in 0..num_roots + num_unrooted { let key = Pubkey::new_unique(); - db.store_cached(i as Slot, &[(&key, &account0)]); + db.store_cached(i as Slot, &[(&key, &account0)], None); keys.push(key); db.mark_slot_frozen(i as Slot); if i < num_roots { @@ -13054,8 +13081,8 @@ pub mod tests { let zero_lamport_account = AccountSharedData::new(0, 0, AccountSharedData::default().owner()); let slot1_account = AccountSharedData::new(1, 1, AccountSharedData::default().owner()); - db.store_cached(0, &[(&account_key, &zero_lamport_account)]); - db.store_cached(1, &[(&account_key, &slot1_account)]); + db.store_cached(0, &[(&account_key, &zero_lamport_account)], None); + db.store_cached(1, &[(&account_key, &slot1_account)], None); db.add_root(0); db.add_root(1); @@ -13077,7 +13104,7 @@ pub mod tests { .unwrap(); assert_eq!(account.lamports(), 1); assert_eq!(db.read_only_accounts_cache.cache_len(), 1); - db.store_cached(2, &[(&account_key, &zero_lamport_account)]); + db.store_cached(2, &[(&account_key, &zero_lamport_account)], None); assert_eq!(db.read_only_accounts_cache.cache_len(), 1); let account = db .load_with_fixed_root(&Ancestors::default(), &account_key) @@ -13102,8 +13129,8 @@ pub mod tests { let zero_lamport_account = AccountSharedData::new(0, 0, AccountSharedData::default().owner()); let slot1_account = AccountSharedData::new(1, 1, AccountSharedData::default().owner()); - db.store_cached(0, &[(&account_key, &zero_lamport_account)]); - db.store_cached(1, &[(&account_key, &slot1_account)]); + db.store_cached(0, &[(&account_key, &zero_lamport_account)], None); + db.store_cached(1, &[(&account_key, &slot1_account)], None); db.add_root(0); db.add_root(1); @@ -13157,16 +13184,20 @@ pub mod tests { AccountSharedData::new(0, 0, AccountSharedData::default().owner()); // Store into slot 0, and then flush the slot to storage - db.store_cached(0, &[(&zero_lamport_account_key, &slot0_account)]); + db.store_cached(0, &[(&zero_lamport_account_key, &slot0_account)], None); // Second key keeps other lamport account entry for slot 0 alive, // preventing clean of the zero_lamport_account in slot 1. - db.store_cached(0, &[(&other_account_key, &slot0_account)]); + db.store_cached(0, &[(&other_account_key, &slot0_account)], None); db.add_root(0); db.flush_accounts_cache(true, None); assert!(!db.storage.get_slot_storage_entries(0).unwrap().is_empty()); // Store into slot 1, a dummy slot that will be dead and purged before flush - db.store_cached(1, &[(&zero_lamport_account_key, &zero_lamport_account)]); + db.store_cached( + 1, + &[(&zero_lamport_account_key, &zero_lamport_account)], + None, + ); // Store into slot 2, which makes all updates from slot 1 outdated. // This means slot 1 is a dead slot. Later, slot 1 will be cleaned/purged @@ -13174,7 +13205,11 @@ pub mod tests { // the refcount of `zero_lamport_account_key` because cached keys do not bump // the refcount in the index. This means clean should *not* remove // `zero_lamport_account_key` from slot 2 - db.store_cached(2, &[(&zero_lamport_account_key, &zero_lamport_account)]); + db.store_cached( + 2, + &[(&zero_lamport_account_key, &zero_lamport_account)], + None, + ); db.add_root(1); db.add_root(2); @@ -13296,11 +13331,11 @@ pub mod tests { / \ 1 2 (root) */ - db.store_cached(0, &[(&account_key, &zero_lamport_account)]); - db.store_cached(1, &[(&account_key, &slot1_account)]); + db.store_cached(0, &[(&account_key, &zero_lamport_account)], None); + db.store_cached(1, &[(&account_key, &slot1_account)], None); // Fodder for the scan so that the lock on `account_key` is not held - db.store_cached(1, &[(&account_key2, &slot1_account)]); - db.store_cached(2, &[(&account_key, &slot2_account)]); + db.store_cached(1, &[(&account_key2, &slot1_account)], None); + db.store_cached(2, &[(&account_key, &slot2_account)], None); db.get_accounts_delta_hash(0); let max_scan_root = 0; @@ -13394,7 +13429,7 @@ pub mod tests { for data_size in 0..num_keys { let account = AccountSharedData::new(1, data_size, &Pubkey::default()); - accounts_db.store_cached(slot, &[(&Pubkey::new_unique(), &account)]); + accounts_db.store_cached(slot, &[(&Pubkey::new_unique(), &account)], None); } accounts_db.add_root(slot); @@ -13462,6 +13497,7 @@ pub mod tests { &scan_stall_key, &AccountSharedData::new(1, 0, &Pubkey::default()), )], + None, ); } @@ -13473,6 +13509,7 @@ pub mod tests { accounts_db.store_cached( *slot, &[(key, &AccountSharedData::new(1, space, &Pubkey::default()))], + None, ); } accounts_db.add_root(*slot as Slot); @@ -13517,6 +13554,7 @@ pub mod tests { accounts_db.store_cached( alive_slot, &[(key, &AccountSharedData::new(1, 0, &Pubkey::default()))], + None, ); accounts_db.add_root(alive_slot); } @@ -13854,8 +13892,8 @@ pub mod tests { let account1 = AccountSharedData::new(1, 0, AccountSharedData::default().owner()); // Store into slot 0 - db.store_cached(0, &[(&account_key1, &account1)]); - db.store_cached(0, &[(&account_key2, &account1)]); + db.store_cached(0, &[(&account_key1, &account1)], None); + db.store_cached(0, &[(&account_key2, &account1)], None); db.add_root(0); if !do_intra_cache_clean { // If we don't want the cache doing purges before flush, @@ -13869,7 +13907,7 @@ pub mod tests { } // Make account_key1 in slot 0 outdated by updating in rooted slot 1 - db.store_cached(1, &[(&account_key1, &account1)]); + db.store_cached(1, &[(&account_key1, &account1)], None); db.add_root(1); // Flushes all roots db.flush_accounts_cache(true, None); @@ -13893,7 +13931,7 @@ pub mod tests { db.shrink_candidate_slots(); // Make slot 0 dead by updating the remaining key - db.store_cached(2, &[(&account_key2, &account1)]); + db.store_cached(2, &[(&account_key2, &account1)], None); db.add_root(2); // Flushes all roots @@ -14122,6 +14160,7 @@ pub mod tests { &pubkey, &AccountSharedData::new(1, 0, AccountSharedData::default().owner()), )], + None, ); db.add_root(0); db.flush_accounts_cache(true, None); @@ -14140,7 +14179,7 @@ pub mod tests { return; } account.set_lamports(slot + 1); - db.store_cached(slot, &[(&pubkey, &account)]); + db.store_cached(slot, &[(&pubkey, &account)], None); db.add_root(slot); sleep(Duration::from_millis(RACY_SLEEP_MS)); db.flush_accounts_cache(true, None); @@ -14308,7 +14347,7 @@ pub mod tests { let num_trials = 10; for _ in 0..num_trials { let pubkey = Pubkey::new_unique(); - db.store_cached(slot, &[(&pubkey, &account)]); + db.store_cached(slot, &[(&pubkey, &account)], None); // Wait for both threads to finish flush_trial_start_sender.send(()).unwrap(); remove_trial_start_sender.send(()).unwrap(); @@ -14399,7 +14438,7 @@ pub mod tests { let slot_to_pubkey_map: HashMap = (0..num_cached_slots) .map(|slot| { let pubkey = Pubkey::new_unique(); - db.store_cached(slot, &[(&pubkey, &account)]); + db.store_cached(slot, &[(&pubkey, &account)], None); (slot, pubkey) }) .collect(); @@ -14830,19 +14869,19 @@ pub mod tests { let slot1 = 1; let account = AccountSharedData::new(111, space, &owner); - accounts_db.store_cached(slot1, &[(&pubkey, &account)]); + accounts_db.store_cached(slot1, &[(&pubkey, &account)], None); accounts_db.get_accounts_delta_hash(slot1); accounts_db.add_root(slot1); let slot2 = 2; let account = AccountSharedData::new(222, space, &owner); - accounts_db.store_cached(slot2, &[(&pubkey, &account)]); + accounts_db.store_cached(slot2, &[(&pubkey, &account)], None); accounts_db.get_accounts_delta_hash(slot2); accounts_db.add_root(slot2); let slot3 = 3; let account = AccountSharedData::new(0, space, &owner); - accounts_db.store_cached(slot3, &[(&pubkey, &account)]); + accounts_db.store_cached(slot3, &[(&pubkey, &account)], None); accounts_db.get_accounts_delta_hash(slot3); accounts_db.add_root(slot3); diff --git a/runtime/src/accounts_db/geyser_plugin_utils.rs b/runtime/src/accounts_db/geyser_plugin_utils.rs index 4789d5142..a4252036a 100644 --- a/runtime/src/accounts_db/geyser_plugin_utils.rs +++ b/runtime/src/accounts_db/geyser_plugin_utils.rs @@ -5,7 +5,7 @@ use { }, solana_measure::measure::Measure, solana_metrics::*, - solana_sdk::{account::AccountSharedData, clock::Slot, pubkey::Pubkey}, + solana_sdk::{account::AccountSharedData, clock::Slot, pubkey::Pubkey, signature::Signature}, std::collections::{hash_map::Entry, HashMap, HashSet}, }; @@ -64,10 +64,11 @@ impl AccountsDb { slot: Slot, meta: &StoredMeta, account: &AccountSharedData, + txn_signature: &Option<&Signature>, ) { if let Some(accounts_update_notifier) = &self.accounts_update_notifier { let notifier = &accounts_update_notifier.read().unwrap(); - notifier.notify_account_update(slot, meta, account); + notifier.notify_account_update(slot, meta, account, txn_signature); } } @@ -160,6 +161,7 @@ pub mod tests { account::{AccountSharedData, ReadableAccount}, clock::Slot, pubkey::Pubkey, + signature::Signature, }, std::sync::{ atomic::{AtomicBool, Ordering}, @@ -186,6 +188,7 @@ pub mod tests { slot: Slot, meta: &StoredMeta, account: &AccountSharedData, + _txn_signature: &Option<&Signature>, ) { self.accounts_notified .entry(meta.pubkey) @@ -337,24 +340,24 @@ pub mod tests { let account1 = AccountSharedData::new(account1_lamports1, 1, AccountSharedData::default().owner()); let slot0 = 0; - accounts.store_cached(slot0, &[(&key1, &account1)]); + accounts.store_cached(slot0, &[(&key1, &account1)], None); let key2 = solana_sdk::pubkey::new_rand(); let account2_lamports: u64 = 200; let account2 = AccountSharedData::new(account2_lamports, 1, AccountSharedData::default().owner()); - accounts.store_cached(slot0, &[(&key2, &account2)]); + accounts.store_cached(slot0, &[(&key2, &account2)], None); let account1_lamports2 = 2; let slot1 = 1; let account1 = AccountSharedData::new(account1_lamports2, 1, account1.owner()); - accounts.store_cached(slot1, &[(&key1, &account1)]); + accounts.store_cached(slot1, &[(&key1, &account1)], None); let key3 = solana_sdk::pubkey::new_rand(); let account3_lamports: u64 = 300; let account3 = AccountSharedData::new(account3_lamports, 1, AccountSharedData::default().owner()); - accounts.store_cached(slot1, &[(&key3, &account3)]); + accounts.store_cached(slot1, &[(&key3, &account3)], None); let notifier = notifier.write().unwrap(); assert_eq!(notifier.accounts_notified.get(&key1).unwrap().len(), 2); diff --git a/runtime/src/accounts_update_notifier_interface.rs b/runtime/src/accounts_update_notifier_interface.rs index 18ccd58f4..5b6053fc5 100644 --- a/runtime/src/accounts_update_notifier_interface.rs +++ b/runtime/src/accounts_update_notifier_interface.rs @@ -1,12 +1,18 @@ use { crate::append_vec::{StoredAccountMeta, StoredMeta}, - solana_sdk::{account::AccountSharedData, clock::Slot}, + solana_sdk::{account::AccountSharedData, clock::Slot, signature::Signature}, std::sync::{Arc, RwLock}, }; pub trait AccountsUpdateNotifierInterface: std::fmt::Debug { /// Notified when an account is updated at runtime, due to transaction activities - fn notify_account_update(&self, slot: Slot, meta: &StoredMeta, account: &AccountSharedData); + fn notify_account_update( + &self, + slot: Slot, + meta: &StoredMeta, + account: &AccountSharedData, + txn_signature: &Option<&Signature>, + ); /// Notified when the AccountsDb is initialized at start when restored /// from a snapshot.