diff --git a/rust/main/Cargo.lock b/rust/main/Cargo.lock index 110954d2f7..e29131af94 100644 --- a/rust/main/Cargo.lock +++ b/rust/main/Cargo.lock @@ -4639,6 +4639,7 @@ dependencies = [ "serializable-account-meta", "solana-account-decoder", "solana-client", + "solana-program", "solana-sdk", "solana-transaction-status", "thiserror", diff --git a/rust/main/Cargo.toml b/rust/main/Cargo.toml index 6a0b7cbb35..72e3d6348d 100644 --- a/rust/main/Cargo.toml +++ b/rust/main/Cargo.toml @@ -127,6 +127,7 @@ sha256 = "1.1.4" sha3 = "0.10" solana-account-decoder = "=1.14.13" solana-client = "=1.14.13" +solana-program = "=1.14.13" solana-sdk = "=1.14.13" solana-transaction-status = "=1.14.13" static_assertions = "1.1" diff --git a/rust/main/agents/relayer/src/msg/processor.rs b/rust/main/agents/relayer/src/msg/processor.rs index 59ec32cb69..21c6b1def1 100644 --- a/rust/main/agents/relayer/src/msg/processor.rs +++ b/rust/main/agents/relayer/src/msg/processor.rs @@ -86,24 +86,29 @@ impl ForwardBackwardIterator { loop { let high_nonce_message_status = self.high_nonce_iter.try_get_next_nonce(metrics)?; let low_nonce_message_status = self.low_nonce_iter.try_get_next_nonce(metrics)?; - // Always prioritize the high nonce message + match (high_nonce_message_status, low_nonce_message_status) { - // Keep iterating if only processed messages are found + // Always prioritize advancing the the high nonce iterator, as + // we have a preference for higher nonces (MessageStatus::Processed, _) => { self.high_nonce_iter.iterate(); } - (_, MessageStatus::Processed) => { - self.low_nonce_iter.iterate(); - } - // Otherwise return - either a processable message or nothing to process (MessageStatus::Processable(high_nonce_message), _) => { self.high_nonce_iter.iterate(); return Ok(Some(high_nonce_message)); } + + // Low nonce messages are only processed if the high nonce iterator + // can't make any progress + (_, MessageStatus::Processed) => { + self.low_nonce_iter.iterate(); + } (_, MessageStatus::Processable(low_nonce_message)) => { self.low_nonce_iter.iterate(); return Ok(Some(low_nonce_message)); } + + // If both iterators give us unindexed messages, there are no messages at the moment (MessageStatus::Unindexed, MessageStatus::Unindexed) => return Ok(None), } // This loop may iterate through millions of processed messages, blocking the runtime. @@ -157,7 +162,7 @@ impl DirectionalNonceIterator { } fn try_get_next_nonce( - &mut self, + &self, metrics: &MessageProcessorMetrics, ) -> Result> { if let Some(message) = self.indexed_message_with_nonce()? { diff --git a/rust/main/agents/relayer/src/server/message_retry.rs b/rust/main/agents/relayer/src/server/message_retry.rs index 6d04eed86a..6d160355a5 100644 --- a/rust/main/agents/relayer/src/server/message_retry.rs +++ b/rust/main/agents/relayer/src/server/message_retry.rs @@ -96,9 +96,11 @@ mod tests { let (addr, mut rx) = setup_test_server(); let client = reqwest::Client::new(); - let mut message = HyperlaneMessage::default(); - // Use a random destination domain - message.destination = 42; + let message = HyperlaneMessage { + // Use a random destination domain + destination: 42, + ..Default::default() + }; let pending_operation = MockPendingOperation::with_message_data(message.clone()); let matching_list_body = json!([ { @@ -127,9 +129,11 @@ mod tests { let (addr, mut rx) = setup_test_server(); let client = reqwest::Client::new(); - let mut message = HyperlaneMessage::default(); - // Use a random origin domain - message.origin = 42; + let message = HyperlaneMessage { + // Use a random origin domain + origin: 42, + ..Default::default() + }; let pending_operation = MockPendingOperation::with_message_data(message.clone()); let matching_list_body = json!([ { @@ -216,9 +220,11 @@ mod tests { let (addr, mut rx) = setup_test_server(); let client = reqwest::Client::new(); - let mut message = HyperlaneMessage::default(); - // Use a random origin domain - message.origin = 42; + let message = HyperlaneMessage { + // Use a random origin domain + origin: 42, + ..Default::default() + }; let pending_operation = MockPendingOperation::with_message_data(message.clone()); let matching_list_body = json!([ { diff --git a/rust/main/agents/validator/src/submit.rs b/rust/main/agents/validator/src/submit.rs index 954b8d0d95..f4779c6656 100644 --- a/rust/main/agents/validator/src/submit.rs +++ b/rust/main/agents/validator/src/submit.rs @@ -559,7 +559,7 @@ mod test { let unix_timestamp = chrono::Utc::now().timestamp() as u64; let expected_reorg_period = 12; - let pre_reorg_merke_insertions = vec![ + let pre_reorg_merke_insertions = [ MerkleTreeInsertion::new(0, H256::random()), MerkleTreeInsertion::new(1, H256::random()), MerkleTreeInsertion::new(2, H256::random()), @@ -570,9 +570,9 @@ mod test { } // the last leaf is different post-reorg - let post_reorg_merkle_insertions = vec![ - pre_reorg_merke_insertions[0].clone(), - pre_reorg_merke_insertions[1].clone(), + let post_reorg_merkle_insertions = [ + pre_reorg_merke_insertions[0], + pre_reorg_merke_insertions[1], MerkleTreeInsertion::new(2, H256::random()), ]; let mut mock_onchain_merkle_tree = IncrementalMerkle::default(); @@ -589,9 +589,7 @@ mod test { // the db returns the pre-reorg merkle tree insertions let mut db = MockDb::new(); db.expect_retrieve_merkle_tree_insertion_by_leaf_index() - .returning(move |sequence| { - Ok(Some(pre_reorg_merke_insertions[*sequence as usize].clone())) - }); + .returning(move |sequence| Ok(Some(pre_reorg_merke_insertions[*sequence as usize]))); // boilerplate mocks let mut mock_merkle_tree_hook = MockMerkleTreeHook::new(); diff --git a/rust/main/chains/hyperlane-cosmos/src/libs/account/tests.rs b/rust/main/chains/hyperlane-cosmos/src/libs/account/tests.rs index 0ba8f73d74..1dc7d30331 100644 --- a/rust/main/chains/hyperlane-cosmos/src/libs/account/tests.rs +++ b/rust/main/chains/hyperlane-cosmos/src/libs/account/tests.rs @@ -61,14 +61,14 @@ fn test_ethereum_style() { fn compressed_public_key() -> PublicKey { let hex = hex::decode(COMPRESSED_PUBLIC_KEY).unwrap(); let tendermint = tendermint::PublicKey::from_raw_secp256k1(&hex).unwrap(); - let pub_key = PublicKey::from(tendermint); - pub_key + + PublicKey::from(tendermint) } fn decompressed_public_key() -> PublicKey { let hex = hex::decode(COMPRESSED_PUBLIC_KEY).unwrap(); let decompressed = decompress_public_key(&hex).unwrap(); let tendermint = tendermint::PublicKey::from_raw_secp256k1(&decompressed).unwrap(); - let pub_key = PublicKey::from(tendermint); - pub_key + + PublicKey::from(tendermint) } diff --git a/rust/main/chains/hyperlane-cosmos/src/providers/cosmos/provider/parse.rs b/rust/main/chains/hyperlane-cosmos/src/providers/cosmos/provider/parse.rs index aac9b7ce5a..a2212f6128 100644 --- a/rust/main/chains/hyperlane-cosmos/src/providers/cosmos/provider/parse.rs +++ b/rust/main/chains/hyperlane-cosmos/src/providers/cosmos/provider/parse.rs @@ -153,7 +153,7 @@ mod tests { fn encode_proto(msg: &MsgRecvPacket) -> Any { let mut buf = Vec::with_capacity(msg.encoded_len()); - MsgRecvPacket::encode(&msg, &mut buf).unwrap(); + MsgRecvPacket::encode(msg, &mut buf).unwrap(); Any { type_url: "".to_string(), diff --git a/rust/main/chains/hyperlane-sealevel/Cargo.toml b/rust/main/chains/hyperlane-sealevel/Cargo.toml index 666ebee877..171e7b31ba 100644 --- a/rust/main/chains/hyperlane-sealevel/Cargo.toml +++ b/rust/main/chains/hyperlane-sealevel/Cargo.toml @@ -18,6 +18,7 @@ serde.workspace = true serde_json.workspace = true solana-account-decoder.workspace = true solana-client.workspace = true +solana-program.workspace = true solana-sdk.workspace = true solana-transaction-status.workspace = true thiserror.workspace = true diff --git a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs index d64963e242..66e58131aa 100644 --- a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs +++ b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs @@ -1,6 +1,7 @@ -#![allow(warnings)] // FIXME remove +// Silence a clippy bug https://github.com/rust-lang/rust-clippy/issues/12281 +#![allow(clippy::blocks_in_conditions)] -use std::{collections::HashMap, num::NonZeroU64, ops::RangeInclusive, str::FromStr as _}; +use std::{collections::HashMap, ops::RangeInclusive, str::FromStr as _}; use async_trait::async_trait; use borsh::{BorshDeserialize, BorshSerialize}; @@ -9,10 +10,9 @@ use hyperlane_sealevel_interchain_security_module_interface::{ }; use hyperlane_sealevel_mailbox::{ accounts::{ - DispatchedMessageAccount, Inbox, InboxAccount, OutboxAccount, ProcessedMessage, - ProcessedMessageAccount, DISPATCHED_MESSAGE_DISCRIMINATOR, PROCESSED_MESSAGE_DISCRIMINATOR, + DispatchedMessageAccount, Inbox, InboxAccount, ProcessedMessageAccount, + DISPATCHED_MESSAGE_DISCRIMINATOR, PROCESSED_MESSAGE_DISCRIMINATOR, }, - instruction, instruction::InboxProcess, mailbox_dispatched_message_pda_seeds, mailbox_inbox_pda_seeds, mailbox_outbox_pda_seeds, mailbox_process_authority_pda_seeds, mailbox_processed_message_pda_seeds, @@ -20,54 +20,36 @@ use hyperlane_sealevel_mailbox::{ use hyperlane_sealevel_message_recipient_interface::{ HandleInstruction, MessageRecipientInstruction, }; -use jsonrpc_core::futures_util::TryFutureExt; +use lazy_static::lazy_static; use serializable_account_meta::SimulationReturnData; -use solana_account_decoder::{UiAccountEncoding, UiDataSliceConfig}; -use solana_client::{ - nonblocking::rpc_client::RpcClient, - rpc_client::SerializableTransaction, - rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig, RpcSendTransactionConfig}, - rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType}, - rpc_response::Response, -}; +use solana_client::{rpc_client::SerializableTransaction, rpc_response::Response}; +use solana_program::pubkey; use solana_sdk::{ account::Account, bs58, clock::Slot, commitment_config::CommitmentConfig, compute_budget::ComputeBudgetInstruction, - hash::Hash, instruction::{AccountMeta, Instruction}, - message::Message, pubkey::Pubkey, signature::Signature, signer::{keypair::Keypair, Signer as _}, - transaction::{Transaction, VersionedTransaction}, -}; -use solana_transaction_status::{ - EncodedConfirmedBlock, EncodedTransaction, EncodedTransactionWithStatusMeta, TransactionStatus, - UiCompiledInstruction, UiConfirmedBlock, UiInnerInstructions, UiInstruction, UiMessage, - UiParsedInstruction, UiReturnDataEncoding, UiTransaction, UiTransactionReturnData, - UiTransactionStatusMeta, + transaction::Transaction, }; +use solana_transaction_status::TransactionStatus; use tracing::{debug, info, instrument, warn}; use hyperlane_core::{ - accumulator::incremental::IncrementalMerkle, config::StrOrIntParseError, BatchItem, - ChainCommunicationError, ChainCommunicationError::ContractError, ChainResult, Checkpoint, - ContractLocator, Decode as _, Encode as _, FixedPointNumber, HyperlaneAbi, HyperlaneChain, - HyperlaneContract, HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, Indexed, Indexer, - KnownHyperlaneDomain, LogMeta, Mailbox, MerkleTreeHook, ReorgPeriod, SequenceAwareIndexer, - TxCostEstimate, TxOutcome, H256, H512, U256, + config::StrOrIntParseError, ChainCommunicationError, ChainResult, ContractLocator, Decode as _, + Encode as _, FixedPointNumber, HyperlaneChain, HyperlaneContract, HyperlaneDomain, + HyperlaneMessage, HyperlaneProvider, Indexed, Indexer, KnownHyperlaneDomain, LogMeta, Mailbox, + MerkleTreeHook, ReorgPeriod, SequenceAwareIndexer, TxCostEstimate, TxOutcome, H256, H512, U256, }; use crate::account::{search_accounts_by_discriminator, search_and_validate_account}; -use crate::error::HyperlaneSealevelError; use crate::log_meta_composer::{ - is_interchain_payment_instruction, is_message_delivery_instruction, - is_message_dispatch_instruction, LogMetaComposer, + is_message_delivery_instruction, is_message_dispatch_instruction, LogMetaComposer, }; -use crate::utils::{decode_h256, decode_h512, from_base58}; use crate::{ConnectionConf, SealevelProvider, SealevelRpcClient}; const SYSTEM_PROGRAM: &str = "11111111111111111111111111111111"; @@ -87,14 +69,33 @@ const PROCESS_DESIRED_PRIORITIZATION_FEE_LAMPORTS_PER_TX: u64 = 500000; /// In micro-lamports. Multiply this by the compute units to figure out /// the additional cost of processing a message, in addition to the mandatory /// "base" cost of signature verification. +/// Unused at the moment, but kept for future reference. +#[allow(dead_code)] const PROCESS_COMPUTE_UNIT_PRICE_MICRO_LAMPORTS: u64 = - ( - // Convert to micro-lamports - (PROCESS_DESIRED_PRIORITIZATION_FEE_LAMPORTS_PER_TX * 1_000_000) - // Divide by the max compute units - / PROCESS_COMPUTE_UNITS as u64 - ); - + // Convert to micro-lamports + (PROCESS_DESIRED_PRIORITIZATION_FEE_LAMPORTS_PER_TX * 1_000_000) + // Divide by the max compute units + / PROCESS_COMPUTE_UNITS as u64; + +// Earlier versions of collateral warp routes were deployed off a version where the mint +// was requested as a writeable account for handle instruction. This is not necessary, +// and generally requires a higher priority fee to be paid. +// This is a HashMap of of (collateral warp route recipient -> mint address) that is +// used to force the mint address to be readonly. +lazy_static! { + static ref RECIPIENT_FORCED_READONLY_ACCOUNTS: HashMap = HashMap::from([ + // EZSOL + (pubkey!("b5pMgizA9vrGRt3hVqnU7vUVGBQUnLpwPzcJhG1ucyQ"), pubkey!("ezSoL6fY1PVdJcJsUpe5CM3xkfmy3zoVCABybm5WtiC")), + // ORCA + (pubkey!("8acihSm2QTGswniKgdgr4JBvJihZ1cakfvbqWCPBLoSp"), pubkey!("orcaEKTdK7LKz57vaAYr9QeNsVEPfiu6QeMU1kektZE")), + // USDC + (pubkey!("3EpVCPUgyjq2MfGeCttyey6bs5zya5wjYZ2BE6yDg6bm"), pubkey!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")), + // USDT + (pubkey!("Bk79wMjvpPCh5iQcCEjPWFcG1V2TfgdwaBsWBEYFYSNU"), pubkey!("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB")), + // WIF + (pubkey!("CuQmsT4eSF4dYiiGUGYYQxJ7c58pUAD5ADE3BbFGzQKx"), pubkey!("EKpQGSJtjMFqKZ9KQanSqYXRcF8fBopzLHYxdM65zcjm")), + ]); +} /// A reference to a Mailbox contract on some Sealevel chain pub struct SealevelMailbox { pub(crate) program_id: Pubkey, @@ -131,13 +132,17 @@ impl SealevelMailbox { }) } + /// Get the Inbox account pubkey and bump seed. pub fn inbox(&self) -> (Pubkey, u8) { self.inbox } + + /// Get the Outbox account pubkey and bump seed. pub fn outbox(&self) -> (Pubkey, u8) { self.outbox } + /// Get the provider RPC client. pub fn rpc(&self) -> &SealevelRpcClient { self.provider.rpc() } @@ -257,14 +262,26 @@ impl SealevelMailbox { message: message.body.clone(), }); - self.get_account_metas_with_instruction_bytes( - recipient_program_id, - &instruction - .encode() - .map_err(ChainCommunicationError::from_other)?, - hyperlane_sealevel_message_recipient_interface::HANDLE_ACCOUNT_METAS_PDA_SEEDS, - ) - .await + let mut account_metas = self + .get_account_metas_with_instruction_bytes( + recipient_program_id, + &instruction + .encode() + .map_err(ChainCommunicationError::from_other)?, + hyperlane_sealevel_message_recipient_interface::HANDLE_ACCOUNT_METAS_PDA_SEEDS, + ) + .await?; + + if let Some(forced_readonly_account) = + RECIPIENT_FORCED_READONLY_ACCOUNTS.get(&recipient_program_id) + { + account_metas + .iter_mut() + .filter(|account_meta| account_meta.pubkey == *forced_readonly_account) + .for_each(|account_meta| account_meta.is_writable = false); + } + + Ok(account_metas) } async fn get_account_metas_with_instruction_bytes( @@ -306,7 +323,8 @@ impl SealevelMailbox { } } - // Stolen from Solana's non-blocking client, but with Jito! + /// Send a transaction to Jito and wait for it to be confirmed. + /// Logic stolen from Solana's non-blocking client. pub async fn send_and_confirm_transaction_with_jito( &self, transaction: &impl SerializableTransaction, @@ -421,7 +439,7 @@ impl HyperlaneContract for SealevelMailbox { impl HyperlaneChain for SealevelMailbox { fn domain(&self) -> &HyperlaneDomain { - &self.provider.domain() + self.provider.domain() } fn provider(&self) -> Box { @@ -664,6 +682,7 @@ pub struct SealevelMailboxIndexer { } impl SealevelMailboxIndexer { + /// Create a new SealevelMailboxIndexer pub fn new( conf: &ConnectionConf, locator: ContractLocator, @@ -694,7 +713,7 @@ impl SealevelMailboxIndexer { } fn rpc(&self) -> &SealevelRpcClient { - &self.mailbox.rpc() + self.mailbox.rpc() } async fn get_dispatched_message_with_nonce( @@ -707,7 +726,7 @@ impl SealevelMailboxIndexer { let accounts = search_accounts_by_discriminator( self.rpc(), &self.program_id, - &DISPATCHED_MESSAGE_DISCRIMINATOR, + DISPATCHED_MESSAGE_DISCRIMINATOR, &nonce_bytes, unique_dispatched_message_pubkey_offset, unique_dispatch_message_pubkey_length, @@ -715,7 +734,7 @@ impl SealevelMailboxIndexer { .await?; let valid_message_storage_pda_pubkey = search_and_validate_account(accounts, |account| { - self.dispatched_message_account(&account) + self.dispatched_message_account(account) })?; // Now that we have the valid message storage PDA pubkey, we can get the full account data. @@ -800,7 +819,7 @@ impl SealevelMailboxIndexer { let accounts = search_accounts_by_discriminator( self.rpc(), &self.program_id, - &PROCESSED_MESSAGE_DISCRIMINATOR, + PROCESSED_MESSAGE_DISCRIMINATOR, &sequence_bytes, delivered_message_id_offset, delivered_message_id_length, @@ -810,7 +829,7 @@ impl SealevelMailboxIndexer { debug!(account_len = ?accounts.len(), "Found accounts with processed message discriminator"); let valid_message_storage_pda_pubkey = search_and_validate_account(accounts, |account| { - self.delivered_message_account(&account) + self.delivered_message_account(account) })?; // Now that we have the valid delivered message storage PDA pubkey, @@ -965,14 +984,3 @@ impl SequenceAwareIndexer for SealevelMailboxIndexer { Ok((Some(sequence), tip)) } } - -struct SealevelMailboxAbi; - -// TODO figure out how this is used and if we can support it for sealevel. -impl HyperlaneAbi for SealevelMailboxAbi { - const SELECTOR_SIZE_BYTES: usize = 8; - - fn fn_map() -> HashMap, &'static str> { - todo!() - } -} diff --git a/rust/sealevel/programs/hyperlane-sealevel-token-collateral/src/plugin.rs b/rust/sealevel/programs/hyperlane-sealevel-token-collateral/src/plugin.rs index 0fc224667e..ff8cf8dd7b 100644 --- a/rust/sealevel/programs/hyperlane-sealevel-token-collateral/src/plugin.rs +++ b/rust/sealevel/programs/hyperlane-sealevel-token-collateral/src/plugin.rs @@ -444,7 +444,7 @@ impl HyperlaneSealevelTokenPlugin for CollateralPlugin { vec![ AccountMeta::new_readonly(token.plugin_data.spl_token_program, false).into(), AccountMeta::new_readonly(spl_associated_token_account::id(), false).into(), - AccountMeta::new(token.plugin_data.mint, false).into(), + AccountMeta::new_readonly(token.plugin_data.mint, false).into(), AccountMeta::new(recipient_associated_token_account, false).into(), AccountMeta::new(ata_payer_account_key, false).into(), AccountMeta::new(token.plugin_data.escrow, false).into(), diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index ad075baccc..9fa5ede3c2 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -276,7 +276,8 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< degenchain: true, dogechain: true, duckchain: true, - eclipsemainnet: true, + // Disabled until we get archival RPC for Eclipse + eclipsemainnet: false, endurance: true, ethereum: true, everclear: true,