From 653d3ddeba404729f4ee7e5b7b3c7c617cc6ec81 Mon Sep 17 00:00:00 2001 From: akhercha Date: Sat, 2 Nov 2024 16:39:54 +0100 Subject: [PATCH] dev(better_theoros): Works with N signatures --- rust/theoros/src/rpc/evm/mod.rs | 11 ++++++ rust/theoros/src/storage/checkpoints.rs | 4 +-- rust/theoros/src/types/calldata.rs | 48 +++++++++++++------------ 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/rust/theoros/src/rpc/evm/mod.rs b/rust/theoros/src/rpc/evm/mod.rs index b3bd090..2baafed 100644 --- a/rust/theoros/src/rpc/evm/mod.rs +++ b/rust/theoros/src/rpc/evm/mod.rs @@ -39,4 +39,15 @@ impl HyperlaneValidatorsMapping { pub fn chain_names(&self) -> Vec { self.0.keys().cloned().collect() } + + /// Get the index of a validator for a chain + pub fn validator_index(&self, chain_name: &EvmChainName, searched_validator: &Felt) -> Option { + match self.0.get(chain_name) { + Some(validators) => validators + .iter() + .position(|validator| validator == searched_validator) + .and_then(|pos| pos.try_into().ok()), + None => None, + } + } } diff --git a/rust/theoros/src/storage/checkpoints.rs b/rust/theoros/src/storage/checkpoints.rs index b3b0bf9..6089cd5 100644 --- a/rust/theoros/src/storage/checkpoints.rs +++ b/rust/theoros/src/storage/checkpoints.rs @@ -55,7 +55,7 @@ impl SignedCheckpointsStorage { // For the provided list of validators, returns all their signed checkpoints for the // provided message_id. - pub async fn get(&self, validators: &[Felt], searched_nonce: u32) -> Vec { + pub async fn get(&self, validators: &[Felt], searched_nonce: u32) -> Vec<(Felt, SignedCheckpointWithMessageId)> { let lock = self.0.read().await; let mut checkpoints = Vec::new(); @@ -63,7 +63,7 @@ impl SignedCheckpointsStorage { for ((validator, nonce), checkpoint) in lock.iter() { // Only include if validator is in the provided list and message_id matches if nonce == &searched_nonce && validators.contains(validator) { - checkpoints.push(checkpoint.clone()); + checkpoints.push((*validator, checkpoint.clone())); } } diff --git a/rust/theoros/src/types/calldata.rs b/rust/theoros/src/types/calldata.rs index fce70be..553b00a 100644 --- a/rust/theoros/src/types/calldata.rs +++ b/rust/theoros/src/types/calldata.rs @@ -8,7 +8,7 @@ use std::str::FromStr; use crate::{ configs::evm_config::EvmChainName, constants::{HYPERLANE_VERSION, PRAGMA_MAJOR_VERSION, PRAGMA_MINOR_VERSION, TRAILING_HEADER_SIZE}, - types::hyperlane::{CheckpointWithMessageId, DispatchUpdate, DispatchUpdateInfos}, + types::hyperlane::{CheckpointWithMessageId, DispatchUpdate}, types::state::AppState, }; @@ -32,36 +32,44 @@ pub struct Calldata { impl Calldata { pub async fn build_from(state: &AppState, chain_name: EvmChainName, feed_id: String) -> anyhow::Result { - let update_info: DispatchUpdateInfos = state + // Get latest update for this feed + let update_info = state .storage .latest_update_per_feed() - .get(&hex_str_to_u256(&feed_id).unwrap()) + .get(&hex_str_to_u256(&feed_id)?) .await? .context("No update found")?; + // Get validators and their signatures let validators = state.hyperlane_validators_mapping.get_validators(chain_name).context("No validators found")?; let checkpoints = state.storage.signed_checkpoints().get(validators, update_info.nonce).await; - - let checkpoint_infos = checkpoints.last().unwrap(); - + anyhow::ensure!(!checkpoints.is_empty(), "No signatures found"); + + let signatures: Vec = checkpoints + .iter() + .filter_map(|(validator, signed_checkpoint)| { + state + .hyperlane_validators_mapping + .validator_index(&chain_name, validator) + .map(|idx| ValidatorSignature { validator_index: idx, signature: signed_checkpoint.signature }) + }) + .collect(); + + // Build payload using first checkpoint (all validators sign the same checkpoint since it's the same nonce) let update = match update_info.update { - DispatchUpdate::SpotMedian { update, feed_id: _ } => update, + DispatchUpdate::SpotMedian { update, .. } => update, }; let payload = Payload { - checkpoint: checkpoint_infos.value.clone(), - // TODO: We only handle 1 update per calldata. See if possible to have more. - // TODO: If not remove this and consider it to be always one? + checkpoint: checkpoints[0].1.value.clone(), num_updates: 1, - // TODO: Remove proof proof_len: 0, proof: vec![], update_data_len: update.to_bytes().len() as u16, update_data: update.to_bytes(), - feed_id: U256::from_str(&feed_id).unwrap(), - // TODO: publish_time is already available in the update metadata. Remove. + feed_id: U256::from_str(&feed_id)?, publish_time: update.metadata.timestamp, }; @@ -70,23 +78,19 @@ impl Calldata { emitter_chain_id: update_info.emitter_chain_id, emitter_address: update_info.emitter_address, nonce: update_info.nonce, - // TODO: Adapt for N signers - signers_len: 1_u8, - signatures: vec![ValidatorSignature { validator_index: 0, signature: checkpoint_infos.signature }], - // TODO: Means we store once again the timestamp? Delete this? + signers_len: signatures.len() as u8, + signatures, timestamp: update.metadata.timestamp, payload, }; - let calldata = Calldata { + Ok(Calldata { major_version: PRAGMA_MAJOR_VERSION, minor_version: PRAGMA_MINOR_VERSION, trailing_header_size: TRAILING_HEADER_SIZE, - hyperlane_msg_size: hyperlane_message.as_bytes().len().try_into().unwrap(), + hyperlane_msg_size: hyperlane_message.as_bytes().len().try_into()?, hyperlane_msg: hyperlane_message, - }; - - Ok(calldata) + }) } }