From a4b1f67897424c84d2247af2b45bc40d7dd1bdf9 Mon Sep 17 00:00:00 2001 From: lukacan Date: Wed, 26 Jun 2024 10:13:34 +0200 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=94=A5=20remove=20explorer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/lint.yml | 22 - .vscode/settings.json | 1 - Cargo.lock | 72 -- Cargo.toml | 14 +- crates/cli/Cargo.toml | 1 - crates/cli/src/command.rs | 3 - crates/cli/src/command/explorer.rs | 204 ---- crates/cli/src/command/explorer/account.rs | 37 - crates/cli/src/command/explorer/program.rs | 25 - .../cli/src/command/explorer/transaction.rs | 43 - crates/cli/src/lib.rs | 7 - crates/explorer/Cargo.toml | 33 - crates/explorer/src/account.rs | 216 ---- crates/explorer/src/config.rs | 64 -- crates/explorer/src/display.rs | 23 - crates/explorer/src/error.rs | 25 - crates/explorer/src/lib.rs | 11 - crates/explorer/src/output.rs | 365 ------- crates/explorer/src/parse.rs | 145 --- .../src/parse/associated_token_account.rs | 96 -- crates/explorer/src/parse/bpf_loader.rs | 98 -- .../src/parse/bpf_upgradeable_loader.rs | 420 -------- crates/explorer/src/parse/memo.rs | 42 - crates/explorer/src/parse/stake.rs | 934 ------------------ crates/explorer/src/parse/system.rs | 540 ---------- crates/explorer/src/parse/token.rs | 651 ------------ crates/explorer/src/parse/vote.rs | 470 --------- crates/explorer/src/program.rs | 257 ----- crates/explorer/src/transaction.rs | 788 --------------- 29 files changed, 1 insertion(+), 5606 deletions(-) delete mode 100644 crates/cli/src/command/explorer.rs delete mode 100644 crates/cli/src/command/explorer/account.rs delete mode 100644 crates/cli/src/command/explorer/program.rs delete mode 100644 crates/cli/src/command/explorer/transaction.rs delete mode 100644 crates/explorer/Cargo.toml delete mode 100644 crates/explorer/src/account.rs delete mode 100644 crates/explorer/src/config.rs delete mode 100644 crates/explorer/src/display.rs delete mode 100644 crates/explorer/src/error.rs delete mode 100644 crates/explorer/src/lib.rs delete mode 100644 crates/explorer/src/output.rs delete mode 100644 crates/explorer/src/parse.rs delete mode 100644 crates/explorer/src/parse/associated_token_account.rs delete mode 100644 crates/explorer/src/parse/bpf_loader.rs delete mode 100644 crates/explorer/src/parse/bpf_upgradeable_loader.rs delete mode 100644 crates/explorer/src/parse/memo.rs delete mode 100644 crates/explorer/src/parse/stake.rs delete mode 100644 crates/explorer/src/parse/system.rs delete mode 100644 crates/explorer/src/parse/token.rs delete mode 100644 crates/explorer/src/parse/vote.rs delete mode 100644 crates/explorer/src/program.rs delete mode 100644 crates/explorer/src/transaction.rs diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index efa72d86..d29e2f02 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -10,33 +10,11 @@ jobs: checks: runs-on: ubuntu-20.04 needs: - - explorer - cli - client - test steps: - run: echo "Done" - - explorer: - runs-on: ubuntu-20.04 - defaults: - run: - working-directory: crates/explorer - steps: - - uses: actions/checkout@v3 - - uses: ./.github/actions/setup-rust/ - id: rust-setup - - uses: Swatinem/rust-cache@v2 - name: Cache Rust and it's packages - - name: Cargo build - run: cargo build - - name: Cargo fmt - run: cargo fmt -- --check - - name: Cargo clippy - run: cargo clippy -- -D warnings - - name: Cargo test - run: cargo test - cli: runs-on: ubuntu-20.04 defaults: diff --git a/.vscode/settings.json b/.vscode/settings.json index 8083b162..4baf4fc8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,7 +3,6 @@ // autodiscovery is disabled, must list all crates "crates/cli/Cargo.toml", "crates/client/Cargo.toml", - "crates/explorer/Cargo.toml", "crates/test/Cargo.toml", ], "rust-analyzer.diagnostics.disabled": [ diff --git a/Cargo.lock b/Cargo.lock index fc817579..a774bc81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2995,48 +2995,6 @@ dependencies = [ "num", ] -[[package]] -name = "phf" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" -dependencies = [ - "phf_macros", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" -dependencies = [ - "phf_shared", - "rand 0.8.5", -] - -[[package]] -name = "phf_macros" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro2", - "quote", - "syn 2.0.50", -] - -[[package]] -name = "phf_shared" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" -dependencies = [ - "siphasher", -] - [[package]] name = "pin-project" version = "1.1.4" @@ -6035,7 +5993,6 @@ dependencies = [ "solana-sdk", "tokio", "trident-client", - "trident-explorer", ] [[package]] @@ -6123,35 +6080,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "trident-explorer" -version = "0.3.2" -dependencies = [ - "base64 0.13.1", - "bincode", - "bs58 0.5.0", - "chrono", - "console", - "num-derive 0.4.2", - "num-traits", - "phf", - "pretty-hex", - "serde", - "serde_json", - "solana-account-decoder", - "solana-cli-config", - "solana-client", - "solana-logger", - "solana-program", - "solana-sdk", - "solana-transaction-status", - "solana-vote-program", - "spl-associated-token-account", - "spl-memo", - "spl-token", - "thiserror", -] - [[package]] name = "trident-test" version = "0.3.2" diff --git a/Cargo.toml b/Cargo.toml index b13b50cb..7c80c445 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["crates/cli", "crates/client", "crates/explorer", "crates/test"] +members = ["crates/cli", "crates/client", "crates/test"] exclude = ["examples/"] resolver = "1" @@ -7,19 +7,13 @@ resolver = "1" [workspace.dependencies] trident-test = { path = "./crates/test", version = "0.3.2" } trident-client = { path = "./crates/client", version = "0.6.0" } -trident-explorer = { path = "./crates/explorer", version = "0.3.2" } anchor-client = { version=">=0.29.0", features = ["async"]} solana-sdk = "1.16" solana-cli-output = "1.16" solana-transaction-status = "1.16" solana-account-decoder = "1.16" -solana-cli-config = "1.16" -solana-client = "1.16" solana-program = "1.16" -solana-logger = "1.16" -solana-vote-program = "1.16" spl-token = "4.0.0" -spl-memo = "4.0.0" spl-associated-token-account = "2.0.0" tokio = { version = "1", default-features = false } rand = "0.8.5" @@ -43,12 +37,6 @@ rstest = "0.18.1" lazy_static = "1.4.0" bs58 = "0.5.0" base64 = "0.13.0" -pretty-hex = "0.3.0" -console = "0.15.0" -chrono = "0.4.19" -phf = { version = "0.11.2", features = ["macros"] } -num-derive = "0.4.0" -num-traits = "0.2.14" proc-macro2 = { version = "1.0.66", default-features = false } darling = "0.13.1" clap = { version = "=4.3.19", features = ["derive"] } diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 353c32e3..689bd712 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -14,4 +14,3 @@ anyhow = { workspace = true } fehler = { workspace = true } solana-sdk = { workspace = true } trident-client = { workspace = true } -trident-explorer = { workspace = true } diff --git a/crates/cli/src/command.rs b/crates/cli/src/command.rs index f21c1392..44694d4e 100644 --- a/crates/cli/src/command.rs +++ b/crates/cli/src/command.rs @@ -13,9 +13,6 @@ pub use test::test; mod localnet; pub use localnet::localnet; -mod explorer; -pub use explorer::{explorer, ExplorerCommand}; - mod init; pub use init::{init, TestsType}; diff --git a/crates/cli/src/command/explorer.rs b/crates/cli/src/command/explorer.rs deleted file mode 100644 index 1cb53a42..00000000 --- a/crates/cli/src/command/explorer.rs +++ /dev/null @@ -1,204 +0,0 @@ -use anyhow::Error; -use clap::Subcommand; -use fehler::throws; -use solana_sdk::{pubkey::Pubkey, signature::Signature}; -use trident_explorer::display::DisplayFormat; - -mod account; -mod program; -mod transaction; - -#[derive(Subcommand)] -pub enum ExplorerCommand { - /// Show the contents of an account - Account { - /// Ed25519 pubkey, PDA or hash of a pubkey - pubkey: Pubkey, - /// Pretty-printed JSON output - #[clap(long = "json-pretty", conflicts_with = "json")] - jsonpretty: bool, - /// JSON output - #[clap(long, conflicts_with = "jsonpretty")] - json: bool, - #[clap(long = "hide-lamports")] - /// Hide lamports in the output - hidelamports: bool, - /// Hide data in the output - #[clap(long = "hide-data")] - hidedata: bool, - #[clap(long = "hide-owner")] - /// Hide owner in the output - hideowner: bool, - #[clap(long = "hide-executable")] - /// Hide executable in the output - hideexecutable: bool, - /// Hide rent epoch in the output - #[clap(long = "hide-rent-epoch")] - hiderentepoch: bool, - }, - /// Show the details of a program - Program { - /// Address of a program to show - pubkey: Pubkey, - /// Pretty-printed JSON output - #[clap(long = "json-pretty", conflicts_with = "json")] - jsonpretty: bool, - /// JSON output - #[clap(long, conflicts_with = "jsonpretty")] - json: bool, - /// Hide program account in the output - #[clap(long = "hide-program-account")] - hideprogramaccount: bool, - /// Hide programdata account in the output - #[clap(long = "hide-programdata-account")] - hideprogramdataaccount: bool, - }, - /// Show the contents of a transaction - Transaction { - /// Signature of a transaction - signature: Signature, - /// Raw transaction without interpretation - #[clap(short, long)] - raw: bool, - /// Pretty-printed JSON output - #[clap(long = "json-pretty", conflicts_with = "json")] - jsonpretty: bool, - /// JSON output - #[clap(long, conflicts_with = "jsonpretty")] - json: bool, - /// Hide overview in the output - #[clap(long = "hide-overview")] - hideoverview: bool, - /// Hide transaction content in the output - #[clap(long = "hide-transaction")] - hidetransaction: bool, - /// Hide log messages in the output - #[clap(long = "hide-log-messages", conflicts_with = "raw")] - hidelogmessages: bool, - }, -} - -#[throws] -pub async fn explorer(subcmd: ExplorerCommand) { - match subcmd { - ExplorerCommand::Account { - pubkey, - jsonpretty, - json, - hidelamports, - hidedata, - hideowner, - hideexecutable, - hiderentepoch, - } => { - if jsonpretty { - account::view( - pubkey, - hidelamports, - hidedata, - hideowner, - hideexecutable, - hiderentepoch, - DisplayFormat::JSONPretty, - ) - .await? - } else if json { - account::view( - pubkey, - hidelamports, - hidedata, - hideowner, - hideexecutable, - hiderentepoch, - DisplayFormat::JSON, - ) - .await? - } else { - account::view( - pubkey, - hidelamports, - hidedata, - hideowner, - hideexecutable, - hiderentepoch, - DisplayFormat::Cli, - ) - .await? - } - } - ExplorerCommand::Program { - pubkey, - jsonpretty, - json, - hideprogramaccount, - hideprogramdataaccount, - } => { - if jsonpretty { - program::view( - pubkey, - hideprogramaccount, - hideprogramdataaccount, - DisplayFormat::JSONPretty, - ) - .await? - } else if json { - program::view( - pubkey, - hideprogramaccount, - hideprogramdataaccount, - DisplayFormat::JSON, - ) - .await? - } else { - program::view( - pubkey, - hideprogramaccount, - hideprogramdataaccount, - DisplayFormat::Cli, - ) - .await? - } - } - ExplorerCommand::Transaction { - signature, - raw, - jsonpretty, - json, - hideoverview, - hidetransaction, - hidelogmessages, - } => { - if jsonpretty { - transaction::view( - signature, - raw, - hideoverview, - hidetransaction, - hidelogmessages, - DisplayFormat::JSONPretty, - ) - .await? - } else if json { - transaction::view( - signature, - raw, - hideoverview, - hidetransaction, - hidelogmessages, - DisplayFormat::JSON, - ) - .await? - } else { - transaction::view( - signature, - raw, - hideoverview, - hidetransaction, - hidelogmessages, - DisplayFormat::Cli, - ) - .await? - } - } - } -} diff --git a/crates/cli/src/command/explorer/account.rs b/crates/cli/src/command/explorer/account.rs deleted file mode 100644 index 92726605..00000000 --- a/crates/cli/src/command/explorer/account.rs +++ /dev/null @@ -1,37 +0,0 @@ -use anyhow::Error; -use fehler::throws; -use solana_sdk::pubkey::Pubkey; -use trident_explorer::{ - account::AccountFieldVisibility, config::ExplorerConfig, display::DisplayFormat, - output::print_account, -}; - -#[throws] -pub async fn view( - pubkey: Pubkey, - hidelamports: bool, - hidedata: bool, - hideowner: bool, - hideexecutable: bool, - hiderentepoch: bool, - format: DisplayFormat, -) { - let mut visibility = AccountFieldVisibility::new_all_enabled(); - if hidelamports { - visibility.disable_lamports(); - } - if hidedata { - visibility.disable_data(); - } - if hideowner { - visibility.disable_owner(); - } - if hideexecutable { - visibility.disable_executable(); - } - if hiderentepoch { - visibility.disable_rent_epoch(); - } - let config = ExplorerConfig::default(); - print_account(&pubkey, &visibility, format, &config).await?; -} diff --git a/crates/cli/src/command/explorer/program.rs b/crates/cli/src/command/explorer/program.rs deleted file mode 100644 index 2026c0cd..00000000 --- a/crates/cli/src/command/explorer/program.rs +++ /dev/null @@ -1,25 +0,0 @@ -use anyhow::Error; -use fehler::throws; -use solana_sdk::pubkey::Pubkey; -use trident_explorer::{ - config::ExplorerConfig, display::DisplayFormat, output::print_program, - program::ProgramFieldVisibility, -}; - -#[throws] -pub async fn view( - pubkey: Pubkey, - hideprogramaccount: bool, - hideprogramdataaccount: bool, - format: DisplayFormat, -) { - let mut visibility = ProgramFieldVisibility::new_all_enabled(); - if hideprogramaccount { - visibility.disable_program_account(); - } - if hideprogramdataaccount { - visibility.disable_programdata_account(); - } - let config = ExplorerConfig::default(); - print_program(&pubkey, &visibility, format, &config).await?; -} diff --git a/crates/cli/src/command/explorer/transaction.rs b/crates/cli/src/command/explorer/transaction.rs deleted file mode 100644 index 0685dc26..00000000 --- a/crates/cli/src/command/explorer/transaction.rs +++ /dev/null @@ -1,43 +0,0 @@ -use anyhow::Error; -use fehler::throws; -use solana_sdk::signature::Signature; -use trident_explorer::{ - config::ExplorerConfig, - display::DisplayFormat, - output::{print_raw_transaction, print_transaction}, - transaction::{RawTransactionFieldVisibility, TransactionFieldVisibility}, -}; - -#[throws] -pub async fn view( - signature: Signature, - raw: bool, - hideoverview: bool, - hidetransaction: bool, - hidelogmessages: bool, - format: DisplayFormat, -) { - let config = ExplorerConfig::default(); - if raw { - let mut visibility = RawTransactionFieldVisibility::new_all_enabled(); - if hideoverview { - visibility.disable_overview(); - } - if hidetransaction { - visibility.disable_transaction(); - } - print_raw_transaction(&signature, &visibility, format, &config).await? - } else { - let mut visibility = TransactionFieldVisibility::new_all_enabled(); - if hideoverview { - visibility.disable_overview(); - } - if hidetransaction { - visibility.disable_transaction(); - } - if hidelogmessages { - visibility.disable_log_messages(); - } - print_transaction(&signature, &visibility, format, &config).await? - }; -} diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index 5aef3fac..f1173b00 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -7,7 +7,6 @@ use fehler::throws; // subcommand functions to call and nested subcommands mod command; // bring nested subcommand enums into scope -use command::ExplorerCommand; use command::FuzzCommand; use command::KeyPairCommand; @@ -48,11 +47,6 @@ enum Command { }, /// Run local test validator Localnet, - /// The Hacker's Explorer - Explorer { - #[clap(subcommand)] - subcmd: ExplorerCommand, - }, /// Initialize test environment Init { /// Specifies the types of tests for which the frameworks should be initialized. @@ -73,7 +67,6 @@ pub async fn start() { Command::Test { root } => command::test(root).await?, Command::Fuzz { root, subcmd } => command::fuzz(root, subcmd).await?, Command::Localnet => command::localnet().await?, - Command::Explorer { subcmd } => command::explorer(subcmd).await?, Command::Init { tests_type } => command::init(tests_type).await?, Command::Clean => command::clean().await?, } diff --git a/crates/explorer/Cargo.toml b/crates/explorer/Cargo.toml deleted file mode 100644 index 68b68a2d..00000000 --- a/crates/explorer/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "trident-explorer" -version = "0.3.2" -edition = "2021" -repository = "https://github.com/Ackee-Blockchain/trident" -license-file = "../../LICENSE" -readme = "../../README.md" -description = "Trident explorer for Solana." - -[dependencies] -solana-cli-config = { workspace = true } -solana-client = { workspace = true } -solana-sdk = { workspace = true } -solana-program = { workspace = true } -solana-logger = { workspace = true } -solana-transaction-status = { workspace = true } -solana-vote-program = { workspace = true } -solana-account-decoder = { workspace = true } -spl-token = { workspace = true } -spl-memo = { workspace = true } -spl-associated-token-account = { workspace = true } -thiserror = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -bs58 = { workspace = true } -base64 = { workspace = true } -pretty-hex = { workspace = true } -console = { workspace = true } -chrono = { workspace = true } -phf = { workspace = true } -bincode = { workspace = true } -num-derive = { workspace = true } -num-traits = { workspace = true } diff --git a/crates/explorer/src/account.rs b/crates/explorer/src/account.rs deleted file mode 100644 index bb176db4..00000000 --- a/crates/explorer/src/account.rs +++ /dev/null @@ -1,216 +0,0 @@ -use crate::output::pretty_lamports_to_sol; -use console::style; -use serde::Serialize; -use solana_sdk::{account::Account, pubkey::Pubkey}; -use std::fmt; - -#[derive(Serialize)] -pub struct KeyedAccount { - pub pubkey: Pubkey, - pub account: Account, -} - -pub struct AccountFieldVisibility { - lamports: bool, - data: bool, - owner: bool, - executable: bool, - rent_epoch: bool, -} - -impl AccountFieldVisibility { - pub fn new_all_enabled() -> Self { - Self { - lamports: true, - data: true, - owner: true, - executable: true, - rent_epoch: true, - } - } - - pub fn new_all_disabled() -> Self { - Self { - lamports: false, - data: false, - owner: false, - executable: false, - rent_epoch: false, - } - } - - pub fn lamports(&self) -> bool { - self.lamports - } - - pub fn enable_lamports(&mut self) -> &mut Self { - self.lamports = true; - self - } - - pub fn disable_lamports(&mut self) -> &mut Self { - self.lamports = false; - self - } - - pub fn data(&self) -> bool { - self.data - } - - pub fn enable_data(&mut self) -> &mut Self { - self.data = true; - self - } - - pub fn disable_data(&mut self) -> &mut Self { - self.data = false; - self - } - - pub fn owner(&self) -> bool { - self.owner - } - - pub fn enable_owner(&mut self) -> &mut Self { - self.owner = true; - self - } - - pub fn disable_owner(&mut self) -> &mut Self { - self.owner = false; - self - } - - pub fn executable(&self) -> bool { - self.executable - } - - pub fn enable_executable(&mut self) -> &mut Self { - self.executable = true; - self - } - - pub fn disable_executable(&mut self) -> &mut Self { - self.executable = false; - self - } - - pub fn rent_epoch(&self) -> bool { - self.rent_epoch - } - - pub fn enable_rent_epoch(&mut self) -> &mut Self { - self.rent_epoch = true; - self - } - - pub fn disable_rent_epoch(&mut self) -> &mut Self { - self.rent_epoch = false; - self - } -} - -#[derive(Serialize)] -pub struct DisplayAccount { - #[serde(skip_serializing_if = "Option::is_none")] - pub lamports: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub data: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub owner: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub executable: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub rent_epoch: Option, -} - -#[derive(Serialize)] -pub struct DisplayKeyedAccount { - pub pubkey: String, - pub account: DisplayAccount, -} - -impl DisplayKeyedAccount { - pub fn from_keyed_account( - keyed_account: &KeyedAccount, - visibility: &AccountFieldVisibility, - ) -> Self { - Self { - pubkey: keyed_account.pubkey.to_string(), - account: DisplayAccount { - lamports: if visibility.lamports { - Some(keyed_account.account.lamports) - } else { - None - }, - data: if visibility.data { - Some(base64::encode(&keyed_account.account.data)) - } else { - None - }, - owner: if visibility.owner { - Some(keyed_account.account.owner.to_string()) - } else { - None - }, - executable: if visibility.executable { - Some(keyed_account.account.executable) - } else { - None - }, - rent_epoch: if visibility.rent_epoch { - Some(keyed_account.account.rent_epoch) - } else { - None - }, - }, - } - } -} - -impl fmt::Display for DisplayKeyedAccount { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - writeln!( - f, - "========================================================" - )?; - writeln!(f, "{} {}", style("Public Key:").bold(), self.pubkey)?; - writeln!( - f, - "========================================================" - )?; - - if let Some(lamports) = self.account.lamports { - writeln!(f)?; - write!( - f, - "{} {} (◎ {})", - style("Lamports:").bold(), - lamports, - pretty_lamports_to_sol(lamports) - )?; - } - if let Some(data) = &self.account.data { - writeln!(f)?; - if data.is_empty() { - write!(f, "{} [Empty]", style("Data:").bold())?; - } else { - write!(f, "{} [Hexdump below]", style("Data:").bold())?; - } - } - if let Some(owner) = &self.account.owner { - writeln!(f)?; - write!(f, "{} {}", style("Owner").bold(), owner)?; - } - if let Some(executable) = self.account.executable { - writeln!(f)?; - write!(f, "{} {}", style("Executable:").bold(), executable)?; - } - if let Some(rent_epoch) = self.account.rent_epoch { - writeln!(f)?; - write!(f, "{} {}", style("Rent Epoch:").bold(), rent_epoch)?; - } - - Ok(()) - } -} diff --git a/crates/explorer/src/config.rs b/crates/explorer/src/config.rs deleted file mode 100644 index 87d26536..00000000 --- a/crates/explorer/src/config.rs +++ /dev/null @@ -1,64 +0,0 @@ -use solana_cli_config::{Config, CONFIG_FILE}; -use solana_client::rpc_client::RpcClient; -use solana_sdk::commitment_config::CommitmentConfig; - -pub struct ExplorerConfig { - json_rpc_url: String, - rpc_client: RpcClient, -} - -impl ExplorerConfig { - pub fn new() -> Self { - let json_rpc_url = if let Some(ref config_file) = *CONFIG_FILE { - Config::load(config_file).unwrap_or_default().json_rpc_url - } else { - Config::default().json_rpc_url - }; - - let rpc_client = - RpcClient::new_with_commitment(json_rpc_url.clone(), CommitmentConfig::confirmed()); - - // setup_logging(LogLevel::DEBUG); - - ExplorerConfig { - json_rpc_url, - rpc_client, - } - } - - pub fn json_rpc_url(&self) -> &String { - &self.json_rpc_url - } - - pub fn rpc_client(&self) -> &RpcClient { - &self.rpc_client - } -} - -impl Default for ExplorerConfig { - fn default() -> Self { - Self::new() - } -} - -pub enum LogLevel { - ERROR, - WARN, - INFO, - DEBUG, - TRACE, -} - -pub fn setup_logging(level: LogLevel) { - match level { - LogLevel::ERROR => solana_logger::setup_with_default("error"), - LogLevel::WARN => solana_logger::setup_with_default("warn"), - LogLevel::INFO => solana_logger::setup_with_default("info"), - LogLevel::DEBUG => solana_logger::setup_with_default("debug"), - LogLevel::TRACE => solana_logger::setup_with_default("trace"), - } -} - -pub fn reset_logging() { - setup_logging(LogLevel::ERROR); -} diff --git a/crates/explorer/src/display.rs b/crates/explorer/src/display.rs deleted file mode 100644 index 4847eca6..00000000 --- a/crates/explorer/src/display.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::error::Result; -use serde::Serialize; -use std::fmt; - -#[derive(Clone, Copy)] -pub enum DisplayFormat { - Cli, - JSONPretty, - JSON, -} - -impl DisplayFormat { - pub fn formatted_string(&self, item: &T) -> Result - where - T: fmt::Display + Serialize, - { - match self { - DisplayFormat::Cli => Ok(format!("{item}")), - DisplayFormat::JSONPretty => Ok(serde_json::to_string_pretty(&item)?), - DisplayFormat::JSON => Ok(serde_json::to_string(&item)?), - } - } -} diff --git a/crates/explorer/src/error.rs b/crates/explorer/src/error.rs deleted file mode 100644 index 0d1b5dbf..00000000 --- a/crates/explorer/src/error.rs +++ /dev/null @@ -1,25 +0,0 @@ -use serde_json::error::Error as SerdeError; -use solana_client::client_error::ClientError; -use solana_sdk::instruction::InstructionError; -use std::fmt::Error as FmtError; -use thiserror::Error; - -pub type Result = std::result::Result; - -#[derive(Debug, Error)] -pub enum ExplorerError { - #[error("{0}")] - SolanaClient(#[from] ClientError), - - #[error("{0}")] - SerdeJson(#[from] SerdeError), - - #[error("{0}")] - Fmt(#[from] FmtError), - - #[error("{0}")] - Instruction(#[from] InstructionError), - - #[error("{0}")] - Custom(String), -} diff --git a/crates/explorer/src/lib.rs b/crates/explorer/src/lib.rs deleted file mode 100644 index 86dd9450..00000000 --- a/crates/explorer/src/lib.rs +++ /dev/null @@ -1,11 +0,0 @@ -pub mod config; -pub mod error; - -pub mod display; -pub mod output; - -pub mod parse; - -pub mod account; -pub mod program; -pub mod transaction; diff --git a/crates/explorer/src/output.rs b/crates/explorer/src/output.rs deleted file mode 100644 index aa772da4..00000000 --- a/crates/explorer/src/output.rs +++ /dev/null @@ -1,365 +0,0 @@ -use crate::{ - account::{AccountFieldVisibility, DisplayKeyedAccount, KeyedAccount}, - config::ExplorerConfig, - display::DisplayFormat, - error::{ExplorerError, Result}, - program::{DisplayUpgradeableProgram, ProgramFieldVisibility}, - transaction::{ - DisplayRawTransaction, DisplayTransaction, RawTransactionFieldVisibility, - TransactionFieldVisibility, - }, -}; -use console::style; -use pretty_hex::*; -use solana_client::rpc_config::RpcTransactionConfig; -use solana_sdk::{ - account_utils::StateMut, bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, - bpf_loader_upgradeable::UpgradeableLoaderState, commitment_config::CommitmentConfig, - native_token, pubkey::Pubkey, signature::Signature, -}; -use solana_transaction_status::{TransactionConfirmationStatus, UiTransactionEncoding}; -use std::{cmp::Ordering, fmt::Write}; - -pub fn pretty_lamports_to_sol(lamports: u64) -> String { - let sol_str = format!("{:.9}", native_token::lamports_to_sol(lamports)); - sol_str - .trim_end_matches('0') - .trim_end_matches('.') - .to_string() -} - -pub fn classify_account(fee_payer: bool, writable: bool, signer: bool, program: bool) -> String { - let mut account_type_string = String::new(); - let mut started = false; - if fee_payer { - account_type_string.push_str("[Fee Payer]"); - started = true; - } - if writable { - if started { - account_type_string.push(' '); - } - account_type_string.push_str("[Writable]"); - started = true; - } - if signer { - if started { - account_type_string.push(' '); - } - account_type_string.push_str("[Signer]"); - started = true; - } - if program { - if started { - account_type_string.push(' '); - } - account_type_string.push_str("[Program]"); - } - account_type_string -} - -pub fn calculate_change(post: u64, pre: u64) -> String { - match post.cmp(&pre) { - Ordering::Greater => format!( - "◎ {} (+{})", - pretty_lamports_to_sol(post), - pretty_lamports_to_sol(post - pre) - ), - Ordering::Less => format!( - "◎ {} (-{})", - pretty_lamports_to_sol(post), - pretty_lamports_to_sol(pre - post) - ), - Ordering::Equal => format!("◎ {}", pretty_lamports_to_sol(post)), - } -} - -pub fn change_in_sol(post: u64, pre: u64) -> String { - match post.cmp(&pre) { - Ordering::Greater => format!("+{}", pretty_lamports_to_sol(post - pre)), - Ordering::Less => format!("-{}", pretty_lamports_to_sol(pre - post)), - Ordering::Equal => "0".to_string(), - } -} - -pub fn status_to_string(status: &TransactionConfirmationStatus) -> String { - match status { - TransactionConfirmationStatus::Processed => "Processed".to_string(), - TransactionConfirmationStatus::Confirmed => "Confirmed".to_string(), - TransactionConfirmationStatus::Finalized => "Finalized".to_string(), - } -} - -pub async fn print_account( - pubkey: &Pubkey, - visibility: &AccountFieldVisibility, - format: DisplayFormat, - config: &ExplorerConfig, -) -> Result<()> { - let account_string = get_account_string(pubkey, visibility, format, config).await?; - println!("{account_string}"); - Ok(()) -} - -pub async fn print_program( - program_id: &Pubkey, - visibility: &ProgramFieldVisibility, - format: DisplayFormat, - config: &ExplorerConfig, -) -> Result<()> { - let program_string = get_program_string(program_id, visibility, format, config).await?; - println!("{program_string}"); - Ok(()) -} - -pub async fn print_raw_transaction( - signature: &Signature, - visibility: &RawTransactionFieldVisibility, - format: DisplayFormat, - config: &ExplorerConfig, -) -> Result<()> { - let raw_transaction_string = - get_raw_transaction_string(signature, visibility, format, config).await?; - println!("{raw_transaction_string}"); - Ok(()) -} - -pub async fn print_transaction( - signature: &Signature, - visibility: &TransactionFieldVisibility, - format: DisplayFormat, - config: &ExplorerConfig, -) -> Result<()> { - let transaction_string = get_transaction_string(signature, visibility, format, config).await?; - println!("{transaction_string}"); - Ok(()) -} - -pub async fn get_account_string( - pubkey: &Pubkey, - visibility: &AccountFieldVisibility, - format: DisplayFormat, - config: &ExplorerConfig, -) -> Result { - let rpc_client = config.rpc_client(); - let account = rpc_client.get_account(pubkey)?; - let keyed_account = KeyedAccount { - pubkey: *pubkey, - account, - }; - let display_keyed_account = DisplayKeyedAccount::from_keyed_account(&keyed_account, visibility); - let mut account_string = format.formatted_string(&display_keyed_account)?; - - if display_keyed_account.account.data.is_some() { - let data = &keyed_account.account.data; - if let DisplayFormat::Cli = format { - if !data.is_empty() { - writeln!(&mut account_string)?; - writeln!(&mut account_string)?; - - writeln!( - &mut account_string, - "{} {} bytes", - style("Hexdump:").bold(), - data.len() - )?; - // Show hexdump of not more than MAX_BYTES_SHOWN bytes - const MAX_BYTES_SHOWN: usize = 64; - let len = data.len(); - let (end, finished) = if MAX_BYTES_SHOWN > len { - (len, true) - } else { - (MAX_BYTES_SHOWN, false) - }; - let raw_account_data = &data[..end]; - let cfg = HexConfig { - title: false, - width: 16, - group: 0, - chunk: 2, - ..HexConfig::default() - }; - write!(&mut account_string, "{:?}", raw_account_data.hex_conf(cfg))?; - if !finished { - writeln!(&mut account_string)?; - write!(&mut account_string, "... (skipped)")?; - } - } - }; - } - - Ok(account_string) -} - -pub async fn get_program_string( - program_id: &Pubkey, - visibility: &ProgramFieldVisibility, - format: DisplayFormat, - config: &ExplorerConfig, -) -> Result { - let rpc_client = config.rpc_client(); - let program_account = rpc_client.get_account(program_id)?; - let program_keyed_account = KeyedAccount { - pubkey: *program_id, - account: program_account, - }; - - if program_keyed_account.account.owner == bpf_loader::id() - || program_keyed_account.account.owner == bpf_loader_deprecated::id() - { - // these loaders are not interesting, just accounts with the program.so in data - let mut program_string = get_account_string( - program_id, - &AccountFieldVisibility::new_all_enabled(), - format, - config, - ) - .await?; - - if let DisplayFormat::Cli = format { - program_string.push_str( - "\n\nNote: the program is loaded either by the deprecated BPFLoader or BPFLoader2, -it is an executable account with program.so in its data, hence this output.", - ); - } - - Ok(program_string) - } else if program_keyed_account.account.owner == bpf_loader_upgradeable::id() { - // this is the only interesting loader which uses redirection to programdata account - if let Ok(UpgradeableLoaderState::Program { - programdata_address, - }) = program_keyed_account.account.state() - { - if let Ok(programdata_account) = rpc_client.get_account(&programdata_address) { - let programdata_keyed_account = KeyedAccount { - pubkey: programdata_address, - account: programdata_account, - }; - if let Ok(UpgradeableLoaderState::ProgramData { - upgrade_authority_address, - slot, - }) = programdata_keyed_account.account.state() - { - let program = DisplayUpgradeableProgram::from( - &program_keyed_account, - &programdata_keyed_account, - slot, - &upgrade_authority_address, - visibility, - ); - let mut program_string = format.formatted_string(&program)?; - - if program.programdata_account.is_some() { - if let DisplayFormat::Cli = format { - writeln!(&mut program_string)?; - writeln!(&mut program_string)?; - writeln!( - &mut program_string, - "{} {} bytes", - style("Followed by Raw Program Data (program.so):").bold(), - programdata_keyed_account.account.data.len() - - UpgradeableLoaderState::size_of_programdata_metadata() - )?; - - // Show hexdump of not more than MAX_BYTES_SHOWN bytes - const MAX_BYTES_SHOWN: usize = 64; - let len = programdata_keyed_account.account.data.len(); - let offset = UpgradeableLoaderState::size_of_programdata_metadata(); - let (end, finished) = if offset + MAX_BYTES_SHOWN > len { - (len, true) - } else { - (offset + MAX_BYTES_SHOWN, false) - }; - let raw_program_data = - &programdata_keyed_account.account.data[offset..end]; - let cfg = HexConfig { - title: false, - width: 16, - group: 0, - chunk: 2, - ..HexConfig::default() - }; - write!(&mut program_string, "{:?}", raw_program_data.hex_conf(cfg))?; - if !finished { - writeln!(&mut program_string)?; - write!(&mut program_string, "... (skipped)")?; - } - } - } - - Ok(program_string) - } else { - Err(ExplorerError::Custom(format!( - "Program {program_id} has been closed" - ))) - } - } else { - Err(ExplorerError::Custom(format!( - "Program {program_id} has been closed" - ))) - } - } else { - Err(ExplorerError::Custom(format!( - "{program_id} is not a Program account" - ))) - } - } else { - Err(ExplorerError::Custom(format!( - "{program_id} is not a pubkey of an on-chain BPF program." - ))) - } -} - -pub async fn get_raw_transaction_string( - signature: &Signature, - visibility: &RawTransactionFieldVisibility, - format: DisplayFormat, - config: &ExplorerConfig, -) -> Result { - let rpc_client = config.rpc_client(); - let config = RpcTransactionConfig { - encoding: Some(UiTransactionEncoding::Binary), - commitment: Some(CommitmentConfig::confirmed()), - max_supported_transaction_version: None, - }; - - let transaction = rpc_client.get_transaction_with_config(signature, config)?; - - let response = rpc_client.get_signature_statuses_with_history(&[*signature])?; - - let transaction_status = response.value[0].as_ref().unwrap(); - - let display_transaction = - DisplayRawTransaction::from(&transaction, transaction_status, visibility)?; - - let transaction_string = format.formatted_string(&display_transaction)?; - - Ok(transaction_string) -} - -pub async fn get_transaction_string( - signature: &Signature, - visibility: &TransactionFieldVisibility, - format: DisplayFormat, - config: &ExplorerConfig, -) -> Result { - let rpc_client = config.rpc_client(); - let config = RpcTransactionConfig { - encoding: Some(UiTransactionEncoding::Binary), - commitment: Some(CommitmentConfig::confirmed()), - max_supported_transaction_version: Some(0), - }; - - let transaction = rpc_client.get_transaction_with_config(signature, config)?; - - let response = rpc_client.get_signature_statuses_with_history(&[*signature])?; - - let transaction_status = response.value[0].as_ref().unwrap(); - - let display_transaction = - DisplayTransaction::from(&transaction, transaction_status, visibility)?; - - let transaction_string = format.formatted_string(&display_transaction)?; - - Ok(transaction_string) -} diff --git a/crates/explorer/src/parse.rs b/crates/explorer/src/parse.rs deleted file mode 100644 index 4287d461..00000000 --- a/crates/explorer/src/parse.rs +++ /dev/null @@ -1,145 +0,0 @@ -use self::{ - associated_token_account::parse_associated_token_account, bpf_loader::parse_bpf_loader, - bpf_upgradeable_loader::parse_bpf_upgradeable_loader, memo::parse_memo, stake::parse_stake, - system::parse_system, token::parse_token, vote::parse_vote, -}; -use crate::transaction::{DisplayParsedInstruction, DisplayPartiallyParsedInstruction}; -use phf::phf_map; -use serde::Serialize; -use serde_json::Value; -use solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey}; -use thiserror::Error; - -mod associated_token_account; -mod bpf_loader; -mod bpf_upgradeable_loader; -mod memo; -mod stake; -mod system; -mod token; -mod vote; - -#[derive(Clone, Debug)] -pub enum ParsableProgram { - System, - BPFLoaderDeprecated, - BPFLoader, - BPFLoaderUpgradeable, - Stake, - Vote, - SPLMemoV1, - SPLMemo, - SPLToken, - SPLAssociatedTokenAccount, -} - -static PARSABLE_PROGRAM_IDS: phf::Map<&'static str, ParsableProgram> = phf_map! { - // System - "11111111111111111111111111111111" => ParsableProgram::System, - // BPF Loader Deprecated - "BPFLoader1111111111111111111111111111111111" => ParsableProgram::BPFLoaderDeprecated, - // BPF Loader - "BPFLoader2111111111111111111111111111111111" => ParsableProgram::BPFLoader, - // BPF Loader Upgradeable - "BPFLoaderUpgradeab1e11111111111111111111111" => ParsableProgram::BPFLoaderUpgradeable, - // Stake - "Stake11111111111111111111111111111111111111" => ParsableProgram::Stake, - // Vote - "Vote111111111111111111111111111111111111111" => ParsableProgram::Vote, - // SPL Memo v1 - "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo" => ParsableProgram::SPLMemoV1, - // SPL Memo (current) - "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" => ParsableProgram::SPLMemo, - // SPL Token - "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" => ParsableProgram::SPLToken, - // SPL Associated Token Account - "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" => ParsableProgram::SPLAssociatedTokenAccount -}; - -#[derive(Error, Debug)] -pub enum ParseInstructionError { - #[error("{0:?} instruction not parsable")] - InstructionNotParsable(ParsableProgram), - - #[error("{0:?} instruction key mismatch")] - InstructionKeyMismatch(ParsableProgram), - - #[error("Program not parsable")] - ProgramNotParsable, - - #[error("Internal error, please report")] - SerdeJsonError(#[from] serde_json::error::Error), -} - -#[derive(Serialize, PartialEq, Eq, Debug)] -pub struct ParsedInstructionEnum { - #[serde(rename = "type")] - pub instruction_type: String, - #[serde(skip_serializing_if = "Value::is_null")] - pub info: Value, -} - -pub fn parse( - program_id: &Pubkey, - instruction: &CompiledInstruction, - account_keys: &[Pubkey], -) -> Result { - let program_name = PARSABLE_PROGRAM_IDS - .get(&program_id.to_string()) - .ok_or(ParseInstructionError::ProgramNotParsable)?; - - let parsed_json = match program_name { - ParsableProgram::System => serde_json::to_value(parse_system(instruction, account_keys)?)?, - ParsableProgram::BPFLoaderDeprecated | ParsableProgram::BPFLoader => { - serde_json::to_value(parse_bpf_loader(instruction, account_keys)?)? - } - ParsableProgram::BPFLoaderUpgradeable => { - serde_json::to_value(parse_bpf_upgradeable_loader(instruction, account_keys)?)? - } - ParsableProgram::Stake => serde_json::to_value(parse_stake(instruction, account_keys)?)?, - ParsableProgram::Vote => serde_json::to_value(parse_vote(instruction, account_keys)?)?, - ParsableProgram::SPLMemoV1 | ParsableProgram::SPLMemo => { - serde_json::to_value(parse_memo(instruction)?)? - } - ParsableProgram::SPLToken => serde_json::to_value(parse_token(instruction, account_keys)?)?, - ParsableProgram::SPLAssociatedTokenAccount => { - serde_json::to_value(parse_associated_token_account(instruction, account_keys)?)? - } - }; - - Ok(DisplayParsedInstruction { - program: format!("{program_name:?}"), - program_id: program_id.to_string(), - parsed: parsed_json, - }) -} - -pub fn check_num_accounts( - accounts: &[u8], - num: usize, - parsable_program: ParsableProgram, -) -> Result<(), ParseInstructionError> { - if accounts.len() < num { - Err(ParseInstructionError::InstructionKeyMismatch( - parsable_program, - )) - } else { - Ok(()) - } -} - -pub fn partially_parse( - program_id: &Pubkey, - instruction: &CompiledInstruction, - account_keys: &[Pubkey], -) -> DisplayPartiallyParsedInstruction { - DisplayPartiallyParsedInstruction { - program_id: program_id.to_string(), - accounts: instruction - .accounts - .iter() - .map(|&i| account_keys[i as usize].to_string()) - .collect(), - data: bs58::encode(instruction.data.clone()).into_string(), - } -} diff --git a/crates/explorer/src/parse/associated_token_account.rs b/crates/explorer/src/parse/associated_token_account.rs deleted file mode 100644 index d9acfb3c..00000000 --- a/crates/explorer/src/parse/associated_token_account.rs +++ /dev/null @@ -1,96 +0,0 @@ -use crate::parse::{ - check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum, -}; -use serde_json::json; -use solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey}; - -pub fn parse_associated_token_account( - instruction: &CompiledInstruction, - account_keys: &[Pubkey], -) -> Result { - match instruction.accounts.iter().max() { - Some(index) if (*index as usize) < account_keys.len() => {} - _ => { - // Runtime should prevent this from ever happening - return Err(ParseInstructionError::InstructionKeyMismatch( - ParsableProgram::SPLAssociatedTokenAccount, - )); - } - } - check_num_associated_token_accounts(&instruction.accounts, 6)?; - Ok(ParsedInstructionEnum { - instruction_type: "Create".to_string(), - info: json!({ - "Source": account_keys[instruction.accounts[0] as usize].to_string(), - "Account": account_keys[instruction.accounts[1] as usize].to_string(), - "Wallet": account_keys[instruction.accounts[2] as usize].to_string(), - "Mint": account_keys[instruction.accounts[3] as usize].to_string(), - "System Program": account_keys[instruction.accounts[4] as usize].to_string(), - "Token Program": account_keys[instruction.accounts[5] as usize].to_string(), - }), - }) -} - -fn check_num_associated_token_accounts( - accounts: &[u8], - num: usize, -) -> Result<(), ParseInstructionError> { - check_num_accounts(accounts, num, ParsableProgram::SPLAssociatedTokenAccount) -} - -#[cfg(test)] -mod test { - use super::*; - use spl_associated_token_account::instruction::create_associated_token_account; - use spl_associated_token_account::{ - get_associated_token_address, - solana_program::{ - instruction::CompiledInstruction as SplAssociatedTokenCompiledInstruction, - message::Message, pubkey::Pubkey as SplAssociatedTokenPubkey, - }, - }; - - fn convert_pubkey(pubkey: Pubkey) -> SplAssociatedTokenPubkey { - SplAssociatedTokenPubkey::new_from_array(pubkey.to_bytes()) - } - - fn convert_compiled_instruction( - instruction: &SplAssociatedTokenCompiledInstruction, - ) -> CompiledInstruction { - CompiledInstruction { - program_id_index: instruction.program_id_index, - accounts: instruction.accounts.clone(), - data: instruction.data.clone(), - } - } - #[test] - fn test_parse_associated_token() { - let funder = Pubkey::new_unique(); - let wallet_address = Pubkey::new_unique(); - let mint = Pubkey::new_unique(); - let associated_account_address = - get_associated_token_address(&convert_pubkey(wallet_address), &convert_pubkey(mint)); - let create_ix = create_associated_token_account( - &convert_pubkey(funder), - &convert_pubkey(wallet_address), - &convert_pubkey(mint), - &&spl_token::id(), - ); - let message = Message::new(&[create_ix], None); - let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); - assert_eq!( - parse_associated_token_account(&compiled_instruction, &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "Create".to_string(), - info: json!({ - "Source": funder.to_string(), - "Account": associated_account_address.to_string(), - "Wallet": wallet_address.to_string(), - "Mint": mint.to_string(), - "System Program": solana_sdk::system_program::id().to_string(), - "Token Program": &spl_token::id().to_string(), - }) - } - ); - } -} diff --git a/crates/explorer/src/parse/bpf_loader.rs b/crates/explorer/src/parse/bpf_loader.rs deleted file mode 100644 index 85614395..00000000 --- a/crates/explorer/src/parse/bpf_loader.rs +++ /dev/null @@ -1,98 +0,0 @@ -use crate::parse::{ParsableProgram, ParseInstructionError, ParsedInstructionEnum}; -use serde_json::json; -use solana_sdk::{ - instruction::CompiledInstruction, loader_instruction::LoaderInstruction, pubkey::Pubkey, -}; - -pub fn parse_bpf_loader( - instruction: &CompiledInstruction, - account_keys: &[Pubkey], -) -> Result { - let bpf_loader_instruction: LoaderInstruction = bincode::deserialize(&instruction.data) - .map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::BPFLoader))?; - if instruction.accounts.is_empty() || instruction.accounts[0] as usize >= account_keys.len() { - return Err(ParseInstructionError::InstructionKeyMismatch( - ParsableProgram::BPFLoader, - )); - } - match bpf_loader_instruction { - LoaderInstruction::Write { offset, bytes } => Ok(ParsedInstructionEnum { - instruction_type: "Write".to_string(), - info: json!({ - "Offset": offset, - "Bytes": base64::encode(bytes), - "Account": account_keys[instruction.accounts[0] as usize].to_string(), - }), - }), - LoaderInstruction::Finalize => Ok(ParsedInstructionEnum { - instruction_type: "Finalize".to_string(), - info: json!({ - "Account": account_keys[instruction.accounts[0] as usize].to_string(), - }), - }), - } -} - -#[cfg(test)] -mod test { - use super::*; - use solana_sdk::{message::Message, pubkey}; - - #[test] - fn test_parse_bpf_loader_instructions() { - let account_pubkey = pubkey::new_rand(); - let program_id = pubkey::new_rand(); - let offset = 4242; - let bytes = vec![8; 99]; - let fee_payer = pubkey::new_rand(); - let account_keys = vec![fee_payer, account_pubkey]; - let missing_account_keys = vec![account_pubkey]; - - let instruction = solana_sdk::loader_instruction::write( - &account_pubkey, - &program_id, - offset, - bytes.clone(), - ); - let message = Message::new(&[instruction], Some(&fee_payer)); - assert_eq!( - parse_bpf_loader(&message.instructions[0], &account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "Write".to_string(), - info: json!({ - "Offset": offset, - "Bytes": base64::encode(&bytes), - "Account": account_pubkey.to_string(), - }), - } - ); - assert!(parse_bpf_loader(&message.instructions[0], &missing_account_keys).is_err()); - - let instruction = solana_sdk::loader_instruction::finalize(&account_pubkey, &program_id); - let message = Message::new(&[instruction], Some(&fee_payer)); - assert_eq!( - parse_bpf_loader(&message.instructions[0], &account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "Finalize".to_string(), - info: json!({ - "Account": account_pubkey.to_string(), - }), - } - ); - assert!(parse_bpf_loader(&message.instructions[0], &missing_account_keys).is_err()); - - let bad_compiled_instruction = CompiledInstruction { - program_id_index: 3, - accounts: vec![1, 2], - data: vec![2, 0, 0, 0], // LoaderInstruction enum only has 2 variants - }; - assert!(parse_bpf_loader(&bad_compiled_instruction, &account_keys).is_err()); - - let bad_compiled_instruction = CompiledInstruction { - program_id_index: 3, - accounts: vec![], - data: vec![1, 0, 0, 0], - }; - assert!(parse_bpf_loader(&bad_compiled_instruction, &account_keys).is_err()); - } -} diff --git a/crates/explorer/src/parse/bpf_upgradeable_loader.rs b/crates/explorer/src/parse/bpf_upgradeable_loader.rs deleted file mode 100644 index 5b881b57..00000000 --- a/crates/explorer/src/parse/bpf_upgradeable_loader.rs +++ /dev/null @@ -1,420 +0,0 @@ -use crate::parse::{ - check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum, -}; -use serde_json::json; -use solana_sdk::{ - instruction::CompiledInstruction, loader_upgradeable_instruction::UpgradeableLoaderInstruction, - pubkey::Pubkey, -}; - -pub fn parse_bpf_upgradeable_loader( - instruction: &CompiledInstruction, - account_keys: &[Pubkey], -) -> Result { - let bpf_upgradeable_loader_instruction: UpgradeableLoaderInstruction = - bincode::deserialize(&instruction.data).map_err(|_| { - ParseInstructionError::InstructionNotParsable(ParsableProgram::BPFLoaderUpgradeable) - })?; - match instruction.accounts.iter().max() { - Some(index) if (*index as usize) < account_keys.len() => {} - _ => { - // Runtime should prevent this from ever happening - return Err(ParseInstructionError::InstructionKeyMismatch( - ParsableProgram::BPFLoaderUpgradeable, - )); - } - } - match bpf_upgradeable_loader_instruction { - UpgradeableLoaderInstruction::InitializeBuffer => { - check_num_bpf_upgradeable_loader_accounts(&instruction.accounts, 1)?; - let mut value = json!({ - "Account": account_keys[instruction.accounts[0] as usize].to_string(), - }); - let map = value.as_object_mut().unwrap(); - if instruction.accounts.len() > 1 { - map.insert( - "Authority".to_string(), - json!(account_keys[instruction.accounts[1] as usize].to_string()), - ); - } - Ok(ParsedInstructionEnum { - instruction_type: "InitializeBuffer".to_string(), - info: value, - }) - } - UpgradeableLoaderInstruction::Write { offset, bytes } => { - check_num_bpf_upgradeable_loader_accounts(&instruction.accounts, 2)?; - Ok(ParsedInstructionEnum { - instruction_type: "Write".to_string(), - info: json!({ - "Offset": offset, - "Bytes": base64::encode(bytes), - "Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Authority": account_keys[instruction.accounts[1] as usize].to_string(), - }), - }) - } - UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len } => { - check_num_bpf_upgradeable_loader_accounts(&instruction.accounts, 8)?; - Ok(ParsedInstructionEnum { - instruction_type: "DeployWithMaxDataLen".to_string(), - info: json!({ - "MaxDataLen": max_data_len, - "Payer Account": account_keys[instruction.accounts[0] as usize].to_string(), - "ProgramData Account": account_keys[instruction.accounts[1] as usize].to_string(), - "Program Account": account_keys[instruction.accounts[2] as usize].to_string(), - "Buffer Account": account_keys[instruction.accounts[3] as usize].to_string(), - "Rent Sysvar": account_keys[instruction.accounts[4] as usize].to_string(), - "Clock Sysvar": account_keys[instruction.accounts[5] as usize].to_string(), - "System Program": account_keys[instruction.accounts[6] as usize].to_string(), - "Authority": account_keys[instruction.accounts[7] as usize].to_string(), - }), - }) - } - UpgradeableLoaderInstruction::Upgrade => { - check_num_bpf_upgradeable_loader_accounts(&instruction.accounts, 7)?; - Ok(ParsedInstructionEnum { - instruction_type: "Upgrade".to_string(), - info: json!({ - "ProgramData Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Program Account": account_keys[instruction.accounts[1] as usize].to_string(), - "Buffer Account": account_keys[instruction.accounts[2] as usize].to_string(), - "Spill Account": account_keys[instruction.accounts[3] as usize].to_string(), - "Rent Sysvar": account_keys[instruction.accounts[4] as usize].to_string(), - "Clock Sysvar": account_keys[instruction.accounts[5] as usize].to_string(), - "Authority": account_keys[instruction.accounts[6] as usize].to_string(), - }), - }) - } - UpgradeableLoaderInstruction::SetAuthority => { - check_num_bpf_upgradeable_loader_accounts(&instruction.accounts, 2)?; - Ok(ParsedInstructionEnum { - instruction_type: "SetAuthority".to_string(), - info: json!({ - "Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Authority": account_keys[instruction.accounts[1] as usize].to_string(), - "New Authority": if instruction.accounts.len() > 2 { - Some(account_keys[instruction.accounts[2] as usize].to_string()) - } else { - None - }, - }), - }) - } - UpgradeableLoaderInstruction::Close => { - check_num_bpf_upgradeable_loader_accounts(&instruction.accounts, 3)?; - Ok(ParsedInstructionEnum { - instruction_type: "Close".to_string(), - info: json!({ - "Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Recipient": account_keys[instruction.accounts[1] as usize].to_string(), - "Authority": account_keys[instruction.accounts[2] as usize].to_string() - }), - }) - } - UpgradeableLoaderInstruction::ExtendProgram { additional_bytes } => { - check_num_bpf_upgradeable_loader_accounts(&instruction.accounts, 4)?; - Ok(ParsedInstructionEnum { - instruction_type: "ExtendProgram".to_string(), - info: json!({ - "AdditionalBytes": additional_bytes, - "ProgramData Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Program Account": account_keys[instruction.accounts[1] as usize].to_string(), - "System Program": account_keys[instruction.accounts[2] as usize].to_string(), - "Payer Account": account_keys[instruction.accounts[3] as usize].to_string(), - }), - }) - } - UpgradeableLoaderInstruction::SetAuthorityChecked => { - check_num_bpf_upgradeable_loader_accounts(&instruction.accounts, 3)?; - Ok(ParsedInstructionEnum { - instruction_type: "SetAuthorityChecked".to_string(), - info: json!({ - "Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Authority": account_keys[instruction.accounts[1] as usize].to_string(), - "New Authority": account_keys[instruction.accounts[2] as usize].to_string() - }), - }) - } - } -} - -fn check_num_bpf_upgradeable_loader_accounts( - accounts: &[u8], - num: usize, -) -> Result<(), ParseInstructionError> { - check_num_accounts(accounts, num, ParsableProgram::BPFLoaderUpgradeable) -} - -#[cfg(test)] -mod test { - use super::*; - use serde_json::Value; - use solana_sdk::{ - bpf_loader_upgradeable, message::Message, pubkey::Pubkey, system_program, sysvar, - }; - - #[test] - fn test_parse_bpf_upgradeable_loader_create_buffer_ix() { - let max_data_len = 54321; - - let payer_address = Pubkey::new_unique(); - let buffer_address = Pubkey::new_unique(); - let authority_address = Pubkey::new_unique(); - let instructions = bpf_loader_upgradeable::create_buffer( - &payer_address, - &buffer_address, - &authority_address, - 55, - max_data_len, - ) - .unwrap(); - let message = Message::new(&instructions, None); - assert_eq!( - parse_bpf_upgradeable_loader(&message.instructions[1], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "InitializeBuffer".to_string(), - info: json!({ - "Account": buffer_address.to_string(), - "Authority": authority_address.to_string(), - }), - } - ); - assert!(parse_bpf_upgradeable_loader( - &message.instructions[1], - &message.account_keys[0..2] - ) - .is_err()); - } - - #[test] - fn test_parse_bpf_upgradeable_loader_write_ix() { - let offset = 4242; - let bytes = vec![8; 99]; - - let buffer_address = Pubkey::new_unique(); - let authority_address = Pubkey::new_unique(); - let instruction = bpf_loader_upgradeable::write( - &buffer_address, - &authority_address, - offset, - bytes.clone(), - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_bpf_upgradeable_loader(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "Write".to_string(), - info: json!({ - "Offset": offset, - "Bytes": base64::encode(&bytes), - "Account": buffer_address.to_string(), - "Authority": authority_address.to_string(), - }), - } - ); - assert!(parse_bpf_upgradeable_loader( - &message.instructions[0], - &message.account_keys[0..1] - ) - .is_err()); - } - - #[test] - fn test_parse_bpf_upgradeable_loader_deploy_ix() { - let max_data_len = 54321; - - let payer_address = Pubkey::new_unique(); - let program_address = Pubkey::new_unique(); - let buffer_address = Pubkey::new_unique(); - let upgrade_authority_address = Pubkey::new_unique(); - let programdata_address = Pubkey::find_program_address( - &[program_address.as_ref()], - &bpf_loader_upgradeable::id(), - ) - .0; - let instructions = bpf_loader_upgradeable::deploy_with_max_program_len( - &payer_address, - &program_address, - &buffer_address, - &upgrade_authority_address, - 55, - max_data_len, - ) - .unwrap(); - let message = Message::new(&instructions, None); - assert_eq!( - parse_bpf_upgradeable_loader(&message.instructions[1], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "DeployWithMaxDataLen".to_string(), - info: json!({ - "MaxDataLen": max_data_len, - "Payer Account": payer_address.to_string(), - "Program Account": program_address.to_string(), - "Authority": upgrade_authority_address.to_string(), - "ProgramData Account": programdata_address.to_string(), - "Buffer Account": buffer_address.to_string(), - "Rent Sysvar": sysvar::rent::ID.to_string(), - "Clock Sysvar": sysvar::clock::ID.to_string(), - "System Program": system_program::ID.to_string(), - }), - } - ); - assert!(parse_bpf_upgradeable_loader( - &message.instructions[1], - &message.account_keys[0..7] - ) - .is_err()); - } - - #[test] - fn test_parse_bpf_upgradeable_loader_upgrade_ix() { - let program_address = Pubkey::new_unique(); - let buffer_address = Pubkey::new_unique(); - let authority_address = Pubkey::new_unique(); - let spill_address = Pubkey::new_unique(); - let programdata_address = Pubkey::find_program_address( - &[program_address.as_ref()], - &bpf_loader_upgradeable::id(), - ) - .0; - let instruction = bpf_loader_upgradeable::upgrade( - &program_address, - &buffer_address, - &authority_address, - &spill_address, - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_bpf_upgradeable_loader(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "Upgrade".to_string(), - info: json!({ - "Authority": authority_address.to_string(), - "ProgramData Account": programdata_address.to_string(), - "Program Account": program_address.to_string(), - "Buffer Account": buffer_address.to_string(), - "Spill Account": spill_address.to_string(), - "Rent Sysvar": sysvar::rent::ID.to_string(), - "Clock Sysvar": sysvar::clock::ID.to_string(), - }), - } - ); - assert!(parse_bpf_upgradeable_loader( - &message.instructions[0], - &message.account_keys[0..6] - ) - .is_err()); - } - - #[test] - fn test_parse_bpf_upgradeable_loader_set_buffer_authority_ix() { - let buffer_address = Pubkey::new_unique(); - let current_authority_address = Pubkey::new_unique(); - let new_authority_address = Pubkey::new_unique(); - let instruction = bpf_loader_upgradeable::set_buffer_authority( - &buffer_address, - ¤t_authority_address, - &new_authority_address, - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_bpf_upgradeable_loader(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "SetAuthority".to_string(), - info: json!({ - "Account": buffer_address.to_string(), - "Authority": current_authority_address.to_string(), - "New Authority": new_authority_address.to_string(), - }), - } - ); - assert!(parse_bpf_upgradeable_loader( - &message.instructions[0], - &message.account_keys[0..1] - ) - .is_err()); - } - - #[test] - fn test_parse_bpf_upgradeable_loader_set_upgrade_authority_ix() { - let program_address = Pubkey::new_unique(); - let current_authority_address = Pubkey::new_unique(); - let new_authority_address = Pubkey::new_unique(); - let (programdata_address, _) = Pubkey::find_program_address( - &[program_address.as_ref()], - &bpf_loader_upgradeable::id(), - ); - let instruction = bpf_loader_upgradeable::set_upgrade_authority( - &program_address, - ¤t_authority_address, - Some(&new_authority_address), - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_bpf_upgradeable_loader(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "SetAuthority".to_string(), - info: json!({ - "Account": programdata_address.to_string(), - "Authority": current_authority_address.to_string(), - "New Authority": new_authority_address.to_string(), - }), - } - ); - assert!(parse_bpf_upgradeable_loader( - &message.instructions[0], - &message.account_keys[0..1] - ) - .is_err()); - - let instruction = bpf_loader_upgradeable::set_upgrade_authority( - &program_address, - ¤t_authority_address, - None, - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_bpf_upgradeable_loader(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "SetAuthority".to_string(), - info: json!({ - "Account": programdata_address.to_string(), - "Authority": current_authority_address.to_string(), - "New Authority": Value::Null, - }), - } - ); - assert!(parse_bpf_upgradeable_loader( - &message.instructions[0], - &message.account_keys[0..1] - ) - .is_err()); - } - - #[test] - fn test_parse_bpf_upgradeable_loader_close_ix() { - let close_address = Pubkey::new_unique(); - let recipient_address = Pubkey::new_unique(); - let authority_address = Pubkey::new_unique(); - let instruction = - bpf_loader_upgradeable::close(&close_address, &recipient_address, &authority_address); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_bpf_upgradeable_loader(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "Close".to_string(), - info: json!({ - "Account": close_address.to_string(), - "Recipient": recipient_address.to_string(), - "Authority": authority_address.to_string(), - }), - } - ); - assert!(parse_bpf_upgradeable_loader( - &message.instructions[0], - &message.account_keys[0..1] - ) - .is_err()); - } -} diff --git a/crates/explorer/src/parse/memo.rs b/crates/explorer/src/parse/memo.rs deleted file mode 100644 index 61fc368a..00000000 --- a/crates/explorer/src/parse/memo.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::parse::{ParsableProgram, ParseInstructionError}; -use serde_json::Value; -use solana_sdk::instruction::CompiledInstruction; -use std::str::{from_utf8, Utf8Error}; - -pub fn parse_memo(instruction: &CompiledInstruction) -> Result { - parse_memo_data(&instruction.data) - .map(Value::String) - .map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::SPLMemo)) -} - -pub fn parse_memo_data(data: &[u8]) -> Result { - from_utf8(data).map(|s| s.to_string()) -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_parse_memo() { - let good_memo = "good memo".to_string(); - assert_eq!( - parse_memo(&CompiledInstruction { - program_id_index: 0, - accounts: vec![], - data: good_memo.as_bytes().to_vec(), - }) - .unwrap(), - Value::String(good_memo), - ); - - let bad_memo = vec![128u8]; - assert!(std::str::from_utf8(&bad_memo).is_err()); - assert!(parse_memo(&CompiledInstruction { - program_id_index: 0, - data: bad_memo, - accounts: vec![], - }) - .is_err(),); - } -} diff --git a/crates/explorer/src/parse/stake.rs b/crates/explorer/src/parse/stake.rs deleted file mode 100644 index 640c57c8..00000000 --- a/crates/explorer/src/parse/stake.rs +++ /dev/null @@ -1,934 +0,0 @@ -use crate::parse::{ - check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum, -}; -use serde_json::{json, Map}; -use solana_sdk::{ - instruction::CompiledInstruction, pubkey::Pubkey, stake::instruction::StakeInstruction, -}; - -pub fn parse_stake( - instruction: &CompiledInstruction, - account_keys: &[Pubkey], -) -> Result { - let stake_instruction: StakeInstruction = bincode::deserialize(&instruction.data) - .map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::Stake))?; - match instruction.accounts.iter().max() { - Some(index) if (*index as usize) < account_keys.len() => {} - _ => { - // Runtime should prevent this from ever happening - return Err(ParseInstructionError::InstructionKeyMismatch( - ParsableProgram::Stake, - )); - } - } - match stake_instruction { - StakeInstruction::Initialize(authorized, lockup) => { - check_num_stake_accounts(&instruction.accounts, 2)?; - let authorized = json!({ - "Staker": authorized.staker.to_string(), - "Withdrawer": authorized.withdrawer.to_string(), - }); - let lockup = json!({ - "Unix Timestamp": lockup.unix_timestamp, - "Epoch": lockup.epoch, - "Custodian": lockup.custodian.to_string(), - }); - Ok(ParsedInstructionEnum { - instruction_type: "Initialize".to_string(), - info: json!({ - "Stake Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Rent Sysvar": account_keys[instruction.accounts[1] as usize].to_string(), - "Authorized": authorized, - "Lockup": lockup, - }), - }) - } - StakeInstruction::Authorize(new_authorized, authority_type) => { - check_num_stake_accounts(&instruction.accounts, 3)?; - let mut value = json!({ - "Stake Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Clock Sysvar": account_keys[instruction.accounts[1] as usize].to_string(), - "Authority": account_keys[instruction.accounts[2] as usize].to_string(), - "New Authority": new_authorized.to_string(), - "Authority Type": authority_type, - }); - let map = value.as_object_mut().unwrap(); - if instruction.accounts.len() >= 4 { - map.insert( - "Custodian".to_string(), - json!(account_keys[instruction.accounts[3] as usize].to_string()), - ); - } - Ok(ParsedInstructionEnum { - instruction_type: "Authorize".to_string(), - info: value, - }) - } - StakeInstruction::DelegateStake => { - check_num_stake_accounts(&instruction.accounts, 6)?; - Ok(ParsedInstructionEnum { - instruction_type: "Delegate".to_string(), - info: json!({ - "Stake Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Vote Account": account_keys[instruction.accounts[1] as usize].to_string(), - "Clock Sysvar": account_keys[instruction.accounts[2] as usize].to_string(), - "Stake History Sysvar": account_keys[instruction.accounts[3] as usize].to_string(), - "Stake Config Account": account_keys[instruction.accounts[4] as usize].to_string(), - "Stake Authority": account_keys[instruction.accounts[5] as usize].to_string(), - }), - }) - } - StakeInstruction::Split(lamports) => { - check_num_stake_accounts(&instruction.accounts, 3)?; - Ok(ParsedInstructionEnum { - instruction_type: "Split".to_string(), - info: json!({ - "Stake Account": account_keys[instruction.accounts[0] as usize].to_string(), - "New Split Account": account_keys[instruction.accounts[1] as usize].to_string(), - "Stake Authority": account_keys[instruction.accounts[2] as usize].to_string(), - "Lamports": lamports, - }), - }) - } - StakeInstruction::Withdraw(lamports) => { - check_num_stake_accounts(&instruction.accounts, 5)?; - let mut value = json!({ - "Stake Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Destination": account_keys[instruction.accounts[1] as usize].to_string(), - "Clock Sysvar": account_keys[instruction.accounts[2] as usize].to_string(), - "Stake History Sysvar": account_keys[instruction.accounts[3] as usize].to_string(), - "Withdraw Authority": account_keys[instruction.accounts[4] as usize].to_string(), - "Lamports": lamports, - }); - let map = value.as_object_mut().unwrap(); - if instruction.accounts.len() >= 6 { - map.insert( - "Custodian".to_string(), - json!(account_keys[instruction.accounts[5] as usize].to_string()), - ); - } - Ok(ParsedInstructionEnum { - instruction_type: "Withdraw".to_string(), - info: value, - }) - } - StakeInstruction::Deactivate => { - check_num_stake_accounts(&instruction.accounts, 3)?; - Ok(ParsedInstructionEnum { - instruction_type: "Deactivate".to_string(), - info: json!({ - "Stake Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Clock Sysvar": account_keys[instruction.accounts[1] as usize].to_string(), - "Stake Authority": account_keys[instruction.accounts[2] as usize].to_string(), - }), - }) - } - StakeInstruction::SetLockup(lockup_args) => { - check_num_stake_accounts(&instruction.accounts, 2)?; - let mut lockup_map = Map::new(); - if let Some(timestamp) = lockup_args.unix_timestamp { - lockup_map.insert("Unix Timestamp".to_string(), json!(timestamp)); - } - if let Some(epoch) = lockup_args.epoch { - lockup_map.insert("Epoch".to_string(), json!(epoch)); - } - if let Some(custodian) = lockup_args.custodian { - lockup_map.insert("Custodian".to_string(), json!(custodian.to_string())); - } - Ok(ParsedInstructionEnum { - instruction_type: "SetLockup".to_string(), - info: json!({ - "Stake Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Custodian": account_keys[instruction.accounts[1] as usize].to_string(), - "Lockup": lockup_map, - }), - }) - } - StakeInstruction::Merge => { - check_num_stake_accounts(&instruction.accounts, 5)?; - Ok(ParsedInstructionEnum { - instruction_type: "Merge".to_string(), - info: json!({ - "Destination": account_keys[instruction.accounts[0] as usize].to_string(), - "Source": account_keys[instruction.accounts[1] as usize].to_string(), - "Clock Sysvar": account_keys[instruction.accounts[2] as usize].to_string(), - "Stake History Sysvar": account_keys[instruction.accounts[3] as usize].to_string(), - "Stake Authority": account_keys[instruction.accounts[4] as usize].to_string(), - }), - }) - } - StakeInstruction::AuthorizeWithSeed(args) => { - check_num_stake_accounts(&instruction.accounts, 2)?; - let mut value = json!({ - "Stake Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Authority Base": account_keys[instruction.accounts[1] as usize].to_string(), - "New Authorized": args.new_authorized_pubkey.to_string(), - "Authority Type": args.stake_authorize, - "Authority Seed": args.authority_seed, - "Authority Owner": args.authority_owner.to_string(), - }); - let map = value.as_object_mut().unwrap(); - if instruction.accounts.len() >= 3 { - map.insert( - "Clock Sysvar".to_string(), - json!(account_keys[instruction.accounts[2] as usize].to_string()), - ); - } - if instruction.accounts.len() >= 4 { - map.insert( - "Custodian".to_string(), - json!(account_keys[instruction.accounts[3] as usize].to_string()), - ); - } - Ok(ParsedInstructionEnum { - instruction_type: "AuthorizeWithSeed".to_string(), - info: value, - }) - } - StakeInstruction::InitializeChecked => { - check_num_stake_accounts(&instruction.accounts, 4)?; - Ok(ParsedInstructionEnum { - instruction_type: "InitializeChecked".to_string(), - info: json!({ - "Stake Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Rent Sysvar": account_keys[instruction.accounts[1] as usize].to_string(), - "Staker": account_keys[instruction.accounts[2] as usize].to_string(), - "Withdrawer": account_keys[instruction.accounts[3] as usize].to_string(), - }), - }) - } - StakeInstruction::AuthorizeChecked(authority_type) => { - check_num_stake_accounts(&instruction.accounts, 4)?; - let mut value = json!({ - "Stake Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Clock Sysvar": account_keys[instruction.accounts[1] as usize].to_string(), - "Authority": account_keys[instruction.accounts[2] as usize].to_string(), - "New Authority": account_keys[instruction.accounts[3] as usize].to_string(), - "Authority Type": authority_type, - }); - let map = value.as_object_mut().unwrap(); - if instruction.accounts.len() >= 5 { - map.insert( - "Custodian".to_string(), - json!(account_keys[instruction.accounts[4] as usize].to_string()), - ); - } - Ok(ParsedInstructionEnum { - instruction_type: "AuthorizeChecked".to_string(), - info: value, - }) - } - StakeInstruction::AuthorizeCheckedWithSeed(args) => { - check_num_stake_accounts(&instruction.accounts, 4)?; - let mut value = json!({ - "Stake Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Authority Base": account_keys[instruction.accounts[1] as usize].to_string(), - "Clock Sysvar": account_keys[instruction.accounts[2] as usize].to_string(), - "New Authorized": account_keys[instruction.accounts[3] as usize].to_string(), - "Authority Type": args.stake_authorize, - "Authority Seed": args.authority_seed, - "Authority Owner": args.authority_owner.to_string(), - }); - let map = value.as_object_mut().unwrap(); - if instruction.accounts.len() >= 5 { - map.insert( - "Custodian".to_string(), - json!(account_keys[instruction.accounts[4] as usize].to_string()), - ); - } - Ok(ParsedInstructionEnum { - instruction_type: "AuthorizeCheckedWithSeed".to_string(), - info: value, - }) - } - StakeInstruction::SetLockupChecked(lockup_args) => { - check_num_stake_accounts(&instruction.accounts, 2)?; - let mut lockup_map = Map::new(); - if let Some(timestamp) = lockup_args.unix_timestamp { - lockup_map.insert("Unix Timestamp".to_string(), json!(timestamp)); - } - if let Some(epoch) = lockup_args.epoch { - lockup_map.insert("Epoch".to_string(), json!(epoch)); - } - if instruction.accounts.len() >= 3 { - lockup_map.insert( - "Custodian".to_string(), - json!(account_keys[instruction.accounts[2] as usize].to_string()), - ); - } - Ok(ParsedInstructionEnum { - instruction_type: "SetLockupChecked".to_string(), - info: json!({ - "Stake Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Custodian": account_keys[instruction.accounts[1] as usize].to_string(), - "Lockup": lockup_map, - }), - }) - } - StakeInstruction::GetMinimumDelegation => Ok(ParsedInstructionEnum { - instruction_type: "GetMinimumDelegation".to_string(), - info: json!({}), - }), - StakeInstruction::DeactivateDelinquent => { - check_num_stake_accounts(&instruction.accounts, 3)?; - Ok(ParsedInstructionEnum { - instruction_type: "DeactivateDelinquent".to_string(), - info: json!({ - "Stake Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Delinquent Vote Account": account_keys[instruction.accounts[1] as usize].to_string(), - "Reference Vote Account": account_keys[instruction.accounts[2] as usize].to_string(), - }), - }) - } - StakeInstruction::Redelegate => { - check_num_stake_accounts(&instruction.accounts, 5)?; - Ok(ParsedInstructionEnum { - instruction_type: "Redelegate".to_string(), - info: json!({ - "Stake Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Uninitialized Stake Account": account_keys[instruction.accounts[1] as usize].to_string(), - "Vote Account": account_keys[instruction.accounts[2] as usize].to_string(), - "Config Address": account_keys[instruction.accounts[3] as usize].to_string(), - "Stake Authority": account_keys[instruction.accounts[4] as usize].to_string(), - }), - }) - } - } -} - -fn check_num_stake_accounts(accounts: &[u8], num: usize) -> Result<(), ParseInstructionError> { - check_num_accounts(accounts, num, ParsableProgram::Stake) -} - -#[cfg(test)] -mod test { - use { - super::*, - solana_sdk::{ - message::Message, - pubkey::Pubkey, - stake::{ - config, - instruction::{self, LockupArgs}, - state::{Authorized, Lockup, StakeAuthorize}, - }, - sysvar, - }, - }; - - #[test] - fn test_parse_stake_initialize_ix() { - let from_pubkey = Pubkey::new_unique(); - let stake_pubkey = Pubkey::new_unique(); - let authorized = Authorized { - staker: Pubkey::new_unique(), - withdrawer: Pubkey::new_unique(), - }; - let lockup = Lockup { - unix_timestamp: 1_234_567_890, - epoch: 11, - custodian: Pubkey::new_unique(), - }; - let lamports = 55; - - let instructions = instruction::create_account( - &from_pubkey, - &stake_pubkey, - &authorized, - &lockup, - lamports, - ); - let message = Message::new(&instructions, None); - assert_eq!( - parse_stake(&message.instructions[1], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "Initialize".to_string(), - info: json!({ - "Stake Account": stake_pubkey.to_string(), - "Rent Sysvar": sysvar::rent::ID.to_string(), - "Authorized": { - "Staker": authorized.staker.to_string(), - "Withdrawer": authorized.withdrawer.to_string(), - }, - "Lockup": { - "Unix Timestamp": lockup.unix_timestamp, - "Epoch": lockup.epoch, - "Custodian": lockup.custodian.to_string(), - } - }), - } - ); - assert!(parse_stake(&message.instructions[1], &message.account_keys[0..2]).is_err()); - } - - #[test] - fn test_parse_stake_authorize_ix() { - let stake_pubkey = Pubkey::new_unique(); - let authorized_pubkey = Pubkey::new_unique(); - let new_authorized_pubkey = Pubkey::new_unique(); - let custodian_pubkey = Pubkey::new_unique(); - let instruction = instruction::authorize( - &stake_pubkey, - &authorized_pubkey, - &new_authorized_pubkey, - StakeAuthorize::Staker, - None, - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_stake(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "Authorize".to_string(), - info: json!({ - "Stake Account": stake_pubkey.to_string(), - "Clock Sysvar": sysvar::clock::ID.to_string(), - "Authority": authorized_pubkey.to_string(), - "New Authority": new_authorized_pubkey.to_string(), - "Authority Type": StakeAuthorize::Staker, - }), - } - ); - assert!(parse_stake(&message.instructions[0], &message.account_keys[0..2]).is_err()); - - let instruction = instruction::authorize( - &stake_pubkey, - &authorized_pubkey, - &new_authorized_pubkey, - StakeAuthorize::Withdrawer, - Some(&custodian_pubkey), - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_stake(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "Authorize".to_string(), - info: json!({ - "Stake Account": stake_pubkey.to_string(), - "Clock Sysvar": sysvar::clock::ID.to_string(), - "Authority": authorized_pubkey.to_string(), - "New Authority": new_authorized_pubkey.to_string(), - "Authority Type": StakeAuthorize::Withdrawer, - "Custodian": custodian_pubkey.to_string(), - }), - } - ); - assert!(parse_stake(&message.instructions[0], &message.account_keys[0..2]).is_err()); - } - - #[test] - fn test_parse_stake_delegate_ix() { - let stake_pubkey = Pubkey::new_unique(); - let authorized_pubkey = Pubkey::new_unique(); - let vote_pubkey = Pubkey::new_unique(); - let instruction = - instruction::delegate_stake(&stake_pubkey, &authorized_pubkey, &vote_pubkey); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_stake(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "Delegate".to_string(), - info: json!({ - "Stake Account": stake_pubkey.to_string(), - "Vote Account": vote_pubkey.to_string(), - "Clock Sysvar": sysvar::clock::ID.to_string(), - "Stake History Sysvar": sysvar::stake_history::ID.to_string(), - "Stake Config Account": config::ID.to_string(), - "Stake Authority": authorized_pubkey.to_string(), - }), - } - ); - assert!(parse_stake(&message.instructions[0], &message.account_keys[0..5]).is_err()); - } - - #[test] - fn test_parse_stake_split_ix() { - let lamports = 55; - let stake_pubkey = Pubkey::new_unique(); - let authorized_pubkey = Pubkey::new_unique(); - let split_stake_pubkey = Pubkey::new_unique(); - let instructions = instruction::split( - &stake_pubkey, - &authorized_pubkey, - lamports, - &split_stake_pubkey, - ); - let message = Message::new(&instructions, None); - assert_eq!( - parse_stake(&message.instructions[2], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "Split".to_string(), - info: json!({ - "Stake Account": stake_pubkey.to_string(), - "New Split Account": split_stake_pubkey.to_string(), - "Stake Authority": authorized_pubkey.to_string(), - "Lamports": lamports, - }), - } - ); - assert!(parse_stake(&message.instructions[2], &message.account_keys[0..2]).is_err()); - } - - #[test] - fn test_parse_stake_withdraw_ix() { - let lamports = 55; - let stake_pubkey = Pubkey::new_unique(); - let withdrawer_pubkey = Pubkey::new_unique(); - let to_pubkey = Pubkey::new_unique(); - let custodian_pubkey = Pubkey::new_unique(); - let instruction = instruction::withdraw( - &stake_pubkey, - &withdrawer_pubkey, - &to_pubkey, - lamports, - None, - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_stake(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "Withdraw".to_string(), - info: json!({ - "Stake Account": stake_pubkey.to_string(), - "Destination": to_pubkey.to_string(), - "Clock Sysvar": sysvar::clock::ID.to_string(), - "Stake History Sysvar": sysvar::stake_history::ID.to_string(), - "Withdraw Authority": withdrawer_pubkey.to_string(), - "Lamports": lamports, - }), - } - ); - let instruction = instruction::withdraw( - &stake_pubkey, - &withdrawer_pubkey, - &to_pubkey, - lamports, - Some(&custodian_pubkey), - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_stake(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "Withdraw".to_string(), - info: json!({ - "Stake Account": stake_pubkey.to_string(), - "Destination": to_pubkey.to_string(), - "Clock Sysvar": sysvar::clock::ID.to_string(), - "Stake History Sysvar": sysvar::stake_history::ID.to_string(), - "Withdraw Authority": withdrawer_pubkey.to_string(), - "Custodian": custodian_pubkey.to_string(), - "Lamports": lamports, - }), - } - ); - assert!(parse_stake(&message.instructions[0], &message.account_keys[0..4]).is_err()); - } - - #[test] - fn test_parse_stake_deactivate_stake_ix() { - let stake_pubkey = Pubkey::new_unique(); - let authorized_pubkey = Pubkey::new_unique(); - let instruction = instruction::deactivate_stake(&stake_pubkey, &authorized_pubkey); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_stake(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "Deactivate".to_string(), - info: json!({ - "Stake Account": stake_pubkey.to_string(), - "Clock Sysvar": sysvar::clock::ID.to_string(), - "Stake Authority": authorized_pubkey.to_string(), - }), - } - ); - assert!(parse_stake(&message.instructions[0], &message.account_keys[0..2]).is_err()); - } - - #[test] - fn test_parse_stake_merge_ix() { - let destination_stake_pubkey = Pubkey::new_unique(); - let source_stake_pubkey = Pubkey::new_unique(); - let authorized_pubkey = Pubkey::new_unique(); - let instructions = instruction::merge( - &destination_stake_pubkey, - &source_stake_pubkey, - &authorized_pubkey, - ); - let message = Message::new(&instructions, None); - assert_eq!( - parse_stake(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "Merge".to_string(), - info: json!({ - "Destination": destination_stake_pubkey.to_string(), - "Source": source_stake_pubkey.to_string(), - "Clock Sysvar": sysvar::clock::ID.to_string(), - "Stake History Sysvar": sysvar::stake_history::ID.to_string(), - "Stake Authority": authorized_pubkey.to_string(), - }), - } - ); - assert!(parse_stake(&message.instructions[0], &message.account_keys[0..4]).is_err()); - } - - #[test] - fn test_parse_stake_authorize_with_seed_ix() { - let stake_pubkey = Pubkey::new_unique(); - let authority_base_pubkey = Pubkey::new_unique(); - let authority_owner_pubkey = Pubkey::new_unique(); - let new_authorized_pubkey = Pubkey::new_unique(); - let custodian_pubkey = Pubkey::new_unique(); - - let seed = "test_seed"; - let instruction = instruction::authorize_with_seed( - &stake_pubkey, - &authority_base_pubkey, - seed.to_string(), - &authority_owner_pubkey, - &new_authorized_pubkey, - StakeAuthorize::Staker, - None, - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_stake(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "AuthorizeWithSeed".to_string(), - info: json!({ - "Stake Account": stake_pubkey.to_string(), - "Authority Owner": authority_owner_pubkey.to_string(), - "New Authorized": new_authorized_pubkey.to_string(), - "Authority Base": authority_base_pubkey.to_string(), - "Authority Seed": seed, - "Authority Type": StakeAuthorize::Staker, - "Clock Sysvar": sysvar::clock::ID.to_string(), - }), - } - ); - assert!(parse_stake(&message.instructions[0], &message.account_keys[0..2]).is_err()); - - let instruction = instruction::authorize_with_seed( - &stake_pubkey, - &authority_base_pubkey, - seed.to_string(), - &authority_owner_pubkey, - &new_authorized_pubkey, - StakeAuthorize::Withdrawer, - Some(&custodian_pubkey), - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_stake(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "AuthorizeWithSeed".to_string(), - info: json!({ - "Stake Account": stake_pubkey.to_string(), - "Authority Owner": authority_owner_pubkey.to_string(), - "New Authorized": new_authorized_pubkey.to_string(), - "Authority Base": authority_base_pubkey.to_string(), - "Authority Seed": seed, - "Authority Type": StakeAuthorize::Withdrawer, - "Clock Sysvar": sysvar::clock::ID.to_string(), - "Custodian": custodian_pubkey.to_string(), - }), - } - ); - assert!(parse_stake(&message.instructions[0], &message.account_keys[0..3]).is_err()); - } - - #[test] - #[allow(clippy::same_item_push)] - fn test_parse_stake_set_lockup() { - let mut keys: Vec = vec![]; - for _ in 0..3 { - keys.push(Pubkey::new_unique()); - } - let unix_timestamp = 1_234_567_890; - let epoch = 11; - let custodian = Pubkey::new_unique(); - - let lockup = LockupArgs { - unix_timestamp: Some(unix_timestamp), - epoch: None, - custodian: None, - }; - let instruction = instruction::set_lockup(&keys[1], &lockup, &keys[0]); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_stake(&message.instructions[0], &keys[0..2]).unwrap(), - ParsedInstructionEnum { - instruction_type: "SetLockup".to_string(), - info: json!({ - "Stake Account": keys[1].to_string(), - "Custodian": keys[0].to_string(), - "Lockup": { - "Unix Timestamp": unix_timestamp - } - }), - } - ); - - let lockup = LockupArgs { - unix_timestamp: Some(unix_timestamp), - epoch: Some(epoch), - custodian: None, - }; - let instruction = instruction::set_lockup(&keys[1], &lockup, &keys[0]); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_stake(&message.instructions[0], &keys[0..2]).unwrap(), - ParsedInstructionEnum { - instruction_type: "SetLockup".to_string(), - info: json!({ - "Stake Account": keys[1].to_string(), - "Custodian": keys[0].to_string(), - "Lockup": { - "Unix Timestamp": unix_timestamp, - "Epoch": epoch, - } - }), - } - ); - - let lockup = LockupArgs { - unix_timestamp: Some(unix_timestamp), - epoch: Some(epoch), - custodian: Some(custodian), - }; - let instruction = instruction::set_lockup(&keys[1], &lockup, &keys[0]); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_stake(&message.instructions[0], &keys[0..2]).unwrap(), - ParsedInstructionEnum { - instruction_type: "SetLockup".to_string(), - info: json!({ - "Stake Account": keys[1].to_string(), - "Custodian": keys[0].to_string(), - "Lockup": { - "Unix Timestamp": unix_timestamp, - "Epoch": epoch, - "Custodian": custodian.to_string(), - } - }), - } - ); - - assert!(parse_stake(&message.instructions[0], &keys[0..1]).is_err()); - - let lockup = LockupArgs { - unix_timestamp: Some(unix_timestamp), - epoch: None, - custodian: None, - }; - let instruction = instruction::set_lockup_checked(&keys[1], &lockup, &keys[0]); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_stake(&message.instructions[0], &keys[0..2]).unwrap(), - ParsedInstructionEnum { - instruction_type: "SetLockupChecked".to_string(), - info: json!({ - "Stake Account": keys[1].to_string(), - "Custodian": keys[0].to_string(), - "Lockup": { - "Unix Timestamp": unix_timestamp - } - }), - } - ); - - let lockup = LockupArgs { - unix_timestamp: Some(unix_timestamp), - epoch: Some(epoch), - custodian: None, - }; - let instruction = instruction::set_lockup_checked(&keys[1], &lockup, &keys[0]); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_stake(&message.instructions[0], &keys[0..2]).unwrap(), - ParsedInstructionEnum { - instruction_type: "SetLockupChecked".to_string(), - info: json!({ - "Stake Account": keys[1].to_string(), - "Custodian": keys[0].to_string(), - "Lockup": { - "Unix Timestamp": unix_timestamp, - "Epoch": epoch, - } - }), - } - ); - assert!(parse_stake(&message.instructions[0], &keys[0..1]).is_err()); - - let lockup = LockupArgs { - unix_timestamp: Some(unix_timestamp), - epoch: Some(epoch), - custodian: Some(keys[1]), - }; - let instruction = instruction::set_lockup_checked(&keys[2], &lockup, &keys[0]); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_stake(&message.instructions[0], &keys[0..3]).unwrap(), - ParsedInstructionEnum { - instruction_type: "SetLockupChecked".to_string(), - info: json!({ - "Stake Account": keys[2].to_string(), - "Custodian": keys[0].to_string(), - "Lockup": { - "Unix Timestamp": unix_timestamp, - "Epoch": epoch, - "Custodian": keys[1].to_string(), - } - }), - } - ); - assert!(parse_stake(&message.instructions[0], &keys[0..2]).is_err()); - } - - #[test] - fn test_parse_stake_create_account_checked_ix() { - let from_pubkey = Pubkey::new_unique(); - let stake_pubkey = Pubkey::new_unique(); - - let authorized = Authorized { - staker: Pubkey::new_unique(), - withdrawer: Pubkey::new_unique(), - }; - let lamports = 55; - - let instructions = - instruction::create_account_checked(&from_pubkey, &stake_pubkey, &authorized, lamports); - let message = Message::new(&instructions, None); - assert_eq!( - parse_stake(&message.instructions[1], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "InitializeChecked".to_string(), - info: json!({ - "Stake Account": stake_pubkey.to_string(), - "Rent Sysvar": sysvar::rent::ID.to_string(), - "Staker": authorized.staker.to_string(), - "Withdrawer": authorized.withdrawer.to_string(), - }), - } - ); - assert!(parse_stake(&message.instructions[1], &message.account_keys[0..3]).is_err()); - } - - #[test] - fn test_parse_stake_authorize_checked_ix() { - let stake_pubkey = Pubkey::new_unique(); - let authorized_pubkey = Pubkey::new_unique(); - let new_authorized_pubkey = Pubkey::new_unique(); - let custodian_pubkey = Pubkey::new_unique(); - - let instruction = instruction::authorize_checked( - &stake_pubkey, - &authorized_pubkey, - &new_authorized_pubkey, - StakeAuthorize::Staker, - None, - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_stake(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "AuthorizeChecked".to_string(), - info: json!({ - "Stake Account": stake_pubkey.to_string(), - "Clock Sysvar": sysvar::clock::ID.to_string(), - "Authority": authorized_pubkey.to_string(), - "New Authority": new_authorized_pubkey.to_string(), - "Authority Type": StakeAuthorize::Staker, - }), - } - ); - assert!(parse_stake(&message.instructions[0], &message.account_keys[0..3]).is_err()); - - let instruction = instruction::authorize_checked( - &stake_pubkey, - &authorized_pubkey, - &new_authorized_pubkey, - StakeAuthorize::Withdrawer, - Some(&custodian_pubkey), - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_stake(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "AuthorizeChecked".to_string(), - info: json!({ - "Stake Account": stake_pubkey.to_string(), - "Clock Sysvar": sysvar::clock::ID.to_string(), - "Authority": authorized_pubkey.to_string(), - "New Authority": new_authorized_pubkey.to_string(), - "Authority Type": StakeAuthorize::Withdrawer, - "Custodian": custodian_pubkey.to_string(), - }), - } - ); - assert!(parse_stake(&message.instructions[0], &message.account_keys[0..4]).is_err()); - } - - #[test] - fn test_parse_stake_authorize_checked_with_seed_ix() { - let stake_pubkey = Pubkey::new_unique(); - let authority_base_pubkey = Pubkey::new_unique(); - let authority_owner_pubkey = Pubkey::new_unique(); - let new_authorized_pubkey = Pubkey::new_unique(); - let custodian_pubkey = Pubkey::new_unique(); - - let seed = "test_seed"; - let instruction = instruction::authorize_checked_with_seed( - &stake_pubkey, - &authority_base_pubkey, - seed.to_string(), - &authority_owner_pubkey, - &new_authorized_pubkey, - StakeAuthorize::Staker, - None, - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_stake(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "AuthorizeCheckedWithSeed".to_string(), - info: json!({ - "Stake Account": stake_pubkey.to_string(), - "Authority Owner": authority_owner_pubkey.to_string(), - "New Authorized": new_authorized_pubkey.to_string(), - "Authority Base": authority_base_pubkey.to_string(), - "Authority Seed": seed, - "Authority Type": StakeAuthorize::Staker, - "Clock Sysvar": sysvar::clock::ID.to_string(), - }), - } - ); - assert!(parse_stake(&message.instructions[0], &message.account_keys[0..3]).is_err()); - - let instruction = instruction::authorize_checked_with_seed( - &stake_pubkey, - &authority_base_pubkey, - seed.to_string(), - &authority_owner_pubkey, - &new_authorized_pubkey, - StakeAuthorize::Withdrawer, - Some(&custodian_pubkey), - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_stake(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "AuthorizeCheckedWithSeed".to_string(), - info: json!({ - "Stake Account": stake_pubkey.to_string(), - "Authority Owner": authority_owner_pubkey.to_string(), - "New Authorized": new_authorized_pubkey.to_string(), - "Authority Base": authority_base_pubkey.to_string(), - "Authority Seed": seed, - "Authority Type": StakeAuthorize::Withdrawer, - "Clock Sysvar": sysvar::clock::ID.to_string(), - "Custodian": custodian_pubkey.to_string(), - }), - } - ); - assert!(parse_stake(&message.instructions[0], &message.account_keys[0..4]).is_err()); - } -} diff --git a/crates/explorer/src/parse/system.rs b/crates/explorer/src/parse/system.rs deleted file mode 100644 index 76a4017e..00000000 --- a/crates/explorer/src/parse/system.rs +++ /dev/null @@ -1,540 +0,0 @@ -use crate::parse::{ - check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum, -}; -use serde_json::json; -use solana_sdk::{ - instruction::CompiledInstruction, pubkey::Pubkey, system_instruction::SystemInstruction, -}; - -pub fn parse_system( - instruction: &CompiledInstruction, - account_keys: &[Pubkey], -) -> Result { - let system_instruction: SystemInstruction = bincode::deserialize(&instruction.data) - .map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::System))?; - match instruction.accounts.iter().max() { - Some(index) if (*index as usize) < account_keys.len() => {} - _ => { - // Runtime should prevent this from ever happening - return Err(ParseInstructionError::InstructionKeyMismatch( - ParsableProgram::System, - )); - } - } - match system_instruction { - SystemInstruction::CreateAccount { - lamports, - space, - owner, - } => { - check_num_system_accounts(&instruction.accounts, 2)?; - Ok(ParsedInstructionEnum { - instruction_type: "CreateAccount".to_string(), - info: json!({ - "Source": account_keys[instruction.accounts[0] as usize].to_string(), - "New Account": account_keys[instruction.accounts[1] as usize].to_string(), - "Lamports": lamports, - "Space": space, - "Owner": owner.to_string(), - }), - }) - } - SystemInstruction::UpgradeNonceAccount => { - check_num_system_accounts(&instruction.accounts, 1)?; - Ok(ParsedInstructionEnum { - instruction_type: "UpgradeNonceAccount".to_string(), - info: json!({ - "Nonce Account": account_keys[instruction.accounts[0] as usize].to_string(), - }), - }) - } - SystemInstruction::Assign { owner } => { - check_num_system_accounts(&instruction.accounts, 1)?; - Ok(ParsedInstructionEnum { - instruction_type: "Assign".to_string(), - info: json!({ - "Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Owner": owner.to_string(), - }), - }) - } - SystemInstruction::Transfer { lamports } => { - check_num_system_accounts(&instruction.accounts, 2)?; - Ok(ParsedInstructionEnum { - instruction_type: "Transfer".to_string(), - info: json!({ - "Source": account_keys[instruction.accounts[0] as usize].to_string(), - "Destination": account_keys[instruction.accounts[1] as usize].to_string(), - "Lamports": lamports, - }), - }) - } - SystemInstruction::CreateAccountWithSeed { - base, - seed, - lamports, - space, - owner, - } => { - check_num_system_accounts(&instruction.accounts, 2)?; - Ok(ParsedInstructionEnum { - instruction_type: "CreateAccountWithSeed".to_string(), - info: json!({ - "Source": account_keys[instruction.accounts[0] as usize].to_string(), - "New Account": account_keys[instruction.accounts[1] as usize].to_string(), - "Base": base.to_string(), - "Seed": seed, - "Lamports": lamports, - "Space": space, - "Owner": owner.to_string(), - }), - }) - } - SystemInstruction::AdvanceNonceAccount => { - check_num_system_accounts(&instruction.accounts, 3)?; - Ok(ParsedInstructionEnum { - instruction_type: "AdvanceNonce".to_string(), - info: json!({ - "Nonce Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Recent Blockhashes Sysvar": account_keys[instruction.accounts[1] as usize].to_string(), - "Nonce Authority": account_keys[instruction.accounts[2] as usize].to_string(), - }), - }) - } - SystemInstruction::WithdrawNonceAccount(lamports) => { - check_num_system_accounts(&instruction.accounts, 5)?; - Ok(ParsedInstructionEnum { - instruction_type: "WithdrawFromNonce".to_string(), - info: json!({ - "Nonce Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Destination": account_keys[instruction.accounts[1] as usize].to_string(), - "Recent Blockhashes Sysvar": account_keys[instruction.accounts[2] as usize].to_string(), - "Rent Sysvar": account_keys[instruction.accounts[3] as usize].to_string(), - "Nonce Authority": account_keys[instruction.accounts[4] as usize].to_string(), - "Lamports": lamports, - }), - }) - } - SystemInstruction::InitializeNonceAccount(authority) => { - check_num_system_accounts(&instruction.accounts, 3)?; - Ok(ParsedInstructionEnum { - instruction_type: "InitializeNonce".to_string(), - info: json!({ - "Nonce Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Recent Blockhashes Sysvar": account_keys[instruction.accounts[1] as usize].to_string(), - "Rent Sysvar": account_keys[instruction.accounts[2] as usize].to_string(), - "Nonce Authority": authority.to_string(), - }), - }) - } - SystemInstruction::AuthorizeNonceAccount(authority) => { - check_num_system_accounts(&instruction.accounts, 1)?; - Ok(ParsedInstructionEnum { - instruction_type: "AuthorizeNonce".to_string(), - info: json!({ - "Nonce Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Nonce Authority": account_keys[instruction.accounts[1] as usize].to_string(), - "New Authorized": authority.to_string(), - }), - }) - } - SystemInstruction::Allocate { space } => { - check_num_system_accounts(&instruction.accounts, 1)?; - Ok(ParsedInstructionEnum { - instruction_type: "Allocate".to_string(), - info: json!({ - "Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Space": space, - }), - }) - } - SystemInstruction::AllocateWithSeed { - base, - seed, - space, - owner, - } => { - check_num_system_accounts(&instruction.accounts, 2)?; - Ok(ParsedInstructionEnum { - instruction_type: "AllocateWithSeed".to_string(), - info: json!({ - "Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Base": base.to_string(), - "Seed": seed, - "Space": space, - "Owner": owner.to_string(), - }), - }) - } - SystemInstruction::AssignWithSeed { base, seed, owner } => { - check_num_system_accounts(&instruction.accounts, 2)?; - Ok(ParsedInstructionEnum { - instruction_type: "AssignWithSeed".to_string(), - info: json!({ - "Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Base": base.to_string(), - "Seed": seed, - "Owner": owner.to_string(), - }), - }) - } - SystemInstruction::TransferWithSeed { - lamports, - from_seed, - from_owner, - } => { - check_num_system_accounts(&instruction.accounts, 3)?; - Ok(ParsedInstructionEnum { - instruction_type: "TransferWithSeed".to_string(), - info: json!({ - "Source": account_keys[instruction.accounts[0] as usize].to_string(), - "Source Base": account_keys[instruction.accounts[1] as usize].to_string(), - "Destination": account_keys[instruction.accounts[2] as usize].to_string(), - "Lamports": lamports, - "Source Seed": from_seed, - "Source Owner": from_owner.to_string(), - }), - }) - } - } -} - -fn check_num_system_accounts(accounts: &[u8], num: usize) -> Result<(), ParseInstructionError> { - check_num_accounts(accounts, num, ParsableProgram::System) -} - -#[cfg(test)] -mod test { - use super::*; - use solana_sdk::{message::Message, pubkey::Pubkey, system_instruction, sysvar}; - - #[test] - fn test_parse_system_create_account_ix() { - let lamports = 55; - let space = 128; - let from_pubkey = Pubkey::new_unique(); - let to_pubkey = Pubkey::new_unique(); - let owner_pubkey = Pubkey::new_unique(); - - let instruction = system_instruction::create_account( - &from_pubkey, - &to_pubkey, - lamports, - space, - &owner_pubkey, - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_system(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "CreateAccount".to_string(), - info: json!({ - "Source": from_pubkey.to_string(), - "New Account": to_pubkey.to_string(), - "Lamports": lamports, - "Owner": owner_pubkey.to_string(), - "Space": space, - }), - } - ); - assert!(parse_system(&message.instructions[0], &message.account_keys[0..1]).is_err()); - } - - #[test] - fn test_parse_system_assign_ix() { - let account_pubkey = Pubkey::new_unique(); - let owner_pubkey = Pubkey::new_unique(); - let instruction = system_instruction::assign(&account_pubkey, &owner_pubkey); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_system(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "Assign".to_string(), - info: json!({ - "Account": account_pubkey.to_string(), - "Owner": owner_pubkey.to_string(), - }), - } - ); - assert!(parse_system(&message.instructions[0], &[]).is_err()); - } - - #[test] - fn test_parse_system_transfer_ix() { - let lamports = 55; - let from_pubkey = Pubkey::new_unique(); - let to_pubkey = Pubkey::new_unique(); - let instruction = system_instruction::transfer(&from_pubkey, &to_pubkey, lamports); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_system(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "Transfer".to_string(), - info: json!({ - "Source": from_pubkey.to_string(), - "Destination": to_pubkey.to_string(), - "Lamports": lamports, - }), - } - ); - assert!(parse_system(&message.instructions[0], &message.account_keys[0..1]).is_err()); - } - - #[test] - fn test_parse_system_create_account_with_seed_ix() { - let lamports = 55; - let space = 128; - let seed = "test_seed"; - let from_pubkey = Pubkey::new_unique(); - let to_pubkey = Pubkey::new_unique(); - let base_pubkey = Pubkey::new_unique(); - let owner_pubkey = Pubkey::new_unique(); - let instruction = system_instruction::create_account_with_seed( - &from_pubkey, - &to_pubkey, - &base_pubkey, - seed, - lamports, - space, - &owner_pubkey, - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_system(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "CreateAccountWithSeed".to_string(), - info: json!({ - "Source": from_pubkey.to_string(), - "New Account": to_pubkey.to_string(), - "Lamports": lamports, - "Base": base_pubkey.to_string(), - "Seed": seed, - "Owner": owner_pubkey.to_string(), - "Space": space, - }), - } - ); - - assert!(parse_system(&message.instructions[0], &message.account_keys[0..1]).is_err()); - } - - #[test] - fn test_parse_system_allocate_ix() { - let space = 128; - let account_pubkey = Pubkey::new_unique(); - let instruction = system_instruction::allocate(&account_pubkey, space); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_system(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "Allocate".to_string(), - info: json!({ - "Account": account_pubkey.to_string(), - "Space": space, - }), - } - ); - assert!(parse_system(&message.instructions[0], &[]).is_err()); - } - - #[test] - fn test_parse_system_allocate_with_seed_ix() { - let space = 128; - let seed = "test_seed"; - let account_pubkey = Pubkey::new_unique(); - let base_pubkey = Pubkey::new_unique(); - let owner_pubkey = Pubkey::new_unique(); - let instruction = system_instruction::allocate_with_seed( - &account_pubkey, - &base_pubkey, - seed, - space, - &owner_pubkey, - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_system(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "AllocateWithSeed".to_string(), - info: json!({ - "Account": account_pubkey.to_string(), - "Base": base_pubkey.to_string(), - "Seed": seed, - "Owner": owner_pubkey.to_string(), - "Space": space, - }), - } - ); - assert!(parse_system(&message.instructions[0], &message.account_keys[0..1]).is_err()); - } - - #[test] - fn test_parse_system_assign_with_seed_ix() { - let seed = "test_seed"; - let account_pubkey = Pubkey::new_unique(); - let base_pubkey = Pubkey::new_unique(); - let owner_pubkey = Pubkey::new_unique(); - let instruction = system_instruction::assign_with_seed( - &account_pubkey, - &base_pubkey, - seed, - &owner_pubkey, - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_system(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "AssignWithSeed".to_string(), - info: json!({ - "Account": account_pubkey.to_string(), - "Base": base_pubkey.to_string(), - "Seed": seed, - "Owner": owner_pubkey.to_string(), - }), - } - ); - assert!(parse_system(&message.instructions[0], &message.account_keys[0..1]).is_err()); - } - - #[test] - fn test_parse_system_transfer_with_seed_ix() { - let lamports = 55; - let seed = "test_seed"; - let from_pubkey = Pubkey::new_unique(); - let from_base_pubkey = Pubkey::new_unique(); - let from_owner_pubkey = Pubkey::new_unique(); - let to_pubkey = Pubkey::new_unique(); - let instruction = system_instruction::transfer_with_seed( - &from_pubkey, - &from_base_pubkey, - seed.to_string(), - &from_owner_pubkey, - &to_pubkey, - lamports, - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_system(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "TransferWithSeed".to_string(), - info: json!({ - "Source": from_pubkey.to_string(), - "Source Base": from_base_pubkey.to_string(), - "Source Seed": seed, - "Source Owner": from_owner_pubkey.to_string(), - "Lamports": lamports, - "Destination": to_pubkey.to_string() - }), - } - ); - assert!(parse_system(&message.instructions[0], &message.account_keys[0..2]).is_err()); - } - - #[test] - fn test_parse_system_advance_nonce_account_ix() { - let nonce_pubkey = Pubkey::new_unique(); - let authorized_pubkey = Pubkey::new_unique(); - - let instruction = - system_instruction::advance_nonce_account(&nonce_pubkey, &authorized_pubkey); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_system(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "AdvanceNonce".to_string(), - info: json!({ - "Nonce Account": nonce_pubkey.to_string(), - "Recent Blockhashes Sysvar": sysvar::recent_blockhashes::ID.to_string(), - "Nonce Authority": authorized_pubkey.to_string(), - }), - } - ); - assert!(parse_system(&message.instructions[0], &message.account_keys[0..2]).is_err()); - } - - #[test] - fn test_parse_system_withdraw_nonce_account_ix() { - let nonce_pubkey = Pubkey::new_unique(); - let authorized_pubkey = Pubkey::new_unique(); - let to_pubkey = Pubkey::new_unique(); - - let lamports = 55; - let instruction = system_instruction::withdraw_nonce_account( - &nonce_pubkey, - &authorized_pubkey, - &to_pubkey, - lamports, - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_system(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "WithdrawFromNonce".to_string(), - info: json!({ - "Nonce Account": nonce_pubkey.to_string(), - "Destination": to_pubkey.to_string(), - "Recent Blockhashes Sysvar": sysvar::recent_blockhashes::ID.to_string(), - "Rent Sysvar": sysvar::rent::ID.to_string(), - "Nonce Authority": authorized_pubkey.to_string(), - "Lamports": lamports - }), - } - ); - assert!(parse_system(&message.instructions[0], &message.account_keys[0..4]).is_err()); - } - - #[test] - fn test_parse_system_initialize_nonce_ix() { - let lamports = 55; - let from_pubkey = Pubkey::new_unique(); - let nonce_pubkey = Pubkey::new_unique(); - let authorized_pubkey = Pubkey::new_unique(); - - let instructions = system_instruction::create_nonce_account( - &from_pubkey, - &nonce_pubkey, - &authorized_pubkey, - lamports, - ); - let message = Message::new(&instructions, None); - assert_eq!( - parse_system(&message.instructions[1], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "InitializeNonce".to_string(), - info: json!({ - "Nonce Account": nonce_pubkey.to_string(), - "Recent Blockhashes Sysvar": sysvar::recent_blockhashes::ID.to_string(), - "Rent Sysvar": sysvar::rent::ID.to_string(), - "Nonce Authority": authorized_pubkey.to_string(), - }), - } - ); - assert!(parse_system(&message.instructions[1], &message.account_keys[0..3]).is_err()); - } - - #[test] - fn test_parse_system_authorize_nonce_account_ix() { - let nonce_pubkey = Pubkey::new_unique(); - let authorized_pubkey = Pubkey::new_unique(); - let new_authority_pubkey = Pubkey::new_unique(); - - let instruction = system_instruction::authorize_nonce_account( - &nonce_pubkey, - &authorized_pubkey, - &new_authority_pubkey, - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_system(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "AuthorizeNonce".to_string(), - info: json!({ - "Nonce Account": nonce_pubkey.to_string(), - "New Authorized": new_authority_pubkey.to_string(), - "Nonce Authority": authorized_pubkey.to_string(), - }), - } - ); - assert!(parse_system(&message.instructions[0], &message.account_keys[0..1]).is_err()); - } -} diff --git a/crates/explorer/src/parse/token.rs b/crates/explorer/src/parse/token.rs deleted file mode 100644 index a90a25d1..00000000 --- a/crates/explorer/src/parse/token.rs +++ /dev/null @@ -1,651 +0,0 @@ -use crate::parse::{ - check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum, -}; -use serde::{Deserialize, Serialize}; -use serde_json::{json, Map, Value}; -use solana_account_decoder::parse_token::token_amount_to_ui_amount; -use solana_program::{program_option::COption, pubkey::Pubkey}; -use solana_sdk::instruction::CompiledInstruction; -use spl_token::instruction::{AuthorityType, TokenInstruction}; - -pub fn parse_token( - instruction: &CompiledInstruction, - account_keys: &[Pubkey], -) -> Result { - let token_instruction = TokenInstruction::unpack(&instruction.data) - .map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::SPLToken))?; - match instruction.accounts.iter().max() { - Some(index) if (*index as usize) < account_keys.len() => {} - _ => { - // Runtime should prevent this from ever happening - return Err(ParseInstructionError::InstructionKeyMismatch( - ParsableProgram::SPLToken, - )); - } - } - match token_instruction { - TokenInstruction::InitializeMint { - decimals, - mint_authority, - freeze_authority, - } => { - check_num_token_accounts(&instruction.accounts, 2)?; - let mut value = json!({ - "mint": account_keys[instruction.accounts[0] as usize].to_string(), - "decimals": decimals, - "mintAuthority": mint_authority.to_string(), - "rentSysvar": account_keys[instruction.accounts[1] as usize].to_string(), - }); - let map = value.as_object_mut().unwrap(); - if let COption::Some(freeze_authority) = freeze_authority { - map.insert( - "freezeAuthority".to_string(), - json!(freeze_authority.to_string()), - ); - } - Ok(ParsedInstructionEnum { - instruction_type: "initializeMint".to_string(), - info: value, - }) - } - TokenInstruction::InitializeAccount => { - check_num_token_accounts(&instruction.accounts, 4)?; - Ok(ParsedInstructionEnum { - instruction_type: "initializeAccount".to_string(), - info: json!({ - "account": account_keys[instruction.accounts[0] as usize].to_string(), - "mint": account_keys[instruction.accounts[1] as usize].to_string(), - "owner": account_keys[instruction.accounts[2] as usize].to_string(), - "rentSysvar": account_keys[instruction.accounts[3] as usize].to_string(), - }), - }) - } - TokenInstruction::InitializeAccount2 { owner } => { - check_num_token_accounts(&instruction.accounts, 3)?; - Ok(ParsedInstructionEnum { - instruction_type: "initializeAccount2".to_string(), - info: json!({ - "account": account_keys[instruction.accounts[0] as usize].to_string(), - "mint": account_keys[instruction.accounts[1] as usize].to_string(), - "owner": owner.to_string(), - "rentSysvar": account_keys[instruction.accounts[2] as usize].to_string(), - }), - }) - } - TokenInstruction::InitializeMultisig { m } => { - check_num_token_accounts(&instruction.accounts, 3)?; - let mut signers: Vec = vec![]; - for i in instruction.accounts[2..].iter() { - signers.push(account_keys[*i as usize].to_string()); - } - Ok(ParsedInstructionEnum { - instruction_type: "initializeMultisig".to_string(), - info: json!({ - "multisig": account_keys[instruction.accounts[0] as usize].to_string(), - "rentSysvar": account_keys[instruction.accounts[1] as usize].to_string(), - "signers": signers, - "m": m, - }), - }) - } - TokenInstruction::Transfer { amount } => { - check_num_token_accounts(&instruction.accounts, 3)?; - let mut value = json!({ - "source": account_keys[instruction.accounts[0] as usize].to_string(), - "destination": account_keys[instruction.accounts[1] as usize].to_string(), - "amount": amount.to_string(), - }); - let map = value.as_object_mut().unwrap(); - parse_signers( - map, - 2, - account_keys, - &instruction.accounts, - "authority", - "multisigAuthority", - ); - Ok(ParsedInstructionEnum { - instruction_type: "transfer".to_string(), - info: value, - }) - } - TokenInstruction::Approve { amount } => { - check_num_token_accounts(&instruction.accounts, 3)?; - let mut value = json!({ - "source": account_keys[instruction.accounts[0] as usize].to_string(), - "delegate": account_keys[instruction.accounts[1] as usize].to_string(), - "amount": amount.to_string(), - }); - let map = value.as_object_mut().unwrap(); - parse_signers( - map, - 2, - account_keys, - &instruction.accounts, - "owner", - "multisigOwner", - ); - Ok(ParsedInstructionEnum { - instruction_type: "approve".to_string(), - info: value, - }) - } - TokenInstruction::Revoke => { - check_num_token_accounts(&instruction.accounts, 2)?; - let mut value = json!({ - "source": account_keys[instruction.accounts[0] as usize].to_string(), - }); - let map = value.as_object_mut().unwrap(); - parse_signers( - map, - 1, - account_keys, - &instruction.accounts, - "owner", - "multisigOwner", - ); - Ok(ParsedInstructionEnum { - instruction_type: "revoke".to_string(), - info: value, - }) - } - TokenInstruction::SetAuthority { - authority_type, - new_authority, - } => { - check_num_token_accounts(&instruction.accounts, 2)?; - let owned = match authority_type { - AuthorityType::MintTokens | AuthorityType::FreezeAccount => "mint", - AuthorityType::AccountOwner | AuthorityType::CloseAccount => "account", - }; - let mut value = json!({ - owned: account_keys[instruction.accounts[0] as usize].to_string(), - "authorityType": Into::::into(authority_type), - "newAuthority": match new_authority { - COption::Some(authority) => Some(authority.to_string()), - COption::None => None, - }, - }); - let map = value.as_object_mut().unwrap(); - parse_signers( - map, - 1, - account_keys, - &instruction.accounts, - "authority", - "multisigAuthority", - ); - Ok(ParsedInstructionEnum { - instruction_type: "setAuthority".to_string(), - info: value, - }) - } - TokenInstruction::MintTo { amount } => { - check_num_token_accounts(&instruction.accounts, 3)?; - let mut value = json!({ - "mint": account_keys[instruction.accounts[0] as usize].to_string(), - "account": account_keys[instruction.accounts[1] as usize].to_string(), - "amount": amount.to_string(), - }); - let map = value.as_object_mut().unwrap(); - parse_signers( - map, - 2, - account_keys, - &instruction.accounts, - "mintAuthority", - "multisigMintAuthority", - ); - Ok(ParsedInstructionEnum { - instruction_type: "mintTo".to_string(), - info: value, - }) - } - TokenInstruction::Burn { amount } => { - check_num_token_accounts(&instruction.accounts, 3)?; - let mut value = json!({ - "account": account_keys[instruction.accounts[0] as usize].to_string(), - "mint": account_keys[instruction.accounts[1] as usize].to_string(), - "amount": amount.to_string(), - }); - let map = value.as_object_mut().unwrap(); - parse_signers( - map, - 2, - account_keys, - &instruction.accounts, - "authority", - "multisigAuthority", - ); - Ok(ParsedInstructionEnum { - instruction_type: "burn".to_string(), - info: value, - }) - } - TokenInstruction::CloseAccount => { - check_num_token_accounts(&instruction.accounts, 3)?; - let mut value = json!({ - "account": account_keys[instruction.accounts[0] as usize].to_string(), - "destination": account_keys[instruction.accounts[1] as usize].to_string(), - }); - let map = value.as_object_mut().unwrap(); - parse_signers( - map, - 2, - account_keys, - &instruction.accounts, - "owner", - "multisigOwner", - ); - Ok(ParsedInstructionEnum { - instruction_type: "closeAccount".to_string(), - info: value, - }) - } - TokenInstruction::FreezeAccount => { - check_num_token_accounts(&instruction.accounts, 3)?; - let mut value = json!({ - "account": account_keys[instruction.accounts[0] as usize].to_string(), - "mint": account_keys[instruction.accounts[1] as usize].to_string(), - }); - let map = value.as_object_mut().unwrap(); - parse_signers( - map, - 2, - account_keys, - &instruction.accounts, - "freezeAuthority", - "multisigFreezeAuthority", - ); - Ok(ParsedInstructionEnum { - instruction_type: "freezeAccount".to_string(), - info: value, - }) - } - TokenInstruction::ThawAccount => { - check_num_token_accounts(&instruction.accounts, 3)?; - let mut value = json!({ - "account": account_keys[instruction.accounts[0] as usize].to_string(), - "mint": account_keys[instruction.accounts[1] as usize].to_string(), - }); - let map = value.as_object_mut().unwrap(); - parse_signers( - map, - 2, - account_keys, - &instruction.accounts, - "freezeAuthority", - "multisigFreezeAuthority", - ); - Ok(ParsedInstructionEnum { - instruction_type: "thawAccount".to_string(), - info: value, - }) - } - TokenInstruction::TransferChecked { amount, decimals } => { - check_num_token_accounts(&instruction.accounts, 4)?; - let mut value = json!({ - "source": account_keys[instruction.accounts[0] as usize].to_string(), - "mint": account_keys[instruction.accounts[1] as usize].to_string(), - "destination": account_keys[instruction.accounts[2] as usize].to_string(), - "tokenAmount": token_amount_to_ui_amount(amount, decimals), - }); - let map = value.as_object_mut().unwrap(); - parse_signers( - map, - 3, - account_keys, - &instruction.accounts, - "authority", - "multisigAuthority", - ); - Ok(ParsedInstructionEnum { - instruction_type: "transferChecked".to_string(), - info: value, - }) - } - TokenInstruction::ApproveChecked { amount, decimals } => { - check_num_token_accounts(&instruction.accounts, 4)?; - let mut value = json!({ - "source": account_keys[instruction.accounts[0] as usize].to_string(), - "mint": account_keys[instruction.accounts[1] as usize].to_string(), - "delegate": account_keys[instruction.accounts[2] as usize].to_string(), - "tokenAmount": token_amount_to_ui_amount(amount, decimals), - }); - let map = value.as_object_mut().unwrap(); - parse_signers( - map, - 3, - account_keys, - &instruction.accounts, - "owner", - "multisigOwner", - ); - Ok(ParsedInstructionEnum { - instruction_type: "approveChecked".to_string(), - info: value, - }) - } - TokenInstruction::MintToChecked { amount, decimals } => { - check_num_token_accounts(&instruction.accounts, 3)?; - let mut value = json!({ - "mint": account_keys[instruction.accounts[0] as usize].to_string(), - "account": account_keys[instruction.accounts[1] as usize].to_string(), - "tokenAmount": token_amount_to_ui_amount(amount, decimals), - }); - let map = value.as_object_mut().unwrap(); - parse_signers( - map, - 2, - account_keys, - &instruction.accounts, - "mintAuthority", - "multisigMintAuthority", - ); - Ok(ParsedInstructionEnum { - instruction_type: "mintToChecked".to_string(), - info: value, - }) - } - TokenInstruction::BurnChecked { amount, decimals } => { - check_num_token_accounts(&instruction.accounts, 3)?; - let mut value = json!({ - "account": account_keys[instruction.accounts[0] as usize].to_string(), - "mint": account_keys[instruction.accounts[1] as usize].to_string(), - "tokenAmount": token_amount_to_ui_amount(amount, decimals), - }); - let map = value.as_object_mut().unwrap(); - parse_signers( - map, - 2, - account_keys, - &instruction.accounts, - "authority", - "multisigAuthority", - ); - Ok(ParsedInstructionEnum { - instruction_type: "burnChecked".to_string(), - info: value, - }) - } - TokenInstruction::SyncNative => { - check_num_token_accounts(&instruction.accounts, 1)?; - Ok(ParsedInstructionEnum { - instruction_type: "syncNative".to_string(), - info: json!({ - "account": account_keys[instruction.accounts[0] as usize].to_string(), - }), - }) - } - TokenInstruction::InitializeAccount3 { owner } => { - check_num_token_accounts(&instruction.accounts, 2)?; - Ok(ParsedInstructionEnum { - instruction_type: "initializeAccount3".to_string(), - info: json!({ - "account": account_keys[instruction.accounts[0] as usize].to_string(), - "mintAccount": account_keys[instruction.accounts[1] as usize].to_string(), - "owner": owner.to_string(), - }), - }) - } - TokenInstruction::InitializeMint2 { mint_authority, .. } => { - check_num_token_accounts(&instruction.accounts, 1)?; - Ok(ParsedInstructionEnum { - instruction_type: "initializeMint2".to_string(), - info: json!({ - "account": account_keys[instruction.accounts[0] as usize].to_string(), - "mintAuthority": mint_authority.to_string(), - }), - }) - } - TokenInstruction::InitializeMultisig2 { m } => { - check_num_token_accounts(&instruction.accounts, 2)?; - let mut signers: Vec = vec![]; - for i in instruction.accounts[1..].iter() { - signers.push(account_keys[*i as usize].to_string()); - } - Ok(ParsedInstructionEnum { - instruction_type: "initializeMultisig2".to_string(), - info: json!({ - "multisig": account_keys[instruction.accounts[0] as usize].to_string(), - "signers": signers, - "m": m, - }), - }) - } - TokenInstruction::GetAccountDataSize => { - check_num_token_accounts(&instruction.accounts, 1)?; - Ok(ParsedInstructionEnum { - instruction_type: "getAccountDataSize".to_string(), - info: json!({ - "mint": account_keys[instruction.accounts[0] as usize].to_string(), - }), - }) - } - TokenInstruction::InitializeImmutableOwner => { - check_num_token_accounts(&instruction.accounts, 1)?; - Ok(ParsedInstructionEnum { - instruction_type: "initializeImmutableOwner".to_string(), - info: json!({ - "account": account_keys[instruction.accounts[0] as usize].to_string(), - }), - }) - } - TokenInstruction::AmountToUiAmount { amount } => { - check_num_token_accounts(&instruction.accounts, 1)?; - Ok(ParsedInstructionEnum { - instruction_type: "amountToUiAmount".to_string(), - info: json!({ - "amount": amount, - "mint": account_keys[instruction.accounts[0] as usize].to_string(), - }), - }) - } - TokenInstruction::UiAmountToAmount { ui_amount } => { - check_num_token_accounts(&instruction.accounts, 1)?; - Ok(ParsedInstructionEnum { - instruction_type: "uiAmountToAmount".to_string(), - info: json!({ - "uiAmount": ui_amount, - "mint": account_keys[instruction.accounts[0] as usize].to_string(), - }), - }) - } - } -} - -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] -#[serde(rename_all = "camelCase")] -pub enum UiAuthorityType { - MintTokens, - FreezeAccount, - AccountOwner, - CloseAccount, -} - -impl From for UiAuthorityType { - fn from(authority_type: AuthorityType) -> Self { - match authority_type { - AuthorityType::MintTokens => UiAuthorityType::MintTokens, - AuthorityType::FreezeAccount => UiAuthorityType::FreezeAccount, - AuthorityType::AccountOwner => UiAuthorityType::AccountOwner, - AuthorityType::CloseAccount => UiAuthorityType::CloseAccount, - } - } -} - -fn parse_signers( - map: &mut Map, - last_nonsigner_index: usize, - account_keys: &[Pubkey], - accounts: &[u8], - owner_field_name: &str, - multisig_field_name: &str, -) { - if accounts.len() > last_nonsigner_index + 1 { - let mut signers: Vec = vec![]; - for i in accounts[last_nonsigner_index + 1..].iter() { - signers.push(account_keys[*i as usize].to_string()); - } - map.insert( - multisig_field_name.to_string(), - json!(account_keys[accounts[last_nonsigner_index] as usize].to_string()), - ); - map.insert("signers".to_string(), json!(signers)); - } else { - map.insert( - owner_field_name.to_string(), - json!(account_keys[accounts[last_nonsigner_index] as usize].to_string()), - ); - } -} - -fn check_num_token_accounts(accounts: &[u8], num: usize) -> Result<(), ParseInstructionError> { - check_num_accounts(accounts, num, ParsableProgram::SPLToken) -} - -#[cfg(test)] -mod test { - use super::*; - use solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey}; - use spl_token::{ - instruction::*, - solana_program::{ - instruction::CompiledInstruction as SplTokenCompiledInstruction, - instruction::Instruction as SplTokenInstruction, message::Message, - pubkey::Pubkey as SplTokenPubkey, - }, - }; - use std::str::FromStr; - - fn convert_pubkey(pubkey: Pubkey) -> SplTokenPubkey { - SplTokenPubkey::from_str(&pubkey.to_string()).unwrap() - } - - fn convert_compiled_instruction( - instruction: &SplTokenCompiledInstruction, - ) -> CompiledInstruction { - CompiledInstruction { - program_id_index: instruction.program_id_index, - accounts: instruction.accounts.clone(), - data: instruction.data.clone(), - } - } - - fn make_coerced_message( - mut instruction: SplTokenInstruction, - program_id: &SplTokenPubkey, - ) -> Message { - instruction.program_id = *program_id; - Message::new(&[instruction], None) - } - - #[test] - #[allow(clippy::same_item_push)] - fn test_parse_token_v3() { - test_parse_token(&spl_token::id()); - } - - fn test_parse_token(program_id: &SplTokenPubkey) { - let mint_pubkey = Pubkey::new_unique(); - let mint_authority = Pubkey::new_unique(); - let freeze_authority = Pubkey::new_unique(); - let rent_sysvar = solana_sdk::sysvar::rent::id(); - - // Test InitializeMint variations - let initialize_mint_ix = initialize_mint( - &spl_token::id(), // TODO: replace with `program_id` - &convert_pubkey(mint_pubkey), - &convert_pubkey(mint_authority), - Some(&convert_pubkey(freeze_authority)), - 2, - ) - .unwrap(); - let message = make_coerced_message(initialize_mint_ix, program_id); - let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); - assert_eq!( - parse_token(&compiled_instruction, &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "initializeMint".to_string(), - info: json!({ - "mint": mint_pubkey.to_string(), - "decimals": 2, - "mintAuthority": mint_authority.to_string(), - "freezeAuthority": freeze_authority.to_string(), - "rentSysvar": rent_sysvar.to_string(), - }) - } - ); - - let initialize_mint_ix = initialize_mint( - &spl_token::id(), // TODO: replace with `program_id` - &convert_pubkey(mint_pubkey), - &convert_pubkey(mint_authority), - None, - 2, - ) - .unwrap(); - let message = make_coerced_message(initialize_mint_ix, program_id); - let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); - assert_eq!( - parse_token(&compiled_instruction, &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "initializeMint".to_string(), - info: json!({ - "mint": mint_pubkey.to_string(), - "decimals": 2, - "mintAuthority": mint_authority.to_string(), - "rentSysvar": rent_sysvar.to_string(), - }) - } - ); - - // Test InitializeAccount - let account_pubkey = Pubkey::new_unique(); - let owner = Pubkey::new_unique(); - let initialize_account_ix = initialize_account( - &spl_token::id(), // TODO: replace with `program_id` - &convert_pubkey(account_pubkey), - &convert_pubkey(mint_pubkey), - &convert_pubkey(owner), - ) - .unwrap(); - let message = make_coerced_message(initialize_account_ix, program_id); - let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); - assert_eq!( - parse_token(&compiled_instruction, &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "initializeAccount".to_string(), - info: json!({ - "account": account_pubkey.to_string(), - "mint": mint_pubkey.to_string(), - "owner": owner.to_string(), - "rentSysvar": rent_sysvar.to_string(), - }) - } - ); - - // Test InitializeAccount2 - let initialize_account_ix = initialize_account2( - &spl_token::id(), // TODO: replace with `program_id` - &convert_pubkey(account_pubkey), - &convert_pubkey(mint_pubkey), - &convert_pubkey(owner), - ) - .unwrap(); - let message = make_coerced_message(initialize_account_ix, program_id); - let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); - assert_eq!( - parse_token(&compiled_instruction, &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "initializeAccount2".to_string(), - info: json!({ - "account": account_pubkey.to_string(), - "mint": mint_pubkey.to_string(), - "owner": owner.to_string(), - "rentSysvar": rent_sysvar.to_string(), - }) - } - ); - } -} diff --git a/crates/explorer/src/parse/vote.rs b/crates/explorer/src/parse/vote.rs deleted file mode 100644 index bcda9dfd..00000000 --- a/crates/explorer/src/parse/vote.rs +++ /dev/null @@ -1,470 +0,0 @@ -use crate::parse::{ - check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum, -}; -use serde_json::json; -use solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey}; -use solana_vote_program::vote_instruction::VoteInstruction; - -pub fn parse_vote( - instruction: &CompiledInstruction, - account_keys: &[Pubkey], -) -> Result { - let vote_instruction: VoteInstruction = bincode::deserialize(&instruction.data) - .map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::Vote))?; - match instruction.accounts.iter().max() { - Some(index) if (*index as usize) < account_keys.len() => {} - _ => { - // Runtime should prevent this from ever happening - return Err(ParseInstructionError::InstructionKeyMismatch( - ParsableProgram::Vote, - )); - } - } - match vote_instruction { - VoteInstruction::InitializeAccount(vote_init) => { - check_num_vote_accounts(&instruction.accounts, 4)?; - Ok(ParsedInstructionEnum { - instruction_type: "Initialize".to_string(), - info: json!({ - "Vote Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Rent Sysvar": account_keys[instruction.accounts[1] as usize].to_string(), - "Clock Sysvar": account_keys[instruction.accounts[2] as usize].to_string(), - "Node": account_keys[instruction.accounts[3] as usize].to_string(), - "Authorized Voter": vote_init.authorized_voter.to_string(), - "Authorized Withdrawer": vote_init.authorized_withdrawer.to_string(), - "Commission": vote_init.commission, - }), - }) - } - VoteInstruction::Authorize(new_authorized, authority_type) => { - check_num_vote_accounts(&instruction.accounts, 3)?; - Ok(ParsedInstructionEnum { - instruction_type: "Authorize".to_string(), - info: json!({ - "Vote Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Clock Sysvar": account_keys[instruction.accounts[1] as usize].to_string(), - "Authority": account_keys[instruction.accounts[2] as usize].to_string(), - "New Authority": new_authorized.to_string(), - "Authority Type": authority_type, - }), - }) - } - VoteInstruction::Vote(vote) => { - check_num_vote_accounts(&instruction.accounts, 4)?; - let vote = json!({ - "Slots": vote.slots, - "Hash": vote.hash.to_string(), - "Timestamp": vote.timestamp, - }); - Ok(ParsedInstructionEnum { - instruction_type: "Vote".to_string(), - info: json!({ - "Vote Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Slot Hashes Sysvar": account_keys[instruction.accounts[1] as usize].to_string(), - "Clock Sysvar": account_keys[instruction.accounts[2] as usize].to_string(), - "Vote Authority": account_keys[instruction.accounts[3] as usize].to_string(), - "Vote": vote, - }), - }) - } - VoteInstruction::Withdraw(lamports) => { - check_num_vote_accounts(&instruction.accounts, 3)?; - Ok(ParsedInstructionEnum { - instruction_type: "Withdraw".to_string(), - info: json!({ - "Vote Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Destination": account_keys[instruction.accounts[1] as usize].to_string(), - "Withdraw Authority": account_keys[instruction.accounts[2] as usize].to_string(), - "Lamports": lamports, - }), - }) - } - VoteInstruction::UpdateValidatorIdentity => { - check_num_vote_accounts(&instruction.accounts, 3)?; - Ok(ParsedInstructionEnum { - instruction_type: "UpdateValidatorIdentity".to_string(), - info: json!({ - "Vote Account": account_keys[instruction.accounts[0] as usize].to_string(), - "New Validator Identity": account_keys[instruction.accounts[1] as usize].to_string(), - "Withdraw Authority": account_keys[instruction.accounts[2] as usize].to_string(), - }), - }) - } - VoteInstruction::UpdateCommission(commission) => { - check_num_vote_accounts(&instruction.accounts, 2)?; - Ok(ParsedInstructionEnum { - instruction_type: "UpdateCommission".to_string(), - info: json!({ - "Vote Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Withdraw Authority": account_keys[instruction.accounts[1] as usize].to_string(), - "Commission": commission, - }), - }) - } - VoteInstruction::VoteSwitch(vote, hash) => { - check_num_vote_accounts(&instruction.accounts, 4)?; - let vote = json!({ - "Slots": vote.slots, - "Hash": vote.hash.to_string(), - "Timestamp": vote.timestamp, - }); - Ok(ParsedInstructionEnum { - instruction_type: "VoteSwitch".to_string(), - info: json!({ - "Vote Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Slot Hashes Sysvar": account_keys[instruction.accounts[1] as usize].to_string(), - "Clock Sysvar": account_keys[instruction.accounts[2] as usize].to_string(), - "Vote Authority": account_keys[instruction.accounts[3] as usize].to_string(), - "Vote": vote, - "Hash": hash.to_string(), - }), - }) - } - VoteInstruction::AuthorizeChecked(authority_type) => { - check_num_vote_accounts(&instruction.accounts, 4)?; - Ok(ParsedInstructionEnum { - instruction_type: "AuthorizeChecked".to_string(), - info: json!({ - "Vote Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Clock Sysvar": account_keys[instruction.accounts[1] as usize].to_string(), - "Authority": account_keys[instruction.accounts[2] as usize].to_string(), - "New Authority": account_keys[instruction.accounts[3] as usize].to_string(), - "Authority Type": authority_type, - }), - }) - } - VoteInstruction::UpdateVoteState(state) => { - check_num_vote_accounts(&instruction.accounts, 2)?; - Ok(ParsedInstructionEnum { - instruction_type: "UpdateVoteState".to_string(), - info: json!({ - "Vote Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Vote Authority": account_keys[instruction.accounts[1] as usize].to_string(), - "State Hash": state.hash.to_string(), - }), - }) - } - VoteInstruction::UpdateVoteStateSwitch(state, hash) => { - check_num_vote_accounts(&instruction.accounts, 2)?; - Ok(ParsedInstructionEnum { - instruction_type: "UpdateVoteStateSwitch".to_string(), - info: json!({ - "Vote Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Vote Authority": account_keys[instruction.accounts[1] as usize].to_string(), - "Hash": hash.to_string(), - "State Hash": state.hash.to_string(), - }), - }) - } - VoteInstruction::AuthorizeWithSeed(authority_type) => { - check_num_vote_accounts(&instruction.accounts, 3)?; - Ok(ParsedInstructionEnum { - instruction_type: "AuthorizeWithSeed".to_string(), - info: json!({ - "Vote Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Clock Sysvar": account_keys[instruction.accounts[1] as usize].to_string(), - "Authority": account_keys[instruction.accounts[2] as usize].to_string(), - "Authority Type": authority_type, - }), - }) - } - VoteInstruction::AuthorizeCheckedWithSeed(authority_type) => { - check_num_vote_accounts(&instruction.accounts, 4)?; - Ok(ParsedInstructionEnum { - instruction_type: "AuthorizeCheckedWithSeed".to_string(), - info: json!({ - "Vote Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Clock Sysvar": account_keys[instruction.accounts[1] as usize].to_string(), - "Authority": account_keys[instruction.accounts[2] as usize].to_string(), - "New Authority": account_keys[instruction.accounts[3] as usize].to_string(), - "Authority Type": authority_type, - }), - }) - } - VoteInstruction::CompactUpdateVoteState(state) => { - check_num_vote_accounts(&instruction.accounts, 2)?; - Ok(ParsedInstructionEnum { - instruction_type: "CompactUpdateVoteState".to_string(), - info: json!({ - "Vote Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Vote Authority": account_keys[instruction.accounts[1] as usize].to_string(), - "State Hash": state.hash.to_string(), - }), - }) - } - VoteInstruction::CompactUpdateVoteStateSwitch(state, hash) => { - check_num_vote_accounts(&instruction.accounts, 2)?; - Ok(ParsedInstructionEnum { - instruction_type: "CompactUpdateVoteStateSwitch".to_string(), - info: json!({ - "Vote Account": account_keys[instruction.accounts[0] as usize].to_string(), - "Vote Authority": account_keys[instruction.accounts[1] as usize].to_string(), - "Hash": hash.to_string(), - "State Hash": state.hash.to_string(), - }), - }) - } - } -} - -fn check_num_vote_accounts(accounts: &[u8], num: usize) -> Result<(), ParseInstructionError> { - check_num_accounts(accounts, num, ParsableProgram::Vote) -} - -#[cfg(test)] -mod test { - use super::*; - use solana_program::vote::instruction::CreateVoteAccountConfig; - use solana_sdk::{hash::Hash, message::Message, pubkey::Pubkey, sysvar}; - use solana_vote_program::{ - vote_instruction, - vote_state::{Vote, VoteAuthorize, VoteInit}, - }; - - #[test] - fn test_parse_vote_initialize_ix() { - let lamports = 55; - - let commission = 10; - let node_pubkey = Pubkey::new_unique(); - let vote_pubkey = Pubkey::new_unique(); - let authorized_voter = Pubkey::new_unique(); - let authorized_withdrawer = Pubkey::new_unique(); - let vote_init = VoteInit { - node_pubkey, - authorized_voter, - authorized_withdrawer, - commission, - }; - - let instructions = vote_instruction::create_account_with_config( - &Pubkey::new_unique(), - &vote_pubkey, - &vote_init, - lamports, - CreateVoteAccountConfig::default(), - ); - let message = Message::new(&instructions, None); - assert_eq!( - parse_vote(&message.instructions[1], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "Initialize".to_string(), - info: json!({ - "Vote Account": vote_pubkey.to_string(), - "Rent Sysvar": sysvar::rent::ID.to_string(), - "Clock Sysvar": sysvar::clock::ID.to_string(), - "Node": node_pubkey.to_string(), - "Authorized Voter": authorized_voter.to_string(), - "Authorized Withdrawer": authorized_withdrawer.to_string(), - "Commission": commission, - }), - } - ); - assert!(parse_vote(&message.instructions[1], &message.account_keys[0..3]).is_err()); - } - - #[test] - fn test_parse_vote_authorize_ix() { - let vote_pubkey = Pubkey::new_unique(); - let authorized_pubkey = Pubkey::new_unique(); - let new_authorized_pubkey = Pubkey::new_unique(); - let authority_type = VoteAuthorize::Voter; - let instruction = vote_instruction::authorize( - &vote_pubkey, - &authorized_pubkey, - &new_authorized_pubkey, - authority_type, - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_vote(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "Authorize".to_string(), - info: json!({ - "Vote Account": vote_pubkey.to_string(), - "Clock Sysvar": sysvar::clock::ID.to_string(), - "Authority": authorized_pubkey.to_string(), - "New Authority": new_authorized_pubkey.to_string(), - "Authority Type": authority_type, - }), - } - ); - assert!(parse_vote(&message.instructions[0], &message.account_keys[0..2]).is_err()); - } - - #[test] - fn test_parse_vote_ix() { - let hash = Hash::new_from_array([1; 32]); - let vote = Vote { - slots: vec![1, 2, 4], - hash, - timestamp: Some(1_234_567_890), - }; - - let vote_pubkey = Pubkey::new_unique(); - let authorized_voter_pubkey = Pubkey::new_unique(); - let instruction = vote_instruction::vote(&vote_pubkey, &authorized_voter_pubkey, vote); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_vote(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "Vote".to_string(), - info: json!({ - "Vote Account": vote_pubkey.to_string(), - "Slot Hashes Sysvar": sysvar::slot_hashes::ID.to_string(), - "Clock Sysvar": sysvar::clock::ID.to_string(), - "Vote Authority": authorized_voter_pubkey.to_string(), - "Vote": { - "Slots": [1, 2, 4], - "Hash": hash.to_string(), - "Timestamp": 1_234_567_890, - }, - }), - } - ); - assert!(parse_vote(&message.instructions[0], &message.account_keys[0..3]).is_err()); - } - - #[test] - fn test_parse_vote_withdraw_ix() { - let lamports = 55; - let vote_pubkey = Pubkey::new_unique(); - let authorized_withdrawer_pubkey = Pubkey::new_unique(); - let to_pubkey = Pubkey::new_unique(); - let instruction = vote_instruction::withdraw( - &vote_pubkey, - &authorized_withdrawer_pubkey, - lamports, - &to_pubkey, - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_vote(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "Withdraw".to_string(), - info: json!({ - "Vote Account": vote_pubkey.to_string(), - "Destination": to_pubkey.to_string(), - "Withdraw Authority": authorized_withdrawer_pubkey.to_string(), - "Lamports": lamports, - }), - } - ); - assert!(parse_vote(&message.instructions[0], &message.account_keys[0..2]).is_err()); - } - - #[test] - fn test_parse_vote_update_validator_identity_ix() { - let vote_pubkey = Pubkey::new_unique(); - let authorized_withdrawer_pubkey = Pubkey::new_unique(); - let node_pubkey = Pubkey::new_unique(); - let instruction = vote_instruction::update_validator_identity( - &vote_pubkey, - &authorized_withdrawer_pubkey, - &node_pubkey, - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_vote(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "UpdateValidatorIdentity".to_string(), - info: json!({ - "Vote Account": vote_pubkey.to_string(), - "New Validator Identity": node_pubkey.to_string(), - "Withdraw Authority": authorized_withdrawer_pubkey.to_string(), - }), - } - ); - assert!(parse_vote(&message.instructions[0], &message.account_keys[0..2]).is_err()); - } - - #[test] - fn test_parse_vote_update_commission_ix() { - let commission = 10; - let vote_pubkey = Pubkey::new_unique(); - let authorized_withdrawer_pubkey = Pubkey::new_unique(); - let instruction = vote_instruction::update_commission( - &vote_pubkey, - &authorized_withdrawer_pubkey, - commission, - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_vote(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "UpdateCommission".to_string(), - info: json!({ - "Vote Account": vote_pubkey.to_string(), - "Withdraw Authority": authorized_withdrawer_pubkey.to_string(), - "Commission": commission, - }), - } - ); - assert!(parse_vote(&message.instructions[0], &message.account_keys[0..1]).is_err()); - } - - #[test] - fn test_parse_vote_switch_ix() { - let hash = Hash::new_from_array([1; 32]); - let vote = Vote { - slots: vec![1, 2, 4], - hash, - timestamp: Some(1_234_567_890), - }; - - let vote_pubkey = Pubkey::new_unique(); - let authorized_voter_pubkey = Pubkey::new_unique(); - let proof_hash = Hash::new_from_array([2; 32]); - let instruction = - vote_instruction::vote_switch(&vote_pubkey, &authorized_voter_pubkey, vote, proof_hash); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_vote(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "VoteSwitch".to_string(), - info: json!({ - "Vote Account": vote_pubkey.to_string(), - "Slot Hashes Sysvar": sysvar::slot_hashes::ID.to_string(), - "Clock Sysvar": sysvar::clock::ID.to_string(), - "Vote Authority": authorized_voter_pubkey.to_string(), - "Vote": { - "Slots": [1, 2, 4], - "Hash": hash.to_string(), - "Timestamp": 1_234_567_890, - }, - "Hash": proof_hash.to_string(), - }), - } - ); - assert!(parse_vote(&message.instructions[0], &message.account_keys[0..3]).is_err()); - } - - #[test] - fn test_parse_vote_authorized_checked_ix() { - let vote_pubkey = Pubkey::new_unique(); - let authorized_pubkey = Pubkey::new_unique(); - let new_authorized_pubkey = Pubkey::new_unique(); - let authority_type = VoteAuthorize::Voter; - let instruction = vote_instruction::authorize_checked( - &vote_pubkey, - &authorized_pubkey, - &new_authorized_pubkey, - authority_type, - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_vote(&message.instructions[0], &message.account_keys).unwrap(), - ParsedInstructionEnum { - instruction_type: "AuthorizeChecked".to_string(), - info: json!({ - "Vote Account": vote_pubkey.to_string(), - "Clock Sysvar": sysvar::clock::ID.to_string(), - "Authority": authorized_pubkey.to_string(), - "New Authority": new_authorized_pubkey.to_string(), - "Authority Type": authority_type, - }), - } - ); - assert!(parse_vote(&message.instructions[0], &message.account_keys[0..3]).is_err()); - } -} diff --git a/crates/explorer/src/program.rs b/crates/explorer/src/program.rs deleted file mode 100644 index ba6a5495..00000000 --- a/crates/explorer/src/program.rs +++ /dev/null @@ -1,257 +0,0 @@ -use crate::{account::KeyedAccount, output::pretty_lamports_to_sol}; -use console::style; -use serde::Serialize; -use solana_sdk::{bpf_loader_upgradeable::UpgradeableLoaderState, pubkey::Pubkey}; -use std::fmt; - -pub struct ProgramFieldVisibility { - program_account: bool, - programdata_account: bool, -} - -impl ProgramFieldVisibility { - pub fn new_all_enabled() -> Self { - Self { - program_account: true, - programdata_account: true, - } - } - - pub fn new_all_disabled() -> Self { - Self { - program_account: false, - programdata_account: false, - } - } - - pub fn program_account(&self) -> bool { - self.program_account - } - - pub fn enable_program_account(&mut self) -> &mut Self { - self.program_account = true; - self - } - - pub fn disable_program_account(&mut self) -> &mut Self { - self.program_account = false; - self - } - - pub fn programdata_account(&self) -> bool { - self.programdata_account - } - - pub fn enable_programdata_account(&mut self) -> &mut Self { - self.programdata_account = true; - self - } - - pub fn disable_programdata_account(&mut self) -> &mut Self { - self.programdata_account = false; - self - } -} - -#[derive(Serialize)] -pub struct ProgramDataDeserialized { - pub slot: u64, - pub upgrade_authority_address: String, - pub raw_program_data: String, -} - -#[derive(Serialize)] -pub struct ProgramDeserialized { - pub programdata_address: String, -} - -#[derive(Serialize)] -pub struct DisplayProgramDataAccount { - pub lamports: u64, - pub data: ProgramDataDeserialized, - pub owner: String, - pub executable: bool, - pub rent_epoch: u64, -} - -#[derive(Serialize)] -pub struct DisplayProgramAccount { - pub lamports: u64, - pub data: ProgramDeserialized, - pub owner: String, - pub executable: bool, - pub rent_epoch: u64, -} - -#[derive(Serialize)] -pub struct DisplayUpgradeableProgram { - pub program_id: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub program_account: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub programdata_account: Option, -} - -impl DisplayUpgradeableProgram { - pub fn from( - program_account: &KeyedAccount, - programdata_account: &KeyedAccount, - slot: u64, - upgrade_authority_address: &Option, - visibility: &ProgramFieldVisibility, - ) -> Self { - Self { - program_id: program_account.pubkey.to_string(), - program_account: if visibility.program_account { - Some(DisplayProgramAccount { - lamports: program_account.account.lamports, - data: ProgramDeserialized { - programdata_address: programdata_account.pubkey.to_string(), - }, - owner: program_account.account.owner.to_string(), - executable: program_account.account.executable, - rent_epoch: program_account.account.rent_epoch, - }) - } else { - None - }, - programdata_account: if visibility.programdata_account { - Some(DisplayProgramDataAccount { - lamports: programdata_account.account.lamports, - data: ProgramDataDeserialized { - slot, - upgrade_authority_address: upgrade_authority_address - .map(|pubkey| pubkey.to_string()) - .unwrap_or_else(|| "none".to_string()), - raw_program_data: base64::encode( - &programdata_account.account.data - [UpgradeableLoaderState::size_of_programdata_metadata()..], - ), - }, - owner: programdata_account.account.owner.to_string(), - executable: programdata_account.account.executable, - rent_epoch: programdata_account.account.rent_epoch, - }) - } else { - None - }, - } - } -} - -impl fmt::Display for DisplayUpgradeableProgram { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - writeln!( - f, - "========================================================" - )?; - writeln!(f, "{} {}", style("Program Id:").bold(), self.program_id)?; - writeln!( - f, - "========================================================" - )?; - - if let Some(program_account) = &self.program_account { - writeln!(f)?; - - writeln!(f, "{}", style("--> Program Account").bold(),)?; - - writeln!(f)?; - - writeln!( - f, - "{} {} (◎ {})", - style("Lamports:").bold(), - program_account.lamports, - pretty_lamports_to_sol(program_account.lamports) - )?; - writeln!( - f, - "{} [Deserialized and interpreted below]", - style("Data:").bold() - )?; - writeln!(f, "{} {}", style("Owner").bold(), program_account.owner)?; - writeln!( - f, - "{} {}", - style("Executable:").bold(), - program_account.executable - )?; - writeln!( - f, - "{} {}", - style("Rent Epoch:").bold(), - program_account.rent_epoch - )?; - - writeln!(f)?; - - writeln!(f, "{}", style("Deserialized:").bold())?; - write!(f, " - ")?; - write!( - f, - "{} {}", - style("ProgramData Address:").bold(), - program_account.data.programdata_address - )?; - - if self.programdata_account.is_some() { - writeln!(f)?; - } - } - - if let Some(programdata_account) = &self.programdata_account { - writeln!(f)?; - - writeln!(f, "{}", style("--> ProgramData Account").bold())?; - - writeln!(f)?; - - writeln!( - f, - "{} {} (◎ {})", - style("Lamports:").bold(), - programdata_account.lamports, - pretty_lamports_to_sol(programdata_account.lamports) - )?; - writeln!( - f, - "{} [Deserialized and interpreted below]", - style("Data:").bold() - )?; - writeln!(f, "{} {}", style("Owner").bold(), programdata_account.owner)?; - writeln!( - f, - "{} {}", - style("Executable:").bold(), - programdata_account.executable - )?; - writeln!( - f, - "{} {}", - style("Rent Epoch:").bold(), - programdata_account.rent_epoch - )?; - - writeln!(f)?; - - writeln!(f, "{}", style("Deserialized:").bold())?; - write!(f, " - ")?; - writeln!( - f, - "{} {}", - style("Last Deployed Slot:").bold(), - programdata_account.data.slot - )?; - write!(f, " - ")?; - write!( - f, - "{} {}", - style("Upgrade Authority:").bold(), - programdata_account.data.upgrade_authority_address - )?; - } - - Ok(()) - } -} diff --git a/crates/explorer/src/transaction.rs b/crates/explorer/src/transaction.rs deleted file mode 100644 index bd903dce..00000000 --- a/crates/explorer/src/transaction.rs +++ /dev/null @@ -1,788 +0,0 @@ -use crate::{ - error::Result, - output::{change_in_sol, classify_account, pretty_lamports_to_sol, status_to_string}, - parse::{parse, partially_parse}, -}; -use chrono::{TimeZone, Utc}; -use console::style; -use serde::Serialize; -use serde_json::Value; -use solana_program::message::VersionedMessage; -use solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey}; -use solana_transaction_status::{ - option_serializer::OptionSerializer, EncodedConfirmedTransactionWithStatusMeta, - EncodedTransactionWithStatusMeta, TransactionStatus, -}; -use std::fmt; - -pub struct RawTransactionFieldVisibility { - overview: bool, - transaction: bool, -} - -impl RawTransactionFieldVisibility { - pub fn new_all_enabled() -> Self { - Self { - overview: true, - transaction: true, - } - } - - pub fn new_all_disabled() -> Self { - Self { - overview: false, - transaction: false, - } - } - - pub fn overview(&self) -> bool { - self.overview - } - - pub fn enable_overview(&mut self) -> &mut Self { - self.overview = true; - self - } - - pub fn disable_overview(&mut self) -> &mut Self { - self.overview = false; - self - } - - pub fn transaction(&self) -> bool { - self.transaction - } - - pub fn enable_transaction(&mut self) -> &mut Self { - self.transaction = true; - self - } - - pub fn disable_transaction(&mut self) -> &mut Self { - self.transaction = false; - self - } -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct DisplayRawMessageHeader { - pub num_required_signatures: u8, - pub num_readonly_signed_accounts: u8, - pub num_readonly_unsigned_accounts: u8, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct DisplayRawInstruction { - pub program_id_index: u8, - pub accounts: Vec, - pub data: String, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct DisplayRawMessage { - pub header: DisplayRawMessageHeader, - pub account_keys: Vec, - pub recent_blockhash: String, - pub instructions: Vec, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct DisplayRawTransactionContent { - pub signatures: Vec, - pub message: DisplayRawMessage, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct DisplayRawTransactionOverview { - pub signature: String, - pub result: String, - pub timestamp: String, - pub confirmation_status: String, - pub confirmations: String, - pub slot: u64, - pub recent_blockhash: String, - pub fee: String, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct DisplayRawTransaction { - #[serde(skip_serializing_if = "Option::is_none")] - pub overview: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub transaction: Option, -} - -impl DisplayRawTransaction { - pub fn from( - transaction: &EncodedConfirmedTransactionWithStatusMeta, - transaction_status: &TransactionStatus, - visibility: &RawTransactionFieldVisibility, - ) -> Result { - let EncodedConfirmedTransactionWithStatusMeta { - slot, - transaction, - block_time, - } = transaction; - - let EncodedTransactionWithStatusMeta { - transaction, meta, .. - } = transaction; - - let decoded_transaction = transaction.decode().unwrap(); - - let message = decoded_transaction.message; - - let overview = if visibility.overview { - Some(DisplayRawTransactionOverview { - signature: decoded_transaction.signatures[0].to_string(), - result: meta - .as_ref() - .unwrap() - .err - .as_ref() - .map(|err| err.to_string()) - .unwrap_or_else(|| "Success".to_string()), - timestamp: Utc - .timestamp_opt(block_time.unwrap(), 0) - .unwrap() - .to_string(), - confirmation_status: status_to_string( - transaction_status.confirmation_status.as_ref().unwrap(), - ), - confirmations: transaction_status - .confirmations - .map_or_else(|| "MAX (32)".to_string(), |n| n.to_string()), - slot: *slot, - recent_blockhash: message.recent_blockhash().to_string(), - fee: format!("◎ {}", pretty_lamports_to_sol(meta.as_ref().unwrap().fee)), - }) - } else { - None - }; - - let transaction = if visibility.transaction { - Some(DisplayRawTransactionContent { - signatures: decoded_transaction - .signatures - .into_iter() - .map(|sig| sig.to_string()) - .collect(), - message: DisplayRawMessage { - header: DisplayRawMessageHeader { - num_required_signatures: message.header().num_required_signatures, - num_readonly_signed_accounts: message.header().num_readonly_signed_accounts, - num_readonly_unsigned_accounts: message - .header() - .num_readonly_unsigned_accounts, - }, - account_keys: message - .static_account_keys() - .iter() - .map(|key| key.to_string()) - .collect(), - recent_blockhash: message.recent_blockhash().to_string(), - instructions: message - .instructions() - .iter() - .map(|instruction| DisplayRawInstruction { - program_id_index: instruction.program_id_index, - accounts: instruction.accounts.clone(), - data: bs58::encode(instruction.data.clone()).into_string(), - }) - .collect(), - }, - }) - } else { - None - }; - - Ok(DisplayRawTransaction { - overview, - transaction, - }) - } -} - -impl fmt::Display for DisplayRawTransaction { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if let Some(overview) = &self.overview { - writeln!( - f, - "================================================================================" - )?; - writeln!(f, "{:^80}", style("Overview").bold())?; - writeln!( - f, - "================================================================================" - )?; - - writeln!(f)?; - - writeln!(f, "{} {}", style("Signature:").bold(), overview.signature)?; - writeln!(f, "{} {}", style("Result:").bold(), overview.result)?; - writeln!(f, "{} {}", style("Timestamp:").bold(), overview.timestamp)?; - writeln!( - f, - "{} {}", - style("Confirmation Status:").bold(), - overview.confirmation_status - )?; - writeln!( - f, - "{} {}", - style("Confirmations:").bold(), - overview.confirmations - )?; - writeln!(f, "{} {}", style("Slot:").bold(), overview.slot)?; - writeln!( - f, - "{} {}", - style("Recent Blockhash:").bold(), - overview.recent_blockhash - )?; - write!(f, "{} {}", style("Fee:").bold(), overview.fee)?; - } - - if self.overview.is_some() && self.transaction.is_some() { - writeln!(f)?; - writeln!(f)?; - } - - if let Some(transaction) = &self.transaction { - writeln!( - f, - "================================================================================" - )?; - writeln!(f, "{:^80}", style("Raw Transaction").bold())?; - writeln!( - f, - "================================================================================" - )?; - - writeln!(f)?; - - writeln!( - f, - "{}", - style(format!("Signatures ({}):", transaction.signatures.len())).bold() - )?; - - for (index, signature) in transaction.signatures.iter().enumerate() { - writeln!(f, " {:>2} {}", style(index).bold(), signature)?; - } - - writeln!(f)?; - - writeln!(f, "{}", style("Message:").bold())?; - - writeln!(f, " {}", style("Header:").bold())?; - - writeln!( - f, - " {} {}", - style("# of required signatures:").bold(), - transaction.message.header.num_required_signatures - )?; - - writeln!( - f, - " {} {}", - style("# of read-only signed accounts:").bold(), - transaction.message.header.num_readonly_signed_accounts - )?; - - writeln!( - f, - " {} {}", - style("# of read-only unsigned accounts:").bold(), - transaction.message.header.num_readonly_unsigned_accounts - )?; - - writeln!( - f, - " {}", - style(format!( - "Account Keys ({}):", - transaction.message.account_keys.len() - )) - .bold() - )?; - - for (index, account_key) in transaction.message.account_keys.iter().enumerate() { - writeln!(f, " {:>2} {}", style(index).bold(), account_key)?; - } - - writeln!(f, " {}", style("Recent Blockhash:").bold())?; - - writeln!(f, " {}", transaction.message.recent_blockhash)?; - - write!( - f, - " {}", - style(format!( - "Instructions ({}):", - transaction.message.instructions.len() - )) - .bold() - )?; - - for ( - index, - DisplayRawInstruction { - program_id_index, - accounts, - data, - }, - ) in transaction.message.instructions.iter().enumerate() - { - writeln!(f)?; - writeln!( - f, - " {:>2} {} {}", - style(index).bold(), - style("Program Id Index:").bold(), - program_id_index - )?; - writeln!( - f, - " {} {:?}", - style("Account Indices:").bold(), - accounts - )?; - write!(f, " {} {:?}", style("Data:").bold(), data)?; - } - } - - Ok(()) - } -} - -pub struct TransactionFieldVisibility { - overview: bool, - transaction: bool, - log_messages: bool, -} - -impl TransactionFieldVisibility { - pub fn new_all_enabled() -> Self { - Self { - overview: true, - transaction: true, - log_messages: true, - } - } - - pub fn new_all_disabled() -> Self { - Self { - overview: false, - transaction: false, - log_messages: false, - } - } - - pub fn overview(&self) -> bool { - self.overview - } - - pub fn enable_overview(&mut self) -> &mut Self { - self.overview = true; - self - } - - pub fn disable_overview(&mut self) -> &mut Self { - self.overview = false; - self - } - - pub fn transaction(&self) -> bool { - self.transaction - } - - pub fn enable_transaction(&mut self) -> &mut Self { - self.transaction = true; - self - } - - pub fn disable_transaction(&mut self) -> &mut Self { - self.transaction = false; - self - } - - pub fn log_messages(&self) -> bool { - self.log_messages - } - - pub fn enable_log_messages(&mut self) -> &mut Self { - self.log_messages = true; - self - } - - pub fn disable_log_messages(&mut self) -> &mut Self { - self.log_messages = false; - self - } -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct DisplayPartiallyParsedInstruction { - pub program_id: String, - pub accounts: Vec, - pub data: String, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct DisplayParsedInstruction { - pub program: String, - pub program_id: String, - pub parsed: Value, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub enum DisplayInstruction { - Parsed(DisplayParsedInstruction), - PartiallyParsed(DisplayPartiallyParsedInstruction), -} - -impl DisplayInstruction { - fn parse(instruction: &CompiledInstruction, account_keys: &[Pubkey]) -> Self { - let program_id = &account_keys[instruction.program_id_index as usize]; - if let Ok(parsed_instruction) = parse(program_id, instruction, account_keys) { - DisplayInstruction::Parsed(parsed_instruction) - } else { - DisplayInstruction::PartiallyParsed(partially_parse( - program_id, - instruction, - account_keys, - )) - } - } -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct DisplayInputAccount { - pub pubkey: String, - pub fee_payer: bool, - pub writable: bool, - pub signer: bool, - pub program: bool, - pub post_balance_in_sol: String, - pub balance_change_in_sol: String, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct DisplayTransactionContent { - pub accounts: Vec, - pub instructions: Vec, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct DisplayTransactionOverview { - pub signature: String, - pub result: String, - pub timestamp: String, - pub confirmation_status: String, - pub confirmations: String, - pub slot: u64, - pub recent_blockhash: String, - pub fee: String, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct DisplayTransaction { - #[serde(skip_serializing_if = "Option::is_none")] - pub overview: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub transaction: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub log_messages: Option>>, -} - -impl DisplayTransaction { - pub fn from( - transaction: &EncodedConfirmedTransactionWithStatusMeta, - transaction_status: &TransactionStatus, - visibility: &TransactionFieldVisibility, - ) -> Result { - let EncodedConfirmedTransactionWithStatusMeta { - slot, - transaction, - block_time, - } = transaction; - - let EncodedTransactionWithStatusMeta { - transaction, meta, .. - } = transaction; - - let decoded_transaction = transaction.decode().unwrap(); - - let message = decoded_transaction.message; - let overview = if visibility.overview { - Some(DisplayTransactionOverview { - signature: decoded_transaction.signatures[0].to_string(), - result: meta - .as_ref() - .unwrap() - .err - .as_ref() - .map(|err| err.to_string()) - .unwrap_or_else(|| "Success".to_string()), - timestamp: Utc - .timestamp_opt(block_time.unwrap(), 0) - .unwrap() - .to_string(), - confirmation_status: status_to_string( - transaction_status.confirmation_status.as_ref().unwrap(), - ), - confirmations: transaction_status - .confirmations - .map_or_else(|| "MAX (32)".to_string(), |n| n.to_string()), - slot: *slot, - recent_blockhash: message.recent_blockhash().to_string(), - fee: format!("◎ {}", pretty_lamports_to_sol(meta.as_ref().unwrap().fee)), - }) - } else { - None - }; - - let mut fee_payer_found = false; // always first account - let transaction = if visibility.transaction { - Some(DisplayTransactionContent { - accounts: message - .static_account_keys() - .iter() - .enumerate() - .map(|(index, account_key)| DisplayInputAccount { - pubkey: account_key.to_string(), - fee_payer: if !fee_payer_found { - fee_payer_found = true; - true - } else { - false - }, - writable: message.is_maybe_writable(index), - signer: message.is_signer(index), - program: match message.clone() { - VersionedMessage::Legacy(m) => m.maybe_executable(index), - VersionedMessage::V0(m) => m.is_key_called_as_program(index), - }, - post_balance_in_sol: pretty_lamports_to_sol( - meta.as_ref().unwrap().post_balances[index], - ), - balance_change_in_sol: change_in_sol( - meta.as_ref().unwrap().post_balances[index], - meta.as_ref().unwrap().pre_balances[index], - ), - }) - .collect(), - instructions: message - .instructions() - .iter() - .map(|instruction| { - DisplayInstruction::parse(instruction, message.static_account_keys()) - }) - .collect(), - }) - } else { - None - }; - - let log_messages = if visibility.log_messages { - Some(meta.as_ref().unwrap().log_messages.clone()) - } else { - None - }; - - Ok(DisplayTransaction { - overview, - transaction, - log_messages, - }) - } -} - -impl fmt::Display for DisplayTransaction { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if let Some(overview) = &self.overview { - writeln!( - f, - "================================================================================" - )?; - writeln!(f, "{:^80}", style("Overview").bold())?; - writeln!( - f, - "================================================================================" - )?; - - writeln!(f)?; - - writeln!(f, "{} {}", style("Signature:").bold(), overview.signature)?; - writeln!(f, "{} {}", style("Result:").bold(), overview.result)?; - writeln!(f, "{} {}", style("Timestamp:").bold(), overview.timestamp)?; - writeln!( - f, - "{} {}", - style("Confirmation Status:").bold(), - overview.confirmation_status - )?; - writeln!( - f, - "{} {}", - style("Confirmations:").bold(), - overview.confirmations - )?; - writeln!(f, "{} {}", style("Slot:").bold(), overview.slot)?; - writeln!( - f, - "{} {}", - style("Recent Blockhash:").bold(), - overview.recent_blockhash - )?; - write!(f, "{} {}", style("Fee:").bold(), overview.fee)?; - } - - if self.overview.is_some() && self.transaction.is_some() { - writeln!(f)?; - writeln!(f)?; - } - - if let Some(transaction) = &self.transaction { - writeln!( - f, - "================================================================================" - )?; - writeln!(f, "{:^80}", style("Transaction").bold())?; - writeln!( - f, - "================================================================================" - )?; - - writeln!(f)?; - - writeln!( - f, - "{}", - style(format!("Accounts ({}):", transaction.accounts.len())).bold() - )?; - - for (index, account) in transaction.accounts.iter().enumerate() { - let account_type_string = classify_account( - account.fee_payer, - account.writable, - account.signer, - account.program, - ); - - let balance_information_string = if account.balance_change_in_sol != "0" { - format!( - "◎ {} (◎ {})", - account.post_balance_in_sol, account.balance_change_in_sol - ) - } else { - format!("◎ {}", account.post_balance_in_sol) - }; - - writeln!( - f, - " {:>2} {:<44} {:31} {}", - style(index).bold(), - account.pubkey, - account_type_string, - balance_information_string - )?; - } - - writeln!(f)?; - - writeln!( - f, - "{}", - style(format!( - "Instructions ({}):", - transaction.instructions.len() - )) - .bold() - )?; - - for (index, instruction) in transaction.instructions.iter().enumerate() { - if let DisplayInstruction::Parsed(instruction) = instruction { - writeln!( - f, - " {:>2} {} {} {}", - style(index).bold(), - style(&instruction.program).bold(), - style("Program:").bold(), - instruction.parsed["type"].to_string().trim_matches('"') - )?; - writeln!(f, " [{}]", instruction.program_id)?; - for (name, value) in instruction.parsed["info"].as_object().unwrap() { - writeln!( - f, - " {}{} {}", - style(name).bold(), - style(":").bold(), - value - )?; - } - } else if let DisplayInstruction::PartiallyParsed(instruction) = instruction { - writeln!( - f, - " {:>2} {} Unknown Instruction", - style(index).bold(), - style("Unknown Program:").bold(), - )?; - writeln!(f, " [{}]", instruction.program_id)?; - for (index, account) in instruction.accounts.iter().enumerate() { - writeln!( - f, - " {} {}{} {:<44}", - style("Account").bold(), - style(index).bold(), - style(":").bold(), - account, - )?; - } - writeln!( - f, - " {} {:?}", - style("Data:").bold(), - bs58::encode(instruction.data.clone()).into_string() - )?; - } - writeln!(f)?; - } - } - - if self.overview.is_some() && self.transaction.is_none() && self.log_messages.is_some() { - writeln!(f)?; - writeln!(f)?; - } - - if let Some(OptionSerializer::Some(log_messages)) = &self.log_messages { - write!( - f, - "{}", - style(format!("Log Messages ({}):", log_messages.len())).bold() - )?; - - for (log_message_index, log_message) in log_messages.iter().enumerate() { - writeln!(f)?; - write!(f, " {:>2} {}", style(log_message_index).bold(), log_message)?; - } - } - - Ok(()) - } -} From ea027849f10e3d780e6a0289c4fcee60f97b3cc3 Mon Sep 17 00:00:00 2001 From: lukacan Date: Wed, 26 Jun 2024 10:38:33 +0200 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=94=A5=20remove=20tests=20for=20deriv?= =?UTF-8?q?e=20macros=20and=20update=20CHANGELOG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + .../fuzzer_macros/fuzz_display_ix.expanded.rs | 62 ----- .../fuzzer_macros/fuzz_display_ix.rs | 41 --- .../fuzz_fuzz_deserialize.expanded.rs | 58 ----- .../fuzzer_macros/fuzz_fuzz_deserialize.rs | 41 --- .../fuzz_fuzz_test_executor.expanded.rs | 243 ------------------ .../fuzzer_macros/fuzz_fuzz_test_executor.rs | 41 --- .../fuzz_fuzz_trident.expanded.rs | 25 -- .../fuzzer_macros/fuzz_fuzz_trident.rs | 12 - crates/client/tests/test_fuzz.rs | 39 --- 10 files changed, 1 insertion(+), 562 deletions(-) delete mode 100644 crates/client/tests/test_data/fuzzer_macros/fuzz_display_ix.expanded.rs delete mode 100644 crates/client/tests/test_data/fuzzer_macros/fuzz_display_ix.rs delete mode 100644 crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_deserialize.expanded.rs delete mode 100644 crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_deserialize.rs delete mode 100644 crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_test_executor.expanded.rs delete mode 100644 crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_test_executor.rs delete mode 100644 crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_trident.expanded.rs delete mode 100644 crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_trident.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 969566f1..2adc50cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 incremented upon a breaking change and the patch version will be incremented for features. ## [dev] - Unreleased +- del/remove Trident explorer ([#171](https://github.com/Ackee-Blockchain/trident/pull/171)) - fix/snapshot's zeroed account as optional ([#170](https://github.com/Ackee-Blockchain/trident/pull/170)) - feat/fuzzer-stats-logging, an optional statistics output for fuzzing session ([#144](https://github.com/Ackee-Blockchain/trident/pull/144)) diff --git a/crates/client/tests/test_data/fuzzer_macros/fuzz_display_ix.expanded.rs b/crates/client/tests/test_data/fuzzer_macros/fuzz_display_ix.expanded.rs deleted file mode 100644 index bdf5e32c..00000000 --- a/crates/client/tests/test_data/fuzzer_macros/fuzz_display_ix.expanded.rs +++ /dev/null @@ -1,62 +0,0 @@ -use trident_client::DisplayIx; -pub enum FuzzInstruction { - InitVesting(InitVesting), - WithdrawUnlocked(WithdrawUnlocked), -} -impl std::fmt::Display for FuzzInstruction { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - FuzzInstruction::InitVesting(ref content) => { - f.write_fmt(format_args!("InitVesting"))?; - f.write_fmt(format_args!("({0:#?})", content)) - } - FuzzInstruction::WithdrawUnlocked(ref content) => { - f.write_fmt(format_args!("WithdrawUnlocked"))?; - f.write_fmt(format_args!("({0:#?})", content)) - } - } - } -} -impl FuzzInstruction { - fn to_context_string(&self) -> String { - match self { - FuzzInstruction::InitVesting(_) => String::from("InitVesting"), - FuzzInstruction::WithdrawUnlocked(_) => String::from("WithdrawUnlocked"), - } - } -} -pub struct InitVesting { - pub accounts: InitVestingAccounts, - pub data: InitVestingData, -} -pub struct InitVestingAccounts { - pub sender: AccountId, - pub sender_token_account: AccountId, - pub escrow: AccountId, - pub escrow_token_account: AccountId, - pub mint: AccountId, - pub token_program: AccountId, - pub system_program: AccountId, -} -pub struct InitVestingData { - pub recipient: AccountId, - pub amount: u64, - pub start_at: u64, - pub end_at: u64, - pub interval: u64, -} -pub struct WithdrawUnlocked { - pub accounts: WithdrawUnlockedAccounts, - pub data: WithdrawUnlockedData, -} -pub struct WithdrawUnlockedAccounts { - pub recipient: AccountId, - pub recipient_token_account: AccountId, - pub escrow: AccountId, - pub escrow_token_account: AccountId, - pub escrow_pda_authority: AccountId, - pub mint: AccountId, - pub token_program: AccountId, - pub system_program: AccountId, -} -pub struct WithdrawUnlockedData {} diff --git a/crates/client/tests/test_data/fuzzer_macros/fuzz_display_ix.rs b/crates/client/tests/test_data/fuzzer_macros/fuzz_display_ix.rs deleted file mode 100644 index bfc315a2..00000000 --- a/crates/client/tests/test_data/fuzzer_macros/fuzz_display_ix.rs +++ /dev/null @@ -1,41 +0,0 @@ -use trident_client::DisplayIx; -#[derive(DisplayIx)] -pub enum FuzzInstruction { - InitVesting(InitVesting), - WithdrawUnlocked(WithdrawUnlocked), -} -pub struct InitVesting { - pub accounts: InitVestingAccounts, - pub data: InitVestingData, -} -pub struct InitVestingAccounts { - pub sender: AccountId, - pub sender_token_account: AccountId, - pub escrow: AccountId, - pub escrow_token_account: AccountId, - pub mint: AccountId, - pub token_program: AccountId, - pub system_program: AccountId, -} -pub struct InitVestingData { - pub recipient: AccountId, - pub amount: u64, - pub start_at: u64, - pub end_at: u64, - pub interval: u64, -} -pub struct WithdrawUnlocked { - pub accounts: WithdrawUnlockedAccounts, - pub data: WithdrawUnlockedData, -} -pub struct WithdrawUnlockedAccounts { - pub recipient: AccountId, - pub recipient_token_account: AccountId, - pub escrow: AccountId, - pub escrow_token_account: AccountId, - pub escrow_pda_authority: AccountId, - pub mint: AccountId, - pub token_program: AccountId, - pub system_program: AccountId, -} -pub struct WithdrawUnlockedData {} diff --git a/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_deserialize.expanded.rs b/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_deserialize.expanded.rs deleted file mode 100644 index 4774ac7a..00000000 --- a/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_deserialize.expanded.rs +++ /dev/null @@ -1,58 +0,0 @@ -use trident_client::FuzzDeserialize; -pub enum FuzzInstruction { - InitVesting(InitVesting), - WithdrawUnlocked(WithdrawUnlocked), -} -impl<'info> FuzzDeserialize<'info> for InitVesting { - type Ix = InitVestingSnapshot<'info>; - fn deserialize_option( - &self, - accounts: &'info mut [Option>], - ) -> Result { - Self::Ix::deserialize_option(accounts) - } -} -impl<'info> FuzzDeserialize<'info> for WithdrawUnlocked { - type Ix = WithdrawUnlockedSnapshot<'info>; - fn deserialize_option( - &self, - accounts: &'info mut [Option>], - ) -> Result { - Self::Ix::deserialize_option(accounts) - } -} -pub struct InitVesting { - pub accounts: InitVestingAccounts, - pub data: InitVestingData, -} -pub struct InitVestingAccounts { - pub sender: AccountId, - pub sender_token_account: AccountId, - pub escrow: AccountId, - pub escrow_token_account: AccountId, - pub mint: AccountId, - pub token_program: AccountId, - pub system_program: AccountId, -} -pub struct InitVestingData { - pub recipient: AccountId, - pub amount: u64, - pub start_at: u64, - pub end_at: u64, - pub interval: u64, -} -pub struct WithdrawUnlocked { - pub accounts: WithdrawUnlockedAccounts, - pub data: WithdrawUnlockedData, -} -pub struct WithdrawUnlockedAccounts { - pub recipient: AccountId, - pub recipient_token_account: AccountId, - pub escrow: AccountId, - pub escrow_token_account: AccountId, - pub escrow_pda_authority: AccountId, - pub mint: AccountId, - pub token_program: AccountId, - pub system_program: AccountId, -} -pub struct WithdrawUnlockedData {} diff --git a/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_deserialize.rs b/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_deserialize.rs deleted file mode 100644 index 04163f0e..00000000 --- a/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_deserialize.rs +++ /dev/null @@ -1,41 +0,0 @@ -use trident_client::FuzzDeserialize; -#[derive(FuzzDeserialize)] -pub enum FuzzInstruction { - InitVesting(InitVesting), - WithdrawUnlocked(WithdrawUnlocked), -} -pub struct InitVesting { - pub accounts: InitVestingAccounts, - pub data: InitVestingData, -} -pub struct InitVestingAccounts { - pub sender: AccountId, - pub sender_token_account: AccountId, - pub escrow: AccountId, - pub escrow_token_account: AccountId, - pub mint: AccountId, - pub token_program: AccountId, - pub system_program: AccountId, -} -pub struct InitVestingData { - pub recipient: AccountId, - pub amount: u64, - pub start_at: u64, - pub end_at: u64, - pub interval: u64, -} -pub struct WithdrawUnlocked { - pub accounts: WithdrawUnlockedAccounts, - pub data: WithdrawUnlockedData, -} -pub struct WithdrawUnlockedAccounts { - pub recipient: AccountId, - pub recipient_token_account: AccountId, - pub escrow: AccountId, - pub escrow_token_account: AccountId, - pub escrow_pda_authority: AccountId, - pub mint: AccountId, - pub token_program: AccountId, - pub system_program: AccountId, -} -pub struct WithdrawUnlockedData {} diff --git a/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_test_executor.expanded.rs b/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_test_executor.expanded.rs deleted file mode 100644 index 5f32ef5f..00000000 --- a/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_test_executor.expanded.rs +++ /dev/null @@ -1,243 +0,0 @@ -use trident_client::FuzzTestExecutor; -pub enum FuzzInstruction { - InitVesting(InitVesting), - WithdrawUnlocked(WithdrawUnlocked), -} -impl FuzzTestExecutor for FuzzInstruction { - fn run_fuzzer( - &self, - program_id: Pubkey, - accounts: &RefCell, - client: &mut impl FuzzClient, - sent_txs: &mut HashMap, - ) -> core::result::Result<(), FuzzClientErrorWithOrigin> { - match self { - FuzzInstruction::InitVesting(ix) => { - let (mut signers, metas) = ix - .get_accounts(client, &mut accounts.borrow_mut()) - .map_err(|e| { - e.with_origin(Origin::Instruction(self.to_context_string())) - }) - .expect("Accounts calculation expect"); - let mut snaphot = Snapshot::new(&metas, ix); - snaphot.capture_before(client).unwrap(); - let data = ix - .get_data(client, &mut accounts.borrow_mut()) - .map_err(|e| { - e.with_origin(Origin::Instruction(self.to_context_string())) - }) - .expect("Data calculation expect"); - let ixx = Instruction { - program_id, - accounts: metas.clone(), - data: data.data(), - }; - let mut transaction = Transaction::new_with_payer( - &[ixx], - Some(&client.payer().pubkey()), - ); - signers.push(client.payer().clone()); - let sig: Vec<&Keypair> = signers.iter().collect(); - transaction.sign(&sig, client.get_last_blockhash()); - let duplicate_tx = if false { - None - } else { - let message_hash = transaction.message().hash(); - sent_txs.insert(message_hash, ()) - }; - match duplicate_tx { - Some(_) => { - ::std::io::_eprint( - format_args!( - "\u{1b}[1;93mWarning\u{1b}[0m: Skipping duplicate instruction `{0}`\n", - self.to_context_string(), - ), - ); - } - None => { - let tx_result = client - .process_transaction(transaction) - .map_err(|e| { - e.with_origin(Origin::Instruction(self.to_context_string())) - }); - match tx_result { - Ok(_) => { - snaphot.capture_after(client).unwrap(); - let (acc_before, acc_after) = snaphot - .get_snapshot() - .map_err(|e| { - e.with_origin(Origin::Instruction(self.to_context_string())) - }) - .expect("Snapshot deserialization expect"); - if let Err(e) - = ix - .check(acc_before, acc_after, data) - .map_err(|e| { - e.with_origin(Origin::Instruction(self.to_context_string())) - }) - { - { - ::std::io::_eprint( - format_args!( - "\u{1b}[31mCRASH DETECTED!\u{1b}[0m Custom check after the {0} instruction did not pass!\n", - self.to_context_string(), - ), - ); - }; - { - #[cold] - #[track_caller] - #[inline(never)] - #[rustc_const_panic_str] - #[rustc_do_not_const_check] - const fn panic_cold_display( - arg: &T, - ) -> ! { - ::core::panicking::panic_display(arg) - } - panic_cold_display(&e); - } - } - } - Err(e) => { - let mut raw_accounts = snaphot.get_raw_pre_ix_accounts(); - ix.tx_error_handler(e, data, &mut raw_accounts)? - } - } - } - } - } - FuzzInstruction::WithdrawUnlocked(ix) => { - let (mut signers, metas) = ix - .get_accounts(client, &mut accounts.borrow_mut()) - .map_err(|e| { - e.with_origin(Origin::Instruction(self.to_context_string())) - }) - .expect("Accounts calculation expect"); - let mut snaphot = Snapshot::new(&metas, ix); - snaphot.capture_before(client).unwrap(); - let data = ix - .get_data(client, &mut accounts.borrow_mut()) - .map_err(|e| { - e.with_origin(Origin::Instruction(self.to_context_string())) - }) - .expect("Data calculation expect"); - let ixx = Instruction { - program_id, - accounts: metas.clone(), - data: data.data(), - }; - let mut transaction = Transaction::new_with_payer( - &[ixx], - Some(&client.payer().pubkey()), - ); - signers.push(client.payer().clone()); - let sig: Vec<&Keypair> = signers.iter().collect(); - transaction.sign(&sig, client.get_last_blockhash()); - let duplicate_tx = if false { - None - } else { - let message_hash = transaction.message().hash(); - sent_txs.insert(message_hash, ()) - }; - match duplicate_tx { - Some(_) => { - ::std::io::_eprint( - format_args!( - "\u{1b}[1;93mWarning\u{1b}[0m: Skipping duplicate instruction `{0}`\n", - self.to_context_string(), - ), - ); - } - None => { - let tx_result = client - .process_transaction(transaction) - .map_err(|e| { - e.with_origin(Origin::Instruction(self.to_context_string())) - }); - match tx_result { - Ok(_) => { - snaphot.capture_after(client).unwrap(); - let (acc_before, acc_after) = snaphot - .get_snapshot() - .map_err(|e| { - e.with_origin(Origin::Instruction(self.to_context_string())) - }) - .expect("Snapshot deserialization expect"); - if let Err(e) - = ix - .check(acc_before, acc_after, data) - .map_err(|e| { - e.with_origin(Origin::Instruction(self.to_context_string())) - }) - { - { - ::std::io::_eprint( - format_args!( - "\u{1b}[31mCRASH DETECTED!\u{1b}[0m Custom check after the {0} instruction did not pass!\n", - self.to_context_string(), - ), - ); - }; - { - #[cold] - #[track_caller] - #[inline(never)] - #[rustc_const_panic_str] - #[rustc_do_not_const_check] - const fn panic_cold_display( - arg: &T, - ) -> ! { - ::core::panicking::panic_display(arg) - } - panic_cold_display(&e); - } - } - } - Err(e) => { - let mut raw_accounts = snaphot.get_raw_pre_ix_accounts(); - ix.tx_error_handler(e, data, &mut raw_accounts)? - } - } - } - } - } - } - Ok(()) - } -} -pub struct InitVesting { - pub accounts: InitVestingAccounts, - pub data: InitVestingData, -} -pub struct InitVestingAccounts { - pub sender: AccountId, - pub sender_token_account: AccountId, - pub escrow: AccountId, - pub escrow_token_account: AccountId, - pub mint: AccountId, - pub token_program: AccountId, - pub system_program: AccountId, -} -pub struct InitVestingData { - pub recipient: AccountId, - pub amount: u64, - pub start_at: u64, - pub end_at: u64, - pub interval: u64, -} -pub struct WithdrawUnlocked { - pub accounts: WithdrawUnlockedAccounts, - pub data: WithdrawUnlockedData, -} -pub struct WithdrawUnlockedAccounts { - pub recipient: AccountId, - pub recipient_token_account: AccountId, - pub escrow: AccountId, - pub escrow_token_account: AccountId, - pub escrow_pda_authority: AccountId, - pub mint: AccountId, - pub token_program: AccountId, - pub system_program: AccountId, -} -pub struct WithdrawUnlockedData {} diff --git a/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_test_executor.rs b/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_test_executor.rs deleted file mode 100644 index 5c475d87..00000000 --- a/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_test_executor.rs +++ /dev/null @@ -1,41 +0,0 @@ -use trident_client::FuzzTestExecutor; -#[derive(FuzzTestExecutor)] -pub enum FuzzInstruction { - InitVesting(InitVesting), - WithdrawUnlocked(WithdrawUnlocked), -} -pub struct InitVesting { - pub accounts: InitVestingAccounts, - pub data: InitVestingData, -} -pub struct InitVestingAccounts { - pub sender: AccountId, - pub sender_token_account: AccountId, - pub escrow: AccountId, - pub escrow_token_account: AccountId, - pub mint: AccountId, - pub token_program: AccountId, - pub system_program: AccountId, -} -pub struct InitVestingData { - pub recipient: AccountId, - pub amount: u64, - pub start_at: u64, - pub end_at: u64, - pub interval: u64, -} -pub struct WithdrawUnlocked { - pub accounts: WithdrawUnlockedAccounts, - pub data: WithdrawUnlockedData, -} -pub struct WithdrawUnlockedAccounts { - pub recipient: AccountId, - pub recipient_token_account: AccountId, - pub escrow: AccountId, - pub escrow_token_account: AccountId, - pub escrow_pda_authority: AccountId, - pub mint: AccountId, - pub token_program: AccountId, - pub system_program: AccountId, -} -pub struct WithdrawUnlockedData {} diff --git a/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_trident.expanded.rs b/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_trident.expanded.rs deleted file mode 100644 index 505f0903..00000000 --- a/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_trident.expanded.rs +++ /dev/null @@ -1,25 +0,0 @@ -use trident_client::fuzz_trident; -fn main() { - loop { - fuzz(|fuzz_data| { - let mut fuzz_data: FuzzData = { - use arbitrary::Unstructured; - let mut buf = Unstructured::new(fuzz_data); - if let Ok(fuzz_data) = build_ix_fuzz_data(MyFuzzData {}, &mut buf) { - fuzz_data - } else { - return; - } - }; - { - let mut client = ProgramTestClientBlocking::new( - PROGRAM_NAME, - PROGRAM_ID, - xyz, - ) - .unwrap(); - let _ = fuzz_data.run_with_runtime(PROGRAM_ID, &mut client); - } - }); - } -} diff --git a/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_trident.rs b/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_trident.rs deleted file mode 100644 index 6e4f45fd..00000000 --- a/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_trident.rs +++ /dev/null @@ -1,12 +0,0 @@ -use trident_client::fuzz_trident; - -fn main() { - loop { - fuzz_trident!(fuzz_ix: FuzzInstruction, |fuzz_data: MyFuzzData| { - let mut client = - ProgramTestClientBlocking::new(PROGRAM_NAME, PROGRAM_ID, xyz) - .unwrap(); - let _ = fuzz_data.run_with_runtime(PROGRAM_ID, &mut client); - }); - } -} diff --git a/crates/client/tests/test_fuzz.rs b/crates/client/tests/test_fuzz.rs index 5edb38c9..fc278399 100644 --- a/crates/client/tests/test_fuzz.rs +++ b/crates/client/tests/test_fuzz.rs @@ -59,42 +59,3 @@ async fn test_snapshots_and_instructions() { assert_str_eq!(fuzzer_snapshots, expected_accounts_snapshots); assert_str_eq!(fuzz_instructions_code, expected_fuzz_instructions_code); } - -#[throws] -#[tokio::test] -async fn test_display_ix() { - // this will automatically create expanded code within the same directory - // with ".expanded.rs" extension, if the file does not exist already. - // Do not perform any formatting command on the expanded code - // the test will then fail - macrotest::expand("tests/test_data/fuzzer_macros/fuzz_display_ix.rs"); -} -#[throws] -#[tokio::test] -async fn test_fuzz_deserialize() { - // this will automatically create expanded code within the same directory - // with ".expanded.rs" extension, if the file does not exist already. - // Do not perform any formatting command on the expanded code - // the test will then fail - macrotest::expand("tests/test_data/fuzzer_macros/fuzz_fuzz_deserialize.rs"); -} - -#[throws] -#[tokio::test] -async fn test_fuzz_test_executor() { - // this will automatically create expanded code within the same directory - // with ".expanded.rs" extension, if the file does not exist already. - // Do not perform any formatting command on the expanded code - // the test will then fail - macrotest::expand("tests/test_data/fuzzer_macros/fuzz_fuzz_test_executor.rs"); -} - -#[throws] -#[tokio::test] -async fn test_fuzz_trident() { - // this will automatically created expanded code within the same directory - // with ".expanded.rs" extension, if the file does not exist already. - // Do not perform any formatting command on the expanded code - // the test will then fail - macrotest::expand("tests/test_data/fuzzer_macros/fuzz_fuzz_trident.rs"); -}