Skip to content

Commit 1ad419e

Browse files
authored
Guibescos/executor deser test (#290)
* Add tests * Add tests to CI * Remote test from precommit hook
1 parent fdafbe3 commit 1ad419e

File tree

1 file changed

+146
-7
lines changed

1 file changed

+146
-7
lines changed

pythnet/remote-executor/programs/remote-executor/src/state/governance_payload.rs

Lines changed: 146 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,20 @@ use crate::error::ExecutorError;
1515

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

18-
#[derive(AnchorDeserialize, AnchorSerialize)]
18+
#[derive(AnchorDeserialize, AnchorSerialize, Debug, PartialEq, Eq)]
1919
pub struct ExecutorPayload {
2020
pub header: GovernanceHeader,
2121

2222
pub instructions: Vec<InstructionData>,
2323
}
2424

25-
#[derive(AnchorDeserialize, AnchorSerialize, PartialEq, Eq)]
25+
#[derive(AnchorDeserialize, AnchorSerialize, PartialEq, Eq, Debug)]
2626
pub enum Module {
2727
Executor = 0,
2828
Target,
2929
}
3030

31-
#[derive(AnchorDeserialize, AnchorSerialize, PartialEq, Eq)]
31+
#[derive(AnchorDeserialize, AnchorSerialize, PartialEq, Eq, Debug)]
3232
pub enum Action {
3333
ExecutePostedVaa = 0,
3434
}
@@ -38,7 +38,7 @@ pub enum Action {
3838
/// - A one byte module variant (0 for Executor and 1 for Target contracts)
3939
/// - A one byte action variant (for Executor only 0 is currently valid)
4040
/// - A bigendian 2 bytes u16 chain id
41-
#[derive(AnchorDeserialize, AnchorSerialize)]
41+
#[derive(AnchorDeserialize, AnchorSerialize, Eq, PartialEq, Debug)]
4242
pub struct GovernanceHeader {
4343
pub magic_number: u32,
4444
pub module: Module,
@@ -47,6 +47,7 @@ pub struct GovernanceHeader {
4747
}
4848

4949
/// Hack to get Borsh to deserialize, serialize this number with big endian order
50+
#[derive(Eq, PartialEq, Debug)]
5051
pub struct BigEndianU16 {
5152
pub value: u16,
5253
}
@@ -119,6 +120,24 @@ impl From<&InstructionData> for Instruction {
119120
}
120121
}
121122

123+
impl From<&Instruction> for InstructionData {
124+
fn from(instruction: &Instruction) -> Self {
125+
InstructionData {
126+
program_id: instruction.program_id,
127+
accounts: instruction
128+
.accounts
129+
.iter()
130+
.map(|a| AccountMetaData {
131+
pubkey: a.pubkey,
132+
is_signer: a.is_signer,
133+
is_writable: a.is_writable,
134+
})
135+
.collect(),
136+
data: instruction.data.clone(),
137+
}
138+
}
139+
}
140+
122141
impl ExecutorPayload {
123142
const MODULE: Module = Module::Executor;
124143
const ACTION: Action = Action::ExecutePostedVaa;
@@ -127,10 +146,130 @@ impl ExecutorPayload {
127146
(self.header.magic_number == MAGIC_NUMBER)
128147
.ok_or(error!(ExecutorError::GovernanceHeaderInvalidMagicNumber))?;
129148
(self.header.module == ExecutorPayload::MODULE)
130-
.ok_or(error!(ExecutorError::GovernanceHeaderInvalidMagicNumber))?;
149+
.ok_or(error!(ExecutorError::GovernanceHeaderInvalidModule))?;
131150
(self.header.action == ExecutorPayload::ACTION)
132-
.ok_or(error!(ExecutorError::GovernanceHeaderInvalidMagicNumber))?;
151+
.ok_or(error!(ExecutorError::GovernanceHeaderInvalidAction))?;
133152
(Chain::from(self.header.chain.value) == Chain::Pythnet)
134-
.ok_or(error!(ExecutorError::GovernanceHeaderInvalidMagicNumber))
153+
.ok_or(error!(ExecutorError::GovernanceHeaderInvalidReceiverChain))
154+
}
155+
}
156+
157+
#[cfg(test)]
158+
pub mod tests {
159+
use crate::{
160+
error,
161+
error::ExecutorError,
162+
state::governance_payload::InstructionData,
163+
};
164+
165+
use super::{
166+
Action,
167+
BigEndianU16,
168+
ExecutorPayload,
169+
Module,
170+
MAGIC_NUMBER,
171+
};
172+
use anchor_lang::{
173+
prelude::Pubkey,
174+
AnchorDeserialize,
175+
AnchorSerialize,
176+
};
177+
use wormhole::Chain;
178+
179+
#[test]
180+
fn test_check_deserialization_serialization() {
181+
// No instructions
182+
let payload = ExecutorPayload {
183+
header: super::GovernanceHeader {
184+
magic_number: MAGIC_NUMBER,
185+
module: Module::Executor,
186+
action: Action::ExecutePostedVaa,
187+
chain: BigEndianU16 {
188+
value: Chain::Pythnet.try_into().unwrap(),
189+
},
190+
},
191+
instructions: vec![],
192+
};
193+
194+
assert!(payload.check_header().is_ok());
195+
196+
let payload_bytes = payload.try_to_vec().unwrap();
197+
assert_eq!(payload_bytes, vec![80, 84, 71, 77, 0, 0, 0, 26, 0, 0, 0, 0]);
198+
199+
let deserialized_payload =
200+
ExecutorPayload::try_from_slice(payload_bytes.as_slice()).unwrap();
201+
assert_eq!(payload, deserialized_payload);
202+
203+
// One instruction
204+
let payload = ExecutorPayload {
205+
header: super::GovernanceHeader {
206+
magic_number: MAGIC_NUMBER,
207+
module: Module::Executor,
208+
action: Action::ExecutePostedVaa,
209+
chain: BigEndianU16 {
210+
value: Chain::Pythnet.try_into().unwrap(),
211+
},
212+
},
213+
instructions: vec![InstructionData::from(
214+
&anchor_lang::solana_program::system_instruction::create_account(
215+
&Pubkey::new_unique(),
216+
&Pubkey::new_unique(),
217+
1,
218+
1,
219+
&Pubkey::new_unique(),
220+
),
221+
)],
222+
};
223+
224+
assert!(payload.check_header().is_ok());
225+
226+
let payload_bytes = payload.try_to_vec().unwrap();
227+
assert_eq!(
228+
payload_bytes[..12],
229+
vec![80, 84, 71, 77, 0, 0, 0, 26, 1, 0, 0, 0]
230+
);
231+
232+
let deserialized_payload =
233+
ExecutorPayload::try_from_slice(payload_bytes.as_slice()).unwrap();
234+
assert_eq!(payload, deserialized_payload);
235+
236+
// Module outside of range
237+
let payload_bytes = vec![80, 84, 71, 77, 3, 0, 0, 26, 0, 0, 0, 0, 0];
238+
assert!(ExecutorPayload::try_from_slice(payload_bytes.as_slice()).is_err());
239+
240+
// Wrong module
241+
let payload_bytes = vec![80, 84, 71, 77, 1, 0, 0, 26, 0, 0, 0, 0];
242+
let deserialized_payload =
243+
ExecutorPayload::try_from_slice(payload_bytes.as_slice()).unwrap();
244+
assert_eq!(
245+
deserialized_payload.check_header(),
246+
Err(error!(ExecutorError::GovernanceHeaderInvalidModule))
247+
);
248+
249+
// Wrong magic
250+
let payload_bytes = vec![81, 84, 71, 77, 1, 0, 0, 26, 0, 0, 0, 0];
251+
let deserialized_payload =
252+
ExecutorPayload::try_from_slice(payload_bytes.as_slice()).unwrap();
253+
assert_eq!(
254+
deserialized_payload.check_header(),
255+
Err(error!(ExecutorError::GovernanceHeaderInvalidMagicNumber))
256+
);
257+
258+
// Action outside of range
259+
let payload_bytes = vec![80, 84, 71, 77, 0, 1, 0, 26, 0, 0, 0, 0];
260+
assert!(ExecutorPayload::try_from_slice(payload_bytes.as_slice()).is_err());
261+
262+
// Wrong receiver chain endianess
263+
let payload_bytes = vec![80, 84, 71, 77, 0, 0, 26, 0, 0, 0, 0, 0];
264+
let deserialized_payload =
265+
ExecutorPayload::try_from_slice(payload_bytes.as_slice()).unwrap();
266+
assert_eq!(
267+
deserialized_payload.check_header(),
268+
Err(error!(ExecutorError::GovernanceHeaderInvalidReceiverChain))
269+
);
270+
271+
// Wrong vector format
272+
let payload_bytes = vec![80, 84, 71, 77, 0, 0, 0, 26, 1, 0, 0, 0];
273+
assert!(ExecutorPayload::try_from_slice(payload_bytes.as_slice()).is_err());
135274
}
136275
}

0 commit comments

Comments
 (0)