refactor: Simplify nonce management by replacing global cache with direct fetch
Remove NonceCache singleton pattern and replace with fetch_nonce_info function that directly fetches nonce information from RPC. This simplifies the API by eliminating cache initialization and state management, making it easier for users to manage durable nonces.
This commit is contained in:
@@ -1,10 +1,7 @@
|
||||
use crate::common::SolanaRpcClient;
|
||||
use anyhow::Result;
|
||||
use solana_address_lookup_table_interface::state::AddressLookupTable;
|
||||
use solana_sdk::{
|
||||
message::{v0, AddressLookupTableAccount},
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
use solana_sdk::{message::AddressLookupTableAccount, pubkey::Pubkey};
|
||||
|
||||
pub async fn fetch_address_lookup_table_account(
|
||||
rpc: &SolanaRpcClient,
|
||||
@@ -18,53 +15,3 @@ pub async fn fetch_address_lookup_table_account(
|
||||
};
|
||||
Ok(address_lookup_table_account)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn extract_lookup_table_indexes(
|
||||
instructions: &[solana_sdk::instruction::Instruction],
|
||||
lookup_table_account: &AddressLookupTableAccount,
|
||||
) -> Option<v0::MessageAddressTableLookup> {
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
// 构建地址到索引的映射(O(1) 查找)
|
||||
let addr_to_index: HashMap<&Pubkey, u8> = lookup_table_account
|
||||
.addresses
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(idx, addr)| u8::try_from(idx).ok().map(|i| (addr, i)))
|
||||
.collect();
|
||||
|
||||
// 收集所有需要的账户及其权限
|
||||
let mut writable_indexes = Vec::new();
|
||||
let mut readonly_indexes = Vec::new();
|
||||
let mut seen = HashSet::new();
|
||||
|
||||
for instruction in instructions {
|
||||
for account_meta in &instruction.accounts {
|
||||
// 跳过已处理的账户
|
||||
if !seen.insert(&account_meta.pubkey) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 在查找表中查找账户
|
||||
if let Some(&index) = addr_to_index.get(&account_meta.pubkey) {
|
||||
if account_meta.is_writable {
|
||||
writable_indexes.push(index);
|
||||
} else {
|
||||
readonly_indexes.push(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有找到任何账户,返回 None
|
||||
if writable_indexes.is_empty() && readonly_indexes.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(v0::MessageAddressTableLookup {
|
||||
account_key: lookup_table_account.key,
|
||||
writable_indexes,
|
||||
readonly_indexes,
|
||||
})
|
||||
}
|
||||
|
||||
+20
-115
@@ -1,23 +1,10 @@
|
||||
use parking_lot::Mutex;
|
||||
use crate::common::SolanaRpcClient;
|
||||
use solana_hash::Hash;
|
||||
use solana_nonce::state::State;
|
||||
use solana_nonce::versions::Versions;
|
||||
use solana_sdk::account_utils::StateMut;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, OnceLock};
|
||||
use tracing::error;
|
||||
use crate::common::SolanaRpcClient;
|
||||
|
||||
/// NonceInfo structure to store nonce-related information
|
||||
pub struct NonceInfo {
|
||||
/// Nonce account address
|
||||
pub nonce_account: Option<Pubkey>,
|
||||
/// Current nonce value
|
||||
pub current_nonce: Hash,
|
||||
/// Whether it has been used
|
||||
pub used: bool,
|
||||
}
|
||||
|
||||
/// DurableNonceInfo structure to store durable nonce-related information
|
||||
#[derive(Clone)]
|
||||
@@ -28,109 +15,27 @@ pub struct DurableNonceInfo {
|
||||
pub current_nonce: Option<Hash>,
|
||||
}
|
||||
|
||||
/// NonceInfoStore singleton for storing and managing NonceInfo
|
||||
pub struct NonceCache {
|
||||
/// Internally stored NonceInfo data
|
||||
nonce_info: Mutex<NonceInfo>,
|
||||
}
|
||||
|
||||
// Use static OnceLock to ensure thread safety of singleton pattern
|
||||
static NONCE_CACHE: OnceLock<Arc<NonceCache>> = OnceLock::new();
|
||||
|
||||
impl NonceCache {
|
||||
/// Get NonceInfoStore singleton instance
|
||||
pub fn get_instance() -> Arc<NonceCache> {
|
||||
NONCE_CACHE
|
||||
.get_or_init(|| {
|
||||
Arc::new(NonceCache {
|
||||
nonce_info: Mutex::new(NonceInfo {
|
||||
nonce_account: None,
|
||||
current_nonce: Hash::default(),
|
||||
used: false,
|
||||
}),
|
||||
})
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
/// Initialize nonce information
|
||||
pub fn init(&self, nonce_account_str: Option<String>) {
|
||||
let nonce_account = nonce_account_str.and_then(|s| Pubkey::from_str(&s).ok());
|
||||
self.update_nonce_info_partial(nonce_account, None, Some(false));
|
||||
}
|
||||
|
||||
/// Get a copy of NonceInfo
|
||||
pub fn get_nonce_info(&self) -> NonceInfo {
|
||||
let nonce_info = self.nonce_info.lock();
|
||||
NonceInfo {
|
||||
nonce_account: nonce_info.nonce_account,
|
||||
current_nonce: nonce_info.current_nonce,
|
||||
used: nonce_info.used,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_durable_nonce_info() -> DurableNonceInfo {
|
||||
let nonce_info = Self::get_instance().get_nonce_info();
|
||||
let nonce_account = nonce_info.nonce_account;
|
||||
let current_nonce =
|
||||
if nonce_account.is_some() && nonce_info.current_nonce != Hash::default() {
|
||||
Some(nonce_info.current_nonce)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
DurableNonceInfo { nonce_account, current_nonce }
|
||||
}
|
||||
|
||||
/// Partially update NonceInfo, only update the passed fields
|
||||
pub fn update_nonce_info_partial(
|
||||
&self,
|
||||
nonce_account: Option<Pubkey>,
|
||||
current_nonce: Option<Hash>,
|
||||
used: Option<bool>,
|
||||
) {
|
||||
let mut current = self.nonce_info.lock();
|
||||
|
||||
// Only update the passed fields
|
||||
if let Some(account) = nonce_account {
|
||||
current.nonce_account = Some(account);
|
||||
}
|
||||
|
||||
if let Some(nonce) = current_nonce {
|
||||
current.current_nonce = nonce;
|
||||
}
|
||||
|
||||
if let Some(u) = used {
|
||||
current.used = u;
|
||||
}
|
||||
}
|
||||
|
||||
/// Mark nonce as used
|
||||
pub fn mark_used(&self) {
|
||||
self.update_nonce_info_partial(None, None, Some(true));
|
||||
}
|
||||
|
||||
/// Fetch nonce information using RPC
|
||||
pub async fn fetch_nonce_info_use_rpc(
|
||||
&self,
|
||||
rpc: &SolanaRpcClient,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
match rpc.get_account(&self.get_nonce_info().nonce_account.unwrap()).await {
|
||||
Ok(account) => match account.state() {
|
||||
Ok(Versions::Current(state)) => {
|
||||
if let State::Initialized(data) = *state {
|
||||
let blockhash = data.durable_nonce.as_hash();
|
||||
let old_nonce_info = self.get_nonce_info();
|
||||
if old_nonce_info.current_nonce != *blockhash {
|
||||
self.update_nonce_info_partial(None, Some(*blockhash), Some(false));
|
||||
}
|
||||
}
|
||||
/// Fetch nonce information using RPC
|
||||
pub async fn fetch_nonce_info(
|
||||
rpc: &SolanaRpcClient,
|
||||
nonce_account: Pubkey,
|
||||
) -> Option<DurableNonceInfo> {
|
||||
match rpc.get_account(&nonce_account).await {
|
||||
Ok(account) => match account.state() {
|
||||
Ok(Versions::Current(state)) => {
|
||||
if let State::Initialized(data) = *state {
|
||||
let blockhash = data.durable_nonce.as_hash();
|
||||
return Some(DurableNonceInfo {
|
||||
nonce_account: Some(nonce_account),
|
||||
current_nonce: Some(*blockhash),
|
||||
});
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Failed to get nonce account information: {:?}", e);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Failed to get nonce account information: {:?}", e);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user