Quellcode durchsuchen

refactor(target_chains/starknet): use enum errors

Pavel Strakhov vor 1 Jahr
Ursprung
Commit
8b66d0f814

+ 1 - 1
target_chains/starknet/contracts/src/hash.cairo

@@ -55,7 +55,7 @@ pub impl HasherImpl of HasherTrait {
 
     /// Reads all remaining data from the reader and pushes it to
     /// the hashing buffer.
-    fn push_reader(ref self: Hasher, ref reader: Reader) -> Result<(), felt252> {
+    fn push_reader(ref self: Hasher, ref reader: Reader) -> Result<(), pyth::reader::Error> {
         let mut result = Result::Ok(());
         while reader.len() > 0 {
             let mut chunk_len = 8 - self.num_last_bytes;

+ 1 - 1
target_chains/starknet/contracts/src/lib.cairo

@@ -2,4 +2,4 @@ pub mod pyth;
 pub mod wormhole;
 pub mod reader;
 pub mod hash;
-mod util;
+pub mod util;

+ 24 - 15
target_chains/starknet/contracts/src/reader.cairo

@@ -5,8 +5,17 @@ use core::integer::u128_byte_reverse;
 use core::fmt::{Debug, Formatter};
 use pyth::util::{UNEXPECTED_OVERFLOW, UNEXPECTED_ZERO, one_shift_left_bytes_u128};
 
-pub mod errors {
-    pub const EOF: felt252 = 'unexpected end of input';
+#[derive(Copy, Drop, Debug, Serde, PartialEq)]
+pub enum Error {
+    UnexpectedEndOfInput,
+}
+
+impl ErrorIntoFelt252 of Into<Error, felt252> {
+    fn into(self: Error) -> felt252 {
+        match self {
+            Error::UnexpectedEndOfInput => 'unexpected end of input',
+        }
+    }
 }
 
 /// A byte array with storage format similar to `core::ByteArray`, but
@@ -95,7 +104,7 @@ pub impl ReaderImpl of ReaderTrait {
     }
 
     /// Reads the specified number of bytes (up to 16) as a big endian unsigned integer.
-    fn read_num_bytes(ref self: Reader, num_bytes: u8) -> Result<u128, felt252> {
+    fn read_num_bytes(ref self: Reader, num_bytes: u8) -> Result<u128, Error> {
         assert!(num_bytes <= 16, "Reader::read_num_bytes: num_bytes is too large");
         if num_bytes <= self.num_current_bytes {
             let x = self.read_from_current(num_bytes);
@@ -113,40 +122,40 @@ pub impl ReaderImpl of ReaderTrait {
         Result::Ok(value)
     }
 
-    fn read_u256(ref self: Reader) -> Result<u256, felt252> {
+    fn read_u256(ref self: Reader) -> Result<u256, Error> {
         let high = self.read_num_bytes(16)?;
         let low = self.read_num_bytes(16)?;
         let value = u256 { high, low };
         Result::Ok(value)
     }
-    fn read_u160(ref self: Reader) -> Result<u256, felt252> {
+    fn read_u160(ref self: Reader) -> Result<u256, Error> {
         let high = self.read_num_bytes(4)?;
         let low = self.read_num_bytes(16)?;
         let value = u256 { high, low };
         Result::Ok(value)
     }
-    fn read_u128(ref self: Reader) -> Result<u128, felt252> {
+    fn read_u128(ref self: Reader) -> Result<u128, Error> {
         self.read_num_bytes(16)
     }
-    fn read_u64(ref self: Reader) -> Result<u64, felt252> {
+    fn read_u64(ref self: Reader) -> Result<u64, Error> {
         let value = self.read_num_bytes(8)?.try_into().expect(UNEXPECTED_OVERFLOW);
         Result::Ok(value)
     }
-    fn read_u32(ref self: Reader) -> Result<u32, felt252> {
+    fn read_u32(ref self: Reader) -> Result<u32, Error> {
         let value = self.read_num_bytes(4)?.try_into().expect(UNEXPECTED_OVERFLOW);
         Result::Ok(value)
     }
-    fn read_u16(ref self: Reader) -> Result<u16, felt252> {
+    fn read_u16(ref self: Reader) -> Result<u16, Error> {
         let value = self.read_num_bytes(2)?.try_into().expect(UNEXPECTED_OVERFLOW);
         Result::Ok(value)
     }
-    fn read_u8(ref self: Reader) -> Result<u8, felt252> {
+    fn read_u8(ref self: Reader) -> Result<u8, Error> {
         let value = self.read_num_bytes(1)?.try_into().expect(UNEXPECTED_OVERFLOW);
         Result::Ok(value)
     }
 
     // TODO: skip without calculating values
-    fn skip(ref self: Reader, mut num_bytes: u8) -> Result<(), felt252> {
+    fn skip(ref self: Reader, mut num_bytes: u8) -> Result<(), Error> {
         let mut result = Result::Ok(());
         while num_bytes > 0 {
             if num_bytes > 16 {
@@ -173,7 +182,7 @@ pub impl ReaderImpl of ReaderTrait {
     }
 
     /// Reads the specified number of bytes as a new byte array.
-    fn read_byte_array(ref self: Reader, num_bytes: usize) -> Result<ByteArray, felt252> {
+    fn read_byte_array(ref self: Reader, num_bytes: usize) -> Result<ByteArray, Error> {
         let mut array: Array<bytes31> = array![];
         let mut num_last_bytes = Option::None;
         let mut num_remaining_bytes = num_bytes;
@@ -232,7 +241,7 @@ impl ReaderPrivateImpl of ReaderPrivateTrait {
     /// Replenishes `self.current` and `self.num_current_bytes`.
     /// This should only be called when all bytes from `self.current` has been read.
     /// Returns `EOF` error if no more data is available.
-    fn fetch_next(ref self: Reader) -> Result<(), felt252> {
+    fn fetch_next(ref self: Reader) -> Result<(), Error> {
         match self.next {
             Option::Some(next) => {
                 self.next = Option::None;
@@ -240,7 +249,7 @@ impl ReaderPrivateImpl of ReaderPrivateTrait {
                 self.num_current_bytes = 16;
             },
             Option::None => {
-                let (value, bytes) = self.array.pop_front().ok_or(errors::EOF)?;
+                let (value, bytes) = self.array.pop_front().ok_or(Error::UnexpectedEndOfInput)?;
                 let value: u256 = value.into();
                 if bytes > 16 {
                     self.current = value.high;
@@ -258,7 +267,7 @@ impl ReaderPrivateImpl of ReaderPrivateTrait {
     // Moved out from `read_bytes` because we cannot use `return` or `?` within a loop.
     fn read_bytes_iteration(
         ref self: Reader, num_bytes: usize, ref array: Array<bytes31>
-    ) -> Result<(usize, bool), felt252> {
+    ) -> Result<(usize, bool), Error> {
         if num_bytes >= 31 {
             let high = self.read_num_bytes(15)?;
             let low = self.read_num_bytes(16)?;

+ 8 - 0
target_chains/starknet/contracts/src/util.cairo

@@ -54,3 +54,11 @@ pub fn u64_byte_reverse(value: u64) -> u64 {
     let reversed = u128_byte_reverse(value.into()) / ONE_SHIFT_64.try_into().expect('not zero');
     reversed.try_into().unwrap()
 }
+
+/// If `self` is an error, panics with a `felt252` value
+/// corresponding to the error. Otherwise, returns the success value.
+/// This differs from `Result::unwrap` which always panics with
+/// the same message and doesn't include information about the error.
+pub trait UnwrapWithFelt252<T, E> {
+    fn unwrap_with_felt252(self: Result<T, E>) -> T;
+}

+ 143 - 62
target_chains/starknet/contracts/src/wormhole.cairo

@@ -1,10 +1,13 @@
 use pyth::reader::ByteArray;
 use core::starknet::secp256_trait::Signature;
+use pyth::util::UnwrapWithFelt252;
 
 #[starknet::interface]
 pub trait IWormhole<T> {
-    fn submit_new_guardian_set(ref self: T, set_index: u32, guardians: Array<felt252>);
-    fn parse_and_verify_vm(ref self: T, encoded_vm: ByteArray) -> Result<VM, felt252>;
+    fn submit_new_guardian_set(
+        ref self: T, set_index: u32, guardians: Array<felt252>
+    ) -> Result<(), SubmitNewGuardianSetError>;
+    fn parse_and_verify_vm(ref self: T, encoded_vm: ByteArray) -> Result<VM, ParseAndVerifyVmError>;
 }
 
 #[derive(Drop, Debug, Clone, Serde)]
@@ -27,35 +30,90 @@ pub struct VM {
     pub payload: ByteArray,
 }
 
-pub mod errors {
-    pub use pyth::reader::errors as reader;
-
-    pub const NO_GUARDIANS_SPECIFIED: felt252 = 'no guardians specified';
-    pub const TOO_MANY_GUARDIANS: felt252 = 'too many guardians';
-    pub const INVALID_GUARDIAN_KEY: felt252 = 'invalid guardian key';
+#[derive(Copy, Drop, Debug, Serde, PartialEq)]
+pub enum SubmitNewGuardianSetError {
+    NoGuardiansSpecified,
+    TooManyGuardians,
+    InvalidGuardianKey,
     // guardian set index must increase in steps of 1
-    pub const INVALID_GUARDIAN_SET_SEQUENCE: felt252 = 'invalid guardian set sequence';
-    pub const ACCESS_DENIED: felt252 = 'access denied';
-
-    pub const VM_VERSION_INCOMPATIBLE: felt252 = 'VM version incompatible';
-    pub const INVALID_GUARDIAN_SET_INDEX: felt252 = 'invalid guardian set index';
-    pub const INVALID_SIGNATURE: felt252 = 'invalid signature';
-    pub const GUARDIAN_SET_EXPIRED: felt252 = 'guardian set expired';
-    pub const NO_QUORUM: felt252 = 'no quorum';
-    pub const INVALID_SIGNATURE_ORDER: felt252 = 'invalid signature order';
-    pub const INVALID_GUARDIAN_INDEX: felt252 = 'invalid guardian index';
+    InvalidGuardianSetSequence,
+    AccessDenied,
+}
+
+pub impl SubmitNewGuardianSetErrorUnwrapWithFelt252<
+    T
+> of UnwrapWithFelt252<T, SubmitNewGuardianSetError> {
+    fn unwrap_with_felt252(self: Result<T, SubmitNewGuardianSetError>) -> T {
+        match self {
+            Result::Ok(v) => v,
+            Result::Err(err) => core::panic_with_felt252(err.into()),
+        }
+    }
+}
+
+impl SubmitNewGuardianSetErrorIntoFelt252 of Into<SubmitNewGuardianSetError, felt252> {
+    fn into(self: SubmitNewGuardianSetError) -> felt252 {
+        match self {
+            SubmitNewGuardianSetError::NoGuardiansSpecified => 'no guardians specified',
+            SubmitNewGuardianSetError::TooManyGuardians => 'too many guardians',
+            SubmitNewGuardianSetError::InvalidGuardianKey => 'invalid guardian key',
+            SubmitNewGuardianSetError::InvalidGuardianSetSequence => 'invalid guardian set sequence',
+            SubmitNewGuardianSetError::AccessDenied => 'access denied',
+        }
+    }
+}
+
+
+#[derive(Copy, Drop, Debug, Serde, PartialEq)]
+pub enum ParseAndVerifyVmError {
+    Reader: pyth::reader::Error,
+    VmVersionIncompatible,
+    InvalidGuardianSetIndex,
+    InvalidSignature,
+    GuardianSetExpired,
+    NoQuorum,
+    InvalidSignatureOrder,
+    InvalidGuardianIndex,
+}
+
+pub impl ParseAndVerifyVmErrorUnwrapWithFelt252<T> of UnwrapWithFelt252<T, ParseAndVerifyVmError> {
+    fn unwrap_with_felt252(self: Result<T, ParseAndVerifyVmError>) -> T {
+        match self {
+            Result::Ok(v) => v,
+            Result::Err(err) => core::panic_with_felt252(err.into()),
+        }
+    }
+}
+
+
+impl ErrorIntoFelt252 of Into<ParseAndVerifyVmError, felt252> {
+    fn into(self: ParseAndVerifyVmError) -> felt252 {
+        match self {
+            ParseAndVerifyVmError::Reader(err) => err.into(),
+            ParseAndVerifyVmError::VmVersionIncompatible => 'VM version incompatible',
+            ParseAndVerifyVmError::InvalidGuardianSetIndex => 'invalid guardian set index',
+            ParseAndVerifyVmError::InvalidSignature => 'invalid signature',
+            ParseAndVerifyVmError::GuardianSetExpired => 'guardian set expired',
+            ParseAndVerifyVmError::NoQuorum => 'no quorum',
+            ParseAndVerifyVmError::InvalidSignatureOrder => 'invalid signature order',
+            ParseAndVerifyVmError::InvalidGuardianIndex => 'invalid guardian index',
+        }
+    }
 }
 
 pub fn quorum(num_guardians: usize) -> usize {
-    assert(num_guardians < 256, errors::TOO_MANY_GUARDIANS);
+    assert(num_guardians < 256, SubmitNewGuardianSetError::TooManyGuardians.into());
     ((num_guardians * 2) / 3) + 1
 }
 
 #[starknet::contract]
 mod wormhole {
+    use pyth::util::UnwrapWithFelt252;
     use core::box::BoxTrait;
     use core::array::ArrayTrait;
-    use super::{VM, IWormhole, GuardianSignature, errors, quorum};
+    use super::{
+        VM, IWormhole, GuardianSignature, quorum, ParseAndVerifyVmError, SubmitNewGuardianSetError
+    };
     use pyth::reader::{Reader, ReaderImpl, ByteArray};
     use core::starknet::secp256_trait::{Signature, recover_public_key, Secp256PointTrait};
     use core::starknet::secp256k1::Secp256k1Point;
@@ -68,6 +126,16 @@ mod wormhole {
     use pyth::hash::{Hasher, HasherImpl};
     use pyth::util::{ONE_SHIFT_160, UNEXPECTED_OVERFLOW};
 
+    #[generate_trait]
+    impl ResultReaderToWormhole<T> of ResultReaderToWormholeTrait<T> {
+        fn map_err(self: Result<T, pyth::reader::Error>) -> Result<T, ParseAndVerifyVmError> {
+            match self {
+                Result::Ok(v) => Result::Ok(v),
+                Result::Err(err) => Result::Err(ParseAndVerifyVmError::Reader(err)),
+            }
+        }
+    }
+
     #[derive(Drop, Debug, Clone, Serde, starknet::Store)]
     struct GuardianSet {
         num_guardians: usize,
@@ -90,25 +158,37 @@ mod wormhole {
     ) {
         self.owner.write(owner);
         let set_index = 0;
-        store_guardian_set(ref self, set_index, initial_guardians);
+        store_guardian_set(ref self, set_index, initial_guardians).unwrap_with_felt252();
     }
 
-    fn store_guardian_set(ref self: ContractState, set_index: u32, guardians: Array<felt252>) {
-        assert(guardians.len() > 0, errors::NO_GUARDIANS_SPECIFIED);
-        assert(guardians.len() < 256, errors::TOO_MANY_GUARDIANS);
+    fn store_guardian_set(
+        ref self: ContractState, set_index: u32, guardians: Array<felt252>
+    ) -> Result<(), SubmitNewGuardianSetError> {
+        if guardians.len() == 0 {
+            return Result::Err(SubmitNewGuardianSetError::NoGuardiansSpecified.into());
+        }
+        if guardians.len() >= 256 {
+            return Result::Err(SubmitNewGuardianSetError::TooManyGuardians.into());
+        }
         let set = GuardianSet { num_guardians: guardians.len(), expiration_time: 0 };
         self.guardian_sets.write(set_index, set);
         let mut i = 0;
+        let mut result = Result::Ok(());
         while i < guardians.len() {
             let key = *guardians.at(i);
-            assert(key != 0, errors::INVALID_GUARDIAN_KEY);
+            if key == 0 {
+                result = Result::Err(SubmitNewGuardianSetError::InvalidGuardianKey.into());
+                break;
+            }
             // i < 256
             self
                 .guardian_keys
                 .write((set_index, i.try_into().expect(UNEXPECTED_OVERFLOW)), key.into());
             i += 1;
         };
+        result?;
         self.current_guardian_set_index.write(set_index);
+        Result::Ok(())
     }
 
     fn expire_guardian_set(ref self: ContractState, set_index: u32, now: u64) {
@@ -121,32 +201,35 @@ mod wormhole {
     impl WormholeImpl of IWormhole<ContractState> {
         fn submit_new_guardian_set(
             ref self: ContractState, set_index: u32, guardians: Array<felt252>
-        ) {
+        ) -> Result<(), SubmitNewGuardianSetError> {
             let execution_info = get_execution_info().unbox();
-            assert(self.owner.read() == execution_info.caller_address, errors::ACCESS_DENIED);
-
+            if self.owner.read() != execution_info.caller_address {
+                return Result::Err(SubmitNewGuardianSetError::AccessDenied);
+            }
             let current_set_index = self.current_guardian_set_index.read();
-            assert(set_index == current_set_index + 1, errors::INVALID_GUARDIAN_SET_SEQUENCE);
+            if set_index != current_set_index + 1 {
+                return Result::Err(SubmitNewGuardianSetError::InvalidGuardianSetSequence.into());
+            }
             expire_guardian_set(
                 ref self, current_set_index, execution_info.block_info.unbox().block_timestamp
             );
-            store_guardian_set(ref self, set_index, guardians);
+            store_guardian_set(ref self, set_index, guardians)
         }
 
         fn parse_and_verify_vm(
             ref self: ContractState, encoded_vm: ByteArray
-        ) -> Result<VM, felt252> {
+        ) -> Result<VM, ParseAndVerifyVmError> {
             let (vm, body_hash) = parse_vm(encoded_vm)?;
             let guardian_set = self.guardian_sets.read(vm.guardian_set_index);
             if guardian_set.num_guardians == 0 {
-                return Result::Err(errors::INVALID_GUARDIAN_SET_INDEX);
+                return Result::Err(ParseAndVerifyVmError::InvalidGuardianSetIndex);
             }
             if vm.guardian_set_index != self.current_guardian_set_index.read()
                 && guardian_set.expiration_time < get_block_timestamp() {
-                return Result::Err(errors::GUARDIAN_SET_EXPIRED);
+                return Result::Err(ParseAndVerifyVmError::GuardianSetExpired);
             }
             if vm.signatures.len() < quorum(guardian_set.num_guardians) {
-                return Result::Err(errors::NO_QUORUM);
+                return Result::Err(ParseAndVerifyVmError::NoQuorum);
             }
             let mut signatures_clone = vm.signatures.clone();
             let mut last_index = Option::None;
@@ -161,7 +244,7 @@ mod wormhole {
                 match last_index {
                     Option::Some(last_index) => {
                         if *(@signature).guardian_index <= last_index {
-                            result = Result::Err(errors::INVALID_SIGNATURE_ORDER);
+                            result = Result::Err(ParseAndVerifyVmError::InvalidSignatureOrder);
                             break;
                         }
                     },
@@ -170,7 +253,7 @@ mod wormhole {
                 last_index = Option::Some(*(@signature).guardian_index);
 
                 if signature.guardian_index.into() >= guardian_set.num_guardians {
-                    result = Result::Err(errors::INVALID_GUARDIAN_INDEX);
+                    result = Result::Err(ParseAndVerifyVmError::InvalidGuardianIndex);
                     break;
                 }
 
@@ -190,11 +273,11 @@ mod wormhole {
         }
     }
 
-    fn parse_signature(ref reader: Reader) -> Result<GuardianSignature, felt252> {
-        let guardian_index = reader.read_u8()?;
-        let r = reader.read_u256()?;
-        let s = reader.read_u256()?;
-        let recovery_id = reader.read_u8()?;
+    fn parse_signature(ref reader: Reader) -> Result<GuardianSignature, ParseAndVerifyVmError> {
+        let guardian_index = reader.read_u8().map_err()?;
+        let r = reader.read_u256().map_err()?;
+        let s = reader.read_u256().map_err()?;
+        let recovery_id = reader.read_u8().map_err()?;
         let y_parity = (recovery_id % 2) > 0;
         let signature = GuardianSignature {
             guardian_index, signature: Signature { r, s, y_parity }
@@ -202,15 +285,15 @@ mod wormhole {
         Result::Ok(signature)
     }
 
-    fn parse_vm(encoded_vm: ByteArray) -> Result<(VM, u256), felt252> {
+    fn parse_vm(encoded_vm: ByteArray) -> Result<(VM, u256), ParseAndVerifyVmError> {
         let mut reader = ReaderImpl::new(encoded_vm);
-        let version = reader.read_u8()?;
+        let version = reader.read_u8().map_err()?;
         if version != 1 {
-            return Result::Err(errors::VM_VERSION_INCOMPATIBLE);
+            return Result::Err(ParseAndVerifyVmError::VmVersionIncompatible);
         }
-        let guardian_set_index = reader.read_u32()?;
+        let guardian_set_index = reader.read_u32().map_err()?;
 
-        let sig_count = reader.read_u8()?;
+        let sig_count = reader.read_u8().map_err()?;
         let mut i = 0;
         let mut signatures = array![];
 
@@ -229,20 +312,20 @@ mod wormhole {
 
         let mut reader_for_hash = reader.clone();
         let mut hasher = HasherImpl::new();
-        hasher.push_reader(ref reader_for_hash)?;
+        hasher.push_reader(ref reader_for_hash).map_err()?;
         let body_hash1 = hasher.finalize();
         let mut hasher2 = HasherImpl::new();
         hasher2.push_u256(body_hash1);
         let body_hash2 = hasher2.finalize();
 
-        let timestamp = reader.read_u32()?;
-        let nonce = reader.read_u32()?;
-        let emitter_chain_id = reader.read_u16()?;
-        let emitter_address = reader.read_u256()?;
-        let sequence = reader.read_u64()?;
-        let consistency_level = reader.read_u8()?;
+        let timestamp = reader.read_u32().map_err()?;
+        let nonce = reader.read_u32().map_err()?;
+        let emitter_chain_id = reader.read_u16().map_err()?;
+        let emitter_address = reader.read_u256().map_err()?;
+        let sequence = reader.read_u64().map_err()?;
+        let consistency_level = reader.read_u8().map_err()?;
         let payload_len = reader.len();
-        let payload = reader.read_byte_array(payload_len)?;
+        let payload = reader.read_byte_array(payload_len).map_err()?;
 
         let vm = VM {
             version,
@@ -261,23 +344,21 @@ mod wormhole {
 
     fn verify_signature(
         body_hash: u256, signature: Signature, guardian_key: u256,
-    ) -> Result<(), felt252> {
+    ) -> Result<(), ParseAndVerifyVmError> {
         let point: Secp256k1Point = recover_public_key(body_hash, signature)
-            .ok_or(errors::INVALID_SIGNATURE)?;
+            .ok_or(ParseAndVerifyVmError::InvalidSignature)?;
         let address = eth_address(point)?;
-        if guardian_key == 0 {
-            return Result::Err(errors::INVALID_GUARDIAN_KEY);
-        }
+        assert(guardian_key != 0, SubmitNewGuardianSetError::InvalidGuardianKey.into());
         if address != guardian_key {
-            return Result::Err(errors::INVALID_SIGNATURE);
+            return Result::Err(ParseAndVerifyVmError::InvalidSignature);
         }
         Result::Ok(())
     }
 
-    fn eth_address(point: Secp256k1Point) -> Result<u256, felt252> {
+    fn eth_address(point: Secp256k1Point) -> Result<u256, ParseAndVerifyVmError> {
         let (x, y) = match point.get_coordinates() {
             Result::Ok(v) => { v },
-            Result::Err(_) => { return Result::Err(errors::INVALID_SIGNATURE); },
+            Result::Err(_) => { return Result::Err(ParseAndVerifyVmError::InvalidSignature); },
         };
 
         let mut hasher = HasherImpl::new();

+ 15 - 14
target_chains/starknet/contracts/tests/wormhole.cairo

@@ -1,6 +1,7 @@
 use snforge_std::{declare, ContractClassTrait, start_prank, stop_prank, CheatTarget};
-use pyth::wormhole::{IWormholeDispatcher, IWormholeDispatcherTrait};
+use pyth::wormhole::{IWormholeDispatcher, IWormholeDispatcherTrait, ParseAndVerifyVmError};
 use pyth::reader::{ByteArray, ByteArrayImpl, ReaderImpl};
+use pyth::util::UnwrapWithFelt252;
 use core::starknet::ContractAddress;
 use core::panic_with_felt252;
 
@@ -43,14 +44,14 @@ fn test_parse_and_verify_vm_rejects_corrupted_vm(pos: usize, random1: usize, ran
     match r {
         Result::Ok(v) => { println!("no error, output: {:?}", v); },
         Result::Err(err) => {
-            if err == 'invalid signature'
-                || err == 'invalid guardian index'
-                || err == 'invalid guardian set index'
-                || err == 'unexpected end of input'
-                || err == 'VM version incompatible' {
+            if err == ParseAndVerifyVmError::InvalidSignature
+                || err == ParseAndVerifyVmError::InvalidGuardianIndex
+                || err == ParseAndVerifyVmError::InvalidGuardianSetIndex
+                || err == ParseAndVerifyVmError::VmVersionIncompatible
+                || err == ParseAndVerifyVmError::Reader(pyth::reader::Error::UnexpectedEndOfInput) {
                 panic_with_felt252('any_expected');
             } else {
-                panic_with_felt252(err);
+                panic_with_felt252(err.into());
             }
         },
     }
@@ -62,7 +63,7 @@ fn test_submit_guardian_set_rejects_wrong_owner() {
     let owner = 'owner'.try_into().unwrap();
     let dispatcher = deploy(owner, guardian_set1());
     start_prank(CheatTarget::One(dispatcher.contract_address), 'baddy'.try_into().unwrap());
-    dispatcher.submit_new_guardian_set(1, guardian_set1());
+    dispatcher.submit_new_guardian_set(1, guardian_set1()).unwrap_with_felt252();
 }
 
 #[test]
@@ -72,8 +73,8 @@ fn test_submit_guardian_set_rejects_wrong_index() {
     let dispatcher = deploy(owner, guardian_set1());
 
     start_prank(CheatTarget::One(dispatcher.contract_address), owner.try_into().unwrap());
-    dispatcher.submit_new_guardian_set(1, guardian_set1());
-    dispatcher.submit_new_guardian_set(3, guardian_set3());
+    dispatcher.submit_new_guardian_set(1, guardian_set1()).unwrap_with_felt252();
+    dispatcher.submit_new_guardian_set(3, guardian_set3()).unwrap_with_felt252();
 }
 
 #[test]
@@ -90,7 +91,7 @@ fn test_submit_guardian_set_rejects_empty() {
     let dispatcher = deploy(owner, guardian_set1());
 
     start_prank(CheatTarget::One(dispatcher.contract_address), owner.try_into().unwrap());
-    dispatcher.submit_new_guardian_set(1, array![]);
+    dispatcher.submit_new_guardian_set(1, array![]).unwrap_with_felt252();
 }
 
 fn array_left252_to_bytes31(mut input: Array<felt252>) -> Array<bytes31> {
@@ -122,9 +123,9 @@ pub fn deploy_and_init(owner: ContractAddress) -> IWormholeDispatcher {
     let dispatcher = deploy(owner, guardian_set1());
 
     start_prank(CheatTarget::One(dispatcher.contract_address), owner.try_into().unwrap());
-    dispatcher.submit_new_guardian_set(1, guardian_set1());
-    dispatcher.submit_new_guardian_set(2, guardian_set2());
-    dispatcher.submit_new_guardian_set(3, guardian_set3());
+    dispatcher.submit_new_guardian_set(1, guardian_set1()).unwrap_with_felt252();
+    dispatcher.submit_new_guardian_set(2, guardian_set2()).unwrap_with_felt252();
+    dispatcher.submit_new_guardian_set(3, guardian_set3()).unwrap_with_felt252();
     stop_prank(CheatTarget::One(dispatcher.contract_address));
 
     dispatcher