cli: Add subcommands for address lookup tables (#27123)
* cli: Add subcommand for creating address lookup tables * cli: Add additional subcommands for address lookup tables * short commands
This commit is contained in:
parent
0b54b22f58
commit
d8380e4d4a
|
@ -4874,6 +4874,7 @@ dependencies = [
|
|||
"serde_derive",
|
||||
"serde_json",
|
||||
"solana-account-decoder",
|
||||
"solana-address-lookup-table-program",
|
||||
"solana-bpf-loader-program",
|
||||
"solana-clap-utils",
|
||||
"solana-cli-config",
|
||||
|
|
|
@ -2111,6 +2111,75 @@ impl fmt::Display for CliUpgradeableBuffers {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliAddressLookupTable {
|
||||
pub lookup_table_address: String,
|
||||
pub authority: Option<String>,
|
||||
pub deactivation_slot: u64,
|
||||
pub last_extended_slot: u64,
|
||||
pub addresses: Vec<String>,
|
||||
}
|
||||
impl QuietDisplay for CliAddressLookupTable {}
|
||||
impl VerboseDisplay for CliAddressLookupTable {}
|
||||
impl fmt::Display for CliAddressLookupTable {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(f)?;
|
||||
writeln_name_value(f, "Lookup Table Address:", &self.lookup_table_address)?;
|
||||
if let Some(authority) = &self.authority {
|
||||
writeln_name_value(f, "Authority:", authority)?;
|
||||
} else {
|
||||
writeln_name_value(f, "Authority:", "None (frozen)")?;
|
||||
}
|
||||
if self.deactivation_slot == u64::MAX {
|
||||
writeln_name_value(f, "Deactivation Slot:", "None (still active)")?;
|
||||
} else {
|
||||
writeln_name_value(f, "Deactivation Slot:", &self.deactivation_slot.to_string())?;
|
||||
}
|
||||
if self.last_extended_slot == 0 {
|
||||
writeln_name_value(f, "Last Extended Slot:", "None (empty)")?;
|
||||
} else {
|
||||
writeln_name_value(
|
||||
f,
|
||||
"Last Extended Slot:",
|
||||
&self.last_extended_slot.to_string(),
|
||||
)?;
|
||||
}
|
||||
if self.addresses.is_empty() {
|
||||
writeln_name_value(f, "Address Table Entries:", "None (empty)")?;
|
||||
} else {
|
||||
writeln!(f, "{}", style("Address Table Entries:".to_string()).bold())?;
|
||||
writeln!(f)?;
|
||||
writeln!(
|
||||
f,
|
||||
"{}",
|
||||
style(format!(" {:<5} {}", "Index", "Address")).bold()
|
||||
)?;
|
||||
for (index, address) in self.addresses.iter().enumerate() {
|
||||
writeln!(f, " {:<5} {}", index, address)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliAddressLookupTableCreated {
|
||||
pub lookup_table_address: String,
|
||||
pub signature: String,
|
||||
}
|
||||
impl QuietDisplay for CliAddressLookupTableCreated {}
|
||||
impl VerboseDisplay for CliAddressLookupTableCreated {}
|
||||
impl fmt::Display for CliAddressLookupTableCreated {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(f)?;
|
||||
writeln_name_value(f, "Signature:", &self.signature)?;
|
||||
writeln_name_value(f, "Lookup Table Address:", &self.lookup_table_address)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ReturnSignersConfig {
|
||||
pub dump_transaction_message: bool,
|
||||
|
|
|
@ -28,6 +28,7 @@ serde = "1.0.143"
|
|||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.83"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.12.0" }
|
||||
solana-address-lookup-table-program = { path = "../programs/address-lookup-table", version = "=1.12.0" }
|
||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.12.0" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.12.0" }
|
||||
solana-cli-config = { path = "../cli-config", version = "=1.12.0" }
|
||||
|
|
|
@ -0,0 +1,832 @@
|
|||
use {
|
||||
crate::cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult},
|
||||
clap::{App, AppSettings, Arg, ArgMatches, SubCommand},
|
||||
solana_address_lookup_table_program::{
|
||||
instruction::{
|
||||
close_lookup_table, create_lookup_table, deactivate_lookup_table, extend_lookup_table,
|
||||
freeze_lookup_table,
|
||||
},
|
||||
state::AddressLookupTable,
|
||||
},
|
||||
solana_clap_utils::{self, input_parsers::*, input_validators::*, keypair::*},
|
||||
solana_cli_output::{CliAddressLookupTable, CliAddressLookupTableCreated, CliSignature},
|
||||
solana_client::{rpc_client::RpcClient, rpc_config::RpcSendTransactionConfig},
|
||||
solana_remote_wallet::remote_wallet::RemoteWalletManager,
|
||||
solana_sdk::{
|
||||
account::from_account, clock::Clock, commitment_config::CommitmentConfig, message::Message,
|
||||
pubkey::Pubkey, sysvar, transaction::Transaction,
|
||||
},
|
||||
std::sync::Arc,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum AddressLookupTableCliCommand {
|
||||
CreateLookupTable {
|
||||
authority_signer_index: SignerIndex,
|
||||
payer_signer_index: SignerIndex,
|
||||
},
|
||||
FreezeLookupTable {
|
||||
lookup_table_pubkey: Pubkey,
|
||||
authority_signer_index: SignerIndex,
|
||||
bypass_warning: bool,
|
||||
},
|
||||
ExtendLookupTable {
|
||||
lookup_table_pubkey: Pubkey,
|
||||
authority_signer_index: SignerIndex,
|
||||
payer_signer_index: SignerIndex,
|
||||
new_addresses: Vec<Pubkey>,
|
||||
},
|
||||
DeactivateLookupTable {
|
||||
lookup_table_pubkey: Pubkey,
|
||||
authority_signer_index: SignerIndex,
|
||||
bypass_warning: bool,
|
||||
},
|
||||
CloseLookupTable {
|
||||
lookup_table_pubkey: Pubkey,
|
||||
authority_signer_index: SignerIndex,
|
||||
recipient_pubkey: Pubkey,
|
||||
},
|
||||
ShowLookupTable {
|
||||
lookup_table_pubkey: Pubkey,
|
||||
},
|
||||
}
|
||||
|
||||
pub trait AddressLookupTableSubCommands {
|
||||
fn address_lookup_table_subcommands(self) -> Self;
|
||||
}
|
||||
|
||||
impl AddressLookupTableSubCommands for App<'_, '_> {
|
||||
fn address_lookup_table_subcommands(self) -> Self {
|
||||
self.subcommand(
|
||||
SubCommand::with_name("address-lookup-table")
|
||||
.about("Address lookup table management")
|
||||
.setting(AppSettings::SubcommandRequiredElseHelp)
|
||||
.subcommand(
|
||||
SubCommand::with_name("create")
|
||||
.about("Create a lookup table")
|
||||
.arg(
|
||||
Arg::with_name("authority")
|
||||
.long("authority")
|
||||
.value_name("AUTHORITY_SIGNER")
|
||||
.takes_value(true)
|
||||
.validator(is_valid_signer)
|
||||
.help("Lookup table authority [default: the default configured keypair]")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("payer")
|
||||
.long("payer")
|
||||
.value_name("PAYER_SIGNER")
|
||||
.takes_value(true)
|
||||
.validator(is_valid_signer)
|
||||
.help("Account that will pay rent fees for the created lookup table [default: the default configured keypair]")
|
||||
)
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("freeze")
|
||||
.about("Permanently freezes a lookup table")
|
||||
.arg(
|
||||
Arg::with_name("lookup_table_address")
|
||||
.index(1)
|
||||
.value_name("LOOKUP_TABLE_ADDRESS")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.validator(is_pubkey)
|
||||
.help("Address of the lookup table")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("authority")
|
||||
.long("authority")
|
||||
.value_name("AUTHORITY_SIGNER")
|
||||
.takes_value(true)
|
||||
.validator(is_valid_signer)
|
||||
.help("Lookup table authority [default: the default configured keypair]")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("bypass_warning")
|
||||
.long("bypass-warning")
|
||||
.takes_value(false)
|
||||
.help("Bypass the permanent lookup table freeze warning"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("extend")
|
||||
.about("Append more addresses to a lookup table")
|
||||
.arg(
|
||||
Arg::with_name("lookup_table_address")
|
||||
.index(1)
|
||||
.value_name("LOOKUP_TABLE_ADDRESS")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.validator(is_pubkey)
|
||||
.help("Address of the lookup table")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("authority")
|
||||
.long("authority")
|
||||
.value_name("AUTHORITY_SIGNER")
|
||||
.takes_value(true)
|
||||
.validator(is_valid_signer)
|
||||
.help("Lookup table authority [default: the default configured keypair]")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("payer")
|
||||
.long("payer")
|
||||
.value_name("PAYER_SIGNER")
|
||||
.takes_value(true)
|
||||
.validator(is_valid_signer)
|
||||
.help("Account that will pay rent fees for the extended lookup table [default: the default configured keypair]")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("addresses")
|
||||
.long("addresses")
|
||||
.value_name("ADDRESS_1,ADDRESS_2")
|
||||
.takes_value(true)
|
||||
.use_delimiter(true)
|
||||
.required(true)
|
||||
.validator(is_pubkey)
|
||||
.help("Comma separated list of addresses to append")
|
||||
)
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("deactivate")
|
||||
.about("Permanently deactivates a lookup table")
|
||||
.arg(
|
||||
Arg::with_name("lookup_table_address")
|
||||
.index(1)
|
||||
.value_name("LOOKUP_TABLE_ADDRESS")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.help("Address of the lookup table")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("authority")
|
||||
.long("authority")
|
||||
.value_name("AUTHORITY_SIGNER")
|
||||
.takes_value(true)
|
||||
.validator(is_valid_signer)
|
||||
.help("Lookup table authority [default: the default configured keypair]")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("bypass_warning")
|
||||
.long("bypass-warning")
|
||||
.takes_value(false)
|
||||
.help("Bypass the permanent lookup table deactivation warning"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("close")
|
||||
.about("Permanently closes a lookup table")
|
||||
.arg(
|
||||
Arg::with_name("lookup_table_address")
|
||||
.index(1)
|
||||
.value_name("LOOKUP_TABLE_ADDRESS")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.help("Address of the lookup table")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("recipient")
|
||||
.long("recipient")
|
||||
.value_name("RECIPIENT_ADDRESS")
|
||||
.takes_value(true)
|
||||
.validator(is_pubkey)
|
||||
.help("Address of the recipient account to deposit the closed account's lamports [default: the default configured keypair]")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("authority")
|
||||
.long("authority")
|
||||
.value_name("AUTHORITY_SIGNER")
|
||||
.takes_value(true)
|
||||
.validator(is_valid_signer)
|
||||
.help("Lookup table authority [default: the default configured keypair]")
|
||||
)
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("get")
|
||||
.about("Display information about a lookup table")
|
||||
.arg(
|
||||
Arg::with_name("lookup_table_address")
|
||||
.index(1)
|
||||
.value_name("LOOKUP_TABLE_ADDRESS")
|
||||
.takes_value(true)
|
||||
.help("Address of the lookup table to show")
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_address_lookup_table_subcommand(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let (subcommand, sub_matches) = matches.subcommand();
|
||||
|
||||
let response = match (subcommand, sub_matches) {
|
||||
("create", Some(matches)) => {
|
||||
let mut bulk_signers = vec![Some(
|
||||
default_signer.signer_from_path(matches, wallet_manager)?,
|
||||
)];
|
||||
|
||||
let authority_pubkey = if let Ok((authority_signer, Some(authority_pubkey))) =
|
||||
signer_of(matches, "authority", wallet_manager)
|
||||
{
|
||||
bulk_signers.push(authority_signer);
|
||||
Some(authority_pubkey)
|
||||
} else {
|
||||
Some(
|
||||
default_signer
|
||||
.signer_from_path(matches, wallet_manager)?
|
||||
.pubkey(),
|
||||
)
|
||||
};
|
||||
|
||||
let payer_pubkey = if let Ok((payer_signer, Some(payer_pubkey))) =
|
||||
signer_of(matches, "payer", wallet_manager)
|
||||
{
|
||||
bulk_signers.push(payer_signer);
|
||||
Some(payer_pubkey)
|
||||
} else {
|
||||
Some(
|
||||
default_signer
|
||||
.signer_from_path(matches, wallet_manager)?
|
||||
.pubkey(),
|
||||
)
|
||||
};
|
||||
|
||||
let signer_info =
|
||||
default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
|
||||
|
||||
CliCommandInfo {
|
||||
command: CliCommand::AddressLookupTable(
|
||||
AddressLookupTableCliCommand::CreateLookupTable {
|
||||
authority_signer_index: signer_info.index_of(authority_pubkey).unwrap(),
|
||||
payer_signer_index: signer_info.index_of(payer_pubkey).unwrap(),
|
||||
},
|
||||
),
|
||||
signers: signer_info.signers,
|
||||
}
|
||||
}
|
||||
("freeze", Some(matches)) => {
|
||||
let lookup_table_pubkey = pubkey_of(matches, "lookup_table_address").unwrap();
|
||||
|
||||
let mut bulk_signers = vec![Some(
|
||||
default_signer.signer_from_path(matches, wallet_manager)?,
|
||||
)];
|
||||
|
||||
let authority_pubkey = if let Ok((authority_signer, Some(authority_pubkey))) =
|
||||
signer_of(matches, "authority", wallet_manager)
|
||||
{
|
||||
bulk_signers.push(authority_signer);
|
||||
Some(authority_pubkey)
|
||||
} else {
|
||||
Some(
|
||||
default_signer
|
||||
.signer_from_path(matches, wallet_manager)?
|
||||
.pubkey(),
|
||||
)
|
||||
};
|
||||
|
||||
let signer_info =
|
||||
default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
|
||||
|
||||
CliCommandInfo {
|
||||
command: CliCommand::AddressLookupTable(
|
||||
AddressLookupTableCliCommand::FreezeLookupTable {
|
||||
lookup_table_pubkey,
|
||||
authority_signer_index: signer_info.index_of(authority_pubkey).unwrap(),
|
||||
bypass_warning: matches.is_present("bypass_warning"),
|
||||
},
|
||||
),
|
||||
signers: signer_info.signers,
|
||||
}
|
||||
}
|
||||
("extend", Some(matches)) => {
|
||||
let lookup_table_pubkey = pubkey_of(matches, "lookup_table_address").unwrap();
|
||||
|
||||
let mut bulk_signers = vec![Some(
|
||||
default_signer.signer_from_path(matches, wallet_manager)?,
|
||||
)];
|
||||
|
||||
let authority_pubkey = if let Ok((authority_signer, Some(authority_pubkey))) =
|
||||
signer_of(matches, "authority", wallet_manager)
|
||||
{
|
||||
bulk_signers.push(authority_signer);
|
||||
Some(authority_pubkey)
|
||||
} else {
|
||||
Some(
|
||||
default_signer
|
||||
.signer_from_path(matches, wallet_manager)?
|
||||
.pubkey(),
|
||||
)
|
||||
};
|
||||
|
||||
let payer_pubkey = if let Ok((payer_signer, Some(payer_pubkey))) =
|
||||
signer_of(matches, "payer", wallet_manager)
|
||||
{
|
||||
bulk_signers.push(payer_signer);
|
||||
Some(payer_pubkey)
|
||||
} else {
|
||||
Some(
|
||||
default_signer
|
||||
.signer_from_path(matches, wallet_manager)?
|
||||
.pubkey(),
|
||||
)
|
||||
};
|
||||
|
||||
let new_addresses: Vec<Pubkey> = values_of(matches, "addresses").unwrap();
|
||||
|
||||
let signer_info =
|
||||
default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
|
||||
|
||||
CliCommandInfo {
|
||||
command: CliCommand::AddressLookupTable(
|
||||
AddressLookupTableCliCommand::ExtendLookupTable {
|
||||
lookup_table_pubkey,
|
||||
authority_signer_index: signer_info.index_of(authority_pubkey).unwrap(),
|
||||
payer_signer_index: signer_info.index_of(payer_pubkey).unwrap(),
|
||||
new_addresses,
|
||||
},
|
||||
),
|
||||
signers: signer_info.signers,
|
||||
}
|
||||
}
|
||||
("deactivate", Some(matches)) => {
|
||||
let lookup_table_pubkey = pubkey_of(matches, "lookup_table_address").unwrap();
|
||||
|
||||
let mut bulk_signers = vec![Some(
|
||||
default_signer.signer_from_path(matches, wallet_manager)?,
|
||||
)];
|
||||
|
||||
let authority_pubkey = if let Ok((authority_signer, Some(authority_pubkey))) =
|
||||
signer_of(matches, "authority", wallet_manager)
|
||||
{
|
||||
bulk_signers.push(authority_signer);
|
||||
Some(authority_pubkey)
|
||||
} else {
|
||||
Some(
|
||||
default_signer
|
||||
.signer_from_path(matches, wallet_manager)?
|
||||
.pubkey(),
|
||||
)
|
||||
};
|
||||
|
||||
let signer_info =
|
||||
default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
|
||||
|
||||
CliCommandInfo {
|
||||
command: CliCommand::AddressLookupTable(
|
||||
AddressLookupTableCliCommand::DeactivateLookupTable {
|
||||
lookup_table_pubkey,
|
||||
authority_signer_index: signer_info.index_of(authority_pubkey).unwrap(),
|
||||
bypass_warning: matches.is_present("bypass_warning"),
|
||||
},
|
||||
),
|
||||
signers: signer_info.signers,
|
||||
}
|
||||
}
|
||||
("close", Some(matches)) => {
|
||||
let lookup_table_pubkey = pubkey_of(matches, "lookup_table_address").unwrap();
|
||||
|
||||
let mut bulk_signers = vec![Some(
|
||||
default_signer.signer_from_path(matches, wallet_manager)?,
|
||||
)];
|
||||
|
||||
let authority_pubkey = if let Ok((authority_signer, Some(authority_pubkey))) =
|
||||
signer_of(matches, "authority", wallet_manager)
|
||||
{
|
||||
bulk_signers.push(authority_signer);
|
||||
Some(authority_pubkey)
|
||||
} else {
|
||||
Some(
|
||||
default_signer
|
||||
.signer_from_path(matches, wallet_manager)?
|
||||
.pubkey(),
|
||||
)
|
||||
};
|
||||
|
||||
let recipient_pubkey = if let Some(recipient_pubkey) = pubkey_of(matches, "recipient") {
|
||||
recipient_pubkey
|
||||
} else {
|
||||
default_signer
|
||||
.signer_from_path(matches, wallet_manager)?
|
||||
.pubkey()
|
||||
};
|
||||
|
||||
let signer_info =
|
||||
default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
|
||||
|
||||
CliCommandInfo {
|
||||
command: CliCommand::AddressLookupTable(
|
||||
AddressLookupTableCliCommand::CloseLookupTable {
|
||||
lookup_table_pubkey,
|
||||
authority_signer_index: signer_info.index_of(authority_pubkey).unwrap(),
|
||||
recipient_pubkey,
|
||||
},
|
||||
),
|
||||
signers: signer_info.signers,
|
||||
}
|
||||
}
|
||||
("get", Some(matches)) => {
|
||||
let lookup_table_pubkey = pubkey_of(matches, "lookup_table_address").unwrap();
|
||||
|
||||
CliCommandInfo {
|
||||
command: CliCommand::AddressLookupTable(
|
||||
AddressLookupTableCliCommand::ShowLookupTable {
|
||||
lookup_table_pubkey,
|
||||
},
|
||||
),
|
||||
signers: vec![],
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
pub fn process_address_lookup_table_subcommand(
|
||||
rpc_client: Arc<RpcClient>,
|
||||
config: &CliConfig,
|
||||
subcommand: &AddressLookupTableCliCommand,
|
||||
) -> ProcessResult {
|
||||
match subcommand {
|
||||
AddressLookupTableCliCommand::CreateLookupTable {
|
||||
authority_signer_index,
|
||||
payer_signer_index,
|
||||
} => process_create_lookup_table(
|
||||
&rpc_client,
|
||||
config,
|
||||
*authority_signer_index,
|
||||
*payer_signer_index,
|
||||
),
|
||||
AddressLookupTableCliCommand::FreezeLookupTable {
|
||||
lookup_table_pubkey,
|
||||
authority_signer_index,
|
||||
bypass_warning,
|
||||
} => process_freeze_lookup_table(
|
||||
&rpc_client,
|
||||
config,
|
||||
*lookup_table_pubkey,
|
||||
*authority_signer_index,
|
||||
*bypass_warning,
|
||||
),
|
||||
AddressLookupTableCliCommand::ExtendLookupTable {
|
||||
lookup_table_pubkey,
|
||||
authority_signer_index,
|
||||
payer_signer_index,
|
||||
new_addresses,
|
||||
} => process_extend_lookup_table(
|
||||
&rpc_client,
|
||||
config,
|
||||
*lookup_table_pubkey,
|
||||
*authority_signer_index,
|
||||
*payer_signer_index,
|
||||
new_addresses.to_vec(),
|
||||
),
|
||||
AddressLookupTableCliCommand::DeactivateLookupTable {
|
||||
lookup_table_pubkey,
|
||||
authority_signer_index,
|
||||
bypass_warning,
|
||||
} => process_deactivate_lookup_table(
|
||||
&rpc_client,
|
||||
config,
|
||||
*lookup_table_pubkey,
|
||||
*authority_signer_index,
|
||||
*bypass_warning,
|
||||
),
|
||||
AddressLookupTableCliCommand::CloseLookupTable {
|
||||
lookup_table_pubkey,
|
||||
authority_signer_index,
|
||||
recipient_pubkey,
|
||||
} => process_close_lookup_table(
|
||||
&rpc_client,
|
||||
config,
|
||||
*lookup_table_pubkey,
|
||||
*authority_signer_index,
|
||||
*recipient_pubkey,
|
||||
),
|
||||
AddressLookupTableCliCommand::ShowLookupTable {
|
||||
lookup_table_pubkey,
|
||||
} => process_show_lookup_table(&rpc_client, config, *lookup_table_pubkey),
|
||||
}
|
||||
}
|
||||
|
||||
fn process_create_lookup_table(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
authority_signer_index: usize,
|
||||
payer_signer_index: usize,
|
||||
) -> ProcessResult {
|
||||
let authority_signer = config.signers[authority_signer_index];
|
||||
let payer_signer = config.signers[payer_signer_index];
|
||||
|
||||
let get_clock_result = rpc_client
|
||||
.get_account_with_commitment(&sysvar::clock::id(), CommitmentConfig::finalized())?;
|
||||
let clock_account = get_clock_result.value.expect("Clock account doesn't exist");
|
||||
let clock: Clock = from_account(&clock_account).ok_or_else(|| {
|
||||
CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
|
||||
})?;
|
||||
|
||||
let authority_address = authority_signer.pubkey();
|
||||
let payer_address = payer_signer.pubkey();
|
||||
let (create_lookup_table_ix, lookup_table_address) =
|
||||
create_lookup_table(authority_address, payer_address, clock.slot);
|
||||
|
||||
let blockhash = rpc_client.get_latest_blockhash()?;
|
||||
let mut tx = Transaction::new_unsigned(Message::new(
|
||||
&[create_lookup_table_ix],
|
||||
Some(&config.signers[0].pubkey()),
|
||||
));
|
||||
|
||||
tx.try_sign(
|
||||
&[config.signers[0], authority_signer, payer_signer],
|
||||
blockhash,
|
||||
)?;
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
||||
&tx,
|
||||
config.commitment,
|
||||
RpcSendTransactionConfig {
|
||||
skip_preflight: false,
|
||||
preflight_commitment: Some(config.commitment.commitment),
|
||||
..RpcSendTransactionConfig::default()
|
||||
},
|
||||
);
|
||||
match result {
|
||||
Err(err) => Err(format!("Create failed: {}", err).into()),
|
||||
Ok(signature) => Ok(config
|
||||
.output_format
|
||||
.formatted_string(&CliAddressLookupTableCreated {
|
||||
lookup_table_address: lookup_table_address.to_string(),
|
||||
signature: signature.to_string(),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
pub const FREEZE_LOOKUP_TABLE_WARNING: &str = "WARNING! \
|
||||
Once a lookup table is frozen, it can never be modified or unfrozen again. \
|
||||
To proceed with freezing, rerun the `freeze` command with the `--bypass-warning` flag";
|
||||
|
||||
fn process_freeze_lookup_table(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
lookup_table_pubkey: Pubkey,
|
||||
authority_signer_index: usize,
|
||||
bypass_warning: bool,
|
||||
) -> ProcessResult {
|
||||
let authority_signer = config.signers[authority_signer_index];
|
||||
|
||||
let get_lookup_table_result =
|
||||
rpc_client.get_account_with_commitment(&lookup_table_pubkey, config.commitment)?;
|
||||
let lookup_table_account = get_lookup_table_result.value.ok_or_else(|| {
|
||||
format!("Lookup table account {lookup_table_pubkey} not found, was it already closed?")
|
||||
})?;
|
||||
if !solana_address_lookup_table_program::check_id(&lookup_table_account.owner) {
|
||||
return Err(format!(
|
||||
"Lookup table account {lookup_table_pubkey} is not owned by the Address Lookup Table program",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
if !bypass_warning {
|
||||
return Err(String::from(FREEZE_LOOKUP_TABLE_WARNING).into());
|
||||
}
|
||||
|
||||
let authority_address = authority_signer.pubkey();
|
||||
let freeze_lookup_table_ix = freeze_lookup_table(lookup_table_pubkey, authority_address);
|
||||
|
||||
let blockhash = rpc_client.get_latest_blockhash()?;
|
||||
let mut tx = Transaction::new_unsigned(Message::new(
|
||||
&[freeze_lookup_table_ix],
|
||||
Some(&config.signers[0].pubkey()),
|
||||
));
|
||||
|
||||
tx.try_sign(&[config.signers[0], authority_signer], blockhash)?;
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
||||
&tx,
|
||||
config.commitment,
|
||||
RpcSendTransactionConfig {
|
||||
skip_preflight: false,
|
||||
preflight_commitment: Some(config.commitment.commitment),
|
||||
..RpcSendTransactionConfig::default()
|
||||
},
|
||||
);
|
||||
match result {
|
||||
Err(err) => Err(format!("Freeze failed: {}", err).into()),
|
||||
Ok(signature) => Ok(config.output_format.formatted_string(&CliSignature {
|
||||
signature: signature.to_string(),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
fn process_extend_lookup_table(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
lookup_table_pubkey: Pubkey,
|
||||
authority_signer_index: usize,
|
||||
payer_signer_index: usize,
|
||||
new_addresses: Vec<Pubkey>,
|
||||
) -> ProcessResult {
|
||||
let authority_signer = config.signers[authority_signer_index];
|
||||
let payer_signer = config.signers[payer_signer_index];
|
||||
|
||||
if new_addresses.is_empty() {
|
||||
return Err("Lookup tables must be extended by at least one address".into());
|
||||
}
|
||||
|
||||
let get_lookup_table_result =
|
||||
rpc_client.get_account_with_commitment(&lookup_table_pubkey, config.commitment)?;
|
||||
let lookup_table_account = get_lookup_table_result.value.ok_or_else(|| {
|
||||
format!("Lookup table account {lookup_table_pubkey} not found, was it already closed?")
|
||||
})?;
|
||||
if !solana_address_lookup_table_program::check_id(&lookup_table_account.owner) {
|
||||
return Err(format!(
|
||||
"Lookup table account {lookup_table_pubkey} is not owned by the Address Lookup Table program",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
let authority_address = authority_signer.pubkey();
|
||||
let payer_address = payer_signer.pubkey();
|
||||
let extend_lookup_table_ix = extend_lookup_table(
|
||||
lookup_table_pubkey,
|
||||
authority_address,
|
||||
Some(payer_address),
|
||||
new_addresses,
|
||||
);
|
||||
|
||||
let blockhash = rpc_client.get_latest_blockhash()?;
|
||||
let mut tx = Transaction::new_unsigned(Message::new(
|
||||
&[extend_lookup_table_ix],
|
||||
Some(&config.signers[0].pubkey()),
|
||||
));
|
||||
|
||||
tx.try_sign(&[config.signers[0], authority_signer], blockhash)?;
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
||||
&tx,
|
||||
config.commitment,
|
||||
RpcSendTransactionConfig {
|
||||
skip_preflight: false,
|
||||
preflight_commitment: Some(config.commitment.commitment),
|
||||
..RpcSendTransactionConfig::default()
|
||||
},
|
||||
);
|
||||
match result {
|
||||
Err(err) => Err(format!("Extend failed: {}", err).into()),
|
||||
Ok(signature) => Ok(config.output_format.formatted_string(&CliSignature {
|
||||
signature: signature.to_string(),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
pub const DEACTIVATE_LOOKUP_TABLE_WARNING: &str = "WARNING! \
|
||||
Once a lookup table is deactivated, it is no longer usable by transactions.
|
||||
Deactivated lookup tables may only be closed and cannot be recreated at the same address. \
|
||||
To proceed with deactivation, rerun the `deactivate` command with the `--bypass-warning` flag";
|
||||
|
||||
fn process_deactivate_lookup_table(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
lookup_table_pubkey: Pubkey,
|
||||
authority_signer_index: usize,
|
||||
bypass_warning: bool,
|
||||
) -> ProcessResult {
|
||||
let authority_signer = config.signers[authority_signer_index];
|
||||
|
||||
let get_lookup_table_result =
|
||||
rpc_client.get_account_with_commitment(&lookup_table_pubkey, config.commitment)?;
|
||||
let lookup_table_account = get_lookup_table_result.value.ok_or_else(|| {
|
||||
format!("Lookup table account {lookup_table_pubkey} not found, was it already closed?")
|
||||
})?;
|
||||
if !solana_address_lookup_table_program::check_id(&lookup_table_account.owner) {
|
||||
return Err(format!(
|
||||
"Lookup table account {lookup_table_pubkey} is not owned by the Address Lookup Table program",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
if !bypass_warning {
|
||||
return Err(String::from(DEACTIVATE_LOOKUP_TABLE_WARNING).into());
|
||||
}
|
||||
|
||||
let authority_address = authority_signer.pubkey();
|
||||
let deactivate_lookup_table_ix =
|
||||
deactivate_lookup_table(lookup_table_pubkey, authority_address);
|
||||
|
||||
let blockhash = rpc_client.get_latest_blockhash()?;
|
||||
let mut tx = Transaction::new_unsigned(Message::new(
|
||||
&[deactivate_lookup_table_ix],
|
||||
Some(&config.signers[0].pubkey()),
|
||||
));
|
||||
|
||||
tx.try_sign(&[config.signers[0], authority_signer], blockhash)?;
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
||||
&tx,
|
||||
config.commitment,
|
||||
RpcSendTransactionConfig {
|
||||
skip_preflight: false,
|
||||
preflight_commitment: Some(config.commitment.commitment),
|
||||
..RpcSendTransactionConfig::default()
|
||||
},
|
||||
);
|
||||
match result {
|
||||
Err(err) => Err(format!("Deactivate failed: {}", err).into()),
|
||||
Ok(signature) => Ok(config.output_format.formatted_string(&CliSignature {
|
||||
signature: signature.to_string(),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
fn process_close_lookup_table(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
lookup_table_pubkey: Pubkey,
|
||||
authority_signer_index: usize,
|
||||
recipient_pubkey: Pubkey,
|
||||
) -> ProcessResult {
|
||||
let authority_signer = config.signers[authority_signer_index];
|
||||
|
||||
let get_lookup_table_result =
|
||||
rpc_client.get_account_with_commitment(&lookup_table_pubkey, config.commitment)?;
|
||||
let lookup_table_account = get_lookup_table_result.value.ok_or_else(|| {
|
||||
format!("Lookup table account {lookup_table_pubkey} not found, was it already closed?")
|
||||
})?;
|
||||
if !solana_address_lookup_table_program::check_id(&lookup_table_account.owner) {
|
||||
return Err(format!(
|
||||
"Lookup table account {lookup_table_pubkey} is not owned by the Address Lookup Table program",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
let lookup_table_account = AddressLookupTable::deserialize(&lookup_table_account.data)?;
|
||||
if lookup_table_account.meta.deactivation_slot == u64::MAX {
|
||||
return Err(format!(
|
||||
"Lookup table account {lookup_table_pubkey} is not deactivated. Only deactivated lookup tables may be closed",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
let authority_address = authority_signer.pubkey();
|
||||
let close_lookup_table_ix =
|
||||
close_lookup_table(lookup_table_pubkey, authority_address, recipient_pubkey);
|
||||
|
||||
let blockhash = rpc_client.get_latest_blockhash()?;
|
||||
let mut tx = Transaction::new_unsigned(Message::new(
|
||||
&[close_lookup_table_ix],
|
||||
Some(&config.signers[0].pubkey()),
|
||||
));
|
||||
|
||||
tx.try_sign(&[config.signers[0], authority_signer], blockhash)?;
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
||||
&tx,
|
||||
config.commitment,
|
||||
RpcSendTransactionConfig {
|
||||
skip_preflight: false,
|
||||
preflight_commitment: Some(config.commitment.commitment),
|
||||
..RpcSendTransactionConfig::default()
|
||||
},
|
||||
);
|
||||
match result {
|
||||
Err(err) => Err(format!("Close failed: {}", err).into()),
|
||||
Ok(signature) => Ok(config.output_format.formatted_string(&CliSignature {
|
||||
signature: signature.to_string(),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
fn process_show_lookup_table(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
lookup_table_pubkey: Pubkey,
|
||||
) -> ProcessResult {
|
||||
let get_lookup_table_result =
|
||||
rpc_client.get_account_with_commitment(&lookup_table_pubkey, config.commitment)?;
|
||||
let lookup_table_account = get_lookup_table_result.value.ok_or_else(|| {
|
||||
format!("Lookup table account {lookup_table_pubkey} not found, was it already closed?")
|
||||
})?;
|
||||
if !solana_address_lookup_table_program::check_id(&lookup_table_account.owner) {
|
||||
return Err(format!(
|
||||
"Lookup table account {lookup_table_pubkey} is not owned by the Address Lookup Table program",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
let lookup_table_account = AddressLookupTable::deserialize(&lookup_table_account.data)?;
|
||||
Ok(config
|
||||
.output_format
|
||||
.formatted_string(&CliAddressLookupTable {
|
||||
lookup_table_address: lookup_table_pubkey.to_string(),
|
||||
authority: lookup_table_account
|
||||
.meta
|
||||
.authority
|
||||
.as_ref()
|
||||
.map(ToString::to_string),
|
||||
deactivation_slot: lookup_table_account.meta.deactivation_slot,
|
||||
last_extended_slot: lookup_table_account.meta.last_extended_slot,
|
||||
addresses: lookup_table_account
|
||||
.addresses
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect(),
|
||||
}))
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
cli::*, cluster_query::*, feature::*, inflation::*, nonce::*, program::*, stake::*,
|
||||
validator_info::*, vote::*, wallet::*,
|
||||
address_lookup_table::AddressLookupTableSubCommands, cli::*, cluster_query::*, feature::*,
|
||||
inflation::*, nonce::*, program::*, stake::*, validator_info::*, vote::*, wallet::*,
|
||||
},
|
||||
clap::{App, AppSettings, Arg, ArgGroup, SubCommand},
|
||||
solana_clap_utils::{self, input_validators::*, keypair::*},
|
||||
|
@ -130,6 +130,7 @@ pub fn get_clap_app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> A
|
|||
.inflation_subcommands()
|
||||
.nonce_subcommands()
|
||||
.program_subcommands()
|
||||
.address_lookup_table_subcommands()
|
||||
.stake_subcommands()
|
||||
.validator_info_subcommands()
|
||||
.vote_subcommands()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
clap_app::*, cluster_query::*, feature::*, inflation::*, nonce::*, program::*,
|
||||
spend_utils::*, stake::*, validator_info::*, vote::*, wallet::*,
|
||||
address_lookup_table::*, clap_app::*, cluster_query::*, feature::*, inflation::*, nonce::*,
|
||||
program::*, spend_utils::*, stake::*, validator_info::*, vote::*, wallet::*,
|
||||
},
|
||||
clap::{crate_description, crate_name, value_t_or_exit, ArgMatches, Shell},
|
||||
log::*,
|
||||
|
@ -440,6 +440,8 @@ pub enum CliCommand {
|
|||
StakeMinimumDelegation {
|
||||
use_lamports_unit: bool,
|
||||
},
|
||||
// Address lookup table commands
|
||||
AddressLookupTable(AddressLookupTableCliCommand),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
@ -687,6 +689,9 @@ pub fn parse_command(
|
|||
("program", Some(matches)) => {
|
||||
parse_program_subcommand(matches, default_signer, wallet_manager)
|
||||
}
|
||||
("address-lookup-table", Some(matches)) => {
|
||||
parse_address_lookup_table_subcommand(matches, default_signer, wallet_manager)
|
||||
}
|
||||
("wait-for-max-stake", Some(matches)) => {
|
||||
let max_stake_percent = value_t_or_exit!(matches, "max_percent", f32);
|
||||
Ok(CliCommandInfo {
|
||||
|
@ -1627,6 +1632,11 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
|||
derived_address_program_id.as_ref(),
|
||||
compute_unit_price.as_ref(),
|
||||
),
|
||||
|
||||
// Address Lookup Table Commands
|
||||
CliCommand::AddressLookupTable(subcommand) => {
|
||||
process_address_lookup_table_subcommand(rpc_client, config, subcommand)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ extern crate const_format;
|
|||
|
||||
extern crate serde_derive;
|
||||
|
||||
pub mod address_lookup_table;
|
||||
pub mod checks;
|
||||
pub mod clap_app;
|
||||
pub mod cli;
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
use {
|
||||
solana_cli::{
|
||||
address_lookup_table::{
|
||||
AddressLookupTableCliCommand, DEACTIVATE_LOOKUP_TABLE_WARNING,
|
||||
FREEZE_LOOKUP_TABLE_WARNING,
|
||||
},
|
||||
cli::{process_command, CliCommand, CliConfig},
|
||||
},
|
||||
solana_cli_output::{CliAddressLookupTable, CliAddressLookupTableCreated, OutputFormat},
|
||||
solana_faucet::faucet::run_local_faucet,
|
||||
solana_sdk::{
|
||||
native_token::LAMPORTS_PER_SOL,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signer},
|
||||
},
|
||||
solana_streamer::socket::SocketAddrSpace,
|
||||
solana_test_validator::TestValidator,
|
||||
std::str::FromStr,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_cli_create_extend_and_freeze_address_lookup_table() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
|
||||
|
||||
let mut config = CliConfig::recent_for_tests();
|
||||
let keypair = Keypair::new();
|
||||
config.json_rpc_url = test_validator.rpc_url();
|
||||
config.signers = vec![&keypair];
|
||||
config.output_format = OutputFormat::JsonCompact;
|
||||
|
||||
// Airdrop SOL for transaction fees
|
||||
config.command = CliCommand::Airdrop {
|
||||
pubkey: None,
|
||||
lamports: 10 * LAMPORTS_PER_SOL,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
|
||||
// Create lookup table
|
||||
config.command =
|
||||
CliCommand::AddressLookupTable(AddressLookupTableCliCommand::CreateLookupTable {
|
||||
authority_signer_index: 0,
|
||||
payer_signer_index: 0,
|
||||
});
|
||||
let response: CliAddressLookupTableCreated =
|
||||
serde_json::from_str(&process_command(&config).unwrap()).unwrap();
|
||||
let lookup_table_pubkey = Pubkey::from_str(&response.lookup_table_address).unwrap();
|
||||
|
||||
// Validate created lookup table
|
||||
{
|
||||
config.command =
|
||||
CliCommand::AddressLookupTable(AddressLookupTableCliCommand::ShowLookupTable {
|
||||
lookup_table_pubkey,
|
||||
});
|
||||
let response: CliAddressLookupTable =
|
||||
serde_json::from_str(&process_command(&config).unwrap()).unwrap();
|
||||
assert_eq!(
|
||||
response,
|
||||
CliAddressLookupTable {
|
||||
lookup_table_address: lookup_table_pubkey.to_string(),
|
||||
authority: Some(keypair.pubkey().to_string()),
|
||||
deactivation_slot: u64::MAX,
|
||||
last_extended_slot: 0,
|
||||
addresses: vec![],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Extend lookup table
|
||||
let new_addresses: Vec<Pubkey> = (0..5).map(|_| Pubkey::new_unique()).collect();
|
||||
config.command =
|
||||
CliCommand::AddressLookupTable(AddressLookupTableCliCommand::ExtendLookupTable {
|
||||
lookup_table_pubkey,
|
||||
authority_signer_index: 0,
|
||||
payer_signer_index: 0,
|
||||
new_addresses: new_addresses.clone(),
|
||||
});
|
||||
process_command(&config).unwrap();
|
||||
|
||||
// Validate extended lookup table
|
||||
{
|
||||
config.command =
|
||||
CliCommand::AddressLookupTable(AddressLookupTableCliCommand::ShowLookupTable {
|
||||
lookup_table_pubkey,
|
||||
});
|
||||
let CliAddressLookupTable {
|
||||
addresses,
|
||||
last_extended_slot,
|
||||
..
|
||||
} = serde_json::from_str(&process_command(&config).unwrap()).unwrap();
|
||||
assert_eq!(
|
||||
addresses
|
||||
.into_iter()
|
||||
.map(|address| Pubkey::from_str(&address).unwrap())
|
||||
.collect::<Vec<Pubkey>>(),
|
||||
new_addresses
|
||||
);
|
||||
assert!(last_extended_slot > 0);
|
||||
}
|
||||
|
||||
// Freeze lookup table w/o bypass
|
||||
config.command =
|
||||
CliCommand::AddressLookupTable(AddressLookupTableCliCommand::FreezeLookupTable {
|
||||
lookup_table_pubkey,
|
||||
authority_signer_index: 0,
|
||||
bypass_warning: false,
|
||||
});
|
||||
let process_err = process_command(&config).unwrap_err();
|
||||
assert_eq!(process_err.to_string(), FREEZE_LOOKUP_TABLE_WARNING);
|
||||
|
||||
// Freeze lookup table w/ bypass
|
||||
config.command =
|
||||
CliCommand::AddressLookupTable(AddressLookupTableCliCommand::FreezeLookupTable {
|
||||
lookup_table_pubkey,
|
||||
authority_signer_index: 0,
|
||||
bypass_warning: true,
|
||||
});
|
||||
process_command(&config).unwrap();
|
||||
|
||||
// Validate frozen lookup table
|
||||
{
|
||||
config.command =
|
||||
CliCommand::AddressLookupTable(AddressLookupTableCliCommand::ShowLookupTable {
|
||||
lookup_table_pubkey,
|
||||
});
|
||||
let CliAddressLookupTable { authority, .. } =
|
||||
serde_json::from_str(&process_command(&config).unwrap()).unwrap();
|
||||
assert!(authority.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cli_create_and_deactivate_address_lookup_table() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
|
||||
|
||||
let mut config = CliConfig::recent_for_tests();
|
||||
let keypair = Keypair::new();
|
||||
config.json_rpc_url = test_validator.rpc_url();
|
||||
config.signers = vec![&keypair];
|
||||
config.output_format = OutputFormat::JsonCompact;
|
||||
|
||||
// Airdrop SOL for transaction fees
|
||||
config.command = CliCommand::Airdrop {
|
||||
pubkey: None,
|
||||
lamports: 10 * LAMPORTS_PER_SOL,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
|
||||
// Create lookup table
|
||||
config.command =
|
||||
CliCommand::AddressLookupTable(AddressLookupTableCliCommand::CreateLookupTable {
|
||||
authority_signer_index: 0,
|
||||
payer_signer_index: 0,
|
||||
});
|
||||
let response: CliAddressLookupTableCreated =
|
||||
serde_json::from_str(&process_command(&config).unwrap()).unwrap();
|
||||
let lookup_table_pubkey = Pubkey::from_str(&response.lookup_table_address).unwrap();
|
||||
|
||||
// Validate created lookup table
|
||||
{
|
||||
config.command =
|
||||
CliCommand::AddressLookupTable(AddressLookupTableCliCommand::ShowLookupTable {
|
||||
lookup_table_pubkey,
|
||||
});
|
||||
let response: CliAddressLookupTable =
|
||||
serde_json::from_str(&process_command(&config).unwrap()).unwrap();
|
||||
assert_eq!(
|
||||
response,
|
||||
CliAddressLookupTable {
|
||||
lookup_table_address: lookup_table_pubkey.to_string(),
|
||||
authority: Some(keypair.pubkey().to_string()),
|
||||
deactivation_slot: u64::MAX,
|
||||
last_extended_slot: 0,
|
||||
addresses: vec![],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Deactivate lookup table w/o bypass
|
||||
config.command =
|
||||
CliCommand::AddressLookupTable(AddressLookupTableCliCommand::DeactivateLookupTable {
|
||||
lookup_table_pubkey,
|
||||
authority_signer_index: 0,
|
||||
bypass_warning: false,
|
||||
});
|
||||
let process_err = process_command(&config).unwrap_err();
|
||||
assert_eq!(process_err.to_string(), DEACTIVATE_LOOKUP_TABLE_WARNING);
|
||||
|
||||
// Deactivate lookup table w/ bypass
|
||||
config.command =
|
||||
CliCommand::AddressLookupTable(AddressLookupTableCliCommand::DeactivateLookupTable {
|
||||
lookup_table_pubkey,
|
||||
authority_signer_index: 0,
|
||||
bypass_warning: true,
|
||||
});
|
||||
process_command(&config).unwrap();
|
||||
|
||||
// Validate deactivated lookup table
|
||||
{
|
||||
config.command =
|
||||
CliCommand::AddressLookupTable(AddressLookupTableCliCommand::ShowLookupTable {
|
||||
lookup_table_pubkey,
|
||||
});
|
||||
let CliAddressLookupTable {
|
||||
deactivation_slot, ..
|
||||
} = serde_json::from_str(&process_command(&config).unwrap()).unwrap();
|
||||
assert_ne!(deactivation_slot, u64::MAX);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue