From e8c8235474bdc02c9caf9b93c4544823eba73659 Mon Sep 17 00:00:00 2001 From: K-anon <31515050+IntokuSatori@users.noreply.github.com> Date: Wed, 16 Nov 2022 20:39:11 -0700 Subject: [PATCH] Get Index Key Size RPC Support (#28383) Co-authored-by: K-anon --- rpc-client-api/src/config.rs | 8 + rpc/src/rpc.rs | 424 ++++++++++++++++++++- runtime/src/accounts_index.rs | 16 + runtime/src/bank.rs | 14 +- test-validator/src/lib.rs | 1 + validator/src/bin/solana-test-validator.rs | 42 +- 6 files changed, 489 insertions(+), 16 deletions(-) diff --git a/rpc-client-api/src/config.rs b/rpc-client-api/src/config.rs index ee98402a5..9ecff334c 100644 --- a/rpc-client-api/src/config.rs +++ b/rpc-client-api/src/config.rs @@ -137,6 +137,14 @@ pub struct RpcEpochConfig { pub min_context_slot: Option, } +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] +#[serde(rename_all = "camelCase")] +pub enum RpcAccountIndex { + ProgramId, + SplTokenMint, + SplTokenOwner, +} + #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RpcAccountInfoConfig { diff --git a/rpc/src/rpc.rs b/rpc/src/rpc.rs index 476a0ac50..625e9545d 100644 --- a/rpc/src/rpc.rs +++ b/rpc/src/rpc.rs @@ -114,6 +114,14 @@ type RpcCustomResult = std::result::Result; pub const MAX_REQUEST_BODY_SIZE: usize = 50 * (1 << 10); // 50kB 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(bank: &Bank, value: T) -> RpcResponse { RpcResponse { context: RpcResponseContext::new(bank.slot()), @@ -496,6 +504,51 @@ impl JsonRpcRequestProcessor { }) } + pub fn get_secondary_index_key_size( + &self, + index_key: &Pubkey, + config: Option, + ) -> Result>> { + // 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::>(); + + // 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( &self, addresses: Vec, @@ -2970,6 +3023,14 @@ pub mod rpc_accounts { config: Option, ) -> Result>>; + #[rpc(meta, name = "getSecondaryIndexKeySize")] + fn get_secondary_index_key_size( + &self, + meta: Self::Metadata, + pubkey_str: String, + config: Option, + ) -> Result>>; + #[rpc(meta, name = "getBlockCommitment")] fn get_block_commitment( &self, @@ -3121,6 +3182,20 @@ pub mod rpc_accounts { 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, + ) -> Result>> { + 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_block_commitment( &self, meta: Self::Metadata, @@ -4589,6 +4664,7 @@ pub mod tests { jsonrpc_core::{futures, ErrorCode, MetaIoHandler, Output, Response, Value}, jsonrpc_core_client::transports::local, serde::de::DeserializeOwned, + solana_account_decoder::parse_token::spl_token_pubkey, solana_address_lookup_table_program::state::{AddressLookupTable, LookupTableMeta}, solana_entry::entry::next_versioned_entry, solana_gossip::{contact_info::ContactInfo, socketaddr}, @@ -4606,8 +4682,9 @@ pub mod tests { filter::{Memcmp, MemcmpEncodedBytes}, }, solana_runtime::{ - accounts_background_service::AbsRequestSender, commitment::BlockCommitment, - inline_spl_token, non_circulating_supply::non_circulating_accounts, + accounts_background_service::AbsRequestSender, bank::BankTestConfig, + commitment::BlockCommitment, inline_spl_token, + non_circulating_supply::non_circulating_accounts, }, solana_sdk::{ account::{Account, WritableAccount}, @@ -4704,7 +4781,18 @@ pub mod tests { impl RpcHandler { fn start() -> Self { - let (bank_forks, mint_keypair, leader_vote_keypair) = new_bank_forks(); + Self::start_with_config(JsonRpcConfig { + enable_rpc_transaction_history: true, + ..JsonRpcConfig::default() + }) + } + + fn start_with_config(config: JsonRpcConfig) -> Self { + let (bank_forks, mint_keypair, leader_vote_keypair) = + new_bank_forks_with_config(BankTestConfig { + secondary_indexes: config.account_indexes.clone(), + }); + let ledger_path = get_tmp_ledger_path!(); let blockstore = Arc::new(Blockstore::open(&ledger_path).unwrap()); let bank = bank_forks.read().unwrap().working_bank(); @@ -4731,10 +4819,7 @@ pub mod tests { let max_complete_transaction_status_slot = Arc::new(AtomicU64::new(0)); let meta = JsonRpcRequestProcessor::new( - JsonRpcConfig { - enable_rpc_transaction_history: true, - ..JsonRpcConfig::default() - }, + config, None, bank_forks.clone(), block_commitment_cache.clone(), @@ -6535,6 +6620,12 @@ pub mod tests { } fn new_bank_forks() -> (Arc>, Keypair, Arc) { + new_bank_forks_with_config(BankTestConfig::default()) + } + + fn new_bank_forks_with_config( + config: BankTestConfig, + ) -> (Arc>, Keypair, Arc) { let GenesisConfigInfo { mut genesis_config, mint_keypair, @@ -6548,7 +6639,7 @@ pub mod tests { EpochSchedule::custom(TEST_SLOTS_PER_EPOCH, TEST_SLOTS_PER_EPOCH, false); genesis_config.fee_rate_governor = FeeRateGovernor::new(TEST_SIGNATURE_FEE, 0); - let bank = Bank::new_for_tests(&genesis_config); + let bank = Bank::new_for_tests_with_config(&genesis_config, config); ( Arc::new(RwLock::new(BankForks::new(bank))), mint_keypair, @@ -7343,6 +7434,323 @@ 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 = + 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 = + 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 = + 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 = + 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 = + 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 = + 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 = + 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 = + 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 = + 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 = + 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 = + serde_json::from_value(result["result"]["value"].clone()).unwrap(); + assert!(sizes.is_empty()); + } + } + } + #[test] fn test_token_rpcs() { for program_id in solana_account_decoder::parse_token::spl_token_ids() { diff --git a/runtime/src/accounts_index.rs b/runtime/src/accounts_index.rs index 1d7d36e85..8be25bfe0 100644 --- a/runtime/src/accounts_index.rs +++ b/runtime/src/accounts_index.rs @@ -1472,6 +1472,22 @@ impl AccountsIndex { } } + pub fn get_index_key_size(&self, index: &AccountIndex, index_key: &Pubkey) -> Option { + match index { + AccountIndex::ProgramId => self.program_id_index.index.get(index_key).map(|x| x.len()), + AccountIndex::SplTokenOwner => self + .spl_token_owner_index + .index + .get(index_key) + .map(|x| x.len()), + AccountIndex::SplTokenMint => self + .spl_token_mint_index + .index + .get(index_key) + .map(|x| x.len()), + } + } + /// log any secondary index counts, if non-zero pub(crate) fn log_secondary_indexes(&self) { if !self.program_id_index.index.is_empty() { diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 72abccfa0..9939f6185 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -1147,6 +1147,11 @@ pub struct NewBankOptions { pub vote_only_bank: bool, } +#[derive(Debug, Default)] +pub struct BankTestConfig { + pub secondary_indexes: AccountSecondaryIndexes, +} + #[derive(Debug)] struct PrevEpochInflationRewards { validator_rewards: u64, @@ -1209,9 +1214,16 @@ impl Bank { } pub fn new_for_tests(genesis_config: &GenesisConfig) -> Self { + Self::new_for_tests_with_config(genesis_config, BankTestConfig::default()) + } + + pub fn new_for_tests_with_config( + genesis_config: &GenesisConfig, + test_config: BankTestConfig, + ) -> Self { Self::new_with_config_for_tests( genesis_config, - AccountSecondaryIndexes::default(), + test_config.secondary_indexes, false, AccountShrinkThreshold::default(), ) diff --git a/test-validator/src/lib.rs b/test-validator/src/lib.rs index b86540169..e5f53ac76 100644 --- a/test-validator/src/lib.rs +++ b/test-validator/src/lib.rs @@ -823,6 +823,7 @@ impl TestValidator { staked_nodes_overrides: config.staked_nodes_overrides.clone(), accounts_db_config, runtime_config, + account_indexes: config.rpc_config.account_indexes.clone(), ..ValidatorConfig::default_for_test() }; if let Some(ref tower_storage) = config.tower_storage { diff --git a/validator/src/bin/solana-test-validator.rs b/validator/src/bin/solana-test-validator.rs index c9b077b1b..dd9a001ef 100644 --- a/validator/src/bin/solana-test-validator.rs +++ b/validator/src/bin/solana-test-validator.rs @@ -16,6 +16,7 @@ use { rpc_pubsub_service::PubSubConfig, }, solana_rpc_client::rpc_client::RpcClient, + solana_runtime::accounts_index::{AccountIndex, AccountSecondaryIndexes}, solana_sdk::{ account::AccountSharedData, clock::Slot, @@ -140,6 +141,15 @@ fn main() { .conflicts_with("quiet") .help("Log mode: stream the validator log"), ) + .arg( + Arg::with_name("account_indexes") + .long("account-index") + .takes_value(true) + .multiple(true) + .possible_values(&["program-id", "spl-token-owner", "spl-token-mint"]) + .value_name("INDEX") + .help("Enable an accounts index, indexed by the selected account field"), + ) .arg( Arg::with_name("faucet_port") .long("faucet-port") @@ -441,6 +451,22 @@ fn main() { let ledger_path = value_t_or_exit!(matches, "ledger_path", PathBuf); let reset_ledger = matches.is_present("reset"); + let indexes: HashSet = matches + .values_of("account_indexes") + .unwrap_or_default() + .map(|value| match value { + "program-id" => AccountIndex::ProgramId, + "spl-token-mint" => AccountIndex::SplTokenMint, + "spl-token-owner" => AccountIndex::SplTokenOwner, + _ => unreachable!(), + }) + .collect(); + + let account_indexes = AccountSecondaryIndexes { + keys: None, + indexes, + }; + if !ledger_path.exists() { fs::create_dir(&ledger_path).unwrap_or_else(|err| { println!( @@ -752,13 +778,6 @@ fn main() { faucet_pubkey, AccountSharedData::new(faucet_lamports, 0, &system_program::id()), ) - .rpc_config(JsonRpcConfig { - enable_rpc_transaction_history: true, - enable_extended_tx_metadata_storage: true, - rpc_bigtable_config, - faucet_addr, - ..JsonRpcConfig::default_for_test() - }) .pubsub_config(PubSubConfig { enable_vote_subscription, ..PubSubConfig::default() @@ -778,6 +797,15 @@ fn main() { }) .deactivate_features(&features_to_deactivate); + genesis.rpc_config(JsonRpcConfig { + enable_rpc_transaction_history: true, + enable_extended_tx_metadata_storage: true, + rpc_bigtable_config, + faucet_addr, + account_indexes, + ..JsonRpcConfig::default_for_test() + }); + if !accounts_to_clone.is_empty() { if let Err(e) = genesis.clone_accounts( accounts_to_clone,