refactor(target_chains/starknet): remove Result from wormhole (#1541)
This commit is contained in:
parent
ff6b11023c
commit
94b36c4961
|
@ -55,8 +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<(), pyth::reader::Error> {
|
||||
let mut result = Result::Ok(());
|
||||
fn push_reader(ref self: Hasher, ref reader: Reader) {
|
||||
while reader.len() > 0 {
|
||||
let mut chunk_len = 8 - self.num_last_bytes;
|
||||
if reader.len() < chunk_len.into() {
|
||||
|
@ -66,8 +65,7 @@ pub impl HasherImpl of HasherTrait {
|
|||
let value = reader.read_num_bytes(chunk_len);
|
||||
// chunk_len <= 8 so value must fit in u64.
|
||||
self.push_to_last(value.try_into().expect(UNEXPECTED_OVERFLOW), chunk_len);
|
||||
};
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the keccak256 hash of the buffer. The output hash is interpreted
|
||||
|
|
|
@ -27,7 +27,7 @@ impl ResultReaderToMerkleVerification<T> of ResultReaderToMerkleVerificationTrai
|
|||
fn leaf_hash(mut reader: Reader) -> Result<u256, super::reader::Error> {
|
||||
let mut hasher = HasherImpl::new();
|
||||
hasher.push_u8(MERKLE_LEAF_PREFIX);
|
||||
hasher.push_reader(ref reader)?;
|
||||
hasher.push_reader(ref reader);
|
||||
let hash = hasher.finalize() / ONE_SHIFT_96;
|
||||
Result::Ok(hash)
|
||||
}
|
||||
|
|
|
@ -382,7 +382,7 @@ mod pyth {
|
|||
let wh_proof_size = reader.read_u16();
|
||||
let wh_proof = reader.read_byte_array(wh_proof_size.into());
|
||||
let wormhole = IWormholeDispatcher { contract_address: self.wormhole_address.read() };
|
||||
let vm = wormhole.parse_and_verify_vm(wh_proof).map_err()?;
|
||||
let vm = wormhole.parse_and_verify_vm(wh_proof);
|
||||
|
||||
let source = DataSource {
|
||||
emitter_chain_id: vm.emitter_chain_id, emitter_address: vm.emitter_address
|
||||
|
|
|
@ -4,10 +4,8 @@ use pyth::util::UnwrapWithFelt252;
|
|||
|
||||
#[starknet::interface]
|
||||
pub trait IWormhole<T> {
|
||||
fn submit_new_guardian_set(
|
||||
ref self: T, set_index: u32, guardians: Array<felt252>
|
||||
) -> Result<(), SubmitNewGuardianSetError>;
|
||||
fn parse_and_verify_vm(self: @T, encoded_vm: ByteArray) -> Result<VM, ParseAndVerifyVmError>;
|
||||
fn submit_new_guardian_set(ref self: T, set_index: u32, guardians: Array<felt252>);
|
||||
fn parse_and_verify_vm(self: @T, encoded_vm: ByteArray) -> VM;
|
||||
}
|
||||
|
||||
#[derive(Drop, Debug, Clone, Serde)]
|
||||
|
@ -127,16 +125,6 @@ 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,
|
||||
|
@ -159,29 +147,24 @@ mod wormhole {
|
|||
) {
|
||||
self.owner.write(owner);
|
||||
let set_index = 0;
|
||||
store_guardian_set(ref self, set_index, @initial_guardians).unwrap_with_felt252();
|
||||
store_guardian_set(ref self, set_index, @initial_guardians);
|
||||
}
|
||||
|
||||
fn store_guardian_set(
|
||||
ref self: ContractState, set_index: u32, guardians: @Array<felt252>
|
||||
) -> Result<(), SubmitNewGuardianSetError> {
|
||||
fn store_guardian_set(ref self: ContractState, set_index: u32, guardians: @Array<felt252>) {
|
||||
if guardians.len() == 0 {
|
||||
return Result::Err(SubmitNewGuardianSetError::NoGuardiansSpecified.into());
|
||||
panic_with_felt252(SubmitNewGuardianSetError::NoGuardiansSpecified.into());
|
||||
}
|
||||
if guardians.len() >= 256 {
|
||||
return Result::Err(SubmitNewGuardianSetError::TooManyGuardians.into());
|
||||
panic_with_felt252(SubmitNewGuardianSetError::TooManyGuardians.into());
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
let mut result = Result::Ok(());
|
||||
while i < guardians.len() {
|
||||
if *guardians.at(i) == 0 {
|
||||
result = Result::Err(SubmitNewGuardianSetError::InvalidGuardianKey.into());
|
||||
break;
|
||||
panic_with_felt252(SubmitNewGuardianSetError::InvalidGuardianKey.into());
|
||||
}
|
||||
i += 1;
|
||||
};
|
||||
result?;
|
||||
|
||||
let set = GuardianSet { num_guardians: guardians.len(), expiration_time: 0 };
|
||||
self.guardian_sets.write(set_index, set);
|
||||
|
@ -195,7 +178,6 @@ mod wormhole {
|
|||
i += 1;
|
||||
};
|
||||
self.current_guardian_set_index.write(set_index);
|
||||
Result::Ok(())
|
||||
}
|
||||
|
||||
fn expire_guardian_set(ref self: ContractState, set_index: u32, now: u64) {
|
||||
|
@ -208,41 +190,37 @@ 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();
|
||||
if self.owner.read() != execution_info.caller_address {
|
||||
return Result::Err(SubmitNewGuardianSetError::AccessDenied);
|
||||
panic_with_felt252(SubmitNewGuardianSetError::AccessDenied.into());
|
||||
}
|
||||
let current_set_index = self.current_guardian_set_index.read();
|
||||
if set_index != current_set_index + 1 {
|
||||
return Result::Err(SubmitNewGuardianSetError::InvalidGuardianSetSequence.into());
|
||||
panic_with_felt252(SubmitNewGuardianSetError::InvalidGuardianSetSequence.into());
|
||||
}
|
||||
store_guardian_set(ref self, set_index, @guardians)?;
|
||||
store_guardian_set(ref self, set_index, @guardians);
|
||||
expire_guardian_set(
|
||||
ref self, current_set_index, execution_info.block_info.unbox().block_timestamp
|
||||
);
|
||||
Result::Ok(())
|
||||
}
|
||||
|
||||
fn parse_and_verify_vm(
|
||||
self: @ContractState, encoded_vm: ByteArray
|
||||
) -> Result<VM, ParseAndVerifyVmError> {
|
||||
let (vm, body_hash) = parse_vm(encoded_vm)?;
|
||||
fn parse_and_verify_vm(self: @ContractState, encoded_vm: ByteArray) -> VM {
|
||||
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(ParseAndVerifyVmError::InvalidGuardianSetIndex);
|
||||
panic_with_felt252(ParseAndVerifyVmError::InvalidGuardianSetIndex.into());
|
||||
}
|
||||
if vm.guardian_set_index != self.current_guardian_set_index.read()
|
||||
&& guardian_set.expiration_time < get_block_timestamp() {
|
||||
return Result::Err(ParseAndVerifyVmError::GuardianSetExpired);
|
||||
panic_with_felt252(ParseAndVerifyVmError::GuardianSetExpired.into());
|
||||
}
|
||||
if vm.signatures.len() < quorum(guardian_set.num_guardians) {
|
||||
return Result::Err(ParseAndVerifyVmError::NoQuorum);
|
||||
panic_with_felt252(ParseAndVerifyVmError::NoQuorum.into());
|
||||
}
|
||||
let mut signatures_clone = vm.signatures.clone();
|
||||
let mut last_index = Option::None;
|
||||
|
||||
let mut result = Result::Ok(());
|
||||
loop {
|
||||
let signature = match signatures_clone.pop_front() {
|
||||
Option::Some(v) => { v },
|
||||
|
@ -252,8 +230,7 @@ mod wormhole {
|
|||
match last_index {
|
||||
Option::Some(last_index) => {
|
||||
if *(@signature).guardian_index <= last_index {
|
||||
result = Result::Err(ParseAndVerifyVmError::InvalidSignatureOrder);
|
||||
break;
|
||||
panic_with_felt252(ParseAndVerifyVmError::InvalidSignatureOrder.into());
|
||||
}
|
||||
},
|
||||
Option::None => {},
|
||||
|
@ -261,43 +238,33 @@ mod wormhole {
|
|||
last_index = Option::Some(*(@signature).guardian_index);
|
||||
|
||||
if signature.guardian_index.into() >= guardian_set.num_guardians {
|
||||
result = Result::Err(ParseAndVerifyVmError::InvalidGuardianIndex);
|
||||
break;
|
||||
panic_with_felt252(ParseAndVerifyVmError::InvalidGuardianIndex.into());
|
||||
}
|
||||
|
||||
let guardian_key = self
|
||||
.guardian_keys
|
||||
.read((vm.guardian_set_index, signature.guardian_index));
|
||||
|
||||
let r = verify_signature(body_hash, signature.signature, guardian_key);
|
||||
if r.is_err() {
|
||||
result = r;
|
||||
break;
|
||||
}
|
||||
verify_signature(body_hash, signature.signature, guardian_key);
|
||||
};
|
||||
result?;
|
||||
|
||||
Result::Ok(vm)
|
||||
vm
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_signature(ref reader: Reader) -> Result<GuardianSignature, ParseAndVerifyVmError> {
|
||||
fn parse_signature(ref reader: Reader) -> GuardianSignature {
|
||||
let guardian_index = reader.read_u8();
|
||||
let r = reader.read_u256();
|
||||
let s = reader.read_u256();
|
||||
let recovery_id = reader.read_u8();
|
||||
let y_parity = (recovery_id % 2) > 0;
|
||||
let signature = GuardianSignature {
|
||||
guardian_index, signature: Signature { r, s, y_parity }
|
||||
};
|
||||
Result::Ok(signature)
|
||||
GuardianSignature { guardian_index, signature: Signature { r, s, y_parity } }
|
||||
}
|
||||
|
||||
fn parse_vm(encoded_vm: ByteArray) -> Result<(VM, u256), ParseAndVerifyVmError> {
|
||||
fn parse_vm(encoded_vm: ByteArray) -> (VM, u256) {
|
||||
let mut reader = ReaderImpl::new(encoded_vm);
|
||||
let version = reader.read_u8();
|
||||
if version != 1 {
|
||||
return Result::Err(ParseAndVerifyVmError::VmVersionIncompatible);
|
||||
panic_with_felt252(ParseAndVerifyVmError::VmVersionIncompatible.into());
|
||||
}
|
||||
let guardian_set_index = reader.read_u32();
|
||||
|
||||
|
@ -305,22 +272,14 @@ mod wormhole {
|
|||
let mut i = 0;
|
||||
let mut signatures = array![];
|
||||
|
||||
let mut result = Result::Ok(());
|
||||
while i < sig_count {
|
||||
match parse_signature(ref reader) {
|
||||
Result::Ok(signature) => { signatures.append(signature); },
|
||||
Result::Err(err) => {
|
||||
result = Result::Err(err);
|
||||
break;
|
||||
},
|
||||
}
|
||||
signatures.append(parse_signature(ref reader));
|
||||
i += 1;
|
||||
};
|
||||
result?;
|
||||
|
||||
let mut reader_for_hash = reader.clone();
|
||||
let mut hasher = HasherImpl::new();
|
||||
hasher.push_reader(ref reader_for_hash).map_err()?;
|
||||
hasher.push_reader(ref reader_for_hash);
|
||||
let body_hash1 = hasher.finalize();
|
||||
let mut hasher2 = HasherImpl::new();
|
||||
hasher2.push_u256(body_hash1);
|
||||
|
@ -347,32 +306,25 @@ mod wormhole {
|
|||
consistency_level,
|
||||
payload,
|
||||
};
|
||||
Result::Ok((vm, body_hash2))
|
||||
(vm, body_hash2)
|
||||
}
|
||||
|
||||
fn verify_signature(
|
||||
body_hash: u256, signature: Signature, guardian_key: u256,
|
||||
) -> Result<(), ParseAndVerifyVmError> {
|
||||
fn verify_signature(body_hash: u256, signature: Signature, guardian_key: u256,) {
|
||||
let point: Secp256k1Point = recover_public_key(body_hash, signature)
|
||||
.ok_or(ParseAndVerifyVmError::InvalidSignature)?;
|
||||
let address = eth_address(point)?;
|
||||
.expect(ParseAndVerifyVmError::InvalidSignature.into());
|
||||
let address = eth_address(point);
|
||||
assert(guardian_key != 0, SubmitNewGuardianSetError::InvalidGuardianKey.into());
|
||||
if address != guardian_key {
|
||||
return Result::Err(ParseAndVerifyVmError::InvalidSignature);
|
||||
panic_with_felt252(ParseAndVerifyVmError::InvalidSignature.into());
|
||||
}
|
||||
Result::Ok(())
|
||||
}
|
||||
|
||||
fn eth_address(point: Secp256k1Point) -> Result<u256, ParseAndVerifyVmError> {
|
||||
let (x, y) = match point.get_coordinates() {
|
||||
Result::Ok(v) => { v },
|
||||
Result::Err(_) => { return Result::Err(ParseAndVerifyVmError::InvalidSignature); },
|
||||
};
|
||||
fn eth_address(point: Secp256k1Point) -> u256 {
|
||||
let (x, y) = point.get_coordinates().expect(ParseAndVerifyVmError::InvalidSignature.into());
|
||||
|
||||
let mut hasher = HasherImpl::new();
|
||||
hasher.push_u256(x);
|
||||
hasher.push_u256(y);
|
||||
let address = hasher.finalize() % ONE_SHIFT_160;
|
||||
Result::Ok(address)
|
||||
hasher.finalize() % ONE_SHIFT_160
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ fn test_parse_and_verify_vm_works() {
|
|||
let owner = 'owner'.try_into().unwrap();
|
||||
let dispatcher = deploy_and_init(owner);
|
||||
|
||||
let vm = dispatcher.parse_and_verify_vm(good_vm1()).unwrap();
|
||||
let vm = dispatcher.parse_and_verify_vm(good_vm1());
|
||||
assert!(vm.version == 1);
|
||||
assert!(vm.guardian_set_index == 3);
|
||||
assert!(vm.signatures.len() == 13);
|
||||
|
@ -36,26 +36,13 @@ fn test_parse_and_verify_vm_works() {
|
|||
|
||||
#[test]
|
||||
#[fuzzer(runs: 100, seed: 0)]
|
||||
#[should_panic(expected: ('any_expected',))]
|
||||
#[should_panic]
|
||||
fn test_parse_and_verify_vm_rejects_corrupted_vm(pos: usize, random1: usize, random2: usize) {
|
||||
let owner = 'owner'.try_into().unwrap();
|
||||
let dispatcher = deploy_and_init(owner);
|
||||
|
||||
let r = dispatcher.parse_and_verify_vm(corrupted_vm(pos, random1, random2));
|
||||
match r {
|
||||
Result::Ok(v) => { println!("no error, output: {:?}", v); },
|
||||
Result::Err(err) => {
|
||||
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.into());
|
||||
}
|
||||
},
|
||||
}
|
||||
let vm = dispatcher.parse_and_verify_vm(corrupted_vm(pos, random1, random2));
|
||||
println!("no error, output: {:?}", vm);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -64,7 +51,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()).unwrap_with_felt252();
|
||||
dispatcher.submit_new_guardian_set(1, guardian_set1());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -74,8 +61,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()).unwrap_with_felt252();
|
||||
dispatcher.submit_new_guardian_set(3, guardian_set3()).unwrap_with_felt252();
|
||||
dispatcher.submit_new_guardian_set(1, guardian_set1());
|
||||
dispatcher.submit_new_guardian_set(3, guardian_set3());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -92,7 +79,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![]).unwrap_with_felt252();
|
||||
dispatcher.submit_new_guardian_set(1, array![]);
|
||||
}
|
||||
|
||||
fn deploy(owner: ContractAddress, guardians: Array<felt252>) -> IWormholeDispatcher {
|
||||
|
@ -113,9 +100,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()).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();
|
||||
dispatcher.submit_new_guardian_set(1, guardian_set1());
|
||||
dispatcher.submit_new_guardian_set(2, guardian_set2());
|
||||
dispatcher.submit_new_guardian_set(3, guardian_set3());
|
||||
stop_prank(CheatTarget::One(dispatcher.contract_address));
|
||||
|
||||
dispatcher
|
||||
|
|
Loading…
Reference in New Issue