Skip to content

Commit

Permalink
feat: overrides in the relayer to force collateral warp route mints t…
Browse files Browse the repository at this point in the history
…o be read-only (#4918)

### Description

Some context, and the fix for future warp routes:
#4905

This will force messages to certain collateral warp route recipients to
have mint accounts as readonly. This is a first step toward requiring
lower fees for some txs.

An example of Helius fees for a USDC process that includes the USDC mint
as writeable:
```
{"jsonrpc":"2.0","result":{"priorityFeeLevels":{"min":0.0,"low":292041.0,"medium":2207571.0,"high":10405557.0,"veryHigh":22996019.0,"unsafeMax":2000000000.0}},"id":"1"}
```

vs when it's not writeable:
```
{"jsonrpc":"2.0","result":{"priorityFeeLevels":{"min":0.0,"low":0.0,"medium":1.0,"high":150000.0,"veryHigh":10000000.0,"unsafeMax":2000000000.0}},"id":"1"}
```

The alternative to having this PR would be to:
- keep marking mints as writeable, forcing us to overpay
- or do a program upgrade of the warp routes (unlikely to happen)

### Drive-by changes

- Removed `#[allow(warnings)]` from mailbox.rs and made required changes

### Related issues

<!--
- Fixes #[issue number here]
-->

### Backward compatibility

<!--
Are these changes backward compatible? Are there any infrastructure
implications, e.g. changes that would prohibit deploying older commits
using this infra tooling?

Yes/No
-->

### Testing

<!--
What kind of testing have these changes undergone?

None/Manual/Unit Tests
-->
  • Loading branch information
tkporter authored Dec 12, 2024
1 parent f9396fc commit 07ffbbe
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 87 deletions.
1 change: 1 addition & 0 deletions rust/main/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions rust/main/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
24 changes: 15 additions & 9 deletions rust/main/agents/relayer/src/server/message_retry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!([
{
Expand Down Expand Up @@ -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!([
{
Expand Down Expand Up @@ -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!([
{
Expand Down
12 changes: 5 additions & 7 deletions rust/main/agents/validator/src/submit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()),
Expand All @@ -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();
Expand All @@ -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();
Expand Down
8 changes: 4 additions & 4 deletions rust/main/chains/hyperlane-cosmos/src/libs/account/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
1 change: 1 addition & 0 deletions rust/main/chains/hyperlane-sealevel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
140 changes: 74 additions & 66 deletions rust/main/chains/hyperlane-sealevel/src/mailbox.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -9,65 +10,46 @@ 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,
};
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";
Expand All @@ -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<Pubkey, Pubkey> = 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,
Expand Down Expand Up @@ -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()
}
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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<dyn HyperlaneProvider> {
Expand Down Expand Up @@ -664,6 +682,7 @@ pub struct SealevelMailboxIndexer {
}

impl SealevelMailboxIndexer {
/// Create a new SealevelMailboxIndexer
pub fn new(
conf: &ConnectionConf,
locator: ContractLocator,
Expand Down Expand Up @@ -694,7 +713,7 @@ impl SealevelMailboxIndexer {
}

fn rpc(&self) -> &SealevelRpcClient {
&self.mailbox.rpc()
self.mailbox.rpc()
}

async fn get_dispatched_message_with_nonce(
Expand All @@ -707,15 +726,15 @@ 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,
)
.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.
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -965,14 +984,3 @@ impl SequenceAwareIndexer<H256> 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<Vec<u8>, &'static str> {
todo!()
}
}

0 comments on commit 07ffbbe

Please sign in to comment.