Skip to content

Commit 21ad106

Browse files
committed
add sovereign sdk integration
1 parent 4cb6c47 commit 21ad106

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+5122
-153
lines changed

rust/main/Cargo.lock

Lines changed: 214 additions & 104 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/main/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ members = [
1111
"chains/hyperlane-fuel",
1212
"chains/hyperlane-radix",
1313
"chains/hyperlane-sealevel",
14+
"chains/hyperlane-sovereign",
1415
"chains/hyperlane-starknet",
1516
"ethers-prometheus",
1617
"hyperlane-base",

rust/main/agents/scraper/migration/src/m20230309_000001_create_table_domain.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,24 @@ const DOMAINS: &[RawDomain] = &[
544544
chain_id: 9913375,
545545
is_test_net: true,
546546
is_deprecated: false,
547-
}, // ---------- End: E2E tests chains ----------------
547+
},
548+
RawDomain {
549+
name: "sovrollup0",
550+
token: "SOV",
551+
domain: 50001,
552+
chain_id: 50001,
553+
is_test_net: true,
554+
is_deprecated: false,
555+
},
556+
RawDomain {
557+
name: "sovrollup1",
558+
token: "SOV",
559+
domain: 50002,
560+
chain_id: 50002,
561+
is_test_net: true,
562+
is_deprecated: false,
563+
},
564+
// ---------- End: E2E tests chains ----------------
548565
];
549566

550567
#[derive(DeriveMigrationName)]

rust/main/agents/validator/src/reorg_reporter.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ impl LatestCheckpointReorgReporter {
9797
origin: &HyperlaneDomain,
9898
) -> Vec<(Url, ValidatorSettings)> {
9999
use ChainConnectionConf::{
100-
Cosmos, CosmosNative, Ethereum, Fuel, Radix, Sealevel, Starknet,
100+
Cosmos, CosmosNative, Ethereum, Fuel, Radix, Sealevel, Sovereign, Starknet,
101101
};
102102

103103
let chain_conf = settings
@@ -149,6 +149,13 @@ impl LatestCheckpointReorgReporter {
149149
Radix(updated_conn)
150150
})
151151
}
152+
Sovereign(conn) => {
153+
Self::map_urls_to_connections(vec![conn.url.clone()], conn, |conn, url| {
154+
let mut updated_conn = conn.clone();
155+
updated_conn.url = url;
156+
Sovereign(updated_conn)
157+
})
158+
}
152159
};
153160

154161
chain_conn_confs

rust/main/chains/hyperlane-sealevel/src/validator_announce.rs

Lines changed: 116 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,73 @@ use std::sync::Arc;
22

33
use async_trait::async_trait;
44
use hyperlane_core::{
5-
Announcement, ChainResult, ContractLocator, HyperlaneChain, HyperlaneContract, HyperlaneDomain,
6-
SignedType, TxOutcome, ValidatorAnnounce, H160, H256, H512, U256,
5+
Announcement, ChainCommunicationError, ChainResult, ContractLocator, HyperlaneChain,
6+
HyperlaneContract, HyperlaneDomain, SignedType, TxOutcome, ValidatorAnnounce, H160, H256, U256,
77
};
88
use hyperlane_sealevel_validator_announce::{
9-
accounts::ValidatorStorageLocationsAccount, validator_storage_locations_pda_seeds,
9+
accounts::ValidatorStorageLocationsAccount,
10+
instruction::{AnnounceInstruction, Instruction as ValidatorAnnounceInstruction},
11+
replay_protection_pda_seeds, validator_announce_pda_seeds,
12+
validator_storage_locations_pda_seeds,
1013
};
11-
use solana_sdk::pubkey::Pubkey;
12-
use tracing::{info, instrument, warn};
14+
use solana_sdk::{
15+
instruction::{AccountMeta, Instruction},
16+
pubkey::Pubkey,
17+
signer::Signer as _,
18+
system_program,
19+
};
20+
use tracing::{info, instrument};
1321

14-
use crate::SealevelProvider;
22+
use crate::{ConnectionConf, SealevelKeypair, SealevelProvider, TransactionSubmitter};
1523

