Guibescos/executor deser test (#290)
* Add tests * Add tests to CI * Remote test from precommit hook
This commit is contained in:
parent
fdafbe32a4
commit
1ad419e0ba
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue