diff --git a/ipc/provider/src/checkpoint.rs b/ipc/provider/src/checkpoint.rs index db2f218e..9967cc45 100644 --- a/ipc/provider/src/checkpoint.rs +++ b/ipc/provider/src/checkpoint.rs @@ -156,16 +156,27 @@ impl BottomUpCheckpointMan return Ok(()); } - let bundle = self - .child_handler - .checkpoint_bundle_at(next_submission_height) - .await?; - log::debug!("bottom up bundle: {bundle:?}"); + let prev_submission_height = next_submission_height - self.checkpoint_period(); - self.parent_handler - .submit_checkpoint(submitter, bundle) - .await - .map_err(|e| anyhow!("cannot submit bottom up checkpoint due to: {e:}"))?; + for h in (prev_submission_height + 1)..=current_height { + let events = self.child_handler.quorum_reached_events(h).await?; + if events.is_empty() { + continue; + } + + for event in events { + let bundle = self + .child_handler + .checkpoint_bundle_at(event.height) + .await?; + log::debug!("bottom up bundle: {bundle:?}"); + + self.parent_handler + .submit_checkpoint(submitter, bundle) + .await + .map_err(|e| anyhow!("cannot submit bottom up checkpoint due to: {e:}"))?; + } + } Ok(()) } diff --git a/ipc/provider/src/manager/evm/manager.rs b/ipc/provider/src/manager/evm/manager.rs index 8f06ccee..b7075d1c 100644 --- a/ipc/provider/src/manager/evm/manager.rs +++ b/ipc/provider/src/manager/evm/manager.rs @@ -7,8 +7,8 @@ use std::time::Duration; use ethers::types::H256; use ipc_actors_abis::{ - gateway_getter_facet, gateway_manager_facet, gateway_messenger_facet, lib_staking_change_log, - subnet_actor_getter_facet, subnet_actor_manager_facet, subnet_registry, + gateway_getter_facet, gateway_manager_facet, gateway_messenger_facet, gateway_router_facet, + lib_staking_change_log, subnet_actor_getter_facet, subnet_actor_manager_facet, subnet_registry, }; use ipc_sdk::evm::{fil_to_eth_amount, payload_to_evm_address, subnet_id_to_evm_addresses}; use ipc_sdk::validator::from_contract_validators; @@ -33,7 +33,7 @@ use ethers::types::{BlockId, Eip1559TransactionRequest, I256, U256}; use fvm_shared::clock::ChainEpoch; use fvm_shared::{address::Address, econ::TokenAmount}; use ipc_identity::{EthKeyAddress, EvmKeyStore, PersistentKeyStore}; -use ipc_sdk::checkpoint::{BottomUpCheckpoint, BottomUpCheckpointBundle}; +use ipc_sdk::checkpoint::{BottomUpCheckpoint, BottomUpCheckpointBundle, QuorumReachedEvent}; use ipc_sdk::cross::CrossMsg; use ipc_sdk::gateway::Status; use ipc_sdk::staking::StakingChangeRequest; @@ -965,6 +965,28 @@ impl BottomUpCheckpointRelayer for EthSubnetManager { }) } + async fn quorum_reached_events(&self, height: ChainEpoch) -> Result> { + let contract = gateway_router_facet::GatewayRouterFacet::new( + self.ipc_contract_info.gateway_addr, + Arc::new(self.ipc_contract_info.provider.clone()), + ); + + let ev = contract + .event::() + .from_block(height as u64) + .to_block(height as u64); + + let mut events = vec![]; + for (event, _meta) in ev.query_with_meta().await? { + events.push(QuorumReachedEvent { + height: event.height as ChainEpoch, + checkpoint: event.checkpoint.to_vec(), + quorum_weight: eth_to_fil_amount(&event.quorum_weight)?, + }); + } + + Ok(events) + } async fn current_epoch(&self) -> Result { let epoch = self .ipc_contract_info diff --git a/ipc/provider/src/manager/subnet.rs b/ipc/provider/src/manager/subnet.rs index 7377072a..7455302d 100644 --- a/ipc/provider/src/manager/subnet.rs +++ b/ipc/provider/src/manager/subnet.rs @@ -7,7 +7,7 @@ use anyhow::Result; use async_trait::async_trait; use fvm_shared::clock::ChainEpoch; use fvm_shared::{address::Address, econ::TokenAmount}; -use ipc_sdk::checkpoint::BottomUpCheckpointBundle; +use ipc_sdk::checkpoint::{BottomUpCheckpointBundle, QuorumReachedEvent}; use ipc_sdk::cross::CrossMsg; use ipc_sdk::staking::StakingChangeRequest; use ipc_sdk::subnet::ConstructParams; @@ -195,6 +195,8 @@ pub trait BottomUpCheckpointRelayer: Send + Sync { async fn checkpoint_period(&self, subnet_id: &SubnetID) -> Result; /// Get the checkpoint bundle at a specific height. If it does not exist, it will through error. async fn checkpoint_bundle_at(&self, height: ChainEpoch) -> Result; + /// Queries the quorum reached events at target height. + async fn quorum_reached_events(&self, height: ChainEpoch) -> Result>; /// Get the current epoch in the current subnet async fn current_epoch(&self) -> Result; } diff --git a/ipc/sdk/src/checkpoint.rs b/ipc/sdk/src/checkpoint.rs index 7db89e67..7c6ef350 100644 --- a/ipc/sdk/src/checkpoint.rs +++ b/ipc/sdk/src/checkpoint.rs @@ -10,6 +10,7 @@ use cid::Cid; use fvm_ipld_encoding::DAG_CBOR; use fvm_shared::address::Address; use fvm_shared::clock::ChainEpoch; +use fvm_shared::econ::TokenAmount; use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; @@ -23,6 +24,14 @@ lazy_static! { pub type Signature = Vec; +/// The event emitted +#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)] +pub struct QuorumReachedEvent { + pub height: ChainEpoch, + pub checkpoint: Vec, + pub quorum_weight: TokenAmount, +} + /// The collection of items for the bottom up checkpoint submission #[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)] pub struct BottomUpCheckpointBundle {