Browse Source

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
Daniel Chew 1 năm trước cách đây
mục cha
commit
6bc6682e44

+ 2 - 2
governance/remote_executor/Cargo.lock

@@ -1836,9 +1836,9 @@ checksum = "f9b7d56ba4a8344d6be9729995e6b06f928af29998cdf79fe390cbf6b1fee838"
 
 [[package]]
 name = "lazy_static"
-version = "1.4.0"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
 
 [[package]]
 name = "libc"

+ 13 - 0
governance/remote_executor/cli/src/cli.rs

@@ -4,6 +4,7 @@ use {
         Parser,
         Subcommand,
     },
+    remote_executor::state::governance_payload::CHAIN_ID_ARRAY,
     solana_sdk::{
         commitment_config::CommitmentConfig,
         pubkey::Pubkey,
@@ -18,10 +19,22 @@ use {
 pub struct Cli {
     #[clap(long, default_value = "confirmed")]
     pub commitment: CommitmentConfig,
+    #[clap(long, default_value = "pythnet", parse(try_from_str = parse_chain))]
+    pub chain:      u16,
+    #[clap(long, default_value = "https://pythnet.rpcpool.com/")]
+    pub rpc_url:    String,
     #[clap(subcommand)]
     pub action:     Action,
 }
 
+fn parse_chain(chain: &str) -> Result<u16, String> {
+    CHAIN_ID_ARRAY
+        .iter()
+        .find(|&&(name, _)| name == chain)
+        .map(|&(_, id)| id)
+        .ok_or_else(|| format!("Unsupported chain: {}", chain))
+}
+
 #[derive(Subcommand, Debug)]
 pub enum Action {
     #[clap(about = "Post a VAA and execute it through the remote executor")]

+ 5 - 6
governance/remote_executor/cli/src/main.rs

@@ -78,8 +78,7 @@ fn main() -> Result<()> {
         Action::PostAndExecute { vaa, keypair } => {
             let payer =
                 read_keypair_file(&*shellexpand::tilde(&keypair)).expect("Keypair not found");
-            let rpc_client =
-                RpcClient::new_with_commitment("https://pythnet.rpcpool.com/", cli.commitment);
+            let rpc_client = RpcClient::new_with_commitment(&cli.rpc_url, cli.commitment);
 
             let vaa_bytes: Vec<u8> = base64::decode(vaa)?;
             let wormhole = AnchorVaa::owner();
@@ -169,7 +168,7 @@ fn main() -> Result<()> {
                 Config::try_from_slice(&rpc_client.get_account_data(&wormhole_config)?)?;
 
             let payload = ExecutorPayload {
-                header:       GovernanceHeader::executor_governance_header(),
+                header:       GovernanceHeader::executor_governance_header(cli.chain),
                 instructions: vec![],
             }
             .try_to_vec()?;
@@ -197,7 +196,7 @@ fn main() -> Result<()> {
         }
         Action::GetTestPayload {} => {
             let payload = ExecutorPayload {
-                header:       GovernanceHeader::executor_governance_header(),
+                header:       GovernanceHeader::executor_governance_header(cli.chain),
                 instructions: vec![],
             }
             .try_to_vec()?;
@@ -224,7 +223,7 @@ fn main() -> Result<()> {
             instruction.accounts[2].is_signer = true; // Require signature of new authority for safety
             println!("New authority : {:}", instruction.accounts[2].pubkey);
             let payload = ExecutorPayload {
-                header:       GovernanceHeader::executor_governance_header(),
+                header:       GovernanceHeader::executor_governance_header(cli.chain),
                 instructions: vec![InstructionData::from(&instruction)],
             }
             .try_to_vec()?;
@@ -246,7 +245,7 @@ fn main() -> Result<()> {
                 instruction.accounts[3].pubkey
             );
             let payload = ExecutorPayload {
-                header:       GovernanceHeader::executor_governance_header(),
+                header:       GovernanceHeader::executor_governance_header(cli.chain),
                 instructions: vec![InstructionData::from(&instruction)],
             }
             .try_to_vec()?;

+ 9 - 1
governance/remote_executor/programs/remote-executor/Cargo.toml

@@ -13,8 +13,16 @@ no-entrypoint = []
 no-idl = []
 no-log-ix-name = []
 cpi = ["no-entrypoint"]
-default = []
+default = ["pythnet"]
+# These features determine the network for deployment:
+# - Use `pythnet` for deploying on Pythnet
+# - Use `pythtest` for deploying on Pythtest
+# - Use `eclipse_devnet` for deploying on Eclipse Devnet
+# etc...
+pythnet = []
 pythtest = []
+eclipse_devnet = []
+eclipse_testnet = []
 
 [dependencies]
 anchor-lang = {version = "0.25.0", features = ["init-if-needed"]}

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

@@ -10,11 +10,26 @@ use {
         mem::size_of,
         ops::Deref,
     },
-    wormhole_sdk::Chain,
 };
 
 pub const MAGIC_NUMBER: u32 = 0x4d475450; // Reverse order of the solidity contract because borsh uses little endian numbers (the solidity contract uses 0x5054474d)
 
+pub const CHAIN_ID_ARRAY: &[(&str, u16)] = &[
+    ("pythnet", 26),
+    ("pythtest", 26),
+    ("eclipse_devnet", 40001),
+    ("eclipse_testnet", 40002),
+];
+
+#[cfg(any(feature = "pythnet", feature = "pythtest"))]
+pub const CHAIN_ID: u16 = 26;
+
+#[cfg(feature = "eclipse_devnet")]
+pub const CHAIN_ID: u16 = 40001;
+
+#[cfg(feature = "eclipse_testnet")]
+pub const CHAIN_ID: u16 = 40002;
+
 #[derive(AnchorDeserialize, AnchorSerialize, Debug, PartialEq, Eq)]
 pub struct ExecutorPayload {
     pub header: GovernanceHeader,
@@ -48,14 +63,12 @@ pub struct GovernanceHeader {
 
 impl GovernanceHeader {
     #[allow(unused)] // Only used in tests right now
-    pub fn executor_governance_header() -> Self {
+    pub fn executor_governance_header(chain: u16) -> Self {
         Self {
             magic_number: MAGIC_NUMBER,
             module:       Module::Executor,
             action:       Action::ExecutePostedVaa,
-            chain:        BigEndianU16 {
-                value: Chain::Pythnet.try_into().unwrap(),
-            },
+            chain:        BigEndianU16 { value: chain },
         }
     }
 }
@@ -163,7 +176,7 @@ impl ExecutorPayload {
             .ok_or(error!(ExecutorError::GovernanceHeaderInvalidModule))?;
         (self.header.action == ExecutorPayload::ACTION)
             .ok_or(error!(ExecutorError::GovernanceHeaderInvalidAction))?;
-        (Chain::from(self.header.chain.value) == Chain::Pythnet)
+        (self.header.chain.value == CHAIN_ID)
             .ok_or(error!(ExecutorError::GovernanceHeaderInvalidReceiverChain))
     }
 }
@@ -173,12 +186,17 @@ pub mod tests {
     use {
         super::ExecutorPayload,
         crate::{
-            error,
             error::ExecutorError,
-            state::governance_payload::InstructionData,
+            state::governance_payload::{
+                InstructionData,
+                CHAIN_ID,
+            },
         },
         anchor_lang::{
-            prelude::Pubkey,
+            prelude::{
+                Pubkey,
+                *,
+            },
             AnchorDeserialize,
             AnchorSerialize,
         },
@@ -188,14 +206,47 @@ pub mod tests {
     fn test_check_deserialization_serialization() {
         // No instructions
         let payload = ExecutorPayload {
-            header:       super::GovernanceHeader::executor_governance_header(),
+            header:       super::GovernanceHeader::executor_governance_header(CHAIN_ID),
             instructions: vec![],
         };
 
         assert!(payload.check_header().is_ok());
 
         let payload_bytes = payload.try_to_vec().unwrap();
-        assert_eq!(payload_bytes, vec![80, 84, 71, 77, 0, 0, 0, 26, 0, 0, 0, 0]);
+        assert_eq!(
+            payload_bytes,
+            vec![
+                80,
+                84,
+                71,
+                77,
+                0,
+                0,
+                CHAIN_ID.to_be_bytes()[0],
+                CHAIN_ID.to_be_bytes()[1],
+                0,
+                0,
+                0,
+                0
+            ]
+        );
+        assert_eq!(
+            payload_bytes,
+            vec![
+                80,
+                84,
+                71,
+                77,
+                0,
+                0,
+                CHAIN_ID.to_be_bytes()[0],
+                CHAIN_ID.to_be_bytes()[1],
+                0,
+                0,
+                0,
+                0
+            ]
+        );
 
         let deserialized_payload =
             ExecutorPayload::try_from_slice(payload_bytes.as_slice()).unwrap();
@@ -203,7 +254,7 @@ pub mod tests {
 
         // One instruction
         let payload = ExecutorPayload {
-            header: super::GovernanceHeader::executor_governance_header(),
+            header: super::GovernanceHeader::executor_governance_header(CHAIN_ID),
 
             instructions: vec![InstructionData::from(
                 &anchor_lang::solana_program::system_instruction::create_account(
@@ -221,7 +272,20 @@ pub mod tests {
         let payload_bytes = payload.try_to_vec().unwrap();
         assert_eq!(
             payload_bytes[..12],
-            vec![80, 84, 71, 77, 0, 0, 0, 26, 1, 0, 0, 0]
+            vec![
+                80,
+                84,
+                71,
+                77,
+                0,
+                0,
+                CHAIN_ID.to_be_bytes()[0],
+                CHAIN_ID.to_be_bytes()[1],
+                1,
+                0,
+                0,
+                0
+            ]
         );
 
         let deserialized_payload =
@@ -229,11 +293,38 @@ pub mod tests {
         assert_eq!(payload, deserialized_payload);
 
         // Module outside of range
-        let payload_bytes = vec![80, 84, 71, 77, 3, 0, 0, 26, 0, 0, 0, 0, 0];
+        let payload_bytes = vec![
+            80,
+            84,
+            71,
+            77,
+            3,
+            0,
+            CHAIN_ID.to_be_bytes()[0],
+            CHAIN_ID.to_be_bytes()[1],
+            0,
+            0,
+            0,
+            0,
+            0,
+        ];
         assert!(ExecutorPayload::try_from_slice(payload_bytes.as_slice()).is_err());
 
         // Wrong module
-        let payload_bytes = vec![80, 84, 71, 77, 1, 0, 0, 26, 0, 0, 0, 0];
+        let payload_bytes = vec![
+            80,
+            84,
+            71,
+            77,
+            1,
+            0,
+            CHAIN_ID.to_be_bytes()[0],
+            CHAIN_ID.to_be_bytes()[1],
+            0,
+            0,
+            0,
+            0,
+        ];
         let deserialized_payload =
             ExecutorPayload::try_from_slice(payload_bytes.as_slice()).unwrap();
         assert_eq!(
@@ -242,7 +333,20 @@ pub mod tests {
         );
 
         // Wrong magic
-        let payload_bytes = vec![81, 84, 71, 77, 1, 0, 0, 26, 0, 0, 0, 0];
+        let payload_bytes = vec![
+            81,
+            84,
+            71,
+            77,
+            1,
+            0,
+            CHAIN_ID.to_be_bytes()[0],
+            CHAIN_ID.to_be_bytes()[1],
+            0,
+            0,
+            0,
+            0,
+        ];
         let deserialized_payload =
             ExecutorPayload::try_from_slice(payload_bytes.as_slice()).unwrap();
         assert_eq!(
@@ -251,11 +355,37 @@ pub mod tests {
         );
 
         // Action outside of range
-        let payload_bytes = vec![80, 84, 71, 77, 0, 1, 0, 26, 0, 0, 0, 0];
+        let payload_bytes = vec![
+            80,
+            84,
+            71,
+            77,
+            0,
+            1,
+            CHAIN_ID.to_be_bytes()[0],
+            CHAIN_ID.to_be_bytes()[1],
+            0,
+            0,
+            0,
+            0,
+        ];
         assert!(ExecutorPayload::try_from_slice(payload_bytes.as_slice()).is_err());
 
         // Wrong receiver chain endianess
-        let payload_bytes = vec![80, 84, 71, 77, 0, 0, 26, 0, 0, 0, 0, 0];
+        let payload_bytes = vec![
+            80,
+            84,
+            71,
+            77,
+            0,
+            0,
+            CHAIN_ID.to_be_bytes()[1],
+            CHAIN_ID.to_be_bytes()[0],
+            0,
+            0,
+            0,
+            0,
+        ];
         let deserialized_payload =
             ExecutorPayload::try_from_slice(payload_bytes.as_slice()).unwrap();
         assert_eq!(
@@ -264,7 +394,20 @@ pub mod tests {
         );
 
         // Wrong vector format
-        let payload_bytes = vec![80, 84, 71, 77, 0, 0, 0, 26, 1, 0, 0, 0];
+        let payload_bytes = vec![
+            80,
+            84,
+            71,
+            77,
+            0,
+            0,
+            CHAIN_ID.to_be_bytes()[0],
+            CHAIN_ID.to_be_bytes()[1],
+            1,
+            0,
+            0,
+            0,
+        ];
         assert!(ExecutorPayload::try_from_slice(payload_bytes.as_slice()).is_err());
     }
 }

+ 6 - 1
governance/remote_executor/programs/remote-executor/src/state/posted_vaa.rs

@@ -10,7 +10,7 @@ use {
 
 // The current chain's wormhole bridge owns the VAA accounts
 impl Owner for AnchorVaa {
-    #[cfg(not(feature = "pythtest"))]
+    #[cfg(feature = "pythnet")]
     fn owner() -> Pubkey {
         Pubkey::from_str("H3fxXJ86ADW2PNuDDmZJg6mzTtPxkYCpNuQUTgmJ7AjU").unwrap()
     }
@@ -19,6 +19,11 @@ impl Owner for AnchorVaa {
     fn owner() -> Pubkey {
         Pubkey::from_str("EUrRARh92Cdc54xrDn6qzaqjA77NRrCcfbr8kPwoTL4z").unwrap()
     }
+
+    #[cfg(any(feature = "eclipse_devnet", feature = "eclipse_testnet"))]
+    fn owner() -> Pubkey {
+        Pubkey::from_str("HDwcJBJXjL9FpJ7UBsYBtaDjsBUhuLCUYoz3zr8SWWaQ").unwrap()
+    }
 }
 
 impl AccountDeserialize for AnchorVaa {

+ 2 - 2
governance/remote_executor/programs/remote-executor/src/tests/executor_simulator.rs

@@ -7,6 +7,7 @@ use {
                 ExecutorPayload,
                 GovernanceHeader,
                 InstructionData,
+                CHAIN_ID,
             },
             posted_vaa::AnchorVaa,
         },
@@ -89,7 +90,6 @@ impl ExecutorBench {
                 .join(Path::new("../../target/deploy/remote_executor.so")),
         );
 
-
         let mut program_test = ProgramTest::default();
         let program_key = crate::id();
         let programdata_key = Pubkey::new_unique();
@@ -172,7 +172,7 @@ impl ExecutorBench {
         };
 
         let payload = ExecutorPayload {
-            header:       GovernanceHeader::executor_governance_header(),
+            header:       GovernanceHeader::executor_governance_header(CHAIN_ID),
             instructions: instructions.iter().map(InstructionData::from).collect(),
         };