From ad4d61f962014fe5e9185eb5efb7b1375c97d4cb Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Wed, 9 Oct 2024 11:38:11 +0100 Subject: [PATCH] chore: agave 2.x validator in sealevel e2e (#4643) ### Description Solana Mainnet is hard forking in 1-2 weeks and one of the RPC methods we were using will be removed. This PR updates that RPC and runs e2e with an agave `v2.x` network to ensure compatibility. As in the [svm warp route guide](https://docs.hyperlane.xyz/docs/guides/deploy-svm-warp-route), we still use `v1.14.x` to compile programs. More details: - `v2.0` migration [guide](https://github.com/anza-xyz/agave/wiki/Agave-v2.0-Transition-Guide) - `v2.0` release [schedule](https://github.com/anza-xyz/agave/wiki/v2.0-Release-Schedule) - `solana` to `agave` client transition [guide](https://github.com/anza-xyz/agave/wiki/Agave-Transition) ### Drive-by changes The solana cli installer logic in e2e is parameterized with `version` and `url` ### Backward compatibility Yes - no dependency upgrade was needed to switch to the new rpc method ### Testing E2E for general network compatiblity. The Jito submission retry logic (where the new rpc is used) wasn't tested - I'm confident it works but this is non-critical, as failed submission will eventually end up in the prep queue again --- .../chains/hyperlane-sealevel/src/mailbox.rs | 15 +++++------ rust/main/utils/run-locally/src/main.rs | 16 +++++++++--- rust/main/utils/run-locally/src/solana.rs | 25 +++++++++++++++---- rust/sealevel/client/src/cmd_utils.rs | 6 +++++ rust/sealevel/client/src/main.rs | 3 ++- 5 files changed, 49 insertions(+), 16 deletions(-) diff --git a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs index e919b2de7..f101435c2 100644 --- a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs +++ b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs @@ -33,6 +33,7 @@ use solana_client::{ rpc_client::SerializableTransaction, rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig, RpcSendTransactionConfig}, rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType}, + rpc_response::Response, }; use solana_sdk::{ account::Account, @@ -48,7 +49,7 @@ use solana_sdk::{ transaction::{Transaction, VersionedTransaction}, }; use solana_transaction_status::{ - EncodedConfirmedBlock, EncodedTransaction, EncodedTransactionWithStatusMeta, + EncodedConfirmedBlock, EncodedTransaction, EncodedTransactionWithStatusMeta, TransactionStatus, UiInnerInstructions, UiInstruction, UiMessage, UiParsedInstruction, UiReturnDataEncoding, UiTransaction, UiTransactionReturnData, UiTransactionStatusMeta, }; @@ -354,15 +355,15 @@ impl SealevelMailbox { }; for status_retry in 0..GET_STATUS_RETRIES { - match self + let signature_statuses: Response>> = self .provider .rpc() - .get_signature_status(&signature) + .get_signature_statuses(&[*signature]) .await - .map_err(ChainCommunicationError::from_other)? - { - Some(Ok(_)) => return Ok(*signature), - Some(Err(e)) => return Err(ChainCommunicationError::from_other(e)), + .map_err(ChainCommunicationError::from_other)?; + let signature_status = signature_statuses.value.first().cloned().flatten(); + match signature_status { + Some(_) => return Ok(*signature), None => { if !self .provider diff --git a/rust/main/utils/run-locally/src/main.rs b/rust/main/utils/run-locally/src/main.rs index 65ff63199..ac9166063 100644 --- a/rust/main/utils/run-locally/src/main.rs +++ b/rust/main/utils/run-locally/src/main.rs @@ -313,7 +313,11 @@ fn main() -> ExitCode { // let solana_paths = if config.sealevel_enabled { - let (solana_path, solana_path_tempdir) = install_solana_cli_tools().join(); + let (solana_path, solana_path_tempdir) = install_solana_cli_tools( + SOLANA_CONTRACTS_CLI_RELEASE_URL.to_owned(), + SOLANA_CONTRACTS_CLI_VERSION.to_owned(), + ) + .join(); state.data.push(Box::new(solana_path_tempdir)); let solana_program_builder = build_solana_programs(solana_path.clone()); Some((solana_program_builder.join(), solana_path)) @@ -358,8 +362,14 @@ fn main() -> ExitCode { } let solana_ledger_dir = tempdir().unwrap(); - let solana_config_path = if let Some((solana_program_path, solana_path)) = solana_paths.clone() - { + let solana_config_path = if let Some((solana_program_path, _)) = solana_paths.clone() { + // use the agave 2.x validator version to ensure mainnet compatibility + let (solana_path, solana_path_tempdir) = install_solana_cli_tools( + SOLANA_NETWORK_CLI_RELEASE_URL.to_owned(), + SOLANA_NETWORK_CLI_VERSION.to_owned(), + ) + .join(); + state.data.push(Box::new(solana_path_tempdir)); let start_solana_validator = start_solana_test_validator( solana_path.clone(), solana_program_path, diff --git a/rust/main/utils/run-locally/src/solana.rs b/rust/main/utils/run-locally/src/solana.rs index e3c8956b7..022d086a6 100644 --- a/rust/main/utils/run-locally/src/solana.rs +++ b/rust/main/utils/run-locally/src/solana.rs @@ -13,8 +13,14 @@ use crate::program::Program; use crate::utils::{as_task, concat_path, AgentHandles, ArbitraryData, TaskHandle}; use crate::SOLANA_AGNET_BIN_PATH; -/// The Solana CLI tool version to download and use. -const SOLANA_CLI_VERSION: &str = "1.14.20"; +/// Solana CLI version for compiling programs +pub const SOLANA_CONTRACTS_CLI_VERSION: &str = "1.14.20"; +pub const SOLANA_CONTRACTS_CLI_RELEASE_URL: &str = "github.com/solana-labs/solana"; + +/// Solana version used by mainnet validators +pub const SOLANA_NETWORK_CLI_VERSION: &str = "2.0.13"; +pub const SOLANA_NETWORK_CLI_RELEASE_URL: &str = "github.com/anza-xyz/agave"; + const SOLANA_PROGRAM_LIBRARY_ARCHIVE: &str = "https://github.com/hyperlane-xyz/solana-program-library/releases/download/2024-08-23/spl.tar.gz"; @@ -71,10 +77,17 @@ const SOLANA_OVERHEAD_CONFIG_FILE: &str = "../sealevel/environments/local-e2e/ov // Install the CLI tools and return the path to the bin dir. #[apply(as_task)] -pub fn install_solana_cli_tools() -> (PathBuf, impl ArbitraryData) { +pub fn install_solana_cli_tools( + release_url: String, + release_version: String, +) -> (PathBuf, impl ArbitraryData) { let solana_download_dir = tempdir().unwrap(); let solana_tools_dir = tempdir().unwrap(); - log!("Downloading solana cli release v{}", SOLANA_CLI_VERSION); + log!( + "Downloading solana cli release v{} from {}", + release_version, + release_url + ); let solana_release_name = { // best effort to pick one of the supported targets let target = if cfg!(target_os = "linux") { @@ -97,7 +110,9 @@ pub fn install_solana_cli_tools() -> (PathBuf, impl ArbitraryData) { Program::new("curl") .arg("output", &solana_archive_name) .flag("location") - .cmd(format!("https://github.com/solana-labs/solana/releases/download/v{SOLANA_CLI_VERSION}/{solana_archive_name}")) + .cmd(format!( + "https://{release_url}/releases/download/v{release_version}/{solana_archive_name}" + )) .flag("silent") .working_dir(solana_download_dir.as_ref().to_str().unwrap()) .run() diff --git a/rust/sealevel/client/src/cmd_utils.rs b/rust/sealevel/client/src/cmd_utils.rs index 872725112..e0cd3ff68 100644 --- a/rust/sealevel/client/src/cmd_utils.rs +++ b/rust/sealevel/client/src/cmd_utils.rs @@ -4,6 +4,8 @@ use std::{ io::Write, path::{Path, PathBuf}, process::{Command, Stdio}, + thread::sleep, + time::Duration, }; use solana_client::{client_error::ClientError, rpc_client::RpcClient}; @@ -77,6 +79,10 @@ pub(crate) fn deploy_program( } build_cmd(command.as_slice(), None, None); + + // TODO: use commitment level instead of just sleeping here? + println!("Sleeping for 2 seconds to allow program to be deployed"); + sleep(Duration::from_secs(2)); } pub(crate) fn create_new_directory(parent_dir: &Path, name: &str) -> PathBuf { diff --git a/rust/sealevel/client/src/main.rs b/rust/sealevel/client/src/main.rs index 8e9f3e586..bf014785c 100644 --- a/rust/sealevel/client/src/main.rs +++ b/rust/sealevel/client/src/main.rs @@ -895,7 +895,7 @@ fn process_mailbox_cmd(ctx: Context, cmd: MailboxCmd) { }; } -fn process_token_cmd(ctx: Context, cmd: TokenCmd) { +fn process_token_cmd(mut ctx: Context, cmd: TokenCmd) { match cmd.cmd { TokenSubCmd::Query(query) => { let (token_account, token_bump) = @@ -1024,6 +1024,7 @@ fn process_token_cmd(ctx: Context, cmd: TokenCmd) { } TokenSubCmd::TransferRemote(xfer) => { is_keypair(&xfer.sender).unwrap(); + ctx.commitment = CommitmentConfig::finalized(); let sender = read_keypair_file(xfer.sender).unwrap(); let recipient = if xfer.recipient.starts_with("0x") {