// Copyright 2019-2023 Parity Technologies (UK) Ltd. // This file is part of Parity Bridges Common. // Parity Bridges Common is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Parity Bridges Common is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . //! Helper struct used for submitting finality reports and tracking their status. use crate::{EquivocationDetectionPipeline, SourceClient}; use futures::FutureExt; use relay_utils::{TrackedTransactionFuture, TrackedTransactionStatus, TransactionTracker}; use std::{ future::poll_fn, task::{Context, Poll}, }; pub struct EquivocationsReporter<'a, P: EquivocationDetectionPipeline, SC: SourceClient

> { pending_reports: Vec>, } impl<'a, P: EquivocationDetectionPipeline, SC: SourceClient

> EquivocationsReporter<'a, P, SC> { pub fn new() -> Self { Self { pending_reports: vec![] } } /// Submit a `report_equivocation()` transaction to the source chain. /// /// We store the transaction tracker for future monitoring. pub async fn submit_report( &mut self, source_client: &SC, at: P::Hash, equivocation: P::EquivocationProof, ) -> Result<(), SC::Error> { let pending_report = source_client.report_equivocation(at, equivocation).await?; self.pending_reports.push(pending_report.wait()); Ok(()) } fn do_process_pending_reports(&mut self, cx: &mut Context<'_>) -> Poll<()> { self.pending_reports.retain_mut(|pending_report| { match pending_report.poll_unpin(cx) { Poll::Ready(tx_status) => { match tx_status { TrackedTransactionStatus::Lost => { log::error!(target: "bridge", "Equivocation report tx was lost"); }, TrackedTransactionStatus::Finalized(id) => { log::error!(target: "bridge", "Equivocation report tx was finalized in source block {id:?}"); }, } // The future was processed. Drop it. false }, Poll::Pending => { // The future is still pending. Retain it. true }, } }); Poll::Ready(()) } /// Iterate through all the pending `report_equivocation()` transactions /// and log the ones that finished. pub async fn process_pending_reports(&mut self) { poll_fn(|cx| self.do_process_pending_reports(cx)).await } } #[cfg(test)] mod tests { use super::*; use crate::mock::*; use relay_utils::HeaderId; use std::sync::Mutex; #[async_std::test] async fn process_pending_reports_works() { let polled_reports = Mutex::new(vec![]); let finished_reports = Mutex::new(vec![]); let mut reporter = EquivocationsReporter:: { pending_reports: vec![ Box::pin(async { polled_reports.lock().unwrap().push(1); finished_reports.lock().unwrap().push(1); TrackedTransactionStatus::Finalized(HeaderId(1, 1)) }), Box::pin(async { polled_reports.lock().unwrap().push(2); finished_reports.lock().unwrap().push(2); TrackedTransactionStatus::Finalized(HeaderId(2, 2)) }), Box::pin(async { polled_reports.lock().unwrap().push(3); std::future::pending::<()>().await; finished_reports.lock().unwrap().push(3); TrackedTransactionStatus::Finalized(HeaderId(3, 3)) }), Box::pin(async { polled_reports.lock().unwrap().push(4); finished_reports.lock().unwrap().push(4); TrackedTransactionStatus::Finalized(HeaderId(4, 4)) }), ], }; reporter.process_pending_reports().await; assert_eq!(*polled_reports.lock().unwrap(), vec![1, 2, 3, 4]); assert_eq!(*finished_reports.lock().unwrap(), vec![1, 2, 4]); assert_eq!(reporter.pending_reports.len(), 1); } }