From 75e9b7897ef57ccbc97311c6e7002eddb5d65eae Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Krieger Date: Tue, 14 Nov 2023 12:19:20 -0300 Subject: [PATCH] fix(claimer): fix duplicate checker verification Read the NewClaimToHistory event from the History contract instead of calling the getClaim method from the Authority contract. Co-authored-by: Gabriel de Quadros Ligneul <8294320+gligneul@users.noreply.github.com> --- offchain/authority-claimer/src/checker.rs | 138 +++++++++++++++------- offchain/authority-claimer/src/lib.rs | 3 +- 2 files changed, 98 insertions(+), 43 deletions(-) diff --git a/offchain/authority-claimer/src/checker.rs b/offchain/authority-claimer/src/checker.rs index b9cdc6581..95a91f4b9 100644 --- a/offchain/authority-claimer/src/checker.rs +++ b/offchain/authority-claimer/src/checker.rs @@ -2,20 +2,23 @@ // SPDX-License-Identifier: Apache-2.0 (see LICENSE) use async_trait::async_trait; -use contracts::authority::Authority; +use contracts::history::{Claim, History}; use ethers::{ self, - abi::AbiEncode, - providers::{Http, HttpRateLimitRetryPolicy, Provider, RetryClient}, - types::{H160, U256}, + contract::ContractError, + providers::{ + Http, HttpRateLimitRetryPolicy, Middleware, Provider, RetryClient, + }, + types::{ValueOrArray, H160}, }; use rollups_events::{Address, RollupsClaim}; -use snafu::{ResultExt, Snafu}; +use snafu::{ensure, ResultExt, Snafu}; use std::fmt::Debug; use std::sync::Arc; -use tracing::info; +use tracing::trace; use url::{ParseError, Url}; +const GENESIS_BLOCK: u64 = 1; const MAX_RETRIES: u32 = 10; const INITIAL_BACKOFF: u64 = 1000; @@ -25,7 +28,7 @@ pub trait DuplicateChecker: Debug { type Error: snafu::Error + 'static; async fn is_duplicated_rollups_claim( - &self, + &mut self, dapp_address: Address, rollups_claim: &RollupsClaim, ) -> Result; @@ -37,44 +40,61 @@ pub trait DuplicateChecker: Debug { #[derive(Debug)] pub struct DefaultDuplicateChecker { - authority: Authority>>, + provider: Arc>>, + history: History>>, + claims: Vec, + confirmations: usize, + next_block_to_read: u64, } #[derive(Debug, Snafu)] pub enum DuplicateCheckerError { - #[snafu(display("invalid provider URL"))] + #[snafu(display("failed to call contract"))] ContractError { - source: ethers::contract::ContractError< - ethers::providers::Provider>, - >, + source: ContractError>>, + }, + + #[snafu(display("failed to call provider"))] + ProviderError { + source: ethers::providers::ProviderError, }, #[snafu(display("parser error"))] ParseError { source: ParseError }, + + #[snafu(display( + "Depth of `{}` higher than latest block `{}`", + depth, + latest + ))] + DepthTooHigh { depth: u64, latest: u64 }, } impl DefaultDuplicateChecker { pub fn new( http_endpoint: String, - authority_address: Address, + history_address: Address, + confirmations: usize, ) -> Result { let http = Http::new(Url::parse(&http_endpoint).context(ParseSnafu)?); - let retry_client = RetryClient::new( http, Box::new(HttpRateLimitRetryPolicy), MAX_RETRIES, INITIAL_BACKOFF, ); - let provider = Arc::new(Provider::new(retry_client)); - - let authority = Authority::new( - H160(authority_address.inner().to_owned()), - provider, + let history = History::new( + H160(history_address.inner().to_owned()), + provider.clone(), ); - - Ok(Self { authority }) + Ok(Self { + provider, + history, + claims: Vec::new(), + confirmations, + next_block_to_read: GENESIS_BLOCK, + }) } } @@ -83,32 +103,66 @@ impl DuplicateChecker for DefaultDuplicateChecker { type Error = DuplicateCheckerError; async fn is_duplicated_rollups_claim( - &self, + &mut self, dapp_address: Address, rollups_claim: &RollupsClaim, ) -> Result { - let proof_context = - U256([rollups_claim.epoch_index, 0, 0, 0]).encode().into(); - - match self - .authority - .get_claim(H160(dapp_address.inner().to_owned()), proof_context) - .block(ethers::types::BlockNumber::Latest) - .call() + self.update_claims(dapp_address).await?; + Ok(self.claims.iter().any(|read_claim| { + &read_claim.epoch_hash == rollups_claim.epoch_hash.inner() + && read_claim.first_index == rollups_claim.first_index + && read_claim.last_index == rollups_claim.last_index + })) + } +} + +impl DefaultDuplicateChecker { + async fn update_claims( + &mut self, + dapp_address: Address, + ) -> Result<(), DuplicateCheckerError> { + let depth = self.confirmations as u64; + + let current_block = self + .provider + .get_block_number() .await - { - // If there's any response, the claim already exists - Ok(_response) => Ok(true), - Err(e) => { - // If there's an InvalidClaimIndex error, we're asking for an index - // bigger than the current one, which means it's a new claim - if e.to_string().contains("InvalidClaimIndex()") { - Ok(false) - } else { - info!("{:?}", e); - Err(e).context(ContractSnafu) - } + .context(ProviderSnafu)? + .as_u64(); + + ensure!( + depth <= current_block, + DepthTooHighSnafu { + depth: depth, + latest: current_block } + ); + + let block_number = current_block - depth; + + let dapp_address = H160(dapp_address.inner().to_owned()); + let topic = ValueOrArray::Value(Some(dapp_address.into())); + + let mut claims: Vec<_> = self + .history + .new_claim_to_history_filter() + .from_block(self.next_block_to_read) + .to_block(block_number) + .topic1(topic) + .query() + .await + .context(ContractSnafu)? + .into_iter() + .map(|event| event.claim) + .collect(); + + if !claims.is_empty() { + trace!("read new claims {:?}", claims); + self.claims.append(&mut claims); } + + self.next_block_to_read = block_number + 1; + + Ok(()) } } diff --git a/offchain/authority-claimer/src/lib.rs b/offchain/authority-claimer/src/lib.rs index c7d521386..dcbb711f2 100644 --- a/offchain/authority-claimer/src/lib.rs +++ b/offchain/authority-claimer/src/lib.rs @@ -46,7 +46,8 @@ pub async fn run(config: Config) -> Result<(), Box> { trace!("Creating the duplicate checker"); let duplicate_checker = DefaultDuplicateChecker::new( config.tx_manager_config.provider_http_endpoint.clone(), - config.blockchain_config.authority_address.clone(), + config.blockchain_config.history_address.clone(), + config.tx_manager_config.default_confirmations, )?; // Creating the transaction sender.