Skip to content

Commit 134f595

Browse files
authored
Merge pull request #2898 from pyth-network/pyth-stylus-governance-better-tests
feat(stylus): Negative + Invalid Tests for Stylus Governance
2 parents d62a045 + 6c9cc73 commit 134f595

File tree

2 files changed

+215
-39
lines changed

2 files changed

+215
-39
lines changed

target_chains/stylus/contracts/pyth-receiver/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ use stylus_sdk::{
3232
};
3333

3434
use error::PythReceiverError;
35-
use governance_structs::*;
3635
use pythnet_sdk::{
3736
accumulators::merkle::{MerklePath, MerkleRoot},
3837
hashers::keccak256_160::Keccak160,

target_chains/stylus/contracts/pyth-receiver/src/pyth_governance_test.rs

Lines changed: 215 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
#[cfg(test)]
22
mod test {
3-
use crate::{DataSourcesSet, FeeSet, GovernanceDataSourceSet, PythReceiver, TransactionFeeSet};
3+
use crate::{
4+
DataSourcesSet, FeeSet, GovernanceDataSourceSet, PythReceiver, TransactionFeeSet,
5+
ValidPeriodSet,
6+
};
47
use alloy_primitives::{address, Address, FixedBytes, U256};
58
use hex::FromHex;
69
use motsu::prelude::*;
@@ -98,6 +101,9 @@ mod test {
98101
let result = pyth_contract
99102
.sender(alice)
100103
.execute_governance_instruction(bytes.clone());
104+
if result.is_err() {
105+
println!("SetDataSources Error: {:?}", result.as_ref().unwrap_err());
106+
}
101107
assert!(result.is_ok());
102108

103109
let expected_event = DataSourcesSet {
@@ -129,16 +135,27 @@ mod test {
129135
alice: Address,
130136
) {
131137
pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice, 0);
132-
133-
let hex_str = "010000000001006fc16df905b08c16553eda9d5a7898ec7eba4267ce0af7945625c955e8f435fc7df7a4087af360f88c2477f0c2f4e7eaa4bb1e8fd43677f4d6b04ee20e225186000000000100000000000100000000000000000000000000000000000000000000000000000000000000110000000000000001005054474d010400020000000000000000";
138+
let hex_str = "01000000000100c9effcab077af2f3f65a7abfd1883295529eab7c0d4434772ed1f2d10b1de3571c214af45e944a3fee65417c9f0c6024010dadc26d30bb361e05f552ca4de04d000000000100000000000100000000000000000000000000000000000000000000000000000000000000110000000000000001005054474d010400020000000000000003";
134139
let bytes = Vec::from_hex(hex_str).expect("Invalid hex string");
135140

136141
let result = pyth_contract
137142
.sender(alice)
138143
.execute_governance_instruction(bytes.clone());
139144

140-
println!("Result: {:?}", result.unwrap_err());
141-
// assert!(result.is_ok());
145+
assert!(
146+
result.is_ok(),
147+
"SetValidPeriod governance instruction should succeed"
148+
);
149+
150+
let expected_event = ValidPeriodSet {
151+
old_valid_period: U256::from(3600),
152+
new_valid_period: U256::from(3),
153+
};
154+
155+
assert!(
156+
pyth_contract.emitted(&expected_event),
157+
"ValidPeriodSet event should be emitted"
158+
);
142159
}
143160

144161
#[motsu::test]
@@ -177,34 +194,6 @@ mod test {
177194
);
178195
}
179196

180-
#[motsu::test]
181-
fn test_set_fee_in_token(
182-
pyth_contract: Contract<PythReceiver>,
183-
wormhole_contract: Contract<WormholeContract>,
184-
alice: Address,
185-
) {
186-
pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice, 0);
187-
188-
let hex_str = "0100000000010051c35e992b6dcfc81f02b430914694b4fbbeae7f952f3d3f1ff350f332d1d24916e2f56336ce0c392247e8c1fbb1b74eac87a68d681c729fa860f3788ece2788000000000100000000000100000000000000000000000000000000000000000000000000000000000000110000000000000001005054474d0107000200000000000000050000000000000003147e5f4552091a69125d5dfcb7b8c2659029395bdf";
189-
let bytes = Vec::from_hex(hex_str).expect("Invalid hex string");
190-
191-
let result = pyth_contract
192-
.sender(alice)
193-
.execute_governance_instruction(bytes.clone());
194-
if result.is_err() {
195-
println!("Error: {:?}", result.as_ref().unwrap_err());
196-
}
197-
assert!(result.is_ok());
198-
199-
let result2 = pyth_contract
200-
.sender(alice)
201-
.execute_governance_instruction(bytes.clone());
202-
assert!(
203-
result2.is_err(),
204-
"Second execution should fail due to sequence number check"
205-
);
206-
}
207-
208197
// This test is commented out because it requires an already deployed new Wormhole contract.
209198
// This function demonstrates the usage of this instruction, however.
210199
/*
@@ -254,8 +243,7 @@ mod test {
254243
alice: Address,
255244
) {
256245
pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice, 0);
257-
258-
let hex_str = "01000000000100b441e497034be4ee82242a866461d5e6744082654f71301a96f579f629b6bf176cc0c1964cd7d4f792436b7a73fc7024d72b138869b4d81d449740bb08148238000000000100000000000100000000000000000000000000000000000000000000000000000000000000110000000000000001005054474d01010002010000000001009c9dc62e92fefe0806dce30b662a5d319417a62dccc700b5f2678306d39c005f7a5e74d11df287301d85d328a3d000c5d793c57161f3150c7eb1a17668946e6b010000000100000000000100000000000000000000000000000000000000000000000000000000000000110000000000000064005054474d0105000200000000";
246+
let hex_str = "01000000000100eb6abceff17a900422cbe415bd4776aa6477ee6ec7f3f58d1635ea2071fb915e43c6ac312b34996d4a76c52de96a8c2cc1c50aacb45aa2013eb6c8d05a472f94010000000100000000000100000000000000000000000000000000000000000000000000000000000000110000000000000001005054474d01010002010000000001006fc27ac424b300c23a564bcabe1d7888a898cba92b8aec62468c35025baaf4a87056c50d443fbc172c3caa30d28ec57cefc0bbabf4590ffe98c44dff040d0e02000000000100000000000200000000000000000000000000000000000000000000000000000000000011110000000000000001005054474d0105000200000001";
259247
let bytes = Vec::from_hex(hex_str).expect("Invalid hex string");
260248

261249
let result = pyth_contract
@@ -269,12 +257,18 @@ mod test {
269257
}
270258
assert!(result.is_ok());
271259

260+
const NEW_GOVERNANCE_EMITTER: [u8; 32] = [
261+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
262+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
263+
0x00, 0x00, 0x11, 0x11,
264+
];
265+
272266
let expected_event = GovernanceDataSourceSet {
273267
old_chain_id: 0, // Initial governance_data_source_index
274268
old_emitter_address: FixedBytes::from(GOVERNANCE_EMITTER), // Initial governance emitter from pyth_wormhole_init
275-
new_chain_id: 1, // claim_vm.body.emitter_chain from the VAA
276-
new_emitter_address: FixedBytes::from(GOVERNANCE_EMITTER), // emitter_bytes from the VAA
277-
initial_sequence: 100, // claim_vm.body.sequence from the VAA (0x64 = 100)
269+
new_chain_id: 2, // claim_vm.body.emitter_chain from the VAA
270+
new_emitter_address: FixedBytes::from(NEW_GOVERNANCE_EMITTER), // emitter_bytes from the VAA
271+
initial_sequence: 1, // claim_vm.body.sequence from the VAA (0x64 = 100)
278272
};
279273
assert!(
280274
pyth_contract.emitted(&expected_event),
@@ -349,4 +343,187 @@ mod test {
349343
assert!(result.is_ok());
350344
}
351345
*/
346+
347+
#[motsu::test]
348+
fn test_invalid_wormhole_vaa_signature_reverts(
349+
pyth_contract: Contract<PythReceiver>,
350+
wormhole_contract: Contract<WormholeContract>,
351+
alice: Address,
352+
) {
353+
pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice, 0);
354+
355+
let hex_str = "0100000000010067940f58a6a44c93606bd721701539e0da93d5ea1583a735fbb13ecbcf9c01fc70240de519ea76869af14d067d68c5f3f2230f565f41b7009f3c3e63749353ed000000000100000000000100000000000000000000000000000000000000000000000000000000000000110000000000000001005054474d0103000200000000000000050000000000000003";
356+
let bytes = Vec::from_hex(&hex_str).expect("Invalid hex string");
357+
358+
let result = pyth_contract
359+
.sender(alice)
360+
.execute_governance_instruction(bytes.clone());
361+
362+
assert!(result.is_err(), "Invalid VAA should revert the transaction");
363+
}
364+
365+
#[motsu::test]
366+
fn test_invalid_wormhole_vaa_magic_reverts(
367+
pyth_contract: Contract<PythReceiver>,
368+
wormhole_contract: Contract<WormholeContract>,
369+
alice: Address,
370+
) {
371+
pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice, 0);
372+
373+
// Changed the magic signature to an invalid one (6064474d instead of 5054474d)
374+
let hex_str = "0100000000010067940f58a6a44c93606bd721701539e0da93d5ea1583a735fbb13ecbcf9c01fc70240de519ea76869af14d067d68c5f3f2230f565f41b7009f3c3e63749353ed000000000100000000000100000000000000000000000000000000000000000000000000000000000000110000000000000001006064474d0103000200000000000000050000000000000003";
375+
let bytes = Vec::from_hex(&hex_str).expect("Invalid hex string");
376+
377+
let result = pyth_contract
378+
.sender(alice)
379+
.execute_governance_instruction(bytes.clone());
380+
381+
assert!(result.is_err(), "Invalid VAA should revert the transaction");
382+
}
383+
384+
#[motsu::test]
385+
fn test_invalid_wormhole_vaa_random_byte_cut_reverts(
386+
pyth_contract: Contract<PythReceiver>,
387+
wormhole_contract: Contract<WormholeContract>,
388+
alice: Address,
389+
) {
390+
pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice, 0);
391+
392+
let hex_str = "0100000000010067940f58a676869af14d067d68c5f3f2230f565f41b7009f3c3e63749353ed000000000100000000000100000000000000000000000000000000000000000000000000000000000000110000000000000001005054474d0103000200000000000000050000000000000003";
393+
let bytes = Vec::from_hex(&hex_str).expect("Invalid hex string");
394+
395+
let result = pyth_contract
396+
.sender(alice)
397+
.execute_governance_instruction(bytes.clone());
398+
399+
assert!(result.is_err(), "Invalid VAA should revert the transaction");
400+
}
401+
402+
#[motsu::test]
403+
fn test_invalid_wormhole_vaa_invalid_version_number_reverts(
404+
pyth_contract: Contract<PythReceiver>,
405+
wormhole_contract: Contract<WormholeContract>,
406+
alice: Address,
407+
) {
408+
pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice, 0);
409+
410+
// Changed the version number to an invalid one (2 instead of 1)
411+
let hex_str = "0200000000010067940f58a6a44c93606bd721701539e0da93d5ea1583a735fbb13ecbcf9c01fc70240de519ea76869af14d067d68c5f3f2230f565f41b7009f3c3e63749353ed000000000100000000000100000000000000000000000000000000000000000000000000000000000000110000000000000001005054474d0103000200000000000000050000000000000003";
412+
let bytes = Vec::from_hex(&hex_str).expect("Invalid hex string");
413+
414+
let result = pyth_contract
415+
.sender(alice)
416+
.execute_governance_instruction(bytes.clone());
417+
418+
assert!(result.is_err(), "Invalid VAA should revert the transaction");
419+
}
420+
421+
#[motsu::test]
422+
fn test_different_emitter_chain_id_than_wormhole_reverts(
423+
pyth_contract: Contract<PythReceiver>,
424+
wormhole_contract: Contract<WormholeContract>,
425+
alice: Address,
426+
) {
427+
pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice, 0);
428+
429+
// Changed the emitter chain ID to a different one (2 instead of 1)
430+
let hex_str = "0100000000010057940f58a6a44c93606bd721701539e0da93d5ea1583a735fbb13ecbcf9c01fc70240de519ea76869af14d067d68c5f3f2230f565f41b7009f3c3e63749353ed000000000100000000000200000000000000000000000000000000000000000000000000000000000000110000000000000001005054474d0103000200000000000000050000000000000003";
431+
let bytes = Vec::from_hex(&hex_str).expect("Invalid hex string");
432+
433+
let result = pyth_contract
434+
.sender(alice)
435+
.execute_governance_instruction(bytes.clone());
436+
437+
assert!(result.is_err(), "Invalid VAA should revert the transaction");
438+
}
439+
440+
#[motsu::test]
441+
fn test_different_emitter_chain_address_than_wormhole_reverts(
442+
pyth_contract: Contract<PythReceiver>,
443+
wormhole_contract: Contract<WormholeContract>,
444+
alice: Address,
445+
) {
446+
pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice, 0);
447+
448+
// Changed the emitter chain ID to a different one (...0011 to ...0022)
449+
let hex_str = "0100000000010057940f58a6a44c93606bd721701539e0da93d5ea1583a735fbb13ecbcf9c01fc70240de519ea76869af14d067d68c5f3f2230f565f41b7009f3c3e63749353ed000000000100000000000100000000000000000000000000000000000000000000000000000000000000220000000000000001005054474d0103000200000000000000050000000000000003";
450+
let bytes = Vec::from_hex(&hex_str).expect("Invalid hex string");
451+
452+
let result = pyth_contract
453+
.sender(alice)
454+
.execute_governance_instruction(bytes.clone());
455+
456+
assert!(result.is_err(), "Invalid VAA should revert the transaction");
457+
}
458+
459+
#[motsu::test]
460+
fn test_sequence_number_greater_than_last_executed_reverts(
461+
pyth_contract: Contract<PythReceiver>,
462+
wormhole_contract: Contract<WormholeContract>,
463+
alice: Address,
464+
) {
465+
pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice, 0);
466+
467+
let hex_str = "0100000000010057940f58a6a44c93606bd721701539e0da93d5ea1583a735fbb13ecbcf9c01fc70240de519ea76869af14d067d68c5f3f2230f565f41b7009f3c3e63749353ed000000000100000000000100000000000000000000000000000000000000000000000000000000000000110000000000000001005054474d0103000200000000000000050000000000000003";
468+
let bytes = Vec::from_hex(&hex_str).expect("Invalid hex string");
469+
470+
let result = pyth_contract
471+
.sender(alice)
472+
.execute_governance_instruction(bytes.clone());
473+
474+
assert!(result.is_ok(), "This is a valid VAA, should go through");
475+
476+
let result = pyth_contract
477+
.sender(alice)
478+
.execute_governance_instruction(bytes.clone());
479+
480+
assert!(
481+
result.is_err(),
482+
"Cannot execute the same sequence number again, should revert"
483+
);
484+
}
485+
486+
#[motsu::test]
487+
fn test_target_chain_id_from_ethereum_to_solana_reverts(
488+
pyth_contract: Contract<PythReceiver>,
489+
wormhole_contract: Contract<WormholeContract>,
490+
alice: Address,
491+
) {
492+
pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice, 0);
493+
494+
// This VAA is for a target chain ID of 1 (Solana), but the PythReceiver is on chain ID 2 (Ethereum)
495+
let hex_str = "0100000000010057940f58a6a44c93606bd721701539e0da93d5ea1583a735fbb13ecbcf9c01fc70240de519ea76869af14d067d68c5f3f2230f565f41b7009f3c3e63749353ed000000000100000000000100000000000000000000000000000000000000000000000000000000000000110000000000000001005054474d0103000100000000000000050000000000000003";
496+
let bytes = Vec::from_hex(&hex_str).expect("Invalid hex string");
497+
498+
let result = pyth_contract
499+
.sender(alice)
500+
.execute_governance_instruction(bytes.clone());
501+
502+
assert!(
503+
result.is_err(),
504+
"Incorrect target chain ID should revert the transaction"
505+
);
506+
}
507+
508+
#[motsu::test]
509+
fn test_unexpected_governance_action_id_reverts(
510+
pyth_contract: Contract<PythReceiver>,
511+
wormhole_contract: Contract<WormholeContract>,
512+
alice: Address,
513+
) {
514+
pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice, 0);
515+
516+
// Changes this action to be a SetDataSources action instead of a SetFee action
517+
let hex_str = "0100000000010057940f58a6a44c93606bd721701539e0da93d5ea1583a735fbb13ecbcf9c01fc70240de519ea76869af14d067d68c5f3f2230f565f41b7009f3c3e63749353ed000000000100000000000100000000000000000000000000000000000000000000000000000000000000110000000000000001005054474d0102000200000000000000050000000000000003";
518+
let bytes = Vec::from_hex(&hex_str).expect("Invalid hex string");
519+
520+
let result = pyth_contract
521+
.sender(alice)
522+
.execute_governance_instruction(bytes.clone());
523+
524+
assert!(
525+
result.is_err(),
526+
"Wrong action expected should lead to bad parsing"
527+
);
528+
}
352529
}

0 commit comments

Comments
 (0)