1624
/// A reference to a ValidatorAnnounce contract on some Sealevel chain
17-
#[derive(Debug)]
1825
pub struct SealevelValidatorAnnounce {
1926
provider: Arc<SealevelProvider>,
27+
tx_submitter: Arc<dyn TransactionSubmitter>,
2028
program_id: Pubkey,
2129
domain: HyperlaneDomain,
30+
conn: ConnectionConf,
31+
signer: Option<SealevelKeypair>,
32+
}
33+
34+
impl std::fmt::Debug for SealevelValidatorAnnounce {
35+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36+
f.debug_struct("SealevelValidatorAnnounce")
37+
.field("provider", &self.provider)
38+
.field("tx_submitter", &"Arc<dyn TransactionSubmitter>")
39+
.field("program_id", &self.program_id)
40+
.field("domain", &self.domain)
41+
.field("conn", &self.conn)
42+
.field("signer", &self.signer)
43+
.finish()
44+
}
2245
}
2346

2447
impl SealevelValidatorAnnounce {
2548
/// Create a new Sealevel ValidatorAnnounce
26-
pub fn new(provider: Arc<SealevelProvider>, locator: &ContractLocator) -> Self {
49+
pub fn new(
50+
provider: Arc<SealevelProvider>,
51+
tx_submitter: Arc<dyn TransactionSubmitter>,
52+
conn: ConnectionConf,
53+
locator: &ContractLocator,
54+
signer: Option<SealevelKeypair>,
55+
) -> Self {
2756
let program_id = Pubkey::from(<[u8; 32]>::from(locator.address));
2857
Self {
2958
program_id,
3059
domain: locator.domain.clone(),
3160
provider,
61+
tx_submitter,
62+
conn,
63+
signer,
3264
}
3365
}
66+
67+
fn get_signer(&self) -> ChainResult<&SealevelKeypair> {
68+
self.signer
69+
.as_ref()
70+
.ok_or_else(|| ChainCommunicationError::SignerUnavailable)
71+
}
3472
}
3573

3674
impl HyperlaneContract for SealevelValidatorAnnounce {
@@ -112,13 +150,77 @@ impl ValidatorAnnounce for SealevelValidatorAnnounce {
112150

113151
#[instrument(err, ret, skip(self))]
114152
#[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue
115-
async fn announce(&self, _announcement: SignedType<Announcement>) -> ChainResult<TxOutcome> {
116-
warn!(
117-
"Announcing validator storage locations within the agents is not supported on Sealevel"
118-
);
153+
async fn announce(&self, announcement: SignedType<Announcement>) -> ChainResult<TxOutcome> {
154+
let payer = self.get_signer()?;
155+
156+
let announce_instruction = AnnounceInstruction {
157+
validator: announcement.value.validator,
158+
storage_location: announcement.value.storage_location.clone(),
159+
signature: announcement.signature.to_vec(),
160+
};
161+
162+
let (validator_announce_account, _validator_announce_bump) =
163+
Pubkey::find_program_address(validator_announce_pda_seeds!(), &self.program_id);
164+
165+
let (validator_storage_locations_key, _validator_storage_locations_bump_seed) =
166+
Pubkey::find_program_address(
167+
validator_storage_locations_pda_seeds!(announce_instruction.validator),
168+
&self.program_id,
169+
);
170+
171+
let replay_id = announce_instruction.replay_id();
172+
let (replay_protection_pda_key, _replay_protection_bump_seed) =
173+
Pubkey::find_program_address(replay_protection_pda_seeds!(replay_id), &self.program_id);
174+
175+
let ixn = ValidatorAnnounceInstruction::Announce(announce_instruction);
176+
177+
// Accounts:
178+
// 0. [signer] The payer.
179+
// 1. [executable] The system program.
180+
// 2. [] The ValidatorAnnounce PDA account.
181+
// 3. [writeable] The validator-specific ValidatorStorageLocationsAccount PDA account.
182+
// 4. [writeable] The ReplayProtection PDA account specific to the announcement being made.
183+
let accounts = vec![
184+
AccountMeta::new_readonly(payer.pubkey(), true),
185+
AccountMeta::new_readonly(system_program::id(), false),
186+
AccountMeta::new_readonly(validator_announce_account, false),
187+
AccountMeta::new(validator_storage_locations_key, false),
188+
AccountMeta::new(replay_protection_pda_key, false),
189+
];
190+
191+
let instruction = Instruction {
192+
program_id: self.program_id,
193+
data: ixn.into_instruction_data().unwrap(),
194+
accounts,
195+
};
196+
197+
info!(?instruction, "Created validator announce instruction");
198+
199+
let tx = self
200+
.provider
201+
.build_estimated_tx_for_instruction(
202+
instruction,
203+
payer,
204+
self.tx_submitter.clone(),
205+
self.conn.priority_fee_oracle.create_oracle(),
206+
)
207+
.await?;
208+
209+
info!(?tx, "Built transaction for validator announcement");
210+
211+
let signature = self.tx_submitter.send_transaction(&tx, true).await?;
212+
213+
info!(?signature, "Sent validator announcement transaction");
214+
215+
self.tx_submitter
216+
.wait_for_transaction_confirmation(&tx)
217+
.await?;
218+
219+
info!(?signature, "Validator announcement transaction confirmed");
220+
119221
Ok(TxOutcome {
120-
transaction_id: H512::zero(),
121-
executed: false,
222+
transaction_id: signature.into(),
223+
executed: true,
122224
gas_used: U256::zero(),
123225
gas_price: U256::zero().try_into()?,
124226
})
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
[package]
2+
name = "hyperlane-sovereign"
3+
version = "0.1.0"
4+
edition.workspace = true
5+
6+
[dependencies]
7+
hyperlane-core = { path = "../../hyperlane-core", features = ["async"] }
8+
hyperlane-operation-verifier = { path = "../../applications/hyperlane-operation-verifier" }
9+
hyperlane-warp-route = { path = "../../applications/hyperlane-warp-route" }
10+
11+
anyhow.workspace = true
12+
async-trait.workspace = true
13+
base64.workspace = true
14+
bech32.workspace = true
15+
bytes.workspace = true
16+
derive-new.workspace = true
17+
ethers.workspace = true
18+
futures.workspace = true
19+
k256.workspace = true
20+
reqwest.workspace = true
21+
serde.workspace = true
22+
serde_json.workspace = true
23+
sha2.workspace = true
24+
sha3.workspace = true
25+
tokio = { workspace = true, features = ["fs", "macros"] }
26+
tracing.workspace = true
27+
url.workspace = true
28+
hex.workspace = true
29+
num-traits.workspace = true
30+
31+
ed25519-dalek = "2.1.1"
32+
bs58 = { version = "0.5.1", default-features = false, features = [
33+
"std",
34+
"alloc",
35+
] }
36+
sov-universal-wallet = { git = "https://@github.com/Sovereign-Labs/sovereign-sdk.git", branch = "nightly", features = [
37+
"serde",
38+
] }
39+
tokio-tungstenite = "0.23"
40+
tokio-retry = "0.3.0"
41+
42+
[features]
43+
default = []
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
use std::io::Cursor;
2+
3+
use async_trait::async_trait;
4+
use derive_new::new;
5+
use hyperlane_core::{Decode, HyperlaneMessage, H256, U256};
6+
use hyperlane_operation_verifier::{
7+
ApplicationOperationVerifier, ApplicationOperationVerifierReport,
8+
};
9+
use hyperlane_warp_route::TokenMessage;
10+
11+
use crate::signers::sovereign::SOV_HEX_ADDRESS_LEADING_ZEROS;
12+
13+
const WARP_ROUTE_MARKER: &str = "/";
14+
15+
/// Application operation verifier for Sovereign
16+
#[derive(new)]
17+
pub struct SovereignApplicationOperationVerifier {}
18+
19+
#[async_trait]
20+
impl ApplicationOperationVerifier for SovereignApplicationOperationVerifier {
21+
async fn verify(
22+
&self,
23+
app_context: &Option<String>,
24+
message: &HyperlaneMessage,
25+
) -> Option<ApplicationOperationVerifierReport> {
26+
use ApplicationOperationVerifierReport::{MalformedMessage, ZeroAmount};
27+
tracing::trace!(
28+
?app_context,
29+
?message,
30+
"Sovereign application operation verifier",
31+
);
32+
33+
let context = match app_context {
34+
Some(c) => c,
35+
None => return None,
36+
};
37+
38+
if !context.contains(WARP_ROUTE_MARKER) {
39+
return None;
40+
}
41+
42+
// Starting from this point we assume that we are in a warp route context
43+
44+
let mut reader = Cursor::new(message.body.as_slice());
45+
let token_message = match TokenMessage::read_from(&mut reader) {
46+
Ok(m) => m,
47+
Err(_) => return Some(MalformedMessage(message.clone())),
48+
};
49+
50+
if !has_enough_leading_zeroes(token_message.recipient()) {
51+
return Some(MalformedMessage(message.clone()));
52+
}
53+
54+
if token_message.amount() == U256::zero() {
55+
return Some(ZeroAmount);
56+
}
57+
58+
None
59+
}
60+
}
61+
62+
// this uses the ed25519 addresses check as it has less leading zeros
63+
fn has_enough_leading_zeroes(address: H256) -> bool {
64+
address
65+
.as_bytes()
66+
.iter()
67+
.take(SOV_HEX_ADDRESS_LEADING_ZEROS)
68+
.all(|b| *b == 0)
69+
}

0 commit comments

Comments
 (0)