Move getSecondaryIndexKeySize RPC to admin_rpc_service (#29003)
* Move getSecondaryIndexKeySize to admin_rpc_service * Add AdminRpc test framework * Move test to admin_rpc_service * Update test * Remove full-api infra
This commit is contained in:
parent
6ebbaf30c9
commit
c8dd5bbd48
|
@ -6833,6 +6833,7 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_yaml 0.9.13",
|
"serde_yaml 0.9.13",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
|
"solana-account-decoder",
|
||||||
"solana-clap-utils",
|
"solana-clap-utils",
|
||||||
"solana-cli-config",
|
"solana-cli-config",
|
||||||
"solana-core",
|
"solana-core",
|
||||||
|
@ -6859,6 +6860,7 @@ dependencies = [
|
||||||
"solana-tpu-client",
|
"solana-tpu-client",
|
||||||
"solana-version",
|
"solana-version",
|
||||||
"solana-vote-program",
|
"solana-vote-program",
|
||||||
|
"spl-token-2022",
|
||||||
"symlink",
|
"symlink",
|
||||||
"tikv-jemallocator",
|
"tikv-jemallocator",
|
||||||
]
|
]
|
||||||
|
|
392
rpc/src/rpc.rs
392
rpc/src/rpc.rs
|
@ -114,14 +114,6 @@ type RpcCustomResult<T> = std::result::Result<T, RpcCustomError>;
|
||||||
pub const MAX_REQUEST_BODY_SIZE: usize = 50 * (1 << 10); // 50kB
|
pub const MAX_REQUEST_BODY_SIZE: usize = 50 * (1 << 10); // 50kB
|
||||||
pub const PERFORMANCE_SAMPLES_LIMIT: usize = 720;
|
pub const PERFORMANCE_SAMPLES_LIMIT: usize = 720;
|
||||||
|
|
||||||
fn rpc_account_index_from_account_index(account_index: &AccountIndex) -> RpcAccountIndex {
|
|
||||||
match account_index {
|
|
||||||
AccountIndex::ProgramId => RpcAccountIndex::ProgramId,
|
|
||||||
AccountIndex::SplTokenOwner => RpcAccountIndex::SplTokenOwner,
|
|
||||||
AccountIndex::SplTokenMint => RpcAccountIndex::SplTokenMint,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_response<T>(bank: &Bank, value: T) -> RpcResponse<T> {
|
fn new_response<T>(bank: &Bank, value: T) -> RpcResponse<T> {
|
||||||
RpcResponse {
|
RpcResponse {
|
||||||
context: RpcResponseContext::new(bank.slot()),
|
context: RpcResponseContext::new(bank.slot()),
|
||||||
|
@ -504,51 +496,6 @@ impl JsonRpcRequestProcessor {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_secondary_index_key_size(
|
|
||||||
&self,
|
|
||||||
index_key: &Pubkey,
|
|
||||||
config: Option<RpcContextConfig>,
|
|
||||||
) -> Result<RpcResponse<HashMap<RpcAccountIndex, usize>>> {
|
|
||||||
// Acquire the bank
|
|
||||||
let bank = self.get_bank_with_config(config.unwrap_or_default())?;
|
|
||||||
|
|
||||||
// Exit if secondary indexes are not enabled
|
|
||||||
if self.config.account_indexes.is_empty() {
|
|
||||||
debug!("get_secondary_index_key_size: secondary index not enabled.");
|
|
||||||
return Ok(new_response(&bank, HashMap::new()));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Make sure the requested key is not explicitly excluded
|
|
||||||
if !self.config.account_indexes.include_key(index_key) {
|
|
||||||
return Err(RpcCustomError::KeyExcludedFromSecondaryIndex {
|
|
||||||
index_key: index_key.to_string(),
|
|
||||||
}
|
|
||||||
.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grab a ref to the AccountsIndex for this Bank
|
|
||||||
let accounts_index = &bank.accounts().accounts_db.accounts_index;
|
|
||||||
|
|
||||||
// Find the size of the key in every index where it exists
|
|
||||||
let found_sizes = self
|
|
||||||
.config
|
|
||||||
.account_indexes
|
|
||||||
.indexes
|
|
||||||
.iter()
|
|
||||||
.filter_map(|index| {
|
|
||||||
accounts_index
|
|
||||||
.get_index_key_size(index, index_key)
|
|
||||||
.map(|size| (rpc_account_index_from_account_index(index), size))
|
|
||||||
})
|
|
||||||
.collect::<HashMap<_, _>>();
|
|
||||||
|
|
||||||
// Note: Will return an empty HashMap if no keys are found.
|
|
||||||
if found_sizes.is_empty() {
|
|
||||||
debug!("get_secondary_index_key_size: key not found in the secondary index.");
|
|
||||||
}
|
|
||||||
Ok(new_response(&bank, found_sizes))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_inflation_reward(
|
pub async fn get_inflation_reward(
|
||||||
&self,
|
&self,
|
||||||
addresses: Vec<Pubkey>,
|
addresses: Vec<Pubkey>,
|
||||||
|
@ -2262,7 +2209,7 @@ fn verify_filter(input: &RpcFilterType) -> Result<()> {
|
||||||
.map_err(|e| Error::invalid_params(format!("Invalid param: {e:?}")))
|
.map_err(|e| Error::invalid_params(format!("Invalid param: {e:?}")))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_pubkey(input: &str) -> Result<Pubkey> {
|
pub fn verify_pubkey(input: &str) -> Result<Pubkey> {
|
||||||
input
|
input
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|e| Error::invalid_params(format!("Invalid param: {e:?}")))
|
.map_err(|e| Error::invalid_params(format!("Invalid param: {e:?}")))
|
||||||
|
@ -3155,16 +3102,6 @@ pub mod rpc_accounts_scan {
|
||||||
config: Option<RpcProgramAccountsConfig>,
|
config: Option<RpcProgramAccountsConfig>,
|
||||||
) -> Result<OptionalContext<Vec<RpcKeyedAccount>>>;
|
) -> Result<OptionalContext<Vec<RpcKeyedAccount>>>;
|
||||||
|
|
||||||
// This method does not itself do an accounts scan, but returns data only relevant to nodes
|
|
||||||
// that do support accounts-scan RPC apis
|
|
||||||
#[rpc(meta, name = "getSecondaryIndexKeySize")]
|
|
||||||
fn get_secondary_index_key_size(
|
|
||||||
&self,
|
|
||||||
meta: Self::Metadata,
|
|
||||||
pubkey_str: String,
|
|
||||||
config: Option<RpcContextConfig>,
|
|
||||||
) -> Result<RpcResponse<HashMap<RpcAccountIndex, usize>>>;
|
|
||||||
|
|
||||||
#[rpc(meta, name = "getLargestAccounts")]
|
#[rpc(meta, name = "getLargestAccounts")]
|
||||||
fn get_largest_accounts(
|
fn get_largest_accounts(
|
||||||
&self,
|
&self,
|
||||||
|
@ -3245,20 +3182,6 @@ pub mod rpc_accounts_scan {
|
||||||
meta.get_program_accounts(&program_id, config, filters, with_context)
|
meta.get_program_accounts(&program_id, config, filters, with_context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_secondary_index_key_size(
|
|
||||||
&self,
|
|
||||||
meta: Self::Metadata,
|
|
||||||
pubkey_str: String,
|
|
||||||
config: Option<RpcContextConfig>,
|
|
||||||
) -> Result<RpcResponse<HashMap<RpcAccountIndex, usize>>> {
|
|
||||||
debug!(
|
|
||||||
"get_secondary_index_key_size rpc request received: {:?}",
|
|
||||||
pubkey_str
|
|
||||||
);
|
|
||||||
let index_key = verify_pubkey(&pubkey_str)?;
|
|
||||||
meta.get_secondary_index_key_size(&index_key, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_largest_accounts(
|
fn get_largest_accounts(
|
||||||
&self,
|
&self,
|
||||||
meta: Self::Metadata,
|
meta: Self::Metadata,
|
||||||
|
@ -4562,7 +4485,7 @@ fn sanitize_transaction(
|
||||||
.map_err(|err| Error::invalid_params(format!("invalid transaction: {err}")))
|
.map_err(|err| Error::invalid_params(format!("invalid transaction: {err}")))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn create_validator_exit(exit: &Arc<AtomicBool>) -> Arc<RwLock<Exit>> {
|
pub fn create_validator_exit(exit: &Arc<AtomicBool>) -> Arc<RwLock<Exit>> {
|
||||||
let mut validator_exit = Exit::default();
|
let mut validator_exit = Exit::default();
|
||||||
let exit_ = exit.clone();
|
let exit_ = exit.clone();
|
||||||
validator_exit.register_exit(Box::new(move || exit_.store(true, Ordering::Relaxed)));
|
validator_exit.register_exit(Box::new(move || exit_.store(true, Ordering::Relaxed)));
|
||||||
|
@ -4672,7 +4595,6 @@ pub mod tests {
|
||||||
jsonrpc_core::{futures, ErrorCode, MetaIoHandler, Output, Response, Value},
|
jsonrpc_core::{futures, ErrorCode, MetaIoHandler, Output, Response, Value},
|
||||||
jsonrpc_core_client::transports::local,
|
jsonrpc_core_client::transports::local,
|
||||||
serde::de::DeserializeOwned,
|
serde::de::DeserializeOwned,
|
||||||
solana_account_decoder::parse_token::spl_token_pubkey,
|
|
||||||
solana_address_lookup_table_program::state::{AddressLookupTable, LookupTableMeta},
|
solana_address_lookup_table_program::state::{AddressLookupTable, LookupTableMeta},
|
||||||
solana_entry::entry::next_versioned_entry,
|
solana_entry::entry::next_versioned_entry,
|
||||||
solana_gossip::{contact_info::ContactInfo, socketaddr},
|
solana_gossip::{contact_info::ContactInfo, socketaddr},
|
||||||
|
@ -7433,316 +7355,6 @@ pub mod tests {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_secondary_index_key_sizes() {
|
|
||||||
for secondary_index_enabled in [true, false] {
|
|
||||||
let account_indexes = if secondary_index_enabled {
|
|
||||||
AccountSecondaryIndexes {
|
|
||||||
keys: None,
|
|
||||||
indexes: HashSet::from([
|
|
||||||
AccountIndex::ProgramId,
|
|
||||||
AccountIndex::SplTokenMint,
|
|
||||||
AccountIndex::SplTokenOwner,
|
|
||||||
]),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
AccountSecondaryIndexes::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
// RPC & Bank Setup
|
|
||||||
let rpc = RpcHandler::start_with_config(JsonRpcConfig {
|
|
||||||
enable_rpc_transaction_history: true,
|
|
||||||
account_indexes,
|
|
||||||
..JsonRpcConfig::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
let bank = rpc.working_bank();
|
|
||||||
let RpcHandler { io, meta, .. } = rpc;
|
|
||||||
|
|
||||||
// Pubkeys
|
|
||||||
let token_account1_pubkey = Pubkey::new_unique();
|
|
||||||
let token_account2_pubkey = Pubkey::new_unique();
|
|
||||||
let token_account3_pubkey = Pubkey::new_unique();
|
|
||||||
let mint1_pubkey = Pubkey::new_unique();
|
|
||||||
let mint2_pubkey = Pubkey::new_unique();
|
|
||||||
let wallet1_pubkey = Pubkey::new_unique();
|
|
||||||
let wallet2_pubkey = Pubkey::new_unique();
|
|
||||||
let non_existent_pubkey = Pubkey::new_unique();
|
|
||||||
let delegate = spl_token_pubkey(&Pubkey::new_unique());
|
|
||||||
|
|
||||||
let mut num_default_spl_token_program_accounts = 0;
|
|
||||||
let mut num_default_system_program_accounts = 0;
|
|
||||||
|
|
||||||
if !secondary_index_enabled {
|
|
||||||
// Test first with no accounts added & no secondary indexes enabled:
|
|
||||||
let req = format!(
|
|
||||||
r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{token_account1_pubkey}"]}}"#,
|
|
||||||
);
|
|
||||||
let res = io.handle_request_sync(&req, meta.clone());
|
|
||||||
let result: Value = serde_json::from_str(&res.expect("actual response"))
|
|
||||||
.expect("actual response deserialization");
|
|
||||||
let sizes: HashMap<RpcAccountIndex, usize> =
|
|
||||||
serde_json::from_value(result["result"]["value"].clone()).unwrap();
|
|
||||||
assert!(sizes.is_empty());
|
|
||||||
} else {
|
|
||||||
// Count SPL Token Program Default Accounts
|
|
||||||
let req = format!(
|
|
||||||
r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{}"]}}"#,
|
|
||||||
inline_spl_token::id(),
|
|
||||||
);
|
|
||||||
let res = io.handle_request_sync(&req, meta.clone());
|
|
||||||
let result: Value = serde_json::from_str(&res.expect("actual response"))
|
|
||||||
.expect("actual response deserialization");
|
|
||||||
let sizes: HashMap<RpcAccountIndex, usize> =
|
|
||||||
serde_json::from_value(result["result"]["value"].clone()).unwrap();
|
|
||||||
assert_eq!(sizes.len(), 1);
|
|
||||||
num_default_spl_token_program_accounts =
|
|
||||||
*sizes.get(&RpcAccountIndex::ProgramId).unwrap();
|
|
||||||
// Count System Program Default Accounts
|
|
||||||
let req = format!(
|
|
||||||
r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{}"]}}"#,
|
|
||||||
system_program::id(),
|
|
||||||
);
|
|
||||||
let res = io.handle_request_sync(&req, meta.clone());
|
|
||||||
let result: Value = serde_json::from_str(&res.expect("actual response"))
|
|
||||||
.expect("actual response deserialization");
|
|
||||||
let sizes: HashMap<RpcAccountIndex, usize> =
|
|
||||||
serde_json::from_value(result["result"]["value"].clone()).unwrap();
|
|
||||||
assert_eq!(sizes.len(), 1);
|
|
||||||
num_default_system_program_accounts =
|
|
||||||
*sizes.get(&RpcAccountIndex::ProgramId).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add 2 basic wallet accounts
|
|
||||||
let wallet1_account = AccountSharedData::from(Account {
|
|
||||||
lamports: 11111111,
|
|
||||||
owner: system_program::id(),
|
|
||||||
..Account::default()
|
|
||||||
});
|
|
||||||
bank.store_account(&wallet1_pubkey, &wallet1_account);
|
|
||||||
let wallet2_account = AccountSharedData::from(Account {
|
|
||||||
lamports: 11111111,
|
|
||||||
owner: system_program::id(),
|
|
||||||
..Account::default()
|
|
||||||
});
|
|
||||||
bank.store_account(&wallet2_pubkey, &wallet2_account);
|
|
||||||
|
|
||||||
// Add a token account
|
|
||||||
let mut account1_data = vec![0; TokenAccount::get_packed_len()];
|
|
||||||
let token_account1 = TokenAccount {
|
|
||||||
mint: spl_token_pubkey(&mint1_pubkey),
|
|
||||||
owner: spl_token_pubkey(&wallet1_pubkey),
|
|
||||||
delegate: COption::Some(delegate),
|
|
||||||
amount: 420,
|
|
||||||
state: TokenAccountState::Initialized,
|
|
||||||
is_native: COption::None,
|
|
||||||
delegated_amount: 30,
|
|
||||||
close_authority: COption::Some(spl_token_pubkey(&wallet1_pubkey)),
|
|
||||||
};
|
|
||||||
TokenAccount::pack(token_account1, &mut account1_data).unwrap();
|
|
||||||
let token_account1 = AccountSharedData::from(Account {
|
|
||||||
lamports: 111,
|
|
||||||
data: account1_data.to_vec(),
|
|
||||||
owner: inline_spl_token::id(),
|
|
||||||
..Account::default()
|
|
||||||
});
|
|
||||||
bank.store_account(&token_account1_pubkey, &token_account1);
|
|
||||||
|
|
||||||
// Add the mint
|
|
||||||
let mut mint1_data = vec![0; Mint::get_packed_len()];
|
|
||||||
let mint1_state = Mint {
|
|
||||||
mint_authority: COption::Some(spl_token_pubkey(&wallet1_pubkey)),
|
|
||||||
supply: 500,
|
|
||||||
decimals: 2,
|
|
||||||
is_initialized: true,
|
|
||||||
freeze_authority: COption::Some(spl_token_pubkey(&wallet1_pubkey)),
|
|
||||||
};
|
|
||||||
Mint::pack(mint1_state, &mut mint1_data).unwrap();
|
|
||||||
let mint_account1 = AccountSharedData::from(Account {
|
|
||||||
lamports: 222,
|
|
||||||
data: mint1_data.to_vec(),
|
|
||||||
owner: inline_spl_token::id(),
|
|
||||||
..Account::default()
|
|
||||||
});
|
|
||||||
bank.store_account(&mint1_pubkey, &mint_account1);
|
|
||||||
|
|
||||||
// Add another token account with the different owner, but same delegate, and mint
|
|
||||||
let mut account2_data = vec![0; TokenAccount::get_packed_len()];
|
|
||||||
let token_account2 = TokenAccount {
|
|
||||||
mint: spl_token_pubkey(&mint1_pubkey),
|
|
||||||
owner: spl_token_pubkey(&wallet2_pubkey),
|
|
||||||
delegate: COption::Some(delegate),
|
|
||||||
amount: 420,
|
|
||||||
state: TokenAccountState::Initialized,
|
|
||||||
is_native: COption::None,
|
|
||||||
delegated_amount: 30,
|
|
||||||
close_authority: COption::Some(spl_token_pubkey(&wallet2_pubkey)),
|
|
||||||
};
|
|
||||||
TokenAccount::pack(token_account2, &mut account2_data).unwrap();
|
|
||||||
let token_account2 = AccountSharedData::from(Account {
|
|
||||||
lamports: 333,
|
|
||||||
data: account2_data.to_vec(),
|
|
||||||
owner: inline_spl_token::id(),
|
|
||||||
..Account::default()
|
|
||||||
});
|
|
||||||
bank.store_account(&token_account2_pubkey, &token_account2);
|
|
||||||
|
|
||||||
// Add another token account with the same owner and delegate but different mint
|
|
||||||
let mut account3_data = vec![0; TokenAccount::get_packed_len()];
|
|
||||||
let token_account3 = TokenAccount {
|
|
||||||
mint: spl_token_pubkey(&mint2_pubkey),
|
|
||||||
owner: spl_token_pubkey(&wallet2_pubkey),
|
|
||||||
delegate: COption::Some(delegate),
|
|
||||||
amount: 42,
|
|
||||||
state: TokenAccountState::Initialized,
|
|
||||||
is_native: COption::None,
|
|
||||||
delegated_amount: 30,
|
|
||||||
close_authority: COption::Some(spl_token_pubkey(&wallet2_pubkey)),
|
|
||||||
};
|
|
||||||
TokenAccount::pack(token_account3, &mut account3_data).unwrap();
|
|
||||||
let token_account3 = AccountSharedData::from(Account {
|
|
||||||
lamports: 444,
|
|
||||||
data: account3_data.to_vec(),
|
|
||||||
owner: inline_spl_token::id(),
|
|
||||||
..Account::default()
|
|
||||||
});
|
|
||||||
bank.store_account(&token_account3_pubkey, &token_account3);
|
|
||||||
|
|
||||||
// Add the new mint
|
|
||||||
let mut mint2_data = vec![0; Mint::get_packed_len()];
|
|
||||||
let mint2_state = Mint {
|
|
||||||
mint_authority: COption::Some(spl_token_pubkey(&wallet2_pubkey)),
|
|
||||||
supply: 200,
|
|
||||||
decimals: 3,
|
|
||||||
is_initialized: true,
|
|
||||||
freeze_authority: COption::Some(spl_token_pubkey(&wallet2_pubkey)),
|
|
||||||
};
|
|
||||||
Mint::pack(mint2_state, &mut mint2_data).unwrap();
|
|
||||||
let mint_account2 = AccountSharedData::from(Account {
|
|
||||||
lamports: 555,
|
|
||||||
data: mint2_data.to_vec(),
|
|
||||||
owner: inline_spl_token::id(),
|
|
||||||
..Account::default()
|
|
||||||
});
|
|
||||||
bank.store_account(&mint2_pubkey, &mint_account2);
|
|
||||||
|
|
||||||
// Accounts should now look like the following:
|
|
||||||
//
|
|
||||||
// -----system_program------
|
|
||||||
// / \
|
|
||||||
// /-(owns) \-(owns)
|
|
||||||
// / \
|
|
||||||
// wallet1 ---wallet2---
|
|
||||||
// / / \
|
|
||||||
// /-(SPL::owns) /-(SPL::owns) \-(SPL::owns)
|
|
||||||
// / / \
|
|
||||||
// token_account1 token_account2 token_account3
|
|
||||||
// \ / /
|
|
||||||
// \-(SPL::mint) /-(SPL::mint) /-(SPL::mint)
|
|
||||||
// \ / /
|
|
||||||
// --mint_account1-- mint_account2
|
|
||||||
|
|
||||||
if secondary_index_enabled {
|
|
||||||
// ----------- Test for a non-existant key -----------
|
|
||||||
let req = format!(
|
|
||||||
r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{non_existent_pubkey}"]}}"#,
|
|
||||||
);
|
|
||||||
let res = io.handle_request_sync(&req, meta.clone());
|
|
||||||
let result: Value = serde_json::from_str(&res.expect("actual response"))
|
|
||||||
.expect("actual response deserialization");
|
|
||||||
let sizes: HashMap<RpcAccountIndex, usize> =
|
|
||||||
serde_json::from_value(result["result"]["value"].clone()).unwrap();
|
|
||||||
assert!(sizes.is_empty());
|
|
||||||
// --------------- Test Queries ---------------
|
|
||||||
// 1) Wallet1 - Owns 1 SPL Token
|
|
||||||
let req = format!(
|
|
||||||
r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{wallet1_pubkey}"]}}"#,
|
|
||||||
);
|
|
||||||
let res = io.handle_request_sync(&req, meta.clone());
|
|
||||||
let result: Value = serde_json::from_str(&res.expect("actual response"))
|
|
||||||
.expect("actual response deserialization");
|
|
||||||
let sizes: HashMap<RpcAccountIndex, usize> =
|
|
||||||
serde_json::from_value(result["result"]["value"].clone()).unwrap();
|
|
||||||
assert_eq!(sizes.len(), 1);
|
|
||||||
assert_eq!(*sizes.get(&RpcAccountIndex::SplTokenOwner).unwrap(), 1);
|
|
||||||
// 2) Wallet2 - Owns 2 SPL Tokens
|
|
||||||
let req = format!(
|
|
||||||
r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{wallet2_pubkey}"]}}"#,
|
|
||||||
);
|
|
||||||
let res = io.handle_request_sync(&req, meta.clone());
|
|
||||||
let result: Value = serde_json::from_str(&res.expect("actual response"))
|
|
||||||
.expect("actual response deserialization");
|
|
||||||
let sizes: HashMap<RpcAccountIndex, usize> =
|
|
||||||
serde_json::from_value(result["result"]["value"].clone()).unwrap();
|
|
||||||
assert_eq!(sizes.len(), 1);
|
|
||||||
assert_eq!(*sizes.get(&RpcAccountIndex::SplTokenOwner).unwrap(), 2);
|
|
||||||
// 3) Mint1 - Is in 2 SPL Accounts
|
|
||||||
let req = format!(
|
|
||||||
r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{mint1_pubkey}"]}}"#,
|
|
||||||
);
|
|
||||||
let res = io.handle_request_sync(&req, meta.clone());
|
|
||||||
let result: Value = serde_json::from_str(&res.expect("actual response"))
|
|
||||||
.expect("actual response deserialization");
|
|
||||||
let sizes: HashMap<RpcAccountIndex, usize> =
|
|
||||||
serde_json::from_value(result["result"]["value"].clone()).unwrap();
|
|
||||||
assert_eq!(sizes.len(), 1);
|
|
||||||
assert_eq!(*sizes.get(&RpcAccountIndex::SplTokenMint).unwrap(), 2);
|
|
||||||
// 4) Mint2 - Is in 1 SPL Account
|
|
||||||
let req = format!(
|
|
||||||
r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{mint2_pubkey}"]}}"#,
|
|
||||||
);
|
|
||||||
let res = io.handle_request_sync(&req, meta.clone());
|
|
||||||
let result: Value = serde_json::from_str(&res.expect("actual response"))
|
|
||||||
.expect("actual response deserialization");
|
|
||||||
let sizes: HashMap<RpcAccountIndex, usize> =
|
|
||||||
serde_json::from_value(result["result"]["value"].clone()).unwrap();
|
|
||||||
assert_eq!(sizes.len(), 1);
|
|
||||||
assert_eq!(*sizes.get(&RpcAccountIndex::SplTokenMint).unwrap(), 1);
|
|
||||||
// 5) SPL Token Program Owns 6 Accounts - 1 Default, 5 created above.
|
|
||||||
let req = format!(
|
|
||||||
r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{}"]}}"#,
|
|
||||||
inline_spl_token::id(),
|
|
||||||
);
|
|
||||||
let res = io.handle_request_sync(&req, meta.clone());
|
|
||||||
let result: Value = serde_json::from_str(&res.expect("actual response"))
|
|
||||||
.expect("actual response deserialization");
|
|
||||||
let sizes: HashMap<RpcAccountIndex, usize> =
|
|
||||||
serde_json::from_value(result["result"]["value"].clone()).unwrap();
|
|
||||||
assert_eq!(sizes.len(), 1);
|
|
||||||
assert_eq!(
|
|
||||||
*sizes.get(&RpcAccountIndex::ProgramId).unwrap(),
|
|
||||||
(num_default_spl_token_program_accounts + 5)
|
|
||||||
);
|
|
||||||
// 5) System Program Owns 4 Accounts + 2 Default, 2 created above.
|
|
||||||
let req = format!(
|
|
||||||
r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{}"]}}"#,
|
|
||||||
system_program::id(),
|
|
||||||
);
|
|
||||||
let res = io.handle_request_sync(&req, meta.clone());
|
|
||||||
let result: Value = serde_json::from_str(&res.expect("actual response"))
|
|
||||||
.expect("actual response deserialization");
|
|
||||||
let sizes: HashMap<RpcAccountIndex, usize> =
|
|
||||||
serde_json::from_value(result["result"]["value"].clone()).unwrap();
|
|
||||||
assert_eq!(sizes.len(), 1);
|
|
||||||
assert_eq!(
|
|
||||||
*sizes.get(&RpcAccountIndex::ProgramId).unwrap(),
|
|
||||||
(num_default_system_program_accounts + 2)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// ------------ Secondary Indexes Disabled ------------
|
|
||||||
let req = format!(
|
|
||||||
r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{token_account2_pubkey}"]}}"#,
|
|
||||||
);
|
|
||||||
let res = io.handle_request_sync(&req, meta.clone());
|
|
||||||
let result: Value = serde_json::from_str(&res.expect("actual response"))
|
|
||||||
.expect("actual response deserialization");
|
|
||||||
let sizes: HashMap<RpcAccountIndex, usize> =
|
|
||||||
serde_json::from_value(result["result"]["value"].clone()).unwrap();
|
|
||||||
assert!(sizes.is_empty());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_token_rpcs() {
|
fn test_token_rpcs() {
|
||||||
for program_id in solana_account_decoder::parse_token::spl_token_ids() {
|
for program_id in solana_account_decoder::parse_token::spl_token_ids() {
|
||||||
|
|
|
@ -59,6 +59,10 @@ solana-version = { path = "../version", version = "=1.15.0" }
|
||||||
solana-vote-program = { path = "../programs/vote", version = "=1.15.0" }
|
solana-vote-program = { path = "../programs/vote", version = "=1.15.0" }
|
||||||
symlink = "0.1.0"
|
symlink = "0.1.0"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
solana-account-decoder = { path = "../account-decoder", version = "=1.15.0" }
|
||||||
|
spl-token-2022 = { version = "=0.5.0", features = ["no-entrypoint"] }
|
||||||
|
|
||||||
[target.'cfg(not(target_env = "msvc"))'.dependencies]
|
[target.'cfg(not(target_env = "msvc"))'.dependencies]
|
||||||
jemallocator = { package = "tikv-jemallocator", version = "0.4.1", features = ["unprefixed_malloc_on_supported_platforms"] }
|
jemallocator = { package = "tikv-jemallocator", version = "0.4.1", features = ["unprefixed_malloc_on_supported_platforms"] }
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,9 @@ use {
|
||||||
consensus::Tower, tower_storage::TowerStorage, validator::ValidatorStartProgress,
|
consensus::Tower, tower_storage::TowerStorage, validator::ValidatorStartProgress,
|
||||||
},
|
},
|
||||||
solana_gossip::{cluster_info::ClusterInfo, contact_info::ContactInfo},
|
solana_gossip::{cluster_info::ClusterInfo, contact_info::ContactInfo},
|
||||||
solana_runtime::bank_forks::BankForks,
|
solana_rpc::rpc::verify_pubkey,
|
||||||
|
solana_rpc_client_api::{config::RpcAccountIndex, custom_error::RpcCustomError},
|
||||||
|
solana_runtime::{accounts_index::AccountIndex, bank_forks::BankForks},
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
exit::Exit,
|
exit::Exit,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
|
@ -201,6 +203,13 @@ pub trait AdminRpc {
|
||||||
|
|
||||||
#[rpc(meta, name = "setRepairWhitelist")]
|
#[rpc(meta, name = "setRepairWhitelist")]
|
||||||
fn set_repair_whitelist(&self, meta: Self::Metadata, whitelist: Vec<Pubkey>) -> Result<()>;
|
fn set_repair_whitelist(&self, meta: Self::Metadata, whitelist: Vec<Pubkey>) -> Result<()>;
|
||||||
|
|
||||||
|
#[rpc(meta, name = "getSecondaryIndexKeySize")]
|
||||||
|
fn get_secondary_index_key_size(
|
||||||
|
&self,
|
||||||
|
meta: Self::Metadata,
|
||||||
|
pubkey_str: String,
|
||||||
|
) -> Result<HashMap<RpcAccountIndex, usize>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AdminRpcImpl;
|
pub struct AdminRpcImpl;
|
||||||
|
@ -368,6 +377,58 @@ impl AdminRpc for AdminRpcImpl {
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_secondary_index_key_size(
|
||||||
|
&self,
|
||||||
|
meta: Self::Metadata,
|
||||||
|
pubkey_str: String,
|
||||||
|
) -> Result<HashMap<RpcAccountIndex, usize>> {
|
||||||
|
debug!(
|
||||||
|
"get_secondary_index_key_size rpc request received: {:?}",
|
||||||
|
pubkey_str
|
||||||
|
);
|
||||||
|
let index_key = verify_pubkey(&pubkey_str)?;
|
||||||
|
meta.with_post_init(|post_init| {
|
||||||
|
let bank = post_init.bank_forks.read().unwrap().root_bank();
|
||||||
|
|
||||||
|
// Take ref to enabled AccountSecondaryIndexes
|
||||||
|
let enabled_account_indexes = &bank.accounts().accounts_db.account_indexes;
|
||||||
|
|
||||||
|
// Exit if secondary indexes are not enabled
|
||||||
|
if enabled_account_indexes.is_empty() {
|
||||||
|
debug!("get_secondary_index_key_size: secondary index not enabled.");
|
||||||
|
return Ok(HashMap::new());
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make sure the requested key is not explicitly excluded
|
||||||
|
if !enabled_account_indexes.include_key(&index_key) {
|
||||||
|
return Err(RpcCustomError::KeyExcludedFromSecondaryIndex {
|
||||||
|
index_key: index_key.to_string(),
|
||||||
|
}
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab a ref to the AccountsDbfor this Bank
|
||||||
|
let accounts_index = &bank.accounts().accounts_db.accounts_index;
|
||||||
|
|
||||||
|
// Find the size of the key in every index where it exists
|
||||||
|
let found_sizes = enabled_account_indexes
|
||||||
|
.indexes
|
||||||
|
.iter()
|
||||||
|
.filter_map(|index| {
|
||||||
|
accounts_index
|
||||||
|
.get_index_key_size(index, &index_key)
|
||||||
|
.map(|size| (rpc_account_index_from_account_index(index), size))
|
||||||
|
})
|
||||||
|
.collect::<HashMap<_, _>>();
|
||||||
|
|
||||||
|
// Note: Will return an empty HashMap if no keys are found.
|
||||||
|
if found_sizes.is_empty() {
|
||||||
|
debug!("get_secondary_index_key_size: key not found in the secondary index.");
|
||||||
|
}
|
||||||
|
Ok(found_sizes)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AdminRpcImpl {
|
impl AdminRpcImpl {
|
||||||
|
@ -417,6 +478,14 @@ impl AdminRpcImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn rpc_account_index_from_account_index(account_index: &AccountIndex) -> RpcAccountIndex {
|
||||||
|
match account_index {
|
||||||
|
AccountIndex::ProgramId => RpcAccountIndex::ProgramId,
|
||||||
|
AccountIndex::SplTokenOwner => RpcAccountIndex::SplTokenOwner,
|
||||||
|
AccountIndex::SplTokenMint => RpcAccountIndex::SplTokenMint,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Start the Admin RPC interface
|
// Start the Admin RPC interface
|
||||||
pub fn run(ledger_path: &Path, metadata: AdminRpcRequestMetadata) {
|
pub fn run(ledger_path: &Path, metadata: AdminRpcRequestMetadata) {
|
||||||
let admin_rpc_path = admin_rpc_path(ledger_path);
|
let admin_rpc_path = admin_rpc_path(ledger_path);
|
||||||
|
@ -529,3 +598,417 @@ pub fn load_staked_nodes_overrides(
|
||||||
Err(format!("Staked nodes overrides provided '{path}' a non-existing file path.").into())
|
Err(format!("Staked nodes overrides provided '{path}' a non-existing file path.").into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use {
|
||||||
|
super::*,
|
||||||
|
serde_json::Value,
|
||||||
|
solana_account_decoder::parse_token::spl_token_pubkey,
|
||||||
|
solana_core::tower_storage::NullTowerStorage,
|
||||||
|
solana_ledger::genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
||||||
|
solana_rpc::rpc::create_validator_exit,
|
||||||
|
solana_runtime::{
|
||||||
|
accounts_index::AccountSecondaryIndexes,
|
||||||
|
bank::{Bank, BankTestConfig},
|
||||||
|
inline_spl_token,
|
||||||
|
},
|
||||||
|
solana_sdk::{
|
||||||
|
account::{Account, AccountSharedData},
|
||||||
|
system_program,
|
||||||
|
},
|
||||||
|
solana_streamer::socket::SocketAddrSpace,
|
||||||
|
spl_token_2022::{
|
||||||
|
solana_program::{program_option::COption, program_pack::Pack},
|
||||||
|
state::{Account as TokenAccount, AccountState as TokenAccountState, Mint},
|
||||||
|
},
|
||||||
|
std::{collections::HashSet, sync::atomic::AtomicBool},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct TestConfig {
|
||||||
|
account_indexes: AccountSecondaryIndexes,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RpcHandler {
|
||||||
|
io: MetaIoHandler<AdminRpcRequestMetadata>,
|
||||||
|
meta: AdminRpcRequestMetadata,
|
||||||
|
bank_forks: Arc<RwLock<BankForks>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RpcHandler {
|
||||||
|
fn _start() -> Self {
|
||||||
|
Self::start_with_config(TestConfig::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_with_config(config: TestConfig) -> Self {
|
||||||
|
let identity = Pubkey::new_unique();
|
||||||
|
let cluster_info = Arc::new(ClusterInfo::new(
|
||||||
|
ContactInfo {
|
||||||
|
id: identity,
|
||||||
|
..ContactInfo::default()
|
||||||
|
},
|
||||||
|
Arc::new(Keypair::new()),
|
||||||
|
SocketAddrSpace::Unspecified,
|
||||||
|
));
|
||||||
|
let exit = Arc::new(AtomicBool::new(false));
|
||||||
|
let validator_exit = create_validator_exit(&exit);
|
||||||
|
let (bank_forks, vote_keypair) = new_bank_forks_with_config(BankTestConfig {
|
||||||
|
secondary_indexes: config.account_indexes,
|
||||||
|
});
|
||||||
|
let vote_account = vote_keypair.pubkey();
|
||||||
|
let start_progress = Arc::new(RwLock::new(ValidatorStartProgress::default()));
|
||||||
|
let repair_whitelist = Arc::new(RwLock::new(HashSet::new()));
|
||||||
|
let meta = AdminRpcRequestMetadata {
|
||||||
|
rpc_addr: None,
|
||||||
|
start_time: SystemTime::now(),
|
||||||
|
start_progress,
|
||||||
|
validator_exit,
|
||||||
|
authorized_voter_keypairs: Arc::new(RwLock::new(vec![vote_keypair])),
|
||||||
|
tower_storage: Arc::new(NullTowerStorage {}),
|
||||||
|
post_init: Arc::new(RwLock::new(Some(AdminRpcRequestMetadataPostInit {
|
||||||
|
cluster_info,
|
||||||
|
bank_forks: bank_forks.clone(),
|
||||||
|
vote_account,
|
||||||
|
repair_whitelist,
|
||||||
|
}))),
|
||||||
|
staked_nodes_overrides: Arc::new(RwLock::new(HashMap::new())),
|
||||||
|
};
|
||||||
|
let mut io = MetaIoHandler::default();
|
||||||
|
io.extend_with(AdminRpcImpl.to_delegate());
|
||||||
|
|
||||||
|
Self {
|
||||||
|
io,
|
||||||
|
meta,
|
||||||
|
bank_forks,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn root_bank(&self) -> Arc<Bank> {
|
||||||
|
self.bank_forks.read().unwrap().root_bank()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_bank_forks_with_config(
|
||||||
|
config: BankTestConfig,
|
||||||
|
) -> (Arc<RwLock<BankForks>>, Arc<Keypair>) {
|
||||||
|
let GenesisConfigInfo {
|
||||||
|
genesis_config,
|
||||||
|
voting_keypair,
|
||||||
|
..
|
||||||
|
} = create_genesis_config(1_000_000_000);
|
||||||
|
|
||||||
|
let bank = Bank::new_for_tests_with_config(&genesis_config, config);
|
||||||
|
(
|
||||||
|
Arc::new(RwLock::new(BankForks::new(bank))),
|
||||||
|
Arc::new(voting_keypair),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_secondary_index_key_sizes() {
|
||||||
|
for secondary_index_enabled in [true, false] {
|
||||||
|
let account_indexes = if secondary_index_enabled {
|
||||||
|
AccountSecondaryIndexes {
|
||||||
|
keys: None,
|
||||||
|
indexes: HashSet::from([
|
||||||
|
AccountIndex::ProgramId,
|
||||||
|
AccountIndex::SplTokenMint,
|
||||||
|
AccountIndex::SplTokenOwner,
|
||||||
|
]),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
AccountSecondaryIndexes::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// RPC & Bank Setup
|
||||||
|
let rpc = RpcHandler::start_with_config(TestConfig { account_indexes });
|
||||||
|
|
||||||
|
let bank = rpc.root_bank();
|
||||||
|
let RpcHandler { io, meta, .. } = rpc;
|
||||||
|
|
||||||
|
// Pubkeys
|
||||||
|
let token_account1_pubkey = Pubkey::new_unique();
|
||||||
|
let token_account2_pubkey = Pubkey::new_unique();
|
||||||
|
let token_account3_pubkey = Pubkey::new_unique();
|
||||||
|
let mint1_pubkey = Pubkey::new_unique();
|
||||||
|
let mint2_pubkey = Pubkey::new_unique();
|
||||||
|
let wallet1_pubkey = Pubkey::new_unique();
|
||||||
|
let wallet2_pubkey = Pubkey::new_unique();
|
||||||
|
let non_existent_pubkey = Pubkey::new_unique();
|
||||||
|
let delegate = spl_token_pubkey(&Pubkey::new_unique());
|
||||||
|
|
||||||
|
let mut num_default_spl_token_program_accounts = 0;
|
||||||
|
let mut num_default_system_program_accounts = 0;
|
||||||
|
|
||||||
|
if !secondary_index_enabled {
|
||||||
|
// Test first with no accounts added & no secondary indexes enabled:
|
||||||
|
let req = format!(
|
||||||
|
r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{token_account1_pubkey}"]}}"#,
|
||||||
|
);
|
||||||
|
let res = io.handle_request_sync(&req, meta.clone());
|
||||||
|
let result: Value = serde_json::from_str(&res.expect("actual response"))
|
||||||
|
.expect("actual response deserialization");
|
||||||
|
let sizes: HashMap<RpcAccountIndex, usize> =
|
||||||
|
serde_json::from_value(result["result"].clone()).unwrap();
|
||||||
|
assert!(sizes.is_empty());
|
||||||
|
} else {
|
||||||
|
// Count SPL Token Program Default Accounts
|
||||||
|
let req = format!(
|
||||||
|
r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{}"]}}"#,
|
||||||
|
inline_spl_token::id(),
|
||||||
|
);
|
||||||
|
let res = io.handle_request_sync(&req, meta.clone());
|
||||||
|
let result: Value = serde_json::from_str(&res.expect("actual response"))
|
||||||
|
.expect("actual response deserialization");
|
||||||
|
println!("{:?}", result);
|
||||||
|
let sizes: HashMap<RpcAccountIndex, usize> =
|
||||||
|
serde_json::from_value(result["result"].clone()).unwrap();
|
||||||
|
assert_eq!(sizes.len(), 1);
|
||||||
|
num_default_spl_token_program_accounts =
|
||||||
|
*sizes.get(&RpcAccountIndex::ProgramId).unwrap();
|
||||||
|
// Count System Program Default Accounts
|
||||||
|
let req = format!(
|
||||||
|
r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{}"]}}"#,
|
||||||
|
system_program::id(),
|
||||||
|
);
|
||||||
|
let res = io.handle_request_sync(&req, meta.clone());
|
||||||
|
let result: Value = serde_json::from_str(&res.expect("actual response"))
|
||||||
|
.expect("actual response deserialization");
|
||||||
|
let sizes: HashMap<RpcAccountIndex, usize> =
|
||||||
|
serde_json::from_value(result["result"].clone()).unwrap();
|
||||||
|
assert_eq!(sizes.len(), 1);
|
||||||
|
num_default_system_program_accounts =
|
||||||
|
*sizes.get(&RpcAccountIndex::ProgramId).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add 2 basic wallet accounts
|
||||||
|
let wallet1_account = AccountSharedData::from(Account {
|
||||||
|
lamports: 11111111,
|
||||||
|
owner: system_program::id(),
|
||||||
|
..Account::default()
|
||||||
|
});
|
||||||
|
bank.store_account(&wallet1_pubkey, &wallet1_account);
|
||||||
|
let wallet2_account = AccountSharedData::from(Account {
|
||||||
|
lamports: 11111111,
|
||||||
|
owner: system_program::id(),
|
||||||
|
..Account::default()
|
||||||
|
});
|
||||||
|
bank.store_account(&wallet2_pubkey, &wallet2_account);
|
||||||
|
|
||||||
|
// Add a token account
|
||||||
|
let mut account1_data = vec![0; TokenAccount::get_packed_len()];
|
||||||
|
let token_account1 = TokenAccount {
|
||||||
|
mint: spl_token_pubkey(&mint1_pubkey),
|
||||||
|
owner: spl_token_pubkey(&wallet1_pubkey),
|
||||||
|
delegate: COption::Some(delegate),
|
||||||
|
amount: 420,
|
||||||
|
state: TokenAccountState::Initialized,
|
||||||
|
is_native: COption::None,
|
||||||
|
delegated_amount: 30,
|
||||||
|
close_authority: COption::Some(spl_token_pubkey(&wallet1_pubkey)),
|
||||||
|
};
|
||||||
|
TokenAccount::pack(token_account1, &mut account1_data).unwrap();
|
||||||
|
let token_account1 = AccountSharedData::from(Account {
|
||||||
|
lamports: 111,
|
||||||
|
data: account1_data.to_vec(),
|
||||||
|
owner: inline_spl_token::id(),
|
||||||
|
..Account::default()
|
||||||
|
});
|
||||||
|
bank.store_account(&token_account1_pubkey, &token_account1);
|
||||||
|
|
||||||
|
// Add the mint
|
||||||
|
let mut mint1_data = vec![0; Mint::get_packed_len()];
|
||||||
|
let mint1_state = Mint {
|
||||||
|
mint_authority: COption::Some(spl_token_pubkey(&wallet1_pubkey)),
|
||||||
|
supply: 500,
|
||||||
|
decimals: 2,
|
||||||
|
is_initialized: true,
|
||||||
|
freeze_authority: COption::Some(spl_token_pubkey(&wallet1_pubkey)),
|
||||||
|
};
|
||||||
|
Mint::pack(mint1_state, &mut mint1_data).unwrap();
|
||||||
|
let mint_account1 = AccountSharedData::from(Account {
|
||||||
|
lamports: 222,
|
||||||
|
data: mint1_data.to_vec(),
|
||||||
|
owner: inline_spl_token::id(),
|
||||||
|
..Account::default()
|
||||||
|
});
|
||||||
|
bank.store_account(&mint1_pubkey, &mint_account1);
|
||||||
|
|
||||||
|
// Add another token account with the different owner, but same delegate, and mint
|
||||||
|
let mut account2_data = vec![0; TokenAccount::get_packed_len()];
|
||||||
|
let token_account2 = TokenAccount {
|
||||||
|
mint: spl_token_pubkey(&mint1_pubkey),
|
||||||
|
owner: spl_token_pubkey(&wallet2_pubkey),
|
||||||
|
delegate: COption::Some(delegate),
|
||||||
|
amount: 420,
|
||||||
|
state: TokenAccountState::Initialized,
|
||||||
|
is_native: COption::None,
|
||||||
|
delegated_amount: 30,
|
||||||
|
close_authority: COption::Some(spl_token_pubkey(&wallet2_pubkey)),
|
||||||
|
};
|
||||||
|
TokenAccount::pack(token_account2, &mut account2_data).unwrap();
|
||||||
|
let token_account2 = AccountSharedData::from(Account {
|
||||||
|
lamports: 333,
|
||||||
|
data: account2_data.to_vec(),
|
||||||
|
owner: inline_spl_token::id(),
|
||||||
|
..Account::default()
|
||||||
|
});
|
||||||
|
bank.store_account(&token_account2_pubkey, &token_account2);
|
||||||
|
|
||||||
|
// Add another token account with the same owner and delegate but different mint
|
||||||
|
let mut account3_data = vec![0; TokenAccount::get_packed_len()];
|
||||||
|
let token_account3 = TokenAccount {
|
||||||
|
mint: spl_token_pubkey(&mint2_pubkey),
|
||||||
|
owner: spl_token_pubkey(&wallet2_pubkey),
|
||||||
|
delegate: COption::Some(delegate),
|
||||||
|
amount: 42,
|
||||||
|
state: TokenAccountState::Initialized,
|
||||||
|
is_native: COption::None,
|
||||||
|
delegated_amount: 30,
|
||||||
|
close_authority: COption::Some(spl_token_pubkey(&wallet2_pubkey)),
|
||||||
|
};
|
||||||
|
TokenAccount::pack(token_account3, &mut account3_data).unwrap();
|
||||||
|
let token_account3 = AccountSharedData::from(Account {
|
||||||
|
lamports: 444,
|
||||||
|
data: account3_data.to_vec(),
|
||||||
|
owner: inline_spl_token::id(),
|
||||||
|
..Account::default()
|
||||||
|
});
|
||||||
|
bank.store_account(&token_account3_pubkey, &token_account3);
|
||||||
|
|
||||||
|
// Add the new mint
|
||||||
|
let mut mint2_data = vec![0; Mint::get_packed_len()];
|
||||||
|
let mint2_state = Mint {
|
||||||
|
mint_authority: COption::Some(spl_token_pubkey(&wallet2_pubkey)),
|
||||||
|
supply: 200,
|
||||||
|
decimals: 3,
|
||||||
|
is_initialized: true,
|
||||||
|
freeze_authority: COption::Some(spl_token_pubkey(&wallet2_pubkey)),
|
||||||
|
};
|
||||||
|
Mint::pack(mint2_state, &mut mint2_data).unwrap();
|
||||||
|
let mint_account2 = AccountSharedData::from(Account {
|
||||||
|
lamports: 555,
|
||||||
|
data: mint2_data.to_vec(),
|
||||||
|
owner: inline_spl_token::id(),
|
||||||
|
..Account::default()
|
||||||
|
});
|
||||||
|
bank.store_account(&mint2_pubkey, &mint_account2);
|
||||||
|
|
||||||
|
// Accounts should now look like the following:
|
||||||
|
//
|
||||||
|
// -----system_program------
|
||||||
|
// / \
|
||||||
|
// /-(owns) \-(owns)
|
||||||
|
// / \
|
||||||
|
// wallet1 ---wallet2---
|
||||||
|
// / / \
|
||||||
|
// /-(SPL::owns) /-(SPL::owns) \-(SPL::owns)
|
||||||
|
// / / \
|
||||||
|
// token_account1 token_account2 token_account3
|
||||||
|
// \ / /
|
||||||
|
// \-(SPL::mint) /-(SPL::mint) /-(SPL::mint)
|
||||||
|
// \ / /
|
||||||
|
// --mint_account1-- mint_account2
|
||||||
|
|
||||||
|
if secondary_index_enabled {
|
||||||
|
// ----------- Test for a non-existant key -----------
|
||||||
|
let req = format!(
|
||||||
|
r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{non_existent_pubkey}"]}}"#,
|
||||||
|
);
|
||||||
|
let res = io.handle_request_sync(&req, meta.clone());
|
||||||
|
let result: Value = serde_json::from_str(&res.expect("actual response"))
|
||||||
|
.expect("actual response deserialization");
|
||||||
|
let sizes: HashMap<RpcAccountIndex, usize> =
|
||||||
|
serde_json::from_value(result["result"].clone()).unwrap();
|
||||||
|
assert!(sizes.is_empty());
|
||||||
|
// --------------- Test Queries ---------------
|
||||||
|
// 1) Wallet1 - Owns 1 SPL Token
|
||||||
|
let req = format!(
|
||||||
|
r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{wallet1_pubkey}"]}}"#,
|
||||||
|
);
|
||||||
|
let res = io.handle_request_sync(&req, meta.clone());
|
||||||
|
let result: Value = serde_json::from_str(&res.expect("actual response"))
|
||||||
|
.expect("actual response deserialization");
|
||||||
|
let sizes: HashMap<RpcAccountIndex, usize> =
|
||||||
|
serde_json::from_value(result["result"].clone()).unwrap();
|
||||||
|
assert_eq!(sizes.len(), 1);
|
||||||
|
assert_eq!(*sizes.get(&RpcAccountIndex::SplTokenOwner).unwrap(), 1);
|
||||||
|
// 2) Wallet2 - Owns 2 SPL Tokens
|
||||||
|
let req = format!(
|
||||||
|
r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{wallet2_pubkey}"]}}"#,
|
||||||
|
);
|
||||||
|
let res = io.handle_request_sync(&req, meta.clone());
|
||||||
|
let result: Value = serde_json::from_str(&res.expect("actual response"))
|
||||||
|
.expect("actual response deserialization");
|
||||||
|
let sizes: HashMap<RpcAccountIndex, usize> =
|
||||||
|
serde_json::from_value(result["result"].clone()).unwrap();
|
||||||
|
assert_eq!(sizes.len(), 1);
|
||||||
|
assert_eq!(*sizes.get(&RpcAccountIndex::SplTokenOwner).unwrap(), 2);
|
||||||
|
// 3) Mint1 - Is in 2 SPL Accounts
|
||||||
|
let req = format!(
|
||||||
|
r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{mint1_pubkey}"]}}"#,
|
||||||
|
);
|
||||||
|
let res = io.handle_request_sync(&req, meta.clone());
|
||||||
|
let result: Value = serde_json::from_str(&res.expect("actual response"))
|
||||||
|
.expect("actual response deserialization");
|
||||||
|
let sizes: HashMap<RpcAccountIndex, usize> =
|
||||||
|
serde_json::from_value(result["result"].clone()).unwrap();
|
||||||
|
assert_eq!(sizes.len(), 1);
|
||||||
|
assert_eq!(*sizes.get(&RpcAccountIndex::SplTokenMint).unwrap(), 2);
|
||||||
|
// 4) Mint2 - Is in 1 SPL Account
|
||||||
|
let req = format!(
|
||||||
|
r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{mint2_pubkey}"]}}"#,
|
||||||
|
);
|
||||||
|
let res = io.handle_request_sync(&req, meta.clone());
|
||||||
|
let result: Value = serde_json::from_str(&res.expect("actual response"))
|
||||||
|
.expect("actual response deserialization");
|
||||||
|
let sizes: HashMap<RpcAccountIndex, usize> =
|
||||||
|
serde_json::from_value(result["result"].clone()).unwrap();
|
||||||
|
assert_eq!(sizes.len(), 1);
|
||||||
|
assert_eq!(*sizes.get(&RpcAccountIndex::SplTokenMint).unwrap(), 1);
|
||||||
|
// 5) SPL Token Program Owns 6 Accounts - 1 Default, 5 created above.
|
||||||
|
let req = format!(
|
||||||
|
r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{}"]}}"#,
|
||||||
|
inline_spl_token::id(),
|
||||||
|
);
|
||||||
|
let res = io.handle_request_sync(&req, meta.clone());
|
||||||
|
let result: Value = serde_json::from_str(&res.expect("actual response"))
|
||||||
|
.expect("actual response deserialization");
|
||||||
|
let sizes: HashMap<RpcAccountIndex, usize> =
|
||||||
|
serde_json::from_value(result["result"].clone()).unwrap();
|
||||||
|
assert_eq!(sizes.len(), 1);
|
||||||
|
assert_eq!(
|
||||||
|
*sizes.get(&RpcAccountIndex::ProgramId).unwrap(),
|
||||||
|
(num_default_spl_token_program_accounts + 5)
|
||||||
|
);
|
||||||
|
// 5) System Program Owns 4 Accounts + 2 Default, 2 created above.
|
||||||
|
let req = format!(
|
||||||
|
r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{}"]}}"#,
|
||||||
|
system_program::id(),
|
||||||
|
);
|
||||||
|
let res = io.handle_request_sync(&req, meta.clone());
|
||||||
|
let result: Value = serde_json::from_str(&res.expect("actual response"))
|
||||||
|
.expect("actual response deserialization");
|
||||||
|
let sizes: HashMap<RpcAccountIndex, usize> =
|
||||||
|
serde_json::from_value(result["result"].clone()).unwrap();
|
||||||
|
assert_eq!(sizes.len(), 1);
|
||||||
|
assert_eq!(
|
||||||
|
*sizes.get(&RpcAccountIndex::ProgramId).unwrap(),
|
||||||
|
(num_default_system_program_accounts + 2)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// ------------ Secondary Indexes Disabled ------------
|
||||||
|
let req = format!(
|
||||||
|
r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{token_account2_pubkey}"]}}"#,
|
||||||
|
);
|
||||||
|
let res = io.handle_request_sync(&req, meta.clone());
|
||||||
|
let result: Value = serde_json::from_str(&res.expect("actual response"))
|
||||||
|
.expect("actual response deserialization");
|
||||||
|
let sizes: HashMap<RpcAccountIndex, usize> =
|
||||||
|
serde_json::from_value(result["result"].clone()).unwrap();
|
||||||
|
assert!(sizes.is_empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue