From 44b8686e60d0ae32ca3bcbe85b46bc2c3b319e51 Mon Sep 17 00:00:00 2001 From: Fraser Hutchison Date: Tue, 7 May 2024 12:01:52 +0100 Subject: [PATCH] add subcommand to parse Celestia blob data in sequencer-utils --- Cargo.lock | 40 +- Cargo.toml | 2 +- crates/astria-cli/src/cli/mod.rs | 8 +- crates/astria-cli/src/cli/rollup.rs | 70 +-- crates/astria-cli/src/cli/sequencer.rs | 92 ++-- .../src/sequencerblock/v1alpha1/celestia.rs | 5 + crates/astria-sequencer-utils/Cargo.toml | 18 +- .../astria-sequencer-utils/src/blob_parser.rs | 460 ++++++++++++++++++ crates/astria-sequencer-utils/src/cli.rs | 33 ++ crates/astria-sequencer-utils/src/config.rs | 20 - .../src/genesis_parser.rs | 114 +++-- crates/astria-sequencer-utils/src/lib.rs | 3 +- crates/astria-sequencer-utils/src/main.rs | 20 +- .../tests/parse_blob.rs | 107 ++++ .../expected_brief_output.json | 1 + .../expected_brief_output.txt | 42 ++ .../expected_verbose_output.json | 1 + .../expected_verbose_output.txt | 58 +++ .../parse_blob/batched_metadata/input.txt | 1 + .../expected_brief_output.json | 1 + .../expected_brief_output.txt | 10 + .../expected_verbose_output.json | 1 + .../expected_verbose_output.txt | 20 + .../parse_blob/batched_rollup_data/input.txt | 1 + .../expected_brief_output.json | 1 + .../expected_brief_output.txt | 12 + .../expected_verbose_output.json | 1 + .../expected_verbose_output.txt | 20 + .../parse_blob/unbatched_metadata/input.txt | 1 + .../expected_brief_output.json | 1 + .../expected_brief_output.txt | 6 + .../expected_verbose_output.json | 1 + .../expected_verbose_output.txt | 11 + .../unbatched_rollup_data/input.txt | 1 + 34 files changed, 1015 insertions(+), 168 deletions(-) create mode 100644 crates/astria-sequencer-utils/src/blob_parser.rs create mode 100644 crates/astria-sequencer-utils/src/cli.rs delete mode 100644 crates/astria-sequencer-utils/src/config.rs create mode 100644 crates/astria-sequencer-utils/tests/parse_blob.rs create mode 100644 crates/astria-sequencer-utils/tests/resources/parse_blob/batched_metadata/expected_brief_output.json create mode 100644 crates/astria-sequencer-utils/tests/resources/parse_blob/batched_metadata/expected_brief_output.txt create mode 100644 crates/astria-sequencer-utils/tests/resources/parse_blob/batched_metadata/expected_verbose_output.json create mode 100644 crates/astria-sequencer-utils/tests/resources/parse_blob/batched_metadata/expected_verbose_output.txt create mode 100644 crates/astria-sequencer-utils/tests/resources/parse_blob/batched_metadata/input.txt create mode 100644 crates/astria-sequencer-utils/tests/resources/parse_blob/batched_rollup_data/expected_brief_output.json create mode 100644 crates/astria-sequencer-utils/tests/resources/parse_blob/batched_rollup_data/expected_brief_output.txt create mode 100644 crates/astria-sequencer-utils/tests/resources/parse_blob/batched_rollup_data/expected_verbose_output.json create mode 100644 crates/astria-sequencer-utils/tests/resources/parse_blob/batched_rollup_data/expected_verbose_output.txt create mode 100644 crates/astria-sequencer-utils/tests/resources/parse_blob/batched_rollup_data/input.txt create mode 100644 crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_metadata/expected_brief_output.json create mode 100644 crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_metadata/expected_brief_output.txt create mode 100644 crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_metadata/expected_verbose_output.json create mode 100644 crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_metadata/expected_verbose_output.txt create mode 100644 crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_metadata/input.txt create mode 100644 crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_rollup_data/expected_brief_output.json create mode 100644 crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_rollup_data/expected_brief_output.txt create mode 100644 crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_rollup_data/expected_verbose_output.json create mode 100644 crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_rollup_data/expected_verbose_output.txt create mode 100644 crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_rollup_data/input.txt diff --git a/Cargo.lock b/Cargo.lock index 8050b95d3..9a5495f36 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -859,8 +859,17 @@ dependencies = [ name = "astria-sequencer-utils" version = "0.1.0" dependencies = [ + "assert_cmd", + "astria-core", "astria-eyre", + "astria-merkle", + "base64 0.21.7", "clap", + "hex", + "indenter", + "itertools 0.12.1", + "predicates", + "prost", "serde", "serde_json", ] @@ -1714,6 +1723,7 @@ dependencies = [ "anstyle", "clap_lex", "strsim 0.11.1", + "terminal_size", ] [[package]] @@ -3167,6 +3177,15 @@ dependencies = [ "paste", ] +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + [[package]] name = "fnv" version = "1.0.7" @@ -4519,7 +4538,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.4", ] [[package]] @@ -4879,6 +4898,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -5938,7 +5963,10 @@ checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" dependencies = [ "anstyle", "difflib", + "float-cmp", + "normalize-line-endings", "predicates-core", + "regex", ] [[package]] @@ -7636,6 +7664,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "terminal_size" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +dependencies = [ + "rustix", + "windows-sys 0.48.0", +] + [[package]] name = "termtree" version = "0.4.1" diff --git a/Cargo.toml b/Cargo.toml index 0590a5f59..30da97aed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,7 +54,7 @@ base64-serde = "0.7.0" bytes = "1" celestia-tendermint = "0.32.1" celestia-types = "0.1.1" -clap = "4" +clap = "4.5.4" ed25519-consensus = "2.1.0" ethers = "2.0.11" futures = "0.3" diff --git a/crates/astria-cli/src/cli/mod.rs b/crates/astria-cli/src/cli/mod.rs index c34c2581a..82cd29632 100644 --- a/crates/astria-cli/src/cli/mod.rs +++ b/crates/astria-cli/src/cli/mod.rs @@ -17,9 +17,9 @@ const DEFAULT_SEQUENCER_CHAIN_ID: &str = "astria-dusk-4"; /// A CLI for deploying and managing Astria services and related infrastructure. #[derive(Debug, Parser)] -#[clap(name = "astria-cli", version)] +#[command(name = "astria-cli", version)] pub struct Cli { - #[clap(subcommand)] + #[command(subcommand)] pub command: Option, } @@ -39,11 +39,11 @@ impl Cli { #[derive(Debug, Subcommand)] pub enum Command { Rollup { - #[clap(subcommand)] + #[command(subcommand)] command: RollupCommand, }, Sequencer { - #[clap(subcommand)] + #[command(subcommand)] command: SequencerCommand, }, } diff --git a/crates/astria-cli/src/cli/rollup.rs b/crates/astria-cli/src/cli/rollup.rs index 738563ce9..a5f38a6e4 100644 --- a/crates/astria-cli/src/cli/rollup.rs +++ b/crates/astria-cli/src/cli/rollup.rs @@ -31,12 +31,12 @@ fn strip_0x_prefix(s: &str) -> &str { pub enum Command { /// Manage your rollup configs Config { - #[clap(subcommand)] + #[command(subcommand)] command: ConfigCommand, }, /// Manage your rollup deployments Deployment { - #[clap(subcommand)] + #[command(subcommand)] command: DeploymentCommand, }, } @@ -57,73 +57,73 @@ pub enum ConfigCommand { #[derive(Args, Debug, Serialize, Clone)] pub struct ConfigCreateArgs { - #[clap(long, env = "ROLLUP_USE_TTY", default_value = "true")] + #[arg(long, env = "ROLLUP_USE_TTY", default_value = "true")] pub use_tty: bool, - #[clap(long, env = "ROLLUP_LOG_LEVEL", default_value = DEFAULT_LOG_LEVEL)] + #[arg(long, env = "ROLLUP_LOG_LEVEL", default_value = DEFAULT_LOG_LEVEL)] pub log_level: String, // rollup config /// The name of the rollup - #[clap(long = "rollup.name", env = "ROLLUP_NAME")] + #[arg(long = "rollup.name", env = "ROLLUP_NAME")] pub name: String, /// The Network ID for the EVM chain - #[clap(long = "rollup.network-id", env = "ROLLUP_NETWORK_ID", default_value_t = DEFAULT_NETWORK_ID)] + #[arg(long = "rollup.network-id", env = "ROLLUP_NETWORK_ID", default_value_t = DEFAULT_NETWORK_ID)] pub network_id: u64, /// The Execution Commit level - #[clap( + #[arg( long = "rollup.execution-commit-level", env = "ROLLUP_EXECUTION_COMMIT_LEVEL", default_value = DEFAULT_EXECUTION_COMMIT_LEVEL )] pub execution_commit_level: String, /// Choose to allow genesis extra data override - #[clap( + #[arg( long = "rollup.override-genesis-extra-data", env = "ROLLUP_OVERRIDE_GENESIS_EXTRA_DATA" )] pub override_genesis_extra_data: bool, /// Optional. If set, will be used as the bridge address. If not set, nothing happens. - #[clap(long = "rollup.bridge-address", env = "ROLLUP_BRIDGE_ADDRESS")] + #[arg(long = "rollup.bridge-address", env = "ROLLUP_BRIDGE_ADDRESS")] pub bridge_address: Option, /// The allowed asset denom for the bridge - #[clap( + #[arg( long = "rollup.bridge-allowed-asset-denom", env = "ROLLUP_BRIDGE_ALLOWED_ASSET_DENOM", default_value = DEFAULT_ROLLUP_GENESIS_BRIDGE_ALLOWED_ASSET_DENOM )] pub bridge_allowed_asset_denom: String, /// List of genesis accounts to fund, in the form of `address:balance` - #[clap( - long = "rollup.genesis-accounts", - env = "ROLLUP_GENESIS_ACCOUNTS", + #[arg( + long = "rollup.genesis-accounts", + env = "ROLLUP_GENESIS_ACCOUNTS", num_args = 1.., value_delimiter = ',' )] pub genesis_accounts: Vec, // sequencer config /// Optional. If not set, will be determined from the current block height of the sequencer - #[clap( + #[arg( long = "sequencer.initial-block-height", env = "ROLLUP_SEQUENCER_INITIAL_BLOCK_HEIGHT" )] #[arg(value_parser = validate_initial_block_height)] pub sequencer_initial_block_height: Option, /// Optional. If not set, will be default to the devnet sequencer websocket address - #[clap( - long = "sequencer.grpc", - env = "ROLLUP_SEQUENCER_GRPC", + #[arg( + long = "sequencer.grpc", + env = "ROLLUP_SEQUENCER_GRPC", default_value = DEFAULT_SEQUENCER_GRPC )] pub sequencer_grpc: String, /// Optional. If not set, will be default to the devnet sequencer rpc address - #[clap( - long = "sequencer.rpc", - env = "ROLLUP_SEQUENCER_RPC", + #[arg( + long = "sequencer.rpc", + env = "ROLLUP_SEQUENCER_RPC", default_value = crate::cli::DEFAULT_SEQUENCER_RPC )] pub sequencer_rpc: String, /// The chain id of the sequencing chain being used - #[clap( + #[arg( long = "sequencer.chain-id", env = "ROLLUP_SEQUENCER_CHAIN_ID", default_value = crate::cli::DEFAULT_SEQUENCER_CHAIN_ID @@ -131,17 +131,17 @@ pub struct ConfigCreateArgs { pub sequencer_chain_id: String, /// Optional. Will default to 'localdev.me' for local deployments. Will need to separately /// configure other hosts - #[clap( - long = "hostname", - env = "ROLLUP_HOSTNAME", + #[arg( + long = "hostname", + env = "ROLLUP_HOSTNAME", default_value = DEFAULT_HOSTNAME )] pub hostname: String, /// Configures the k8s namespace rollup will be deployed to - #[clap(long, env = "ROLLUP_NAMESPACE", default_value = DEFAULT_NAMESPACE)] + #[arg(long, env = "ROLLUP_NAMESPACE", default_value = DEFAULT_NAMESPACE)] pub namespace: String, /// Choose to enable the Celestia feature - #[clap(long = "celestia-node.enabled", env = "ROLLUP_ENABLE_CELESTIA_NODE")] + #[arg(long = "celestia-node.enabled", env = "ROLLUP_ENABLE_CELESTIA_NODE")] pub enable_celestia_node: bool, } @@ -203,7 +203,7 @@ impl FromStr for GenesisAccountArg { #[derive(Args, Debug)] pub struct ConfigEditArgs { /// The filepath of the config to edit - #[clap(long = "config", env = "ROLLUP_CONFIG_PATH")] + #[arg(long = "config", env = "ROLLUP_CONFIG_PATH")] pub(crate) config_path: String, /// The key of the field to edit. Accepts dot notated yaml path. pub(crate) key: String, @@ -214,7 +214,7 @@ pub struct ConfigEditArgs { #[derive(Args, Debug)] pub struct ConfigDeleteArgs { /// The filepath of the config to delete - #[clap(long = "config", env = "ROLLUP_CONFIG_PATH")] + #[arg(long = "config", env = "ROLLUP_CONFIG_PATH")] pub(crate) config_path: String, } @@ -231,42 +231,42 @@ pub enum DeploymentCommand { #[derive(Args, Debug, Serialize)] pub struct DeploymentCreateArgs { /// Filepath of the config to deploy - #[clap(long = "config", env = "ROLLUP_CONFIG_PATH")] + #[arg(long = "config", env = "ROLLUP_CONFIG_PATH")] pub(crate) config_path: String, /// Optional path to a rollup chart that can override the default remote helm chart - #[clap( + #[arg( long, env = "ROLLUP_CHART_PATH", default_value = DEFAULT_ROLLUP_CHART_PATH )] pub(crate) chart_path: String, /// Set if you want to do a dry run of the deployment - #[clap(long, env = "ROLLUP_DRY_RUN", default_value = "false")] + #[arg(long, env = "ROLLUP_DRY_RUN", default_value = "false")] pub(crate) dry_run: bool, // 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. /// Faucet private key - #[clap(long, env = "ROLLUP_FAUCET_PRIVATE_KEY")] + #[arg(long, env = "ROLLUP_FAUCET_PRIVATE_KEY")] pub(crate) faucet_private_key: String, /// Sequencer private key // 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. - #[clap(long, env = "ROLLUP_SEQUENCER_PRIVATE_KEY")] + #[arg(long, env = "ROLLUP_SEQUENCER_PRIVATE_KEY")] pub(crate) sequencer_private_key: String, /// Set if you want to see all k8s resources created by the deployment /// Set if you want to do a dry run of the deployment - #[clap(long, env = "ROLLUP_DEBUG_DEPLOY", default_value = "false")] + #[arg(long, env = "ROLLUP_DEBUG_DEPLOY", default_value = "false")] pub(crate) debug: bool, } #[derive(Args, Debug)] pub struct DeploymentDeleteArgs { /// The filepath of the target deployment's config - #[clap(long = "config")] + #[arg(long = "config")] pub(crate) config_path: String, } diff --git a/crates/astria-cli/src/cli/sequencer.rs b/crates/astria-cli/src/cli/sequencer.rs index e138598ae..0c2333f48 100644 --- a/crates/astria-cli/src/cli/sequencer.rs +++ b/crates/astria-cli/src/cli/sequencer.rs @@ -15,23 +15,23 @@ use color_eyre::{ pub enum Command { /// Commands for interacting with Sequencer accounts Account { - #[clap(subcommand)] + #[command(subcommand)] command: AccountCommand, }, /// Commands for interacting with Sequencer balances Balance { - #[clap(subcommand)] + #[command(subcommand)] command: BalanceCommand, }, /// Commands for interacting with Sequencer block heights - #[clap(name = "blockheight")] + #[command(name = "blockheight")] BlockHeight { - #[clap(subcommand)] + #[command(subcommand)] command: BlockHeightCommand, }, /// Commands requiring authority for Sequencer Sudo { - #[clap(subcommand)] + #[command(subcommand)] command: SudoCommand, }, /// Command for sending balance between accounts @@ -59,11 +59,11 @@ pub enum BalanceCommand { #[derive(Debug, Subcommand)] pub enum SudoCommand { IbcRelayer { - #[clap(subcommand)] + #[command(subcommand)] command: IbcRelayerChangeCommand, }, FeeAsset { - #[clap(subcommand)] + #[command(subcommand)] command: FeeAssetChangeCommand, }, Mint(MintArgs), @@ -90,14 +90,14 @@ pub enum FeeAssetChangeCommand { #[derive(Args, Debug)] pub struct BasicAccountArgs { /// The url of the Sequencer node - #[clap( + #[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 - #[clap( + #[arg( long = "sequencer.chain-id", env = "ROLLUP_SEQUENCER_CHAIN_ID", default_value = crate::cli::DEFAULT_SEQUENCER_CHAIN_ID @@ -112,24 +112,24 @@ pub struct TransferArgs { // The address of the Sequencer account to send amount to pub(crate) to_address: SequencerAddressArg, // The amount being sent - #[clap(long)] + #[arg(long)] pub(crate) amount: u128, /// The private key of account being sent from - #[clap(long, env = "SEQUENCER_PRIVATE_KEY")] + #[arg(long, env = "SEQUENCER_PRIVATE_KEY")] // 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. pub(crate) private_key: String, /// The url of the Sequencer node - #[clap( + #[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 - #[clap( + #[arg( long = "sequencer.chain-id", env = "ROLLUP_SEQUENCER_CHAIN_ID", default_value = crate::cli::DEFAULT_SEQUENCER_CHAIN_ID @@ -143,24 +143,24 @@ pub struct FeeAssetChangeArgs { // 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. - #[clap(long, env = "SEQUENCER_PRIVATE_KEY")] + #[arg(long, env = "SEQUENCER_PRIVATE_KEY")] pub(crate) private_key: String, /// The url of the Sequencer node - #[clap( + #[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 - #[clap( + #[arg( long = "sequencer.chain-id", env = "ROLLUP_SEQUENCER_CHAIN_ID", default_value = crate::cli::DEFAULT_SEQUENCER_CHAIN_ID )] pub sequencer_chain_id: String, /// Asset's denomination string - #[clap(long)] + #[arg(long)] pub(crate) asset: String, } @@ -170,24 +170,24 @@ pub struct IbcRelayerChangeArgs { // 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. - #[clap(long, env = "SEQUENCER_PRIVATE_KEY")] + #[arg(long, env = "SEQUENCER_PRIVATE_KEY")] pub(crate) private_key: String, /// The url of the Sequencer node - #[clap( + #[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 - #[clap( + #[arg( long = "sequencer.chain-id", env = "ROLLUP_SEQUENCER_CHAIN_ID", default_value = crate::cli::DEFAULT_SEQUENCER_CHAIN_ID )] pub sequencer_chain_id: String, /// The address to add or remove as an IBC relayer - #[clap(long)] + #[arg(long)] pub(crate) address: SequencerAddressArg, } @@ -197,17 +197,17 @@ pub struct InitBridgeAccountArgs { // 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. - #[clap(long, env = "SEQUENCER_PRIVATE_KEY")] + #[arg(long, env = "SEQUENCER_PRIVATE_KEY")] pub(crate) private_key: String, /// The url of the Sequencer node - #[clap( + #[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 - #[clap( + #[arg( long = "sequencer.chain-id", env = "ROLLUP_SEQUENCER_CHAIN_ID", default_value = crate::cli::DEFAULT_SEQUENCER_CHAIN_ID @@ -215,7 +215,7 @@ pub struct InitBridgeAccountArgs { pub sequencer_chain_id: String, /// Plaintext rollup name (to be hashed into a rollup ID) /// to initialize the bridge account with. - #[clap(long)] + #[arg(long)] pub(crate) rollup_name: String, } @@ -224,25 +224,25 @@ pub struct BridgeLockArgs { /// The address of the Sequencer account to lock amount to pub(crate) to_address: SequencerAddressArg, /// The amount being locked - #[clap(long)] + #[arg(long)] pub(crate) amount: u128, - #[clap(long)] + #[arg(long)] pub(crate) destination_chain_address: 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. - #[clap(long, env = "SEQUENCER_PRIVATE_KEY")] + #[arg(long, env = "SEQUENCER_PRIVATE_KEY")] pub(crate) private_key: String, /// The url of the Sequencer node - #[clap( + #[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 - #[clap( + #[arg( long = "sequencer.chain-id", env = "ROLLUP_SEQUENCER_CHAIN_ID", default_value = crate::cli::DEFAULT_SEQUENCER_CHAIN_ID @@ -277,14 +277,14 @@ pub enum BlockHeightCommand { #[derive(Args, Debug)] pub struct BlockHeightGetArgs { /// The url of the Sequencer node - #[clap( + #[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 - #[clap( + #[arg( long = "sequencer.chain-id", env = "ROLLUP_SEQUENCER_CHAIN_ID", default_value = crate::cli::DEFAULT_SEQUENCER_CHAIN_ID @@ -298,27 +298,27 @@ pub struct MintArgs { // 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. - #[clap(long, env = "SEQUENCER_PRIVATE_KEY")] + #[arg(long, env = "SEQUENCER_PRIVATE_KEY")] pub(crate) private_key: String, /// The url of the Sequencer node - #[clap( + #[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 - #[clap( + #[arg( long = "sequencer.chain-id", env = "ROLLUP_SEQUENCER_CHAIN_ID", default_value = crate::cli::DEFAULT_SEQUENCER_CHAIN_ID )] pub sequencer_chain_id: String, /// The address to mint to - #[clap(long)] + #[arg(long)] pub(crate) to_address: SequencerAddressArg, /// The amount to mint - #[clap(long)] + #[arg(long)] pub(crate) amount: u128, } @@ -328,55 +328,55 @@ pub struct SudoAddressChangeArgs { // 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. - #[clap(long, env = "SEQUENCER_PRIVATE_KEY")] + #[arg(long, env = "SEQUENCER_PRIVATE_KEY")] pub(crate) private_key: String, /// The url of the Sequencer node - #[clap( + #[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 - #[clap( + #[arg( long = "sequencer.chain-id", env = "ROLLUP_SEQUENCER_CHAIN_ID", default_value = crate::cli::DEFAULT_SEQUENCER_CHAIN_ID )] pub sequencer_chain_id: String, /// The new address to take over sudo privileges - #[clap(long)] + #[arg(long)] pub(crate) address: SequencerAddressArg, } #[derive(Args, Debug)] pub struct ValidatorUpdateArgs { /// The url of the Sequencer node - #[clap( + #[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 - #[clap( + #[arg( long = "sequencer.chain-id", env = "ROLLUP_SEQUENCER_CHAIN_ID", default_value = crate::cli::DEFAULT_SEQUENCER_CHAIN_ID )] pub sequencer_chain_id: String, /// The private key of the sudo account authorizing change - #[clap(long, env = "SEQUENCER_PRIVATE_KEY")] + #[arg(long, env = "SEQUENCER_PRIVATE_KEY")] // 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. pub(crate) private_key: String, /// The address of the Validator being updated - #[clap(long)] + #[arg(long)] pub(crate) validator_public_key: String, /// The power the validator is being updated to - #[clap(long)] + #[arg(long)] pub(crate) power: u32, } diff --git a/crates/astria-core/src/sequencerblock/v1alpha1/celestia.rs b/crates/astria-core/src/sequencerblock/v1alpha1/celestia.rs index 14aecfd4a..16e2b8174 100644 --- a/crates/astria-core/src/sequencerblock/v1alpha1/celestia.rs +++ b/crates/astria-core/src/sequencerblock/v1alpha1/celestia.rs @@ -520,6 +520,11 @@ impl SubmittedMetadata { &self.header } + /// Returns the rollup IDs. + pub fn rollup_ids(&self) -> impl Iterator { + self.rollup_ids.iter() + } + /// Returns the Merkle Tree Hash constructed from the rollup transactions of the original /// [`SequencerBlock`] this blob was derived from. #[must_use] diff --git a/crates/astria-sequencer-utils/Cargo.toml b/crates/astria-sequencer-utils/Cargo.toml index 94473f8d9..467a16390 100644 --- a/crates/astria-sequencer-utils/Cargo.toml +++ b/crates/astria-sequencer-utils/Cargo.toml @@ -9,8 +9,24 @@ repository = "https://github.com/astriaorg/astria" homepage = "https://astria.org" [dependencies] -clap = { workspace = true, features = ["derive"] } +base64 = { workspace = true } +clap = { workspace = true, features = [ + "cargo", + "deprecated", + "derive", + "wrap_help", +] } +hex = { workspace = true } +indenter = "0.3.3" +itertools = { workspace = true } +prost = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } +astria-core = { path = "../astria-core", features = ["brotli"] } astria-eyre = { path = "../astria-eyre" } +astria-merkle = { path = "../astria-merkle" } + +[dev-dependencies] +assert_cmd = "2.0.14" +predicates = "3.1.0" diff --git a/crates/astria-sequencer-utils/src/blob_parser.rs b/crates/astria-sequencer-utils/src/blob_parser.rs new file mode 100644 index 000000000..1a756113e --- /dev/null +++ b/crates/astria-sequencer-utils/src/blob_parser.rs @@ -0,0 +1,460 @@ +use std::{ + fmt::{ + self, + Display, + Formatter, + Write, + }, + num::NonZeroUsize, +}; + +use astria_core::{ + brotli::decompress_bytes, + generated::sequencerblock::v1alpha1::{ + SubmittedMetadata as RawSubmittedMetadata, + SubmittedMetadataList as RawSubmittedMetadataList, + SubmittedRollupData as RawSubmittedRollupData, + SubmittedRollupDataList as RawSubmittedRollupDataList, + }, + primitive::v1::RollupId, + sequencerblock::v1alpha1::{ + block::SequencerBlockHeader, + celestia::{ + SubmittedRollupData, + UncheckedSubmittedMetadata, + UncheckedSubmittedRollupData, + }, + }, +}; +use astria_eyre::eyre::{ + bail, + Result, + WrapErr, +}; +use astria_merkle::audit::Proof; +use base64::{ + prelude::BASE64_STANDARD, + Engine, +}; +use clap::ValueEnum; +use indenter::indented; +use itertools::Itertools; +use prost::{ + bytes::Bytes, + Message, +}; +use serde::Serialize; + +#[derive(clap::Args, Debug)] +pub struct Args { + /// Base64-encoded blob data + #[arg(value_name = "BLOB")] + input: String, + + /// Configure formatting of output + #[arg( + short, + long, + num_args = 0..=1, + default_value_t = Format::Display, + default_missing_value = "always", + value_enum + )] + format: Format, + + /// Display verbose output (e.g. displays full contents of transactions in rollup data) + #[arg(short, long)] + verbose: bool, +} + +#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq)] +pub enum Format { + Display, + Json, +} + +/// Parses `input` (a base-64-encoded string of Celestia blob data) to the given format. +/// +/// # Errors +/// +/// Returns an error if `input` cannot be parsed. +pub fn run( + Args { + input, + format, + verbose, + }: Args, +) -> Result<()> { + let parsed_list = parse(input, verbose)?; + match format { + Format::Display => println!("\n{parsed_list}"), + Format::Json => println!( + "{}", + serde_json::to_string(&parsed_list).wrap_err("failed to json-encode")? + ), + } + Ok(()) +} + +fn parse(input: String, verbose: bool) -> Result { + let raw = BASE64_STANDARD + .decode(input) + .wrap_err("failed to decode as base64")?; + let decompressed = + Bytes::from(decompress_bytes(&raw).wrap_err("failed to decompress decoded bytes")?); + + // Try to parse as a list of `SequencerBlockMetadata`. + if let Some(metadata_list) = RawSubmittedMetadataList::decode(decompressed.clone()) + .ok() + .and_then(|metadata_list| { + metadata_list + .entries + .into_iter() + .map(|raw_metadata| UncheckedSubmittedMetadata::try_from_raw(raw_metadata).ok()) + .collect::>>() + }) + { + return Ok(if verbose { + metadata_list + .iter() + .map(VerboseSequencerBlockMetadata::new) + .collect() + } else { + metadata_list + .iter() + .map(BriefSequencerBlockMetadata::new) + .collect() + }); + } + + // Try to parse as a list of `RollupData`. + if let Some(rollup_data_list) = RawSubmittedRollupDataList::decode(decompressed.clone()) + .ok() + .and_then(|rollup_data_list| { + rollup_data_list + .entries + .into_iter() + .map(|raw_rollup_data| SubmittedRollupData::try_from_raw(raw_rollup_data).ok()) + .collect::>>() + }) + { + return Ok(if verbose { + rollup_data_list + .into_iter() + .map(|rollup_data| VerboseRollupData::new(&rollup_data.into_unchecked())) + .collect() + } else { + rollup_data_list + .into_iter() + .map(|rollup_data| BriefRollupData::new(&rollup_data.into_unchecked())) + .collect() + }); + } + + // Try to parse as a single `SequencerBlockMetadata`. + if let Some(metadata) = RawSubmittedMetadata::decode(decompressed.clone()) + .ok() + .and_then(|raw_metadata| UncheckedSubmittedMetadata::try_from_raw(raw_metadata).ok()) + { + return Ok(if verbose { + ParsedList::VerboseSequencer(vec![VerboseSequencerBlockMetadata::new(&metadata)]) + } else { + ParsedList::BriefSequencer(vec![BriefSequencerBlockMetadata::new(&metadata)]) + }); + } + + // Try to parse as a single `RollupData`. + if let Some(rollup_data) = RawSubmittedRollupData::decode(decompressed) + .ok() + .and_then(|raw_rollup_data| SubmittedRollupData::try_from_raw(raw_rollup_data).ok()) + { + return Ok(if verbose { + ParsedList::VerboseRollup(vec![VerboseRollupData::new(&rollup_data.into_unchecked())]) + } else { + ParsedList::BriefRollup(vec![BriefRollupData::new(&rollup_data.into_unchecked())]) + }); + } + + bail!("failed to decode as a list of sequencer metadata or rollup data") +} + +#[derive(Serialize, Debug)] +struct PrintableSequencerBlockHeader { + chain_id: String, + height: u64, + time: String, + rollup_transactions_root: String, + data_hash: String, + proposer_address: String, +} + +impl From<&SequencerBlockHeader> for PrintableSequencerBlockHeader { + fn from(header: &SequencerBlockHeader) -> Self { + Self { + chain_id: header.chain_id().to_string(), + height: header.height().value(), + time: header.time().to_string(), + rollup_transactions_root: BASE64_STANDARD.encode(header.rollup_transactions_root()), + data_hash: BASE64_STANDARD.encode(header.data_hash()), + proposer_address: BASE64_STANDARD.encode(header.proposer_address()), + } + } +} + +impl Display for PrintableSequencerBlockHeader { + fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result { + writeln!(formatter, "chain id: {}", self.chain_id)?; + writeln!(formatter, "height: {}", self.height)?; + writeln!(formatter, "time: {}", self.time)?; + writeln!( + formatter, + "rollup transactions root: {}", + self.rollup_transactions_root + )?; + writeln!(formatter, "data hash: {}", self.data_hash)?; + write!(formatter, "proposer address: {}", self.proposer_address) + } +} + +#[derive(Serialize, Debug)] +struct PrintableMerkleProof { + audit_path: String, + leaf_index: usize, + tree_size: NonZeroUsize, +} + +impl From<&Proof> for PrintableMerkleProof { + fn from(proof: &Proof) -> Self { + Self { + audit_path: BASE64_STANDARD.encode(proof.audit_path()), + leaf_index: proof.leaf_index(), + tree_size: proof.tree_size(), + } + } +} + +impl Display for PrintableMerkleProof { + fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result { + writeln!(formatter, "audit path: {}", self.audit_path)?; + writeln!(formatter, "leaf index: {}", self.leaf_index)?; + write!(formatter, "tree size: {}", self.tree_size) + } +} + +#[derive(Serialize, Debug)] +struct BriefSequencerBlockMetadata { + sequencer_block_hash: String, + sequencer_block_header: PrintableSequencerBlockHeader, + rollup_ids: Vec, +} + +impl BriefSequencerBlockMetadata { + fn new(metadata: &UncheckedSubmittedMetadata) -> Self { + let rollup_ids = metadata + .rollup_ids + .iter() + .map(RollupId::to_string) + .collect(); + BriefSequencerBlockMetadata { + sequencer_block_hash: BASE64_STANDARD.encode(metadata.block_hash), + sequencer_block_header: PrintableSequencerBlockHeader::from(&metadata.header), + rollup_ids, + } + } +} + +impl Display for BriefSequencerBlockMetadata { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln!(f, "sequencer block hash: {}", self.sequencer_block_hash)?; + writeln!(f, "sequencer block header:")?; + writeln!(indent(f), "{}", self.sequencer_block_header)?; + if self.rollup_ids.is_empty() { + write!(f, "rollup ids:")?; + } else { + writeln!(f, "rollup ids:")?; + write!(indent(f), "{}", self.rollup_ids.iter().join("\n"))?; + } + Ok(()) + } +} + +#[derive(Serialize, Debug)] +struct VerboseSequencerBlockMetadata { + sequencer_block_hash: String, + sequencer_block_header: PrintableSequencerBlockHeader, + rollup_ids: Vec, + rollup_transactions_proof: PrintableMerkleProof, + rollup_ids_proof: PrintableMerkleProof, +} + +impl VerboseSequencerBlockMetadata { + fn new(metadata: &UncheckedSubmittedMetadata) -> Self { + let rollup_ids = metadata + .rollup_ids + .iter() + .map(RollupId::to_string) + .collect(); + VerboseSequencerBlockMetadata { + sequencer_block_hash: BASE64_STANDARD.encode(metadata.block_hash), + sequencer_block_header: PrintableSequencerBlockHeader::from(&metadata.header), + rollup_ids, + rollup_transactions_proof: PrintableMerkleProof::from( + &metadata.rollup_transactions_proof, + ), + rollup_ids_proof: PrintableMerkleProof::from(&metadata.rollup_ids_proof), + } + } +} + +impl Display for VerboseSequencerBlockMetadata { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln!(f, "sequencer block hash: {}", self.sequencer_block_hash)?; + writeln!(f, "sequencer block header:")?; + writeln!(indent(f), "{}", self.sequencer_block_header)?; + writeln!(f, "rollup ids:")?; + if !self.rollup_ids.is_empty() { + writeln!(indent(f), "{}", self.rollup_ids.iter().join("\n"))?; + } + writeln!(f, "rollup transactions proof:")?; + writeln!(indent(f), "{}", self.rollup_transactions_proof)?; + writeln!(f, "rollup ids proof:")?; + write!(indent(f), "{}", self.rollup_ids_proof) + } +} + +#[derive(Serialize, Debug)] +struct BriefRollupData { + sequencer_block_hash: String, + rollup_id: String, + transaction_count: usize, +} + +impl BriefRollupData { + fn new(rollup_data: &UncheckedSubmittedRollupData) -> Self { + BriefRollupData { + sequencer_block_hash: BASE64_STANDARD.encode(rollup_data.sequencer_block_hash), + rollup_id: rollup_data.rollup_id.to_string(), + transaction_count: rollup_data.transactions.len(), + } + } +} + +impl Display for BriefRollupData { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln!(f, "sequencer block hash: {}", self.sequencer_block_hash)?; + writeln!(f, "rollup id: {}", self.rollup_id)?; + write!(f, "transaction count: {}", self.transaction_count) + } +} + +#[derive(Serialize, Debug)] +struct VerboseRollupData { + sequencer_block_hash: String, + rollup_id: String, + transactions: Vec, + proof: PrintableMerkleProof, +} + +impl VerboseRollupData { + fn new(rollup_data: &UncheckedSubmittedRollupData) -> Self { + let transactions = rollup_data + .transactions + .iter() + .map(|txn| BASE64_STANDARD.encode(txn)) + .collect(); + VerboseRollupData { + sequencer_block_hash: BASE64_STANDARD.encode(rollup_data.sequencer_block_hash), + rollup_id: rollup_data.rollup_id.to_string(), + transactions, + proof: PrintableMerkleProof::from(&rollup_data.proof), + } + } +} + +impl Display for VerboseRollupData { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln!(f, "sequencer block hash: {}", self.sequencer_block_hash)?; + writeln!(f, "rollup id: {}", self.rollup_id)?; + writeln!(f, "transactions:")?; + if !self.transactions.is_empty() { + writeln!(indent(f), "{}", self.transactions.iter().join("\n"))?; + } + writeln!(f, "proof:")?; + write!(indent(f), "{}", self.proof) + } +} + +#[derive(Serialize, Debug)] +enum ParsedList { + #[serde(rename = "sequencer_metadata_list")] + BriefSequencer(Vec), + #[serde(rename = "sequencer_metadata_list")] + VerboseSequencer(Vec), + #[serde(rename = "rollup_data_list")] + BriefRollup(Vec), + #[serde(rename = "rollup_data_list")] + VerboseRollup(Vec), +} + +impl FromIterator for ParsedList { + fn from_iter>(iter: I) -> Self { + Self::BriefSequencer(Vec::from_iter(iter)) + } +} + +impl FromIterator for ParsedList { + fn from_iter>(iter: I) -> Self { + Self::VerboseSequencer(Vec::from_iter(iter)) + } +} + +impl FromIterator for ParsedList { + fn from_iter>(iter: I) -> Self { + Self::BriefRollup(Vec::from_iter(iter)) + } +} + +impl FromIterator for ParsedList { + fn from_iter>(iter: I) -> Self { + Self::VerboseRollup(Vec::from_iter(iter)) + } +} + +impl Display for ParsedList { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + ParsedList::BriefSequencer(list) => { + for (index, item) in list.iter().enumerate() { + writeln!(f, "sequencer metadata {index}:")?; + writeln!(indent(f), "{item}")?; + } + Ok(()) + } + ParsedList::VerboseSequencer(list) => { + for (index, item) in list.iter().enumerate() { + writeln!(f, "sequencer metadata {index}:")?; + writeln!(indent(f), "{item}")?; + } + Ok(()) + } + ParsedList::BriefRollup(list) => { + for (index, item) in list.iter().enumerate() { + writeln!(f, "rollup data {index}:")?; + writeln!(indent(f), "{item}")?; + } + Ok(()) + } + ParsedList::VerboseRollup(list) => { + for (index, item) in list.iter().enumerate() { + writeln!(f, "rollup data {index}:")?; + writeln!(indent(f), "{item}")?; + } + Ok(()) + } + } + } +} + +fn indent<'a, 'b>(f: &'a mut Formatter<'b>) -> indenter::Indented<'a, Formatter<'b>> { + indented(f).with_str(" ") +} diff --git a/crates/astria-sequencer-utils/src/cli.rs b/crates/astria-sequencer-utils/src/cli.rs new file mode 100644 index 000000000..dfcf04557 --- /dev/null +++ b/crates/astria-sequencer-utils/src/cli.rs @@ -0,0 +1,33 @@ +use clap::{ + Parser, + Subcommand, +}; + +use super::{ + blob_parser, + genesis_parser, +}; + +/// Utilities for working with the Astria sequencer network +#[derive(Debug, Parser)] +#[command(version, about, long_about = None)] +struct Cli { + #[command(subcommand)] + command: Command, +} + +#[derive(Debug, Subcommand)] +pub enum Command { + /// Copy genesis state to a JSON file + #[command(arg_required_else_help = true)] + CopyGenesisState(genesis_parser::Args), + + /// Parse blob data + #[command(arg_required_else_help = true)] + ParseBlob(blob_parser::Args), +} + +#[must_use] +pub fn get() -> Command { + Cli::parse().command +} diff --git a/crates/astria-sequencer-utils/src/config.rs b/crates/astria-sequencer-utils/src/config.rs deleted file mode 100644 index ee1a67d9b..000000000 --- a/crates/astria-sequencer-utils/src/config.rs +++ /dev/null @@ -1,20 +0,0 @@ -use clap::Parser; - -#[derive(Debug, Parser)] -pub struct Config { - #[clap(long)] - pub genesis_app_state_file: String, - - #[clap(long)] - pub destination_genesis_file: String, - - #[clap(long)] - pub chain_id: String, -} - -impl Config { - #[must_use] - pub fn get() -> Self { - Config::parse() - } -} diff --git a/crates/astria-sequencer-utils/src/genesis_parser.rs b/crates/astria-sequencer-utils/src/genesis_parser.rs index c10ebaa10..a61d67b68 100644 --- a/crates/astria-sequencer-utils/src/genesis_parser.rs +++ b/crates/astria-sequencer-utils/src/genesis_parser.rs @@ -1,6 +1,6 @@ use std::{ fs::File, - path::Path, + path::PathBuf, }; use astria_eyre::eyre::{ @@ -12,56 +12,68 @@ use serde_json::{ Value, }; -use crate::config::Config; - -pub struct GenesisParser; - -impl GenesisParser { - /// Copies JSON application state from a file to a genesis JSON file, - /// placing it at the key `app_state`. - /// - /// # Errors - /// - /// An `eyre::Result` is returned if either file cannot be opened, - /// or if the destination genesis file cannot be saved. - pub fn propagate_app_state(data: Config) -> Result<()> { - println!("loading genesis app state for propagation:"); - println!( - "\tsource genesis app state: {}", - data.genesis_app_state_file - ); - println!( - "\tdestination genesis file: {}", - data.destination_genesis_file - ); - // load sequencer genesis data - let source_genesis_file_path = File::open(data.genesis_app_state_file) - .wrap_err("failed to open sequencer genesis file")?; - let source_genesis_data: Value = serde_json::from_reader(&source_genesis_file_path) - .wrap_err("failed deserializing sequencer genesis state from file")?; - - // load cometbft genesis data - let destination_genesis_file_path = File::open(data.destination_genesis_file.as_str()) - .wrap_err("failed to open cometbft genesis file")?; - let mut destination_genesis_data: Value = - serde_json::from_reader(&destination_genesis_file_path) - .wrap_err("failed deserializing cometbft genesis state from file")?; - - // insert sequencer app genesis data into cometbft genesis data - insert_app_state_and_chain_id( - &mut destination_genesis_data, - &source_genesis_data, - data.chain_id, - ); - - // write new state - let dest_file = File::create(Path::new(data.destination_genesis_file.as_str())) - .wrap_err("failed to open destination genesis json file")?; - to_writer_pretty(dest_file, &destination_genesis_data) - .wrap_err("failed to write to output json file")?; - - Ok(()) - } +#[derive(clap::Args, Debug)] +pub struct Args { + /// Path to app state file + #[arg(long, value_name = "PATH")] + genesis_app_state_file: PathBuf, + + /// Path to output file + #[arg(long, short, value_name = "PATH", alias = "destination-genesis-file")] + output: PathBuf, + + /// Chain identifier (a.k.a. network name) + #[arg(long)] + chain_id: String, +} + +/// Copies JSON application state from a file to a genesis JSON file, +/// placing it at the key `app_state`. +/// +/// # Errors +/// +/// An `eyre::Result` is returned if either file cannot be opened, +/// or if the destination genesis file cannot be saved. +pub fn run( + Args { + genesis_app_state_file, + output, + chain_id, + }: Args, +) -> Result<()> { + println!("loading genesis app state for propagation:"); + println!( + "\tsource genesis app state: {}", + genesis_app_state_file.display() + ); + println!("\tdestination genesis file: {}", output.display()); + // load sequencer genesis data + let source_genesis_file_path = + File::open(&genesis_app_state_file).wrap_err("failed to open sequencer genesis file")?; + let source_genesis_data: Value = serde_json::from_reader(&source_genesis_file_path) + .wrap_err("failed deserializing sequencer genesis state from file")?; + + // load cometbft genesis data + let destination_genesis_file_path = + File::open(&output).wrap_err("failed to open cometbft genesis file")?; + let mut destination_genesis_data: Value = + serde_json::from_reader(&destination_genesis_file_path) + .wrap_err("failed deserializing cometbft genesis state from file")?; + + // insert sequencer app genesis data into cometbft genesis data + insert_app_state_and_chain_id( + &mut destination_genesis_data, + &source_genesis_data, + chain_id, + ); + + // write new state + let dest_file = + File::create(&output).wrap_err("failed to open destination genesis json file")?; + to_writer_pretty(dest_file, &destination_genesis_data) + .wrap_err("failed to write to output json file")?; + + Ok(()) } fn insert_app_state_and_chain_id(dst: &mut Value, app_state: &Value, chain_id: String) { diff --git a/crates/astria-sequencer-utils/src/lib.rs b/crates/astria-sequencer-utils/src/lib.rs index f716ca973..40b780d80 100644 --- a/crates/astria-sequencer-utils/src/lib.rs +++ b/crates/astria-sequencer-utils/src/lib.rs @@ -1,2 +1,3 @@ -pub mod config; +pub mod blob_parser; +pub mod cli; pub mod genesis_parser; diff --git a/crates/astria-sequencer-utils/src/main.rs b/crates/astria-sequencer-utils/src/main.rs index fd50846da..6bede2c3c 100644 --- a/crates/astria-sequencer-utils/src/main.rs +++ b/crates/astria-sequencer-utils/src/main.rs @@ -1,14 +1,18 @@ +use astria_eyre::eyre::Result; use astria_sequencer_utils::{ - config::Config, - genesis_parser::GenesisParser, + blob_parser, + cli::{ + self, + Command, + }, + genesis_parser, }; -fn main() { +fn main() -> Result<()> { astria_eyre::install() .expect("the astria eyre install hook must be called before eyre reports are constructed"); - let config = Config::get(); - - println!("running genesis parser"); - GenesisParser::propagate_app_state(config).expect("failed to propagate data"); - println!("genesis parsing complete"); + match cli::get() { + Command::CopyGenesisState(args) => genesis_parser::run(args), + Command::ParseBlob(args) => blob_parser::run(args), + } } diff --git a/crates/astria-sequencer-utils/tests/parse_blob.rs b/crates/astria-sequencer-utils/tests/parse_blob.rs new file mode 100644 index 000000000..b2c847e58 --- /dev/null +++ b/crates/astria-sequencer-utils/tests/parse_blob.rs @@ -0,0 +1,107 @@ +use std::{ + fs, + path::Path, + process::Command, +}; + +use assert_cmd::prelude::*; +use astria_eyre::{ + eyre::WrapErr, + Result, +}; +use predicates::prelude::*; + +struct Resources { + input: String, + expected_brief_display: String, + expected_brief_json: String, + expected_verbose_display: String, + expected_verbose_json: String, +} + +impl Resources { + /// Reads the contents of the files in the `tests/resources/parse_blob/` folder to + /// the respective fields of `Self`. + fn new(test_case: &str) -> Result { + let dir = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("tests") + .join("resources") + .join("parse_blob") + .join(test_case); + let read_file = |filename: &str| -> Result { + let path = dir.join(filename); + fs::read_to_string(&path).wrap_err(format!("failed to read {}", path.display())) + }; + Ok(Resources { + input: read_file("input.txt")?, + expected_brief_display: read_file("expected_brief_output.txt")?, + expected_brief_json: read_file("expected_brief_output.json")?, + expected_verbose_display: read_file("expected_verbose_output.txt")?, + expected_verbose_json: read_file("expected_verbose_output.json")?, + }) + } + + #[track_caller] + fn check_parse_blob(self) -> Result<()> { + // No verbose flag, default format ("display"). + let mut cmd = new_command()?; + cmd.arg(&self.input) + .assert() + .success() + .stdout(predicate::eq(self.expected_brief_display)); + + // No verbose flag, JSON format. + let mut cmd = new_command()?; + cmd.arg(&self.input) + .arg("-fjson") + .assert() + .success() + .stdout(predicate::eq(self.expected_brief_json)); + + // With verbose flag, default format ("display"). + let mut cmd = new_command()?; + cmd.arg(&self.input) + .arg("-v") + .assert() + .success() + .stdout(predicate::eq(self.expected_verbose_display)); + + // With verbose flag, JSON format. + let mut cmd = new_command()?; + cmd.arg(&self.input) + .arg("--verbose") + .arg("--format") + .arg("json") + .assert() + .success() + .stdout(predicate::eq(self.expected_verbose_json)); + + Ok(()) + } +} + +fn new_command() -> Result { + let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?; + cmd.arg("parse-blob"); + Ok(cmd) +} + +#[test] +fn should_parse_batched_metadata() -> Result<()> { + Resources::new("batched_metadata")?.check_parse_blob() +} + +#[test] +fn should_parse_batched_rollup_data() -> Result<()> { + Resources::new("batched_rollup_data")?.check_parse_blob() +} + +#[test] +fn should_parse_unbatched_metadata() -> Result<()> { + Resources::new("unbatched_metadata")?.check_parse_blob() +} + +#[test] +fn should_parse_unbatched_rollup_data() -> Result<()> { + Resources::new("unbatched_rollup_data")?.check_parse_blob() +} diff --git a/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_metadata/expected_brief_output.json b/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_metadata/expected_brief_output.json new file mode 100644 index 000000000..059b2fcb5 --- /dev/null +++ b/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_metadata/expected_brief_output.json @@ -0,0 +1 @@ +{"sequencer_metadata_list":[{"sequencer_block_hash":"ZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGQ=","sequencer_block_header":{"chain_id":"test","height":2,"time":"1970-01-01T00:00:00Z","rollup_transactions_root":"eQBF6ACJjaMOXRq2Cs1sXY02uXuDjDX+LeGK3A9Wrec=","data_hash":"AyodXov7fzTBhlnv0Z/oDpiaDbGPC2LiYkamDODRrzw=","proposer_address":"AQEBAQEBAQEBAQEBAQEBAQEBAQE="},"rollup_ids":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE=","AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI=","AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM=","BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ=","ZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGQ=","ZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWU=","ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmY=","Z2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2c=","aGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGg="]},{"sequencer_block_hash":"ZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWU=","sequencer_block_header":{"chain_id":"test","height":3,"time":"1970-01-01T00:00:00Z","rollup_transactions_root":"LFAizuOdU3mlzgldRfpwIWhceH4fbPS6+8kfTZ15x84=","data_hash":"EY3VCLWPcJ0fjHstVJq9VzVyleo3G7mmLgKWKGz4iEQ=","proposer_address":"AgICAgICAgICAgICAgICAgICAgI="},"rollup_ids":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE=","AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI=","AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM=","BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ=","ZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGQ=","ZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWU=","ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmY=","Z2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2c=","aGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGg="]}]} diff --git a/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_metadata/expected_brief_output.txt b/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_metadata/expected_brief_output.txt new file mode 100644 index 000000000..e17f191c5 --- /dev/null +++ b/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_metadata/expected_brief_output.txt @@ -0,0 +1,42 @@ + +sequencer metadata 0: + sequencer block hash: ZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGQ= + sequencer block header: + chain id: test + height: 2 + time: 1970-01-01T00:00:00Z + rollup transactions root: eQBF6ACJjaMOXRq2Cs1sXY02uXuDjDX+LeGK3A9Wrec= + data hash: AyodXov7fzTBhlnv0Z/oDpiaDbGPC2LiYkamDODRrzw= + proposer address: AQEBAQEBAQEBAQEBAQEBAQEBAQE= + rollup ids: + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE= + AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI= + AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM= + BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ= + ZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGQ= + ZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWU= + ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmY= + Z2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2c= + aGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGg= +sequencer metadata 1: + sequencer block hash: ZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWU= + sequencer block header: + chain id: test + height: 3 + time: 1970-01-01T00:00:00Z + rollup transactions root: LFAizuOdU3mlzgldRfpwIWhceH4fbPS6+8kfTZ15x84= + data hash: EY3VCLWPcJ0fjHstVJq9VzVyleo3G7mmLgKWKGz4iEQ= + proposer address: AgICAgICAgICAgICAgICAgICAgI= + rollup ids: + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE= + AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI= + AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM= + BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ= + ZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGQ= + ZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWU= + ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmY= + Z2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2c= + aGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGg= + diff --git a/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_metadata/expected_verbose_output.json b/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_metadata/expected_verbose_output.json new file mode 100644 index 000000000..0e1ec8c4c --- /dev/null +++ b/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_metadata/expected_verbose_output.json @@ -0,0 +1 @@ +{"sequencer_metadata_list":[{"sequencer_block_hash":"ZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGQ=","sequencer_block_header":{"chain_id":"test","height":2,"time":"1970-01-01T00:00:00Z","rollup_transactions_root":"eQBF6ACJjaMOXRq2Cs1sXY02uXuDjDX+LeGK3A9Wrec=","data_hash":"AyodXov7fzTBhlnv0Z/oDpiaDbGPC2LiYkamDODRrzw=","proposer_address":"AQEBAQEBAQEBAQEBAQEBAQEBAQE="},"rollup_ids":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE=","AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI=","AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM=","BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ=","ZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGQ=","ZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWU=","ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmY=","Z2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2c=","aGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGg="],"rollup_transactions_proof":{"audit_path":"moXhfe6bE3fpL0TWVwP4D8deJBZLFUIcl8NL2y73eF3e60KFoN3B0hRE1WodiiopRhQLfAdT1Fd470+gagjg1g==","leaf_index":0,"tree_size":5},"rollup_ids_proof":{"audit_path":"WAurg0/Ciy4kkoitH+Pgx1UOtcLUz8b50fvCKp3E3Rre60KFoN3B0hRE1WodiiopRhQLfAdT1Fd470+gagjg1g==","leaf_index":1,"tree_size":5}},{"sequencer_block_hash":"ZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWU=","sequencer_block_header":{"chain_id":"test","height":3,"time":"1970-01-01T00:00:00Z","rollup_transactions_root":"LFAizuOdU3mlzgldRfpwIWhceH4fbPS6+8kfTZ15x84=","data_hash":"EY3VCLWPcJ0fjHstVJq9VzVyleo3G7mmLgKWKGz4iEQ=","proposer_address":"AgICAgICAgICAgICAgICAgICAgI="},"rollup_ids":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE=","AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI=","AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM=","BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ=","ZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGQ=","ZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWU=","ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmY=","Z2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2c=","aGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGg="],"rollup_transactions_proof":{"audit_path":"moXhfe6bE3fpL0TWVwP4D8deJBZLFUIcl8NL2y73eF3ywbZMC1VIRulkjl3/e0+qcjB433ZW/4LdryKMvXwgSg==","leaf_index":0,"tree_size":5},"rollup_ids_proof":{"audit_path":"DWVGBgVDp/gd0nDL7fcli1DJNWZ2QmeDFiTrTXApMg7ywbZMC1VIRulkjl3/e0+qcjB433ZW/4LdryKMvXwgSg==","leaf_index":1,"tree_size":5}}]} diff --git a/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_metadata/expected_verbose_output.txt b/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_metadata/expected_verbose_output.txt new file mode 100644 index 000000000..97b1e4004 --- /dev/null +++ b/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_metadata/expected_verbose_output.txt @@ -0,0 +1,58 @@ + +sequencer metadata 0: + sequencer block hash: ZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGQ= + sequencer block header: + chain id: test + height: 2 + time: 1970-01-01T00:00:00Z + rollup transactions root: eQBF6ACJjaMOXRq2Cs1sXY02uXuDjDX+LeGK3A9Wrec= + data hash: AyodXov7fzTBhlnv0Z/oDpiaDbGPC2LiYkamDODRrzw= + proposer address: AQEBAQEBAQEBAQEBAQEBAQEBAQE= + rollup ids: + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE= + AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI= + AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM= + BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ= + ZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGQ= + ZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWU= + ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmY= + Z2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2c= + aGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGg= + rollup transactions proof: + audit path: moXhfe6bE3fpL0TWVwP4D8deJBZLFUIcl8NL2y73eF3e60KFoN3B0hRE1WodiiopRhQLfAdT1Fd470+gagjg1g== + leaf index: 0 + tree size: 5 + rollup ids proof: + audit path: WAurg0/Ciy4kkoitH+Pgx1UOtcLUz8b50fvCKp3E3Rre60KFoN3B0hRE1WodiiopRhQLfAdT1Fd470+gagjg1g== + leaf index: 1 + tree size: 5 +sequencer metadata 1: + sequencer block hash: ZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWU= + sequencer block header: + chain id: test + height: 3 + time: 1970-01-01T00:00:00Z + rollup transactions root: LFAizuOdU3mlzgldRfpwIWhceH4fbPS6+8kfTZ15x84= + data hash: EY3VCLWPcJ0fjHstVJq9VzVyleo3G7mmLgKWKGz4iEQ= + proposer address: AgICAgICAgICAgICAgICAgICAgI= + rollup ids: + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE= + AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI= + AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM= + BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ= + ZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGQ= + ZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWU= + ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmY= + Z2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2c= + aGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGg= + rollup transactions proof: + audit path: moXhfe6bE3fpL0TWVwP4D8deJBZLFUIcl8NL2y73eF3ywbZMC1VIRulkjl3/e0+qcjB433ZW/4LdryKMvXwgSg== + leaf index: 0 + tree size: 5 + rollup ids proof: + audit path: DWVGBgVDp/gd0nDL7fcli1DJNWZ2QmeDFiTrTXApMg7ywbZMC1VIRulkjl3/e0+qcjB433ZW/4LdryKMvXwgSg== + leaf index: 1 + tree size: 5 + diff --git a/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_metadata/input.txt b/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_metadata/input.txt new file mode 100644 index 000000000..0b6c9557a --- /dev/null +++ b/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_metadata/input.txt @@ -0,0 +1 @@ +GwEFAGQADgAANYjc+YPS7yTxMmxnGHkQa9QSSwhDmW5FIdSD+hCoPxAoApM9H5IJFIiLqbMLAhAWABEBMBWuXvS3P8tgmOb9Yv4FXEYWbGM8tNGRkVgZzMFiPQ8VCmC3wkTgCSB6ARAZWwyni9UGNWujG8vW6R0cw/pHexi1A69We45FBEUAEO8MIBggMMBgQMCYgDEFYwbGHIyF90YkAiWQFXroe5eN7H5JL7KmDvwBP65HgiaNKoSTPiy9Tffuobt3LRRasDu4hCKyaoUbRUUphgLtA668ou5xL19gBXGwhgFKJQYloAFdHSw/FE1HkhRRi398MK4K1zq0Mj/2ufg9RJU7sou1dy0UWrA7eKkhAGKAfkJjOkUATyPGrkK0xjvk4sd406pk9aqzOqVesWN3ltEBpVHYfESI1AOSvo1oFIjmjnOVPUvnIHVFvxwILXQ8/PBtXrq+J/Flcz3H51p+GmyTgVaVELs0idP995avcmLw2HdT+w/arSeK6fUhkGpbsKZiYKDC5R+4Sw7Tt++k0QqTrGZuQubBaCTXsg6UTHCXKgIgBig= \ No newline at end of file diff --git a/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_rollup_data/expected_brief_output.json b/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_rollup_data/expected_brief_output.json new file mode 100644 index 000000000..782c37a0e --- /dev/null +++ b/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_rollup_data/expected_brief_output.json @@ -0,0 +1 @@ +{"rollup_data_list":[{"sequencer_block_hash":"ZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGQ=","rollup_id":"AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE=","transaction_count":1},{"sequencer_block_hash":"ZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWU=","rollup_id":"AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE=","transaction_count":1}]} diff --git a/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_rollup_data/expected_brief_output.txt b/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_rollup_data/expected_brief_output.txt new file mode 100644 index 000000000..ff49bdf24 --- /dev/null +++ b/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_rollup_data/expected_brief_output.txt @@ -0,0 +1,10 @@ + +rollup data 0: + sequencer block hash: ZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGQ= + rollup id: AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE= + transaction count: 1 +rollup data 1: + sequencer block hash: ZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWU= + rollup id: AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE= + transaction count: 1 + diff --git a/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_rollup_data/expected_verbose_output.json b/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_rollup_data/expected_verbose_output.json new file mode 100644 index 000000000..4492bae1a --- /dev/null +++ b/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_rollup_data/expected_verbose_output.json @@ -0,0 +1 @@ +{"rollup_data_list":[{"sequencer_block_hash":"ZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGQ=","rollup_id":"AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE=","transactions":["CgICAg=="],"proof":{"audit_path":"RmscMceV3kZiQ0km0RLAAI1A4LOuNWGhb5ZixV+/muEcxTDgnSFeweCDgW6rtiR0ncari8YJrhfCEcJ4tJhRGB/2bgKeHmhjRCj9x0qWztNev7f7dBRHqRnAP4EnH9H3WnkZE4YbD7y7EGP7+VIDavou28IyXsbCEQKLJGCkYZg=","leaf_index":1,"tree_size":19}},{"sequencer_block_hash":"ZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWU=","rollup_id":"AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE=","transactions":["CgMDAwM="],"proof":{"audit_path":"fnHQ1mtcS7hAdrw9GQ4oc87iSXurQQWKLipnUpM4nTtHHVJrd4DLH4BKbcWKZ1VPRJquNajE6+Lm79aSjrgp+9t9sYogeYwby67nnMW6NGPSwhf4LMA8VsXCo4xWXgFMMhqoERklhyphEnsoMI2QtZaMdFjC6if8cI71kfFNXgQ=","leaf_index":1,"tree_size":19}}]} diff --git a/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_rollup_data/expected_verbose_output.txt b/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_rollup_data/expected_verbose_output.txt new file mode 100644 index 000000000..ce665217f --- /dev/null +++ b/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_rollup_data/expected_verbose_output.txt @@ -0,0 +1,20 @@ + +rollup data 0: + sequencer block hash: ZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGQ= + rollup id: AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE= + transactions: + CgICAg== + proof: + audit path: RmscMceV3kZiQ0km0RLAAI1A4LOuNWGhb5ZixV+/muEcxTDgnSFeweCDgW6rtiR0ncari8YJrhfCEcJ4tJhRGB/2bgKeHmhjRCj9x0qWztNev7f7dBRHqRnAP4EnH9H3WnkZE4YbD7y7EGP7+VIDavou28IyXsbCEQKLJGCkYZg= + leaf index: 1 + tree size: 19 +rollup data 1: + sequencer block hash: ZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWU= + rollup id: AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE= + transactions: + CgMDAwM= + proof: + audit path: fnHQ1mtcS7hAdrw9GQ4oc87iSXurQQWKLipnUpM4nTtHHVJrd4DLH4BKbcWKZ1VPRJquNajE6+Lm79aSjrgp+9t9sYogeYwby67nnMW6NGPSwhf4LMA8VsXCo4xWXgFMMhqoERklhyphEnsoMI2QtZaMdFjC6if8cI71kfFNXgQ= + leaf index: 1 + tree size: 19 + diff --git a/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_rollup_data/input.txt b/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_rollup_data/input.txt new file mode 100644 index 000000000..0c8bdaf0f --- /dev/null +++ b/crates/astria-sequencer-utils/tests/resources/parse_blob/batched_rollup_data/input.txt @@ -0,0 +1 @@ +G7IBAGQAbqtJwQOuVsSYMyNDpooR3WBjTJ/QmFVgNoAwRGYxKCQwREDggMGAPDJJJBAmCChhi8c8mX4gYSIqTbGMMgSIFzpqbWA3LnbMMBk3HMw5xhtnOsonNhg+Cg12qO0gc8ufqI2dgG7AHEEe8WrLUsYi/HAAKSCwMhWj+p+UyVhYNRjs/HVDk6zGHhIMpiRc/tTxxkaNwEXs60Uy/f1WAbX5YdgdYTGYGEEGiSUzKjPOQgJiocJsAmGIzM3NzcnPGRwGVG1CgPPShq2ebJeQRx8/NgKV68KJtG+tMHgMA42lSipXPq8kvoqtZ9AsYZCM/XiMpbqiWE4De9XY7cnF40ZKQhf1765/cwyRdxzubMNl3ngPm+nKCOYX3RCfxvhIaZyGAVCOBacKGZs8ksYYxZeKKT6pPSPOTWvkhvLPKeE9+VneAAwJiIUK \ No newline at end of file diff --git a/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_metadata/expected_brief_output.json b/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_metadata/expected_brief_output.json new file mode 100644 index 000000000..bc254f2a0 --- /dev/null +++ b/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_metadata/expected_brief_output.json @@ -0,0 +1 @@ +{"sequencer_metadata_list":[{"sequencer_block_hash":"HwCCO2EcW7AX0Cck/z9kl9qu9h3gnb0mpfEOGG+UWZA=","sequencer_block_header":{"chain_id":"astria-dusk-5","height":103623,"time":"2024-04-29T21:39:24.47424482Z","rollup_transactions_root":"47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=","data_hash":"7P54CyIFJkHEJOCs+wvzRlhEqN7zh03qLi3wIKP1T3g=","proposer_address":"yPrVsmT31bVEaXjpauw1SeQfE1M="},"rollup_ids":[]}]} diff --git a/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_metadata/expected_brief_output.txt b/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_metadata/expected_brief_output.txt new file mode 100644 index 000000000..c5e7b956e --- /dev/null +++ b/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_metadata/expected_brief_output.txt @@ -0,0 +1,12 @@ + +sequencer metadata 0: + sequencer block hash: HwCCO2EcW7AX0Cck/z9kl9qu9h3gnb0mpfEOGG+UWZA= + sequencer block header: + chain id: astria-dusk-5 + height: 103623 + time: 2024-04-29T21:39:24.47424482Z + rollup transactions root: 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU= + data hash: 7P54CyIFJkHEJOCs+wvzRlhEqN7zh03qLi3wIKP1T3g= + proposer address: yPrVsmT31bVEaXjpauw1SeQfE1M= + rollup ids: + diff --git a/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_metadata/expected_verbose_output.json b/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_metadata/expected_verbose_output.json new file mode 100644 index 000000000..70ffcd28f --- /dev/null +++ b/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_metadata/expected_verbose_output.json @@ -0,0 +1 @@ +{"sequencer_metadata_list":[{"sequencer_block_hash":"HwCCO2EcW7AX0Cck/z9kl9qu9h3gnb0mpfEOGG+UWZA=","sequencer_block_header":{"chain_id":"astria-dusk-5","height":103623,"time":"2024-04-29T21:39:24.47424482Z","rollup_transactions_root":"47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=","data_hash":"7P54CyIFJkHEJOCs+wvzRlhEqN7zh03qLi3wIKP1T3g=","proposer_address":"yPrVsmT31bVEaXjpauw1SeQfE1M="},"rollup_ids":[],"rollup_transactions_proof":{"audit_path":"JsGJBReVHuey7Fl7QecbOc3c7YsgK9YBpLs4BaQ07HA=","leaf_index":0,"tree_size":3},"rollup_ids_proof":{"audit_path":"JsGJBReVHuey7Fl7QecbOc3c7YsgK9YBpLs4BaQ07HA=","leaf_index":1,"tree_size":3}}]} diff --git a/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_metadata/expected_verbose_output.txt b/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_metadata/expected_verbose_output.txt new file mode 100644 index 000000000..923ec4250 --- /dev/null +++ b/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_metadata/expected_verbose_output.txt @@ -0,0 +1,20 @@ + +sequencer metadata 0: + sequencer block hash: HwCCO2EcW7AX0Cck/z9kl9qu9h3gnb0mpfEOGG+UWZA= + sequencer block header: + chain id: astria-dusk-5 + height: 103623 + time: 2024-04-29T21:39:24.47424482Z + rollup transactions root: 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU= + data hash: 7P54CyIFJkHEJOCs+wvzRlhEqN7zh03qLi3wIKP1T3g= + proposer address: yPrVsmT31bVEaXjpauw1SeQfE1M= + rollup ids: + rollup transactions proof: + audit path: JsGJBReVHuey7Fl7QecbOc3c7YsgK9YBpLs4BaQ07HA= + leaf index: 0 + tree size: 3 + rollup ids proof: + audit path: JsGJBReVHuey7Fl7QecbOc3c7YsgK9YBpLs4BaQ07HA= + leaf index: 1 + tree size: 3 + diff --git a/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_metadata/input.txt b/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_metadata/input.txt new file mode 100644 index 000000000..d9d4cefda --- /dev/null +++ b/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_metadata/input.txt @@ -0,0 +1 @@ +G+wAAGRoXD40+0Y4QWiQEiCmKCMVWdaZYQMGSLi0Da11A8UCO8u/vH3OVt0b3UFRL1vpIz6lV6ZlKnEYNp6N39mYC699gJ8bryjheCWKGheTWD7QiCJcmU07gjKBm79gHCYkm+IIy0HNN86zmoVKxd5zjO4VH+8DKH7VD+YinfhabbJ/X21VcQm+dL0R1TxhIDEWAscNI0q5P7Sk+d8vE3lenSzsdYon9gUZOiVZeTTBRh2mTCzYgG0wDkmRRX/edGMZpnhOIzm7c5sAuNegJd0SyBKRG29KOBcbNmCTCKGUcA== \ No newline at end of file diff --git a/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_rollup_data/expected_brief_output.json b/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_rollup_data/expected_brief_output.json new file mode 100644 index 000000000..52eef6881 --- /dev/null +++ b/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_rollup_data/expected_brief_output.json @@ -0,0 +1 @@ +{"rollup_data_list":[{"sequencer_block_hash":"Y2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2M=","rollup_id":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","transaction_count":1}]} diff --git a/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_rollup_data/expected_brief_output.txt b/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_rollup_data/expected_brief_output.txt new file mode 100644 index 000000000..933f3591f --- /dev/null +++ b/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_rollup_data/expected_brief_output.txt @@ -0,0 +1,6 @@ + +rollup data 0: + sequencer block hash: Y2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2M= + rollup id: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + transaction count: 1 + diff --git a/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_rollup_data/expected_verbose_output.json b/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_rollup_data/expected_verbose_output.json new file mode 100644 index 000000000..f121fb6e2 --- /dev/null +++ b/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_rollup_data/expected_verbose_output.json @@ -0,0 +1 @@ +{"rollup_data_list":[{"sequencer_block_hash":"Y2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2M=","rollup_id":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","transactions":["CgEB"],"proof":{"audit_path":"MNlZ2hLiZd70dUCLZo/iiZDbKE0DJb7iZ5NPUxTNbNt5fK7sp/9p267ADmDS86KtdasicQtPuLgp9swLgYFiny012CwZFMloQHQ9b68pC9PSiZjlYvcN04ar7rU7r5yw93fxRRyRBsaIaEHDdKuIUhESjEaDCrhCnNbMsDrgQmg=","leaf_index":0,"tree_size":19}}]} diff --git a/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_rollup_data/expected_verbose_output.txt b/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_rollup_data/expected_verbose_output.txt new file mode 100644 index 000000000..ff342839f --- /dev/null +++ b/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_rollup_data/expected_verbose_output.txt @@ -0,0 +1,11 @@ + +rollup data 0: + sequencer block hash: Y2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2M= + rollup id: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + transactions: + CgEB + proof: + audit path: MNlZ2hLiZd70dUCLZo/iiZDbKE0DJb7iZ5NPUxTNbNt5fK7sp/9p267ADmDS86KtdasicQtPuLgp9swLgYFiny012CwZFMloQHQ9b68pC9PSiZjlYvcN04ar7rU7r5yw93fxRRyRBsaIaEHDdKuIUhESjEaDCrhCnNbMsDrgQmg= + leaf index: 0 + tree size: 19 + diff --git a/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_rollup_data/input.txt b/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_rollup_data/input.txt new file mode 100644 index 000000000..5e19ba911 --- /dev/null +++ b/crates/astria-sequencer-utils/tests/resources/parse_blob/unbatched_rollup_data/input.txt @@ -0,0 +1 @@ +G9IAAGRgnA70YbQ7vQFgEtSk+GyjG+ECKSiBq5BEKAHghQVGAYGidCAqCah85nhOune//g43zvUovM8uupC2AktM33uW2jhQtvwuouMGnjv/vS8GZnFO+18t/eG9omC0zdSUzO8mOjnZpVFB41SeSVnzMg4zCByUQR/sZ1c9ufxhDzJ6X8f1BuuG/iI/LDjFsOUsL5OFsN4seyIpzzIVNWVad7w5pHtr6sUgAw== \ No newline at end of file