浏览代码

Guibescos/executor deser test (#290)

* Add tests

* Add tests to CI

* Remote test from precommit hook
guibescos 3 年之前
父节点
当前提交
1ad419e0ba
共有 1 个文件被更改,包括 146 次插入7 次删除
  1. 146 7
      pythnet/remote-executor/programs/remote-executor/src/state/governance_payload.rs

+ 146 - 7
pythnet/remote-executor/programs/remote-executor/src/state/governance_payload.rs

@@ -15,20 +15,20 @@ use crate::error::ExecutorError;
 
 pub const MAGIC_NUMBER: u32 = 0x4d475450; // Reverse order of the solidity contract because borsh uses little endian numbers (the solidity contract uses 0x5054474d)
 
-#[derive(AnchorDeserialize, AnchorSerialize)]
+#[derive(AnchorDeserialize, AnchorSerialize, Debug, PartialEq, Eq)]
 pub struct ExecutorPayload {
     pub header: GovernanceHeader,
 
     pub instructions: Vec<InstructionData>,
 }
 
-#[derive(AnchorDeserialize, AnchorSerialize, PartialEq, Eq)]
+#[derive(AnchorDeserialize, AnchorSerialize, PartialEq, Eq, Debug)]
 pub enum Module {
     Executor = 0,
     Target,
 }
 
-#[derive(AnchorDeserialize, AnchorSerialize, PartialEq, Eq)]
+#[derive(AnchorDeserialize, AnchorSerialize, PartialEq, Eq, Debug)]
 pub enum Action {
     ExecutePostedVaa = 0,
 }
@@ -38,7 +38,7 @@ pub enum Action {
 /// - A one byte module variant (0 for Executor and 1 for Target contracts)
 /// - A one byte action variant (for Executor only 0 is currently valid)
 /// - A bigendian 2 bytes u16 chain id
-#[derive(AnchorDeserialize, AnchorSerialize)]
+#[derive(AnchorDeserialize, AnchorSerialize, Eq, PartialEq, Debug)]
 pub struct GovernanceHeader {
     pub magic_number: u32,
     pub module: Module,
@@ -47,6 +47,7 @@ pub struct GovernanceHeader {
 }
 
 /// Hack to get Borsh to deserialize, serialize this number with big endian order
+#[derive(Eq, PartialEq, Debug)]
 pub struct BigEndianU16 {
     pub value: u16,
 }
@@ -119,6 +120,24 @@ impl From<&InstructionData> for Instruction {
     }
 }
 
+impl From<&Instruction> for InstructionData {
+    fn from(instruction: &Instruction) -> Self {
+        InstructionData {
+            program_id: instruction.program_id,
+            accounts: instruction
+                .accounts
+                .iter()
+                .map(|a| AccountMetaData {
+                    pubkey: a.pubkey,
+                    is_signer: a.is_signer,
+                    is_writable: a.is_writable,
+                })
+                .collect(),
+            data: instruction.data.clone(),
+        }
+    }
+}
+
 impl ExecutorPayload {
     const MODULE: Module = Module::Executor;
     const ACTION: Action = Action::ExecutePostedVaa;
@@ -127,10 +146,130 @@ impl ExecutorPayload {
         (self.header.magic_number == MAGIC_NUMBER)
             .ok_or(error!(ExecutorError::GovernanceHeaderInvalidMagicNumber))?;
         (self.header.module == ExecutorPayload::MODULE)
-            .ok_or(error!(ExecutorError::GovernanceHeaderInvalidMagicNumber))?;
+            .ok_or(error!(ExecutorError::GovernanceHeaderInvalidModule))?;
         (self.header.action == ExecutorPayload::ACTION)
-            .ok_or(error!(ExecutorError::GovernanceHeaderInvalidMagicNumber))?;
+            .ok_or(error!(ExecutorError::GovernanceHeaderInvalidAction))?;
         (Chain::from(self.header.chain.value) == Chain::Pythnet)
-            .ok_or(error!(ExecutorError::GovernanceHeaderInvalidMagicNumber))
+            .ok_or(error!(ExecutorError::GovernanceHeaderInvalidReceiverChain))
+    }
+}
+
+#[cfg(test)]
+pub mod tests {
+    use crate::{
+        error,
+        error::ExecutorError,
+        state::governance_payload::InstructionData,
+    };
+
+    use super::{
+        Action,
+        BigEndianU16,
+        ExecutorPayload,
+        Module,
+        MAGIC_NUMBER,
+    };
+    use anchor_lang::{
+        prelude::Pubkey,
+        AnchorDeserialize,
+        AnchorSerialize,
+    };
+    use wormhole::Chain;
+
+    #[test]
+    fn test_check_deserialization_serialization() {
+        // No instructions
+        let payload = ExecutorPayload {
+            header: super::GovernanceHeader {
+                magic_number: MAGIC_NUMBER,
+                module: Module::Executor,
+                action: Action::ExecutePostedVaa,
+                chain: BigEndianU16 {
+                    value: Chain::Pythnet.try_into().unwrap(),
+                },
+            },
+            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]);
+
+        let deserialized_payload =
+            ExecutorPayload::try_from_slice(payload_bytes.as_slice()).unwrap();
+        assert_eq!(payload, deserialized_payload);
+
+        // One instruction
+        let payload = ExecutorPayload {
+            header: super::GovernanceHeader {
+                magic_number: MAGIC_NUMBER,
+                module: Module::Executor,
+                action: Action::ExecutePostedVaa,
+                chain: BigEndianU16 {
+                    value: Chain::Pythnet.try_into().unwrap(),
+                },
+            },
+            instructions: vec![InstructionData::from(
+                &anchor_lang::solana_program::system_instruction::create_account(
+                    &Pubkey::new_unique(),
+                    &Pubkey::new_unique(),
+                    1,
+                    1,
+                    &Pubkey::new_unique(),
+                ),
+            )],
+        };
+
+        assert!(payload.check_header().is_ok());
+
+        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]
+        );
+
+        let deserialized_payload =
+            ExecutorPayload::try_from_slice(payload_bytes.as_slice()).unwrap();
+        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];
+        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 deserialized_payload =
+            ExecutorPayload::try_from_slice(payload_bytes.as_slice()).unwrap();
+        assert_eq!(
+            deserialized_payload.check_header(),
+            Err(error!(ExecutorError::GovernanceHeaderInvalidModule))
+        );
+
+        // Wrong magic
+        let payload_bytes = vec![81, 84, 71, 77, 1, 0, 0, 26, 0, 0, 0, 0];
+        let deserialized_payload =
+            ExecutorPayload::try_from_slice(payload_bytes.as_slice()).unwrap();
+        assert_eq!(
+            deserialized_payload.check_header(),
+            Err(error!(ExecutorError::GovernanceHeaderInvalidMagicNumber))
+        );
+
+        // Action outside of range
+        let payload_bytes = vec![80, 84, 71, 77, 0, 1, 0, 26, 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 deserialized_payload =
+            ExecutorPayload::try_from_slice(payload_bytes.as_slice()).unwrap();
+        assert_eq!(
+            deserialized_payload.check_header(),
+            Err(error!(ExecutorError::GovernanceHeaderInvalidReceiverChain))
+        );
+
+        // Wrong vector format
+        let payload_bytes = vec![80, 84, 71, 77, 0, 0, 0, 26, 1, 0, 0, 0];
+        assert!(ExecutorPayload::try_from_slice(payload_bytes.as_slice()).is_err());
     }
 }