diff --git a/coordinator/src/main.rs b/coordinator/src/main.rs index e9a89e33..17f89c96 100644 --- a/coordinator/src/main.rs +++ b/coordinator/src/main.rs @@ -596,8 +596,21 @@ pub async fn handle_processors( break; } Err(e) => { - // TODO: Check if this failed because the batch was already published by someone - // else + if let Ok(latest_block) = serai.get_latest_block().await { + if let Ok(Some(last)) = + serai.get_last_batch_for_network(latest_block.hash(), batch.batch.network).await + { + if last >= batch.batch.id { + log::info!( + "another coordinator executed batch {:?} {} (block {})", + batch.batch.network, + batch.batch.id, + hex::encode(batch.batch.block), + ); + break; + } + } + } log::error!("couldn't connect to Serai node to publish batch TX: {:?}", e); tokio::time::sleep(Duration::from_secs(10)).await; } diff --git a/substrate/client/src/serai/in_instructions.rs b/substrate/client/src/serai/in_instructions.rs index 4d744030..62e3d74a 100644 --- a/substrate/client/src/serai/in_instructions.rs +++ b/substrate/client/src/serai/in_instructions.rs @@ -22,6 +22,14 @@ impl Serai { self.storage(PALLET, "LatestNetworkBlock", Some(vec![scale_value(network)]), hash).await } + pub async fn get_last_batch_for_network( + &self, + hash: [u8; 32], + network: NetworkId, + ) -> Result, SeraiError> { + self.storage(PALLET, "LastBatch", Some(vec![scale_value(network)]), hash).await + } + pub async fn get_batch_events( &self, block: [u8; 32], diff --git a/substrate/in-instructions/pallet/src/lib.rs b/substrate/in-instructions/pallet/src/lib.rs index f6729c91..dc996186 100644 --- a/substrate/in-instructions/pallet/src/lib.rs +++ b/substrate/in-instructions/pallet/src/lib.rs @@ -52,10 +52,10 @@ pub mod pallet { #[pallet::pallet] pub struct Pallet(PhantomData); - // The amount of batches a network has issued, which is also the ID to use for the next batch + // The ID of the last executed Batch for a network. #[pallet::storage] #[pallet::getter(fn batches)] - pub(crate) type Batches = StorageMap<_, Blake2_256, NetworkId, u32, OptionQuery>; + pub(crate) type LastBatch = StorageMap<_, Blake2_256, NetworkId, u32, OptionQuery>; // The last Serai block in which this validator set included a batch #[pallet::storage] @@ -117,7 +117,7 @@ pub mod pallet { // this to be safe LastBatchBlock::::insert(batch.network, frame_system::Pallet::::block_number()); - Batches::::insert(batch.network, batch.id); + LastBatch::::insert(batch.network, batch.id); LatestNetworkBlock::::insert(batch.network, batch.block); Self::deposit_event(Event::Batch { network: batch.network, @@ -174,9 +174,9 @@ pub mod pallet { } // Verify the batch is sequential - // Batches has the last ID set. The next ID should be it + 1 + // LastBatch has the last ID set. The next ID should be it + 1 // If there's no ID, the next ID should be 0 - let expected = Batches::::get(network).map_or(0, |prev| prev + 1); + let expected = LastBatch::::get(network).map_or(0, |prev| prev + 1); if batch.batch.id < expected { Err(InvalidTransaction::Stale)?; }