@@ -15,20 +15,20 @@ use crate::error::ExecutorError;
15
15
16
16
pub const MAGIC_NUMBER : u32 = 0x4d475450 ; // Reverse order of the solidity contract because borsh uses little endian numbers (the solidity contract uses 0x5054474d)
17
17
18
- #[ derive( AnchorDeserialize , AnchorSerialize ) ]
18
+ #[ derive( AnchorDeserialize , AnchorSerialize , Debug , PartialEq , Eq ) ]
19
19
pub struct ExecutorPayload {
20
20
pub header : GovernanceHeader ,
21
21
22
22
pub instructions : Vec < InstructionData > ,
23
23
}
24
24
25
- #[ derive( AnchorDeserialize , AnchorSerialize , PartialEq , Eq ) ]
25
+ #[ derive( AnchorDeserialize , AnchorSerialize , PartialEq , Eq , Debug ) ]
26
26
pub enum Module {
27
27
Executor = 0 ,
28
28
Target ,
29
29
}
30
30
31
- #[ derive( AnchorDeserialize , AnchorSerialize , PartialEq , Eq ) ]
31
+ #[ derive( AnchorDeserialize , AnchorSerialize , PartialEq , Eq , Debug ) ]
32
32
pub enum Action {
33
33
ExecutePostedVaa = 0 ,
34
34
}
@@ -38,7 +38,7 @@ pub enum Action {
38
38
/// - A one byte module variant (0 for Executor and 1 for Target contracts)
39
39
/// - A one byte action variant (for Executor only 0 is currently valid)
40
40
/// - A bigendian 2 bytes u16 chain id
41
- #[ derive( AnchorDeserialize , AnchorSerialize ) ]
41
+ #[ derive( AnchorDeserialize , AnchorSerialize , Eq , PartialEq , Debug ) ]
42
42
pub struct GovernanceHeader {
43
43
pub magic_number : u32 ,
44
44
pub module : Module ,
@@ -47,6 +47,7 @@ pub struct GovernanceHeader {
47
47
}
48
48
49
49
/// Hack to get Borsh to deserialize, serialize this number with big endian order
50
+ #[ derive( Eq , PartialEq , Debug ) ]
50
51
pub struct BigEndianU16 {
51
52
pub value : u16 ,
52
53
}
@@ -119,6 +120,24 @@ impl From<&InstructionData> for Instruction {
119
120
}
120
121
}
121
122
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
+
122
141
impl ExecutorPayload {
123
142
const MODULE : Module = Module :: Executor ;
124
143
const ACTION : Action = Action :: ExecutePostedVaa ;
@@ -127,10 +146,130 @@ impl ExecutorPayload {
127
146
( self . header . magic_number == MAGIC_NUMBER )
128
147
. ok_or ( error ! ( ExecutorError :: GovernanceHeaderInvalidMagicNumber ) ) ?;
129
148
( self . header . module == ExecutorPayload :: MODULE )
130
- . ok_or ( error ! ( ExecutorError :: GovernanceHeaderInvalidMagicNumber ) ) ?;
149
+ . ok_or ( error ! ( ExecutorError :: GovernanceHeaderInvalidModule ) ) ?;
131
150
( self . header . action == ExecutorPayload :: ACTION )
132
- . ok_or ( error ! ( ExecutorError :: GovernanceHeaderInvalidMagicNumber ) ) ?;
151
+ . ok_or ( error ! ( ExecutorError :: GovernanceHeaderInvalidAction ) ) ?;
133
152
( 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( ) ) ;
135
274
}
136
275
}
0 commit comments