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)
|
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 struct ExecutorPayload {
|
||||||
pub header: GovernanceHeader,
|
pub header: GovernanceHeader,
|
||||||
|
|
||||||
pub instructions: Vec<InstructionData>,
|
pub instructions: Vec<InstructionData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(AnchorDeserialize, AnchorSerialize, PartialEq, Eq)]
|
#[derive(AnchorDeserialize, AnchorSerialize, PartialEq, Eq, Debug)]
|
||||||
pub enum Module {
|
pub enum Module {
|
||||||
Executor = 0,
|
Executor = 0,
|
||||||
Target,
|
Target,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(AnchorDeserialize, AnchorSerialize, PartialEq, Eq)]
|
#[derive(AnchorDeserialize, AnchorSerialize, PartialEq, Eq, Debug)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
ExecutePostedVaa = 0,
|
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 module variant (0 for Executor and 1 for Target contracts)
|
||||||
/// - A one byte action variant (for Executor only 0 is currently valid)
|
/// - A one byte action variant (for Executor only 0 is currently valid)
|
||||||
/// - A bigendian 2 bytes u16 chain id
|
/// - A bigendian 2 bytes u16 chain id
|
||||||
#[derive(AnchorDeserialize, AnchorSerialize)]
|
#[derive(AnchorDeserialize, AnchorSerialize, Eq, PartialEq, Debug)]
|
||||||
pub struct GovernanceHeader {
|
pub struct GovernanceHeader {
|
||||||
pub magic_number: u32,
|
pub magic_number: u32,
|
||||||
pub module: Module,
|
pub module: Module,
|
||||||
|
@ -47,6 +47,7 @@ pub struct GovernanceHeader {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Hack to get Borsh to deserialize, serialize this number with big endian order
|
/// Hack to get Borsh to deserialize, serialize this number with big endian order
|
||||||
|
#[derive(Eq, PartialEq, Debug)]
|
||||||
pub struct BigEndianU16 {
|
pub struct BigEndianU16 {
|
||||||
pub value: u16,
|
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 {
|
impl ExecutorPayload {
|
||||||
const MODULE: Module = Module::Executor;
|
const MODULE: Module = Module::Executor;
|
||||||
const ACTION: Action = Action::ExecutePostedVaa;
|
const ACTION: Action = Action::ExecutePostedVaa;
|
||||||
|
@ -127,10 +146,130 @@ impl ExecutorPayload {
|
||||||
(self.header.magic_number == MAGIC_NUMBER)
|
(self.header.magic_number == MAGIC_NUMBER)
|
||||||
.ok_or(error!(ExecutorError::GovernanceHeaderInvalidMagicNumber))?;
|
.ok_or(error!(ExecutorError::GovernanceHeaderInvalidMagicNumber))?;
|
||||||
(self.header.module == ExecutorPayload::MODULE)
|
(self.header.module == ExecutorPayload::MODULE)
|
||||||
.ok_or(error!(ExecutorError::GovernanceHeaderInvalidMagicNumber))?;
|
.ok_or(error!(ExecutorError::GovernanceHeaderInvalidModule))?;
|
||||||
(self.header.action == ExecutorPayload::ACTION)
|
(self.header.action == ExecutorPayload::ACTION)
|
||||||
.ok_or(error!(ExecutorError::GovernanceHeaderInvalidMagicNumber))?;
|
.ok_or(error!(ExecutorError::GovernanceHeaderInvalidAction))?;
|
||||||
(Chain::from(self.header.chain.value) == Chain::Pythnet)
|
(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