Add ability to clone accounts from an RPC endpoint

This commit is contained in:
Michael Vines 2021-01-21 13:16:40 -08:00
parent c3548f790c
commit cbb9ac19b9
3 changed files with 82 additions and 9 deletions

View File

@ -164,8 +164,8 @@ where
} }
} }
pub fn normalize_to_url_if_moniker(url_or_moniker: &str) -> String { pub fn normalize_to_url_if_moniker<T: AsRef<str>>(url_or_moniker: T) -> String {
match url_or_moniker { match url_or_moniker.as_ref() {
"m" | "mainnet-beta" => "https://api.mainnet-beta.solana.com", "m" | "mainnet-beta" => "https://api.mainnet-beta.solana.com",
"t" | "testnet" => "https://testnet.solana.com", "t" | "testnet" => "https://testnet.solana.com",
"d" | "devnet" => "https://devnet.solana.com", "d" | "devnet" => "https://devnet.solana.com",

View File

@ -84,6 +84,30 @@ impl TestValidatorGenesis {
self self
} }
pub fn add_accounts<T>(&mut self, accounts: T) -> &mut Self
where
T: IntoIterator<Item = (Pubkey, Account)>,
{
for (address, account) in accounts {
self.add_account(address, account);
}
self
}
pub fn clone_accounts<T>(&mut self, addresses: T, rpc_client: &RpcClient) -> &mut Self
where
T: IntoIterator<Item = Pubkey>,
{
for address in addresses {
info!("Fetching {}...", address);
let account = rpc_client.get_account(&address).unwrap_or_else(|err| {
panic!("Failed to fetch {}: {}", address, err);
});
self.add_account(address, account);
}
self
}
/// Add an account to the test environment with the account data in the provided `filename` /// Add an account to the test environment with the account data in the provided `filename`
pub fn add_account_with_file_data( pub fn add_account_with_file_data(
&mut self, &mut self,

View File

@ -1,9 +1,14 @@
use { use {
clap::{value_t_or_exit, App, Arg}, clap::{value_t, value_t_or_exit, App, Arg},
console::style, console::style,
fd_lock::FdLock, fd_lock::FdLock,
indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle}, indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle},
solana_clap_utils::{input_parsers::pubkey_of, input_validators::is_pubkey}, solana_clap_utils::{
input_parsers::{pubkey_of, pubkeys_of},
input_validators::{
is_pubkey, is_pubkey_or_keypair, is_url_or_moniker, normalize_to_url_if_moniker,
},
},
solana_client::{client_error, rpc_client::RpcClient, rpc_request}, solana_client::{client_error, rpc_client::RpcClient, rpc_request},
solana_core::rpc::JsonRpcConfig, solana_core::rpc::JsonRpcConfig,
solana_faucet::faucet::{run_local_faucet_with_port, FAUCET_PORT}, solana_faucet::faucet::{run_local_faucet_with_port, FAUCET_PORT},
@ -19,6 +24,7 @@ use {
}, },
solana_validator::{start_logger, test_validator::*}, solana_validator::{start_logger, test_validator::*},
std::{ std::{
collections::HashSet,
fs, io, fs, io,
net::{IpAddr, Ipv4Addr, SocketAddr}, net::{IpAddr, Ipv4Addr, SocketAddr},
path::{Path, PathBuf}, path::{Path, PathBuf},
@ -70,6 +76,18 @@ fn main() {
arg arg
} }
}) })
.arg(
Arg::with_name("json_rpc_url")
.short("u")
.long("url")
.value_name("URL_OR_MONIKER")
.takes_value(true)
.validator(is_url_or_moniker)
.help(
"URL for Solana's JSON RPC or moniker (or their first letter): \
[mainnet-beta, testnet, devnet, localhost]",
),
)
.arg( .arg(
Arg::with_name("mint_address") Arg::with_name("mint_address")
.long("mint") .long("mint")
@ -78,7 +96,8 @@ fn main() {
.takes_value(true) .takes_value(true)
.help( .help(
"Address of the mint account that will receive tokens \ "Address of the mint account that will receive tokens \
created at genesis [default: client keypair]", created at genesis. If the ledger already exists then \
this parameter is silently ignored [default: client keypair]",
), ),
) )
.arg( .arg(
@ -132,7 +151,25 @@ fn main() {
.takes_value(true) .takes_value(true)
.number_of_values(2) .number_of_values(2)
.multiple(true) .multiple(true)
.help("Add a BPF program to the genesis configuration"), .help(
"Add a BPF program to the genesis configuration. \
If the ledger already exists then this parameter is silently ignored",
),
)
.arg(
Arg::with_name("clone_account")
.long("clone")
.short("c")
.value_name("ADDRESS")
.takes_value(true)
.validator(is_pubkey_or_keypair)
.multiple(true)
.requires("json_rpc_url")
.help(
"Copy an account from the cluster referenced by the --url argument the \
genesis configuration. \
If the ledger already exists then this parameter is silently ignored",
),
) )
.get_matches(); .get_matches();
@ -142,6 +179,8 @@ fn main() {
solana_cli_config::Config::default() solana_cli_config::Config::default()
}; };
let json_rpc_url = value_t!(matches, "json_rpc_url", String).map(normalize_to_url_if_moniker);
let mint_address = pubkey_of(&matches, "mint_address").unwrap_or_else(|| { let mint_address = pubkey_of(&matches, "mint_address").unwrap_or_else(|| {
read_keypair_file(&cli_config.keypair_path) read_keypair_file(&cli_config.keypair_path)
.unwrap_or_else(|_| Keypair::new()) .unwrap_or_else(|_| Keypair::new())
@ -194,6 +233,10 @@ fn main() {
} }
} }
let clone_accounts: HashSet<_> = pubkeys_of(&matches, "clone_account")
.map(|v| v.into_iter().collect())
.unwrap_or_default();
if !ledger_path.exists() { if !ledger_path.exists() {
fs::create_dir(&ledger_path).unwrap_or_else(|err| { fs::create_dir(&ledger_path).unwrap_or_else(|err| {
eprintln!( eprintln!(
@ -285,7 +328,8 @@ fn main() {
None None
}; };
TestValidatorGenesis::default() let mut genesis = TestValidatorGenesis::default();
genesis
.ledger_path(&ledger_path) .ledger_path(&ledger_path)
.add_account( .add_account(
faucet_keypair.pubkey(), faucet_keypair.pubkey(),
@ -298,8 +342,13 @@ fn main() {
..JsonRpcConfig::default() ..JsonRpcConfig::default()
}) })
.rpc_port(rpc_port) .rpc_port(rpc_port)
.add_programs_with_path(&programs) .add_programs_with_path(&programs);
.start_with_mint_address(mint_address)
if !clone_accounts.is_empty() {
let rpc_client = RpcClient::new(json_rpc_url.unwrap());
genesis.clone_accounts(clone_accounts, &rpc_client);
}
genesis.start_with_mint_address(mint_address)
} }
.unwrap_or_else(|err| { .unwrap_or_else(|err| {
eprintln!("Error: failed to start validator: {}", err); eprintln!("Error: failed to start validator: {}", err);