diff --git a/charts/deploy.just b/charts/deploy.just index dbaf90f284..195e880eb3 100644 --- a/charts/deploy.just +++ b/charts/deploy.just @@ -97,7 +97,7 @@ deploy-astrotrek: -n astria-dev-cluster \ delete-astrotrek: - @just delete chart astrotrek + @just delete chart astrotrek deploy-hermes-local: helm install hermes-local-chart ./charts/hermes \ @@ -222,6 +222,8 @@ rollup_multiplier := "1000000000" # 10 RIA sequencer_transfer_amount := "10" sequencer_rpc_url := "http://rpc.sequencer.localdev.me" +sequencer_funded_address:= "astria1qrt4kfc9ggyy548u7rg0d64sgq5c952kzk9tg9" +sequencer_funded_pkey := "934ab488f9e1900f6a08f50605ce1409ca9d95ebdc400dafc2e8a4306419fd52" sequencer_bridge_address := "astria13ahqz4pjqfmynk9ylrqv4fwe4957x2p0h5782u" sequencer_bridge_pkey := "dfa7108e38ab71f89f356c72afc38600d5758f11a8c337164713e4471411d2e0" sequencer_chain_id := "sequencer-test-chain-0" @@ -335,7 +337,6 @@ run-smoke-test tag=defaultTag: echo "Bridge Out Sequencer failure" exit 1 fi - echo "Testing TX Finalization" CHECKS=0 BLOCK_NUM_HEX=$(just evm-get-transaction-receipt {{bridge_tx_hash}} | jq -r '.blockNumber') @@ -461,6 +462,43 @@ run-smoke-cli tag=defaultTag: exit 1 fi + echo "Testing Bridge sudo address change" + astria-cli sequencer bridge-sudo-change {{sequencer_bridge_address}} --private-key={{sequencer_bridge_pkey}} --new-sudo-address={{sequencer_funded_address}} --sequencer-url={{sequencer_rpc_url}} --sequencer.chain-id={{sequencer_chain_id}} + CHECKS=0 + while (( $CHECKS < $MAX_CHECKS )); do + CHECKS=$((CHECKS+1)) + SUDO_ADDRESS=$(astria-cli sequencer get-bridge-account {{sequencer_bridge_address}} --sequencer-url {{sequencer_rpc_url}} | awk '/Sudo Address/{print $(NF)}') + echo "Check $CHECKS, Sudo address: $SUDO_ADDRESS, Expected: {{sequencer_funded_address}}" + if [ "$SUDO_ADDRESS" == "{{sequencer_funded_address}}" ]; then + echo "Bridge Sudo change success" + break + else + sleep 1 + fi + done + if (( $CHECKS >= $MAX_CHECKS )); then + echo "Bridge Sudo Change failure" + exit 1 + fi + + echo "Reverting Bridge sudo address" + astria-cli sequencer bridge-sudo-change {{sequencer_bridge_address}} --private-key={{sequencer_funded_pkey}} --new-sudo-address={{sequencer_bridge_address}} --sequencer-url={{sequencer_rpc_url}} --sequencer.chain-id={{sequencer_chain_id}} + CHECKS=0 + while (( $CHECKS < $MAX_CHECKS )); do + CHECKS=$((CHECKS+1)) + SUDO_ADDRESS=$(astria-cli sequencer get-bridge-account {{sequencer_bridge_address}} --sequencer-url {{sequencer_rpc_url}} | awk '/Sudo Address/{print $(NF)}') + echo "Check $CHECKS, Sudo address: $SUDO_ADDRESS, Expected: {{sequencer_bridge_address}}" + if [ "$SUDO_ADDRESS" == "{{sequencer_bridge_address}}" ]; then + echo "Bridge Sudo change back success" + break + else + sleep 1 + fi + done + if (( $CHECKS >= $MAX_CHECKS )); then + echo "Bridge Sudo Change failure" + exit 1 + fi exit 0 ############################################# diff --git a/crates/astria-cli/src/cli/mod.rs b/crates/astria-cli/src/cli/mod.rs index 9b6af0119a..146c0f19e4 100644 --- a/crates/astria-cli/src/cli/mod.rs +++ b/crates/astria-cli/src/cli/mod.rs @@ -33,6 +33,9 @@ impl Cli { } /// Commands that can be run +// allow: these are one-shot variants. the size doesn't matter as they are +// passed around only once. +#[allow(clippy::large_enum_variant)] #[derive(Debug, Subcommand)] pub(crate) enum Command { Bridge { diff --git a/crates/astria-cli/src/cli/sequencer.rs b/crates/astria-cli/src/cli/sequencer.rs index 783282aedd..7fb4ed4dfa 100644 --- a/crates/astria-cli/src/cli/sequencer.rs +++ b/crates/astria-cli/src/cli/sequencer.rs @@ -1,11 +1,15 @@ use astria_core::primitive::v1::asset; use astria_sequencer_client::Address; use clap::{ + ArgGroup, Args, Subcommand, }; /// Interact with a Sequencer node +// allow: these are one-shot variants. the size doesn't matter as they are +// passed around only once. +#[allow(clippy::large_enum_variant)] #[derive(Debug, Subcommand)] pub(crate) enum Command { /// Commands for interacting with Sequencer accounts @@ -40,6 +44,10 @@ pub(crate) enum Command { InitBridgeAccount(InitBridgeAccountArgs), /// Command for transferring to a bridge account BridgeLock(BridgeLockArgs), + /// Command for changing the bridge account sudo address or withdrawer address + BridgeSudoChange(BridgeSudoChangeArgs), + /// Command for getting bridge account information + GetBridgeAccount(BridgeAccountArgs), } #[derive(Debug, Subcommand)] @@ -105,6 +113,19 @@ pub(crate) struct BasicAccountArgs { pub(crate) address: Address, } +#[derive(Args, Debug)] +pub(crate) struct BridgeAccountArgs { + /// The url of the Sequencer node + #[arg( + long, + env = "SEQUENCER_URL", + default_value = crate::cli::DEFAULT_SEQUENCER_RPC + )] + pub(crate) sequencer_url: String, + /// The address of the Sequencer bridge account + pub(crate) address: Address, +} + #[derive(Args, Debug)] pub(crate) struct Bech32mAddressArgs { /// The hex formatted byte part of the bech32m address @@ -291,6 +312,51 @@ pub(crate) struct BridgeLockArgs { pub(crate) fee_asset: asset::Denom, } +#[derive(Args, Debug)] +#[command(group(ArgGroup::new("new_address") + .required(true) + .multiple(true) + .args(&["new_sudo_address", "new_withdrawer_address"])))] +pub(crate) struct BridgeSudoChangeArgs { + /// The bridge account whose privileges will be modified. + pub(crate) bridge_address: Address, + /// The new address to receive sudo privileges. + #[arg(long, default_value = None)] + pub(crate) new_sudo_address: Option
, + /// The new address to receive withdrawer privileges. + #[arg(long, default_value = None)] + pub(crate) new_withdrawer_address: Option
, + /// The prefix to construct a bech32m address given the private key. + #[arg(long, default_value = "astria")] + pub(crate) prefix: String, + // TODO: https://github.com/astriaorg/astria/issues/594 + // Don't use a plain text private, prefer wrapper like from + // the secrecy crate with specialized `Debug` and `Drop` implementations + // that overwrite the key on drop and don't reveal it when printing. + #[arg(long, env = "SEQUENCER_PRIVATE_KEY")] + pub(crate) private_key: String, + /// The url of the Sequencer node + #[arg( + long, + env = "SEQUENCER_URL", + default_value = crate::cli::DEFAULT_SEQUENCER_RPC + )] + pub(crate) sequencer_url: String, + /// The chain id of the sequencing chain being used + #[arg( + long = "sequencer.chain-id", + env = "ROLLUP_SEQUENCER_CHAIN_ID", + default_value = crate::cli::DEFAULT_SEQUENCER_CHAIN_ID + )] + pub(crate) sequencer_chain_id: String, + /// The asset to lock. + #[arg(long, default_value = "nria")] + pub(crate) asset: asset::Denom, + /// The asset to pay the transfer fees with. + #[arg(long, default_value = "nria")] + pub(crate) fee_asset: asset::Denom, +} + #[derive(Debug, Subcommand)] pub(crate) enum BlockHeightCommand { /// Get the current block height of the Sequencer node diff --git a/crates/astria-cli/src/commands/mod.rs b/crates/astria-cli/src/commands/mod.rs index 436d5c74fc..170d4550d3 100644 --- a/crates/astria-cli/src/commands/mod.rs +++ b/crates/astria-cli/src/commands/mod.rs @@ -98,6 +98,12 @@ pub async fn run(cli: Cli) -> eyre::Result<()> { sequencer::init_bridge_account(&args).await?; } SequencerCommand::BridgeLock(args) => sequencer::bridge_lock(&args).await?, + SequencerCommand::BridgeSudoChange(args) => { + sequencer::bridge_sudo_change(&args).await?; + } + SequencerCommand::GetBridgeAccount(args) => { + sequencer::get_bridge_account(&args).await?; + } }, } } else { diff --git a/crates/astria-cli/src/commands/sequencer.rs b/crates/astria-cli/src/commands/sequencer.rs index 33bade4b4b..f816f94e97 100644 --- a/crates/astria-cli/src/commands/sequencer.rs +++ b/crates/astria-cli/src/commands/sequencer.rs @@ -9,6 +9,7 @@ use astria_core::{ action::{ Action, BridgeLockAction, + BridgeSudoChangeAction, FeeAssetChangeAction, IbcRelayerChangeAction, InitBridgeAccountAction, @@ -39,7 +40,9 @@ use crate::cli::sequencer::{ BasicAccountArgs, Bech32mAddressArgs, BlockHeightGetArgs, + BridgeAccountArgs, BridgeLockArgs, + BridgeSudoChangeArgs, FeeAssetChangeArgs, IbcRelayerChangeArgs, InitBridgeAccountArgs, @@ -162,6 +165,36 @@ pub(crate) async fn get_block_height(args: &BlockHeightGetArgs) -> eyre::Result< Ok(()) } +/// Gets bridge account information +/// +/// # Arguments +/// +/// * `args` - The arguments passed to the command +/// +/// # Errors +/// +/// * If the http client cannot be created +/// * If the bridge account information cannot be retrieved +pub(crate) async fn get_bridge_account(args: &BridgeAccountArgs) -> eyre::Result<()> { + let sequencer_client = HttpClient::new(args.sequencer_url.as_str()) + .wrap_err("failed constructing http sequencer client")?; + + let res = sequencer_client + .get_bridge_account_info(args.address) + .await + .wrap_err("failed to get bridge account")?; + let Some(info) = res.info else { + return Err(eyre::eyre!("no bridge account information found")); + }; + println!("Bridge Account Information for address: {}", args.address); + println!(" Rollup Id: {}", info.rollup_id); + println!(" Asset: {}", info.asset); + println!(" Sudo Address: {}", info.sudo_address); + println!(" Withdrawer Address {}", info.withdrawer_address); + + Ok(()) +} + /// Returns a bech32m sequencer address given a prefix and hex-encoded byte slice pub(crate) fn make_bech32m(args: &Bech32mAddressArgs) -> eyre::Result<()> { use hex::FromHex as _; @@ -330,6 +363,36 @@ pub(crate) async fn bridge_lock(args: &BridgeLockArgs) -> eyre::Result<()> { Ok(()) } +/// Bridge Sudo Change action +/// +/// # Arguments +/// +/// * `args` - The arguments passed to the command +/// +/// # Errors +/// +/// * If the http client cannot be created +/// * If the transaction failed to be included +pub(crate) async fn bridge_sudo_change(args: &BridgeSudoChangeArgs) -> eyre::Result<()> { + let res = submit_transaction( + args.sequencer_url.as_str(), + args.sequencer_chain_id.clone(), + &args.prefix, + args.private_key.as_str(), + Action::BridgeSudoChange(BridgeSudoChangeAction { + bridge_address: args.bridge_address, + new_sudo_address: args.new_sudo_address, + new_withdrawer_address: args.new_withdrawer_address, + fee_asset: args.fee_asset.clone(), + }), + ) + .await + .wrap_err("failed to submit BridgeSudoChnage transaction")?; + println!("BridgeSudoChange completed!"); + println!("Included in block: {}", res.height); + Ok(()) +} + /// Adds a fee asset /// /// # Arguments