Skip to content

Commit

Permalink
feat: SOL (solana <> eclipse) warp route (#4268)
Browse files Browse the repository at this point in the history
### Description

<!--
What's included in this PR?
-->

### Drive-by changes

<!--
Are there any minor or drive-by changes also included?
-->

### 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
-->

---------

Co-authored-by: Trevor Porter <[email protected]>
  • Loading branch information
daniel-savu and tkporter authored Aug 23, 2024
1 parent 853f3e9 commit dac2fb5
Show file tree
Hide file tree
Showing 26 changed files with 419 additions and 65 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,11 @@ jobs:
!./rust
key: ${{ github.event.pull_request.head.sha || github.sha }}

- name: Install system dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -qq -y libudev-dev pkg-config protobuf-compiler
- name: Checkout registry
uses: ./.github/actions/checkout-registry

Expand Down
52 changes: 33 additions & 19 deletions rust/sealevel/client/src/cmd_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ use solana_sdk::{
signature::{Keypair, Signer},
};

const SOLANA_DOMAIN: u32 = 1399811149;

pub(crate) fn account_exists(client: &RpcClient, account: &Pubkey) -> Result<bool, ClientError> {
// Using `get_account_with_commitment` instead of `get_account` so we get Ok(None) when the account
// doesn't exist, rather than an error
Expand All @@ -29,10 +31,17 @@ pub(crate) fn deploy_program_idempotent(
program_keypair_path: &str,
program_path: &str,
url: &str,
local_domain: u32,
) -> Result<(), ClientError> {
let client = RpcClient::new(url.to_string());
if !account_exists(&client, &program_keypair.pubkey())? {
deploy_program(payer_keypair_path, program_keypair_path, program_path, url);
deploy_program(
payer_keypair_path,
program_keypair_path,
program_path,
url,
local_domain,
);
} else {
println!("Program {} already deployed", program_keypair.pubkey());
}
Expand All @@ -45,25 +54,29 @@ pub(crate) fn deploy_program(
program_keypair_path: &str,
program_path: &str,
url: &str,
local_domain: u32,
) {
build_cmd(
&[
"solana",
"--url",
url,
"-k",
payer_keypair_path,
"program",
"deploy",
program_path,
"--upgrade-authority",
payer_keypair_path,
"--program-id",
program_keypair_path,
],
None,
None,
);
let mut command = vec![
"solana",
"--url",
url,
"-k",
payer_keypair_path,
"program",
"deploy",
program_path,
"--upgrade-authority",
payer_keypair_path,
"--program-id",
program_keypair_path,
];

if local_domain.eq(&SOLANA_DOMAIN) {
// May need tweaking depending on gas prices / available balance
command.append(&mut vec!["--with-compute-unit-price", "550000"]);
}

build_cmd(command.as_slice(), None, None);
}

pub(crate) fn create_new_directory(parent_dir: &Path, name: &str) -> PathBuf {
Expand Down Expand Up @@ -112,6 +125,7 @@ fn build_cmd(cmd: &[&str], wd: Option<&str>, env: Option<&HashMap<&str, &str>>)
if let Some(env) = env {
c.envs(env);
}
println!("Running command: {:?}", c);
let status = c.status().expect("Failed to run command");
assert!(
status.success(),
Expand Down
1 change: 1 addition & 0 deletions rust/sealevel/client/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub(crate) struct Context {
pub require_tx_approval: bool,
}

#[derive(Debug)]
pub(crate) struct InstructionWithDescription {
pub instruction: Instruction,
pub description: Option<String>,
Expand Down
57 changes: 55 additions & 2 deletions rust/sealevel/client/src/core.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,67 @@
use borsh::BorshDeserialize;
use hyperlane_sealevel_mailbox::protocol_fee::ProtocolFee;
use serde::{Deserialize, Serialize};

use solana_program::pubkey::Pubkey;
use solana_sdk::signature::Signer;
use solana_sdk::{compute_budget, compute_budget::ComputeBudgetInstruction};

use std::collections::HashMap;
use std::{fs::File, path::Path};

use crate::ONE_SOL_IN_LAMPORTS;
use crate::{
artifacts::{read_json, write_json},
cmd_utils::{create_and_write_keypair, create_new_directory, deploy_program},
multisig_ism::deploy_multisig_ism_message_id,
Context, CoreCmd, CoreDeploy, CoreSubCmd,
};
use crate::{DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT, ONE_SOL_IN_LAMPORTS};
use hyperlane_core::H256;
use hyperlane_sealevel_igp::accounts::{SOL_DECIMALS, TOKEN_EXCHANGE_RATE_SCALE};

pub(crate) fn adjust_gas_price_if_needed(chain_name: &str, ctx: &mut Context) {
if chain_name.eq("solana") {
let mut initial_instructions = ctx.initial_instructions.borrow_mut();
const PROCESS_DESIRED_PRIORITIZATION_FEE_LAMPORTS_PER_TX: u64 = 50_000_000;
const MICRO_LAMPORT_FEE_PER_LIMIT: u64 =
// Convert to micro-lamports
(PROCESS_DESIRED_PRIORITIZATION_FEE_LAMPORTS_PER_TX * 1_000_000)
// Divide by the max compute units
/ DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64;

for i in initial_instructions.iter_mut() {
if i.instruction.program_id != compute_budget::id() {
continue;
}
if let Ok(compute_budget_instruction) =
ComputeBudgetInstruction::try_from_slice(&i.instruction.data)
{
if matches!(
compute_budget_instruction,
ComputeBudgetInstruction::SetComputeUnitPrice { .. }
) {
// The compute unit price has already been set, so we override it and return early
i.instruction = ComputeBudgetInstruction::set_compute_unit_price(
MICRO_LAMPORT_FEE_PER_LIMIT,
);
return;
}
}
}

initial_instructions.push(
(
ComputeBudgetInstruction::set_compute_unit_price(MICRO_LAMPORT_FEE_PER_LIMIT),
Some(format!(
"Set compute unit price to {}",
MICRO_LAMPORT_FEE_PER_LIMIT
)),
)
.into(),
);
}
}

#[derive(serde::Serialize, serde::Deserialize, PartialEq, Debug)]
#[serde(rename_all = "camelCase")]
struct ProtocolFeeConfig {
Expand All @@ -39,6 +84,8 @@ impl Default for ProtocolFeeConfig {
pub(crate) fn process_core_cmd(mut ctx: Context, cmd: CoreCmd) {
match cmd.cmd {
CoreSubCmd::Deploy(core) => {
adjust_gas_price_if_needed(core.chain.as_str(), &mut ctx);

let environments_dir =
create_new_directory(&core.env_args.environments_dir, &core.env_args.environment);
let chain_dir = create_new_directory(&environments_dir, &core.chain);
Expand All @@ -50,9 +97,11 @@ pub(crate) fn process_core_cmd(mut ctx: Context, cmd: CoreCmd) {
&core.built_so_dir,
core.use_existing_keys,
&key_dir,
core.local_domain,
);

let mailbox_program_id = deploy_mailbox(&mut ctx, &core, &key_dir, ism_program_id);
let mailbox_program_id =
deploy_mailbox(&mut ctx, &core, &key_dir, ism_program_id, core.local_domain);

let validator_announce_program_id =
deploy_validator_announce(&mut ctx, &core, &key_dir, mailbox_program_id);
Expand All @@ -78,6 +127,7 @@ fn deploy_mailbox(
core: &CoreDeploy,
key_dir: &Path,
default_ism: Pubkey,
local_domain: u32,
) -> Pubkey {
let (keypair, keypair_path) = create_and_write_keypair(
key_dir,
Expand All @@ -94,6 +144,7 @@ fn deploy_mailbox(
.to_str()
.unwrap(),
&ctx.client.url(),
local_domain,
);

println!("Deployed Mailbox at program ID {}", program_id);
Expand Down Expand Up @@ -152,6 +203,7 @@ fn deploy_validator_announce(
.to_str()
.unwrap(),
&ctx.client.url(),
core.local_domain,
);

println!("Deployed ValidatorAnnounce at program ID {}", program_id);
Expand Down Expand Up @@ -237,6 +289,7 @@ fn deploy_igp(ctx: &mut Context, core: &CoreDeploy, key_dir: &Path) -> (Pubkey,
.to_str()
.unwrap(),
&ctx.client.url(),
core.local_domain,
);

println!("Deployed IGP at program ID {}", program_id);
Expand Down
12 changes: 10 additions & 2 deletions rust/sealevel/client/src/igp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use solana_sdk::{
signature::{Keypair, Signer as _},
};

use hyperlane_core::H256;
use hyperlane_core::{KnownHyperlaneDomain, H256};

use hyperlane_sealevel_igp::{
accounts::{
Expand Down Expand Up @@ -68,8 +68,14 @@ pub(crate) fn process_igp_cmd(mut ctx: Context, cmd: IgpCmd) {
let ism_dir = create_new_directory(&environments_dir, "igp");
let chain_dir = create_new_directory(&ism_dir, &deploy.chain);
let key_dir = create_new_directory(&chain_dir, "keys");
let local_domain = deploy
.chain
.parse::<KnownHyperlaneDomain>()
.map(|v| v as u32)
.expect("Invalid chain name");

let program_id = deploy_igp_program(&mut ctx, &deploy.built_so_dir, true, &key_dir);
let program_id =
deploy_igp_program(&mut ctx, &deploy.built_so_dir, true, &key_dir, local_domain);

write_json::<SingularProgramIdArtifact>(
&chain_dir.join("program-ids.json"),
Expand Down Expand Up @@ -409,6 +415,7 @@ fn deploy_igp_program(
built_so_dir: &Path,
use_existing_keys: bool,
key_dir: &Path,
local_domain: u32,
) -> Pubkey {
let (keypair, keypair_path) = create_and_write_keypair(
key_dir,
Expand All @@ -425,6 +432,7 @@ fn deploy_igp_program(
.to_str()
.unwrap(),
&ctx.client.url(),
local_domain,
);

println!("Deployed IGP at program ID {}", program_id);
Expand Down
3 changes: 2 additions & 1 deletion rust/sealevel/client/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -710,7 +710,7 @@ fn main() {
payer_keypair.pubkey(),
Some(PayerKeypair {
keypair: payer_keypair,
keypair_path,
keypair_path: keypair_path.clone(),
}),
)
} else {
Expand All @@ -724,6 +724,7 @@ fn main() {
let commitment = CommitmentConfig::confirmed();

let mut instructions = vec![];

if cli.compute_budget != DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT {
assert!(cli.compute_budget <= MAX_COMPUTE_UNIT_LIMIT);
instructions.push(
Expand Down
22 changes: 19 additions & 3 deletions rust/sealevel/client/src/multisig_ism.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{
Context, MultisigIsmMessageIdCmd, MultisigIsmMessageIdSubCmd,
};

use hyperlane_core::H160;
use hyperlane_core::{KnownHyperlaneDomain, H160};

use hyperlane_sealevel_multisig_ism_message_id::{
access_control_pda_seeds,
Expand Down Expand Up @@ -53,9 +53,19 @@ pub(crate) fn process_multisig_ism_message_id_cmd(mut ctx: Context, cmd: Multisi
let chain_dir = create_new_directory(&ism_dir, &deploy.chain);
let context_dir = create_new_directory(&chain_dir, &deploy.context);
let key_dir = create_new_directory(&context_dir, "keys");
let local_domain = deploy
.chain
.parse::<KnownHyperlaneDomain>()
.map(|v| v as u32)
.expect("Invalid chain name");

let ism_program_id =
deploy_multisig_ism_message_id(&mut ctx, &deploy.built_so_dir, true, &key_dir);
let ism_program_id = deploy_multisig_ism_message_id(
&mut ctx,
&deploy.built_so_dir,
true,
&key_dir,
local_domain,
);

write_json::<SingularProgramIdArtifact>(
&context_dir.join("program-ids.json"),
Expand Down Expand Up @@ -158,6 +168,7 @@ pub(crate) fn deploy_multisig_ism_message_id(
built_so_dir: &Path,
use_existing_keys: bool,
key_dir: &Path,
local_domain: u32,
) -> Pubkey {
let (keypair, keypair_path) = create_and_write_keypair(
key_dir,
Expand All @@ -174,6 +185,7 @@ pub(crate) fn deploy_multisig_ism_message_id(
.to_str()
.unwrap(),
&ctx.client.url(),
local_domain,
);

println!(
Expand All @@ -197,6 +209,10 @@ pub(crate) fn deploy_multisig_ism_message_id(
),
)
.send_with_payer();
println!(
"initialized Multisig ISM Message ID at program ID {}",
program_id
);

program_id
}
Expand Down
10 changes: 9 additions & 1 deletion rust/sealevel/client/src/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ use hyperlane_sealevel_connection_client::router::RemoteRouterConfig;
use hyperlane_sealevel_igp::accounts::{Igp, InterchainGasPaymasterType, OverheadIgp};

use crate::{
adjust_gas_price_if_needed,
artifacts::{write_json, HexAndBase58ProgramIdArtifact},
cmd_utils::{create_and_write_keypair, create_new_directory, deploy_program_idempotent},
read_core_program_ids, Context, CoreProgramIds,
read_core_program_ids, warp_route, Context, CoreProgramIds,
};

/// Optional connection client configuration.
Expand Down Expand Up @@ -185,6 +186,7 @@ pub(crate) trait RouterDeployer<Config: RouterConfigGetter + std::fmt::Debug>:
.to_str()
.unwrap(),
&chain_config.rpc_urls[0].http,
chain_config.domain_id(),
)
.unwrap();

Expand Down Expand Up @@ -348,6 +350,8 @@ pub(crate) fn deploy_routers<
.filter(|(_, app_config)| app_config.router_config().foreign_deployment.is_none())
.collect::<HashMap<_, _>>();

warp_route::install_spl_token_cli();

// Now we deploy to chains that don't have a foreign deployment
for (chain_name, app_config) in app_configs_to_deploy.iter() {
let chain_config = chain_configs
Expand All @@ -360,6 +364,8 @@ pub(crate) fn deploy_routers<
}
}

adjust_gas_price_if_needed(chain_name.as_str(), ctx);

// Deploy - this is idempotent.
let program_id = deployer.deploy(
ctx,
Expand Down Expand Up @@ -537,6 +543,8 @@ fn enroll_all_remote_routers<
routers: &HashMap<u32, H256>,
) {
for (chain_name, _) in app_configs_to_deploy.iter() {
adjust_gas_price_if_needed(chain_name.as_str(), ctx);

let chain_config = chain_configs
.get(*chain_name)
.unwrap_or_else(|| panic!("Chain config not found for chain: {}", chain_name));
Expand Down
Loading

0 comments on commit dac2fb5

Please sign in to comment.