Skip to content

Commit c179693

Browse files
committed
fixing parse and verify vm incorrect guardian index bug
1 parent 5b74700 commit c179693

File tree

3 files changed

+324
-15
lines changed

3 files changed

+324
-15
lines changed

target_chains/stylus/contracts/wormhole/src/lib.rs

Lines changed: 70 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -145,22 +145,20 @@ impl WormholeContract {
145145
Ok(())
146146
}
147147

148-
149-
150148
pub fn get_guardian_set(&self, index: u32) -> Result<Vec<u8>, Vec<u8>> {
151149
if !self.initialized.get() {
152150
return Err(WormholeError::NotInitialized.into());
153151
}
154152

155153
match self.get_gs_internal(index) {
156-
Some(guardian_set) => {
154+
Ok(guardian_set) => {
157155
let mut encoded = Vec::with_capacity(guardian_set.keys.len() * 20);
158156
for address in &guardian_set.keys {
159157
encoded.extend_from_slice(address.as_slice());
160158
}
161159
Ok(encoded)
162160
},
163-
None => Err(WormholeError::InvalidGuardianSetIndex.into()),
161+
Err(e) => Err(e.into()),
164162
}
165163
}
166164

@@ -175,7 +173,7 @@ impl WormholeContract {
175173

176174
let vaa = self.parse_vm(&encoded_vaa)?;
177175

178-
let _verified = self.verify_vm(&vaa);
176+
let _verified = self.verify_vm(&vaa)?;
179177

180178
Ok(vaa.payload)
181179
}
@@ -300,9 +298,8 @@ impl WormholeContract {
300298
}
301299

302300
fn verify_vm(&self, vaa: &VerifiedVMM) -> Result<(), WormholeError> {
303-
let guardian_set = self.get_gs_internal(vaa.guardian_set_index)
304-
.ok_or(WormholeError::InvalidGuardianSetIndex)?;
305301

302+
let guardian_set = self.get_gs_internal(vaa.guardian_set_index)?;
306303
if vaa.guardian_set_index != self.current_guardian_set_index.get().try_into().unwrap_or(0u32)
307304
&& guardian_set.expiration_time > 0 {
308305
return Err(WormholeError::GuardianSetExpired)
@@ -379,6 +376,22 @@ impl WormholeContract {
379376
Ok(())
380377
}
381378

379+
fn store_guardian_set(&mut self, set_index: u32, guardians: Vec<Address>, expiration_time: u32) -> Result<(), WormholeError> {
380+
if guardians.is_empty() {
381+
return Err(WormholeError::InvalidInput);
382+
}
383+
384+
self.guardian_set_sizes.setter(U256::from(set_index)).set(U256::from(guardians.len()));
385+
self.guardian_set_expiry.setter(U256::from(set_index)).set(U256::from(expiration_time));
386+
387+
for (i, guardian) in guardians.iter().enumerate() {
388+
let key = self.compute_gs_key(set_index, i as u8);
389+
self.guardian_keys.setter(key).set(*guardian);
390+
}
391+
392+
Ok(())
393+
}
394+
382395
fn verify_signature(
383396
&self,
384397
hash: &FixedBytes<32>,
@@ -414,10 +427,10 @@ impl WormholeContract {
414427
Ok(Address::from(address_bytes) == guardian_address)
415428
}
416429

417-
fn get_gs_internal(&self, index: u32) -> Option<GuardianSet> {
430+
fn get_gs_internal(&self, index: u32) -> Result<GuardianSet, WormholeError> {
418431
let size = self.guardian_set_sizes.getter(U256::from(index)).get();
419432
if size.is_zero() {
420-
return None;
433+
return Err(WormholeError::InvalidGuardianSetIndex);
421434
}
422435

423436
let size_u32: u32 = size.try_into().unwrap_or(0);
@@ -426,7 +439,7 @@ impl WormholeContract {
426439
let i_u8: u8 = match i.try_into() {
427440
Ok(val) => val,
428441
Err(_) => {
429-
return None;
442+
return Err(WormholeError::InvalidGuardianIndex);
430443
}
431444
};
432445
let key = self.compute_gs_key(index, i_u8);
@@ -436,7 +449,7 @@ impl WormholeContract {
436449

437450
let expiry = self.guardian_set_expiry.getter(U256::from(index)).get();
438451
let expiry_u32: u32 = expiry.try_into().unwrap_or(0);
439-
Some(GuardianSet {
452+
Ok(GuardianSet {
440453
keys,
441454
expiration_time: expiry_u32,
442455
})
@@ -455,7 +468,7 @@ impl IWormhole for WormholeContract {
455468
}
456469

457470
fn get_guardian_set(&self, index: u32) -> Option<GuardianSet> {
458-
self.get_gs_internal(index)
471+
self.get_gs_internal(index).ok()
459472
}
460473

461474
fn get_current_guardian_set_index(&self) -> u32 {
@@ -537,6 +550,31 @@ mod tests {
537550
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40,
538551
]
539552
}
553+
554+
#[cfg(test)]
555+
fn current_guardians() -> Vec<Address> {
556+
vec![
557+
Address::from_str("0x5893B5A76c3f739645648885bDCcC06cd70a3Cd3").unwrap(), // Rockaway
558+
Address::from_str("0xfF6CB952589BDE862c25Ef4392132fb9D4A42157").unwrap(), // Staked
559+
Address::from_str("0x114De8460193bdf3A2fCf81f86a09765F4762fD1").unwrap(), // Figment
560+
Address::from_str("0x107A0086b32d7A0977926A205131d8731D39cbEB").unwrap(), // ChainodeTech
561+
Address::from_str("0x8C82B2fd82FaeD2711d59AF0F2499D16e726f6b2").unwrap(), // Inotel
562+
Address::from_str("0x11b39756C042441BE6D8650b69b54EbE715E2343").unwrap(), // HashKey Cloud
563+
Address::from_str("0x54Ce5B4D348fb74B958e8966e2ec3dBd4958a7cd").unwrap(), // ChainLayer
564+
Address::from_str("0x15e7cAF07C4e3DC8e7C469f92C8Cd88FB8005a20").unwrap(), // xLabs
565+
Address::from_str("0x74a3bf913953D695260D88BC1aA25A4eeE363ef0").unwrap(), // Forbole
566+
Address::from_str("0x000aC0076727b35FBea2dAc28fEE5cCB0fEA768e").unwrap(), // Staking Fund
567+
Address::from_str("0xAF45Ced136b9D9e24903464AE889F5C8a723FC14").unwrap(), // Moonlet Wallet
568+
Address::from_str("0xf93124b7c738843CBB89E864c862c38cddCccF95").unwrap(), // P2P Validator
569+
Address::from_str("0xD2CC37A4dc036a8D232b48f62cDD4731412f4890").unwrap(), // 01node
570+
Address::from_str("0xDA798F6896A3331F64b48c12D1D57Fd9cbe70811").unwrap(), // MCF
571+
Address::from_str("0x71AA1BE1D36CaFE3867910F99C09e347899C19C3").unwrap(), // Everstake
572+
Address::from_str("0x8192b6E7387CCd768277c17DAb1b7a5027c0b3Cf").unwrap(), // Chorus One
573+
Address::from_str("0x178e21ad2E77AE06711549CFBB1f9c7a9d8096e8").unwrap(), // Syncnode
574+
Address::from_str("0x5E1487F35515d02A92753504a8D75471b9f49EdB").unwrap(), // Triton
575+
Address::from_str("0x6FbEBc898F403E4773E95feB15E80C9A99c8348d").unwrap(), // Staking Facilities
576+
]
577+
}
540578

541579
#[cfg(test)]
542580
fn test_guardian_address1() -> Address {
@@ -575,6 +613,16 @@ mod tests {
575613
contract.initialize(guardians, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract).unwrap();
576614
contract
577615
}
616+
617+
#[cfg(test)]
618+
fn deploy_with_real_guardians() -> WormholeContract {
619+
let mut contract = WormholeContract::default();
620+
let guardians = current_guardians();
621+
let governance_contract = Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
622+
contract.initialize(guardians, CHAIN_ID, GOVERNANCE_CHAIN_ID, governance_contract).unwrap();
623+
contract.store_guardian_set(4, current_guardians(), 0);
624+
contract
625+
}
578626

579627
#[cfg(test)]
580628
fn deploy_with_current_guardians() -> WormholeContract {
@@ -721,6 +769,14 @@ mod tests {
721769
assert_eq!(WormholeContract::quorum(19), 13);
722770
}
723771

772+
#[motsu::test]
773+
fn test_vaa_invalid_guardian_set_idx() {
774+
let contract = deploy_with_real_guardians();
775+
let test_vaa = create_vaa_bytes("AQAHHHQNAKPLun8KH+IfCb2c9rlKrXV8wDcZUeMtLeoxoJLHAu7kH40xE1IY5uaJLT4PRsWDDv+7GHNT8rDP+4hUaJNHMtkBAvbQ7aUofV+VAoXjfqrU+V4Vzgvkpwuowaj0BMzNTSp2PkKz5BsnfvC7cxVwOw9sJnQfPvN8KrmhA0IXgQdkQDIBA/0sVNcwZm1oic2G6r7c3x5DyEO9sRF2sTDyM4nuiOtaWPbgolaK6iU3yTx2bEzjdKsdVD2z3qs/QReV8ZxtA5MBBKSm2RKacsgdvwwNZPB3Ifw3P2niCAhZA435PkYeZpDBd8GQ4hALy+42lffR+AXJu19pNs+thWSxq7GRxF5oKz8BBYYS1n9/PJOybDhuWS+PI6YU0CFVTC9pTFSFTlMcEpjsUbT+cUKYCcFU63YaeVGUEPmhFYKeUeRhhQ5g2cCPIegABqts6uHMo5hrdXujJHVEqngLCSaQpB2W9I32LcIvKBfxLcx9IZTjxJ36tyNo7VJ6Fu1FbXnLW0lzaSIbmVmlGukABzpn+9z3bHT6g16HeroSW/YWNlZD5Jo6Zuw9/LT4VD0ET3DgFZtzytkWlJJKAuEB26wRHZbzLAKXfRl+j8kylWQACTTiIiCjZxmEUWjWzWe3JvvPKMNRvYkGkdGaQ7bWVvdiZvxoDq1XHB2H7WnqaAU6xY2pLyf6JG+lV+XZ/GEY+7YBDD/NU/C/gNZP9RP+UujaeJFWt2dau+/g2vtnX/gs2sgBf+yMYm6/dFaT0TiJAcG42zqOi24DLpsdVefaUV1G7CABDjmSRpA//pdAOL5ZxEFG1ia7TnwslsgsvVOa4pKUp5HSZv1JEUO6xMDkTOrBBt5vv9n6zYp3tpYHgUB/fZDh/qUBDzHxNtrQuL/n8a2HOY34yqljpBOCigAbHj+xQmu85u8ieUyge/2zqTn8PYMcka3pW1WTzOAOZf1pLHO+oPEfkTMBEGUS9UOAeY6IUabiEtAQ6qnR47WgPPHYSZUtKBkU0JscDgW0cFq47qmet9OCo79183dRDYE0kFIhnJDk/r7Cq4ABEfBBD83OEF2LJKKkJIBL/KBiD/Mjh3jwKXqqj28EJt1lKCYiGlPhqOCqRArydP94c37MSdrrPlkh0bhcFYs3deMAaEhJXwAAAAAABQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAAAAAAAEDRXIAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMMN2oOke3QAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABu3yoHkAEAAAAAAAAAAAAAAAAPpLFVLLUvQgzfCF8uDxxgOpZXNaAAAAAAAAAAAAAAAAegpThHd29+lMw1dClxrLIhew24EAAAAAAAAAAAAAAAB6ClOEd3b36UzDV0KXGssiF7DbgQAAAAAAAAAAAAAAACdCjdLT3TKk1/fEl+qqIxMNiUkRAA==");
776+
let result = contract.parse_and_verify_vm(test_vaa);
777+
assert!(matches!(result, Err(WormholeError::InvalidGuardianSetIndex)));
778+
}
779+
724780
#[motsu::test]
725781
fn test_wormhole_vaa_parsing() {
726782
let vaa_vec = test_wormhole_vaa();
@@ -853,7 +909,7 @@ mod tests {
853909
let contract = deploy_with_test_guardian();
854910

855911
let result = contract.get_gs_internal(999);
856-
assert!(result.is_none());
912+
assert!(result.is_err());
857913
}
858914

859915
#[motsu::test]
@@ -948,8 +1004,7 @@ mod tests {
9481004

9491005
let _ = contract.store_gs(0, guardians.clone(), 0);
9501006
let retrieved_set = contract
951-
.get_gs_internal(0)
952-
.ok_or(WormholeError::InvalidGuardianSetIndex)?;
1007+
.get_gs_internal(0)?;
9531008

9541009
assert_eq!(retrieved_set.keys, guardians);
9551010
assert_eq!(retrieved_set.expiration_time, 0);
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/bin/bash
2+
3+
set -e
4+
5+
CONTRACT_ADDRESS="0x7c56d119a916da6593e1fd8c1d010161f20afd70"
6+
RPC_URL="https://sepolia-rollup.arbitrum.io/rpc"
7+
8+
CHAIN_ID=60051
9+
GOVERNANCE_CHAIN_ID=1
10+
GOVERNANCE_CONTRACT="0x0000000000000000000000000000000000000004"
11+
12+
GUARDIAN_ADDRESSES="[0x5893B5A76c3f739645648885bDCcC06cd70a3Cd3,0xfF6CB952589BDE862c25Ef4392132fb9D4A42157,0x114De8460193bdf3A2fCf81f86a09765F4762fD1,0x107A0086b32d7A0977926A205131d8731D39cbEB,0x8C82B2fd82FaeD2711d59AF0F2499D16e726f6b2,0x11b39756C042441BE6D8650b69b54EbE715E2343,0x54Ce5B4D348fb74B958e8966e2ec3dBd4958a7cd,0x15e7cAF07C4e3DC8e7C469f92C8Cd88FB8005a20,0x74a3bf913953D695260D88BC1aA25A4eeE363ef0,0x000aC0076727b35FBea2dAc28fEE5cCB0fEA768e,0xAF45Ced136b9D9e24903464AE889F5C8a723FC14,0xf93124b7c738843CBB89E864c862c38cddCccF95,0xD2CC37A4dc036a8D232b48f62cDD4731412f4890,0xDA798F6896A3331F64b48c12D1D57Fd9cbe70811,0x71AA1BE1D36CaFE3867910F99C09e347899C19C3,0x8192b6E7387CCd768277c17DAb1b7a5027c0b3Cf,0x178e21ad2E77AE06711549CFBB1f9c7a9d8096e8,0x5E1487F35515d02A92753504a8D75471b9f49EdB,0x6FbEBc898F403E4773E95feB15E80C9A99c8348d]"
13+
14+
echo "🔍 Initializing Stylus Wormhole contract"
15+
echo "Contract: $CONTRACT_ADDRESS"
16+
echo "Network: Arbitrum Sepolia"
17+
echo ""
18+
19+
echo "📋 Initialization Parameters:"
20+
echo " Chain ID: $CHAIN_ID"
21+
echo " Governance Chain ID: $GOVERNANCE_CHAIN_ID"
22+
echo " Governance Contract: $GOVERNANCE_CONTRACT"
23+
echo " Guardian Addresses: 19 mainnet guardian set 4 addresses"
24+
echo ""
25+
26+
echo "🚀 Calling initialize function..."
27+
echo "Command: cast send $CONTRACT_ADDRESS \"initialize(address[],uint16,uint16,address)\" \"$GUARDIAN_ADDRESSES\" $CHAIN_ID $GOVERNANCE_CHAIN_ID $GOVERNANCE_CONTRACT --rpc-url $RPC_URL --private-key \$PRIVATE_KEY"
28+
29+
if [ -z "$PRIVATE_KEY" ]; then
30+
echo "❌ Error: PRIVATE_KEY environment variable not set"
31+
echo "Please set your private key: export PRIVATE_KEY=\"your-private-key-here\""
32+
exit 1
33+
fi
34+
35+
if cast send "$CONTRACT_ADDRESS" "initialize(address[],uint16,uint16,address)" "$GUARDIAN_ADDRESSES" $CHAIN_ID $GOVERNANCE_CHAIN_ID $GOVERNANCE_CONTRACT --rpc-url "$RPC_URL" --private-key "$PRIVATE_KEY"; then
36+
echo "✅ Contract initialization successful!"
37+
echo ""
38+
39+
echo "🔍 Verifying initialization..."
40+
echo "Testing quorum function (pure function)..."
41+
if cast call "$CONTRACT_ADDRESS" "quorum(uint32)" 3 --rpc-url "$RPC_URL"; then
42+
echo "✅ Pure function works"
43+
else
44+
echo "❌ Pure function failed"
45+
fi
46+
47+
echo "Testing getGuardianSet function..."
48+
if cast call "$CONTRACT_ADDRESS" "getGuardianSet(uint32)" 0 --rpc-url "$RPC_URL"; then
49+
echo "✅ Guardian set retrieval works - contract is initialized"
50+
else
51+
echo "❌ Guardian set retrieval failed"
52+
fi
53+
54+
echo ""
55+
echo "🎯 Next steps:"
56+
echo "1. Run your test scripts to verify parse_and_verify_vm works"
57+
echo "2. Test with different VAA data for comprehensive validation"
58+
echo "3. Monitor contract functionality for production readiness"
59+
60+
else
61+
echo "❌ Contract initialization failed!"
62+
echo ""
63+
echo "🔧 Debugging hints:"
64+
echo "- Check if contract is already initialized"
65+
echo "- Verify guardian address format is correct"
66+
echo "- Ensure you have sufficient gas and ETH for the transaction"
67+
echo "- Check if the private key has permission to call initialize"
68+
fi

0 commit comments

Comments
 (0)