Skip to content

Commit 6bc6682

Browse files
authored
feat(remote_executor): enable other SVM networks (#1779)
* enable other SVM networks * use feature flag * precommit * add eclipse_testnet wormhole receiver address * use eclipse_devnet instead of eclipse_testnet * precommit * remove lazy_static * make test use CHAIN_ID * add eclipse_testnet
1 parent f669bc2 commit 6bc6682

File tree

7 files changed

+199
-31
lines changed

7 files changed

+199
-31
lines changed

governance/remote_executor/Cargo.lock

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

governance/remote_executor/cli/src/cli.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use {
44
Parser,
55
Subcommand,
66
},
7+
remote_executor::state::governance_payload::CHAIN_ID_ARRAY,
78
solana_sdk::{
89
commitment_config::CommitmentConfig,
910
pubkey::Pubkey,
@@ -18,10 +19,22 @@ use {
1819
pub struct Cli {
1920
#[clap(long, default_value = "confirmed")]
2021
pub commitment: CommitmentConfig,
22+
#[clap(long, default_value = "pythnet", parse(try_from_str = parse_chain))]
23+
pub chain: u16,
24+
#[clap(long, default_value = "https://pythnet.rpcpool.com/")]
25+
pub rpc_url: String,
2126
#[clap(subcommand)]
2227
pub action: Action,
2328
}
2429

30+
fn parse_chain(chain: &str) -> Result<u16, String> {
31+
CHAIN_ID_ARRAY
32+
.iter()
33+
.find(|&&(name, _)| name == chain)
34+
.map(|&(_, id)| id)
35+
.ok_or_else(|| format!("Unsupported chain: {}", chain))
36+
}
37+
2538
#[derive(Subcommand, Debug)]
2639
pub enum Action {
2740
#[clap(about = "Post a VAA and execute it through the remote executor")]

governance/remote_executor/cli/src/main.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,7 @@ fn main() -> Result<()> {
7878
Action::PostAndExecute { vaa, keypair } => {
7979
let payer =
8080
read_keypair_file(&*shellexpand::tilde(&keypair)).expect("Keypair not found");
81-
let rpc_client =
82-
RpcClient::new_with_commitment("https://pythnet.rpcpool.com/", cli.commitment);
81+
let rpc_client = RpcClient::new_with_commitment(&cli.rpc_url, cli.commitment);
8382

8483
let vaa_bytes: Vec<u8> = base64::decode(vaa)?;
8584
let wormhole = AnchorVaa::owner();
@@ -169,7 +168,7 @@ fn main() -> Result<()> {
169168
Config::try_from_slice(&rpc_client.get_account_data(&wormhole_config)?)?;
170169

171170
let payload = ExecutorPayload {
172-
header: GovernanceHeader::executor_governance_header(),
171+
header: GovernanceHeader::executor_governance_header(cli.chain),
173172
instructions: vec![],
174173
}
175174
.try_to_vec()?;
@@ -197,7 +196,7 @@ fn main() -> Result<()> {
197196
}
198197
Action::GetTestPayload {} => {
199198
let payload = ExecutorPayload {
200-
header: GovernanceHeader::executor_governance_header(),
199+
header: GovernanceHeader::executor_governance_header(cli.chain),
201200
instructions: vec![],
202201
}
203202
.try_to_vec()?;
@@ -224,7 +223,7 @@ fn main() -> Result<()> {
224223
instruction.accounts[2].is_signer = true; // Require signature of new authority for safety
225224
println!("New authority : {:}", instruction.accounts[2].pubkey);
226225
let payload = ExecutorPayload {
227-
header: GovernanceHeader::executor_governance_header(),
226+
header: GovernanceHeader::executor_governance_header(cli.chain),
228227
instructions: vec![InstructionData::from(&instruction)],
229228
}
230229
.try_to_vec()?;
@@ -246,7 +245,7 @@ fn main() -> Result<()> {
246245
instruction.accounts[3].pubkey
247246
);
248247
let payload = ExecutorPayload {
249-
header: GovernanceHeader::executor_governance_header(),
248+
header: GovernanceHeader::executor_governance_header(cli.chain),
250249
instructions: vec![InstructionData::from(&instruction)],
251250
}
252251
.try_to_vec()?;

governance/remote_executor/programs/remote-executor/Cargo.toml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,16 @@ no-entrypoint = []
1313
no-idl = []
1414
no-log-ix-name = []
1515
cpi = ["no-entrypoint"]
16-
default = []
16+
default = ["pythnet"]
17+
# These features determine the network for deployment:
18+
# - Use `pythnet` for deploying on Pythnet
19+
# - Use `pythtest` for deploying on Pythtest
20+
# - Use `eclipse_devnet` for deploying on Eclipse Devnet
21+
# etc...
22+
pythnet = []
1723
pythtest = []
24+
eclipse_devnet = []
25+
eclipse_testnet = []
1826

1927
[dependencies]
2028
anchor-lang = {version = "0.25.0", features = ["init-if-needed"]}

governance/remote_executor/programs/remote-executor/src/state/governance_payload.rs

Lines changed: 162 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,26 @@ use {
1010
mem::size_of,
1111
ops::Deref,
1212
},
13-
wormhole_sdk::Chain,
1413
};
1514

1615
pub const MAGIC_NUMBER: u32 = 0x4d475450; // Reverse order of the solidity contract because borsh uses little endian numbers (the solidity contract uses 0x5054474d)
1716

17+
pub const CHAIN_ID_ARRAY: &[(&str, u16)] = &[
18+
("pythnet", 26),
19+
("pythtest", 26),
20+
("eclipse_devnet", 40001),
21+
("eclipse_testnet", 40002),
22+
];
23+
24+
#[cfg(any(feature = "pythnet", feature = "pythtest"))]
25+
pub const CHAIN_ID: u16 = 26;
26+
27+
#[cfg(feature = "eclipse_devnet")]
28+
pub const CHAIN_ID: u16 = 40001;
29+
30+
#[cfg(feature = "eclipse_testnet")]
31+
pub const CHAIN_ID: u16 = 40002;
32+
1833
#[derive(AnchorDeserialize, AnchorSerialize, Debug, PartialEq, Eq)]
1934
pub struct ExecutorPayload {
2035
pub header: GovernanceHeader,
@@ -48,14 +63,12 @@ pub struct GovernanceHeader {
4863

4964
impl GovernanceHeader {
5065
#[allow(unused)] // Only used in tests right now
51-
pub fn executor_governance_header() -> Self {
66+
pub fn executor_governance_header(chain: u16) -> Self {
5267
Self {
5368
magic_number: MAGIC_NUMBER,
5469
module: Module::Executor,
5570
action: Action::ExecutePostedVaa,
56-
chain: BigEndianU16 {
57-
value: Chain::Pythnet.try_into().unwrap(),
58-
},
71+
chain: BigEndianU16 { value: chain },
5972
}
6073
}
6174
}
@@ -163,7 +176,7 @@ impl ExecutorPayload {
163176
.ok_or(error!(ExecutorError::GovernanceHeaderInvalidModule))?;
164177
(self.header.action == ExecutorPayload::ACTION)
165178
.ok_or(error!(ExecutorError::GovernanceHeaderInvalidAction))?;
166-
(Chain::from(self.header.chain.value) == Chain::Pythnet)
179+
(self.header.chain.value == CHAIN_ID)
167180
.ok_or(error!(ExecutorError::GovernanceHeaderInvalidReceiverChain))
168181
}
169182
}
@@ -173,12 +186,17 @@ pub mod tests {
173186
use {
174187
super::ExecutorPayload,
175188
crate::{
176-
error,
177189
error::ExecutorError,
178-
state::governance_payload::InstructionData,
190+
state::governance_payload::{
191+
InstructionData,
192+
CHAIN_ID,
193+
},
179194
},
180195
anchor_lang::{
181-
prelude::Pubkey,
196+
prelude::{
197+
Pubkey,
198+
*,
199+
},
182200
AnchorDeserialize,
183201
AnchorSerialize,
184202
},
@@ -188,22 +206,55 @@ pub mod tests {
188206
fn test_check_deserialization_serialization() {
189207
// No instructions
190208
let payload = ExecutorPayload {
191-
header: super::GovernanceHeader::executor_governance_header(),
209+
header: super::GovernanceHeader::executor_governance_header(CHAIN_ID),
192210
instructions: vec![],
193211
};
194212

195213
assert!(payload.check_header().is_ok());
196214

197215
let payload_bytes = payload.try_to_vec().unwrap();
198-
assert_eq!(payload_bytes, vec![80, 84, 71, 77, 0, 0, 0, 26, 0, 0, 0, 0]);
216+
assert_eq!(
217+
payload_bytes,
218+
vec![
219+
80,
220+
84,
221+
71,
222+
77,
223+
0,
224+
0,
225+
CHAIN_ID.to_be_bytes()[0],
226+
CHAIN_ID.to_be_bytes()[1],
227+
0,
228+
0,
229+
0,
230+
0
231+
]
232+
);
233+
assert_eq!(
234+
payload_bytes,
235+
vec![
236+
80,
237+
84,
238+
71,
239+
77,
240+
0,
241+
0,
242+
CHAIN_ID.to_be_bytes()[0],
243+
CHAIN_ID.to_be_bytes()[1],
244+
0,
245+
0,
246+
0,
247+
0
248+
]
249+
);
199250

200251
let deserialized_payload =
201252
ExecutorPayload::try_from_slice(payload_bytes.as_slice()).unwrap();
202253
assert_eq!(payload, deserialized_payload);
203254

204255
// One instruction
205256
let payload = ExecutorPayload {
206-
header: super::GovernanceHeader::executor_governance_header(),
257+
header: super::GovernanceHeader::executor_governance_header(CHAIN_ID),
207258

208259
instructions: vec![InstructionData::from(
209260
&anchor_lang::solana_program::system_instruction::create_account(
@@ -221,19 +272,59 @@ pub mod tests {
221272
let payload_bytes = payload.try_to_vec().unwrap();
222273
assert_eq!(
223274
payload_bytes[..12],
224-
vec![80, 84, 71, 77, 0, 0, 0, 26, 1, 0, 0, 0]
275+
vec![
276+
80,
277+
84,
278+
71,
279+
77,
280+
0,
281+
0,
282+
CHAIN_ID.to_be_bytes()[0],
283+
CHAIN_ID.to_be_bytes()[1],
284+
1,
285+
0,
286+
0,
287+
0
288+
]
225289
);
226290

227291
let deserialized_payload =
228292
ExecutorPayload::try_from_slice(payload_bytes.as_slice()).unwrap();
229293
assert_eq!(payload, deserialized_payload);
230294

231295
// Module outside of range
232-
let payload_bytes = vec![80, 84, 71, 77, 3, 0, 0, 26, 0, 0, 0, 0, 0];
296+
let payload_bytes = vec![
297+
80,
298+
84,
299+
71,
300+
77,
301+
3,
302+
0,
303+
CHAIN_ID.to_be_bytes()[0],
304+
CHAIN_ID.to_be_bytes()[1],
305+
0,
306+
0,
307+
0,
308+
0,
309+
0,
310+
];
233311
assert!(ExecutorPayload::try_from_slice(payload_bytes.as_slice()).is_err());
234312

235313
// Wrong module
236-
let payload_bytes = vec![80, 84, 71, 77, 1, 0, 0, 26, 0, 0, 0, 0];
314+
let payload_bytes = vec![
315+
80,
316+
84,
317+
71,
318+
77,
319+
1,
320+
0,
321+
CHAIN_ID.to_be_bytes()[0],
322+
CHAIN_ID.to_be_bytes()[1],
323+
0,
324+
0,
325+
0,
326+
0,
327+
];
237328
let deserialized_payload =
238329
ExecutorPayload::try_from_slice(payload_bytes.as_slice()).unwrap();
239330
assert_eq!(
@@ -242,7 +333,20 @@ pub mod tests {
242333
);
243334

244335
// Wrong magic
245-
let payload_bytes = vec![81, 84, 71, 77, 1, 0, 0, 26, 0, 0, 0, 0];
336+
let payload_bytes = vec![
337+
81,
338+
84,
339+
71,
340+
77,
341+
1,
342+
0,
343+
CHAIN_ID.to_be_bytes()[0],
344+
CHAIN_ID.to_be_bytes()[1],
345+
0,
346+
0,
347+
0,
348+
0,
349+
];
246350
let deserialized_payload =
247351
ExecutorPayload::try_from_slice(payload_bytes.as_slice()).unwrap();
248352
assert_eq!(
@@ -251,11 +355,37 @@ pub mod tests {
251355
);
252356

253357
// Action outside of range
254-
let payload_bytes = vec![80, 84, 71, 77, 0, 1, 0, 26, 0, 0, 0, 0];
358+
let payload_bytes = vec![
359+
80,
360+
84,
361+
71,
362+
77,
363+
0,
364+
1,
365+
CHAIN_ID.to_be_bytes()[0],
366+
CHAIN_ID.to_be_bytes()[1],
367+
0,
368+
0,
369+
0,
370+
0,
371+
];
255372
assert!(ExecutorPayload::try_from_slice(payload_bytes.as_slice()).is_err());
256373

257374
// Wrong receiver chain endianess
258-
let payload_bytes = vec![80, 84, 71, 77, 0, 0, 26, 0, 0, 0, 0, 0];
375+
let payload_bytes = vec![
376+
80,
377+
84,
378+
71,
379+
77,
380+
0,
381+
0,
382+
CHAIN_ID.to_be_bytes()[1],
383+
CHAIN_ID.to_be_bytes()[0],
384+
0,
385+
0,
386+
0,
387+
0,
388+
];
259389
let deserialized_payload =
260390
ExecutorPayload::try_from_slice(payload_bytes.as_slice()).unwrap();
261391
assert_eq!(
@@ -264,7 +394,20 @@ pub mod tests {
264394
);
265395

266396
// Wrong vector format
267-
let payload_bytes = vec![80, 84, 71, 77, 0, 0, 0, 26, 1, 0, 0, 0];
397+
let payload_bytes = vec![
398+
80,
399+
84,
400+
71,
401+
77,
402+
0,
403+
0,
404+
CHAIN_ID.to_be_bytes()[0],
405+
CHAIN_ID.to_be_bytes()[1],
406+
1,
407+
0,
408+
0,
409+
0,
410+
];
268411
assert!(ExecutorPayload::try_from_slice(payload_bytes.as_slice()).is_err());
269412
}
270413
}

0 commit comments

Comments
 (0)