fix: sync last_bedrock_msg_id even if posting to Bedrock failed

This commit is contained in:
Daniil Polyakov 2026-02-12 00:01:00 +03:00
parent 0a8d6f1eb4
commit d0f26744eb
4 changed files with 93 additions and 18 deletions

View File

@ -23,7 +23,7 @@ pub trait BlockSettlementClientTrait: Clone {
fn bedrock_signing_key(&self) -> &Ed25519Key;
/// Post a transaction to the node.
async fn submit_block_to_bedrock(&self, block: &Block) -> Result<MsgId>;
async fn submit_inscribe_tx_to_bedrock(&self, tx: SignedMantleTx) -> Result<()>;
/// Create and sign a transaction for inscribing data.
fn create_inscribe_tx(&self, block: &Block) -> Result<(SignedMantleTx, MsgId)> {
@ -89,16 +89,13 @@ impl BlockSettlementClientTrait for BlockSettlementClient {
})
}
async fn submit_block_to_bedrock(&self, block: &Block) -> Result<MsgId> {
let (tx, new_msg_id) = self.create_inscribe_tx(block)?;
// Post the transaction
async fn submit_inscribe_tx_to_bedrock(&self, tx: SignedMantleTx) -> Result<()> {
self.bedrock_client
.post_transaction(tx)
.await
.context("Failed to post transaction to Bedrock")?;
Ok(new_msg_id)
Ok(())
}
fn bedrock_channel_id(&self) -> ChannelId {

View File

@ -167,14 +167,25 @@ impl<BC: BlockSettlementClientTrait, IC: IndexerClientTrait> SequencerCore<BC, I
pub async fn produce_new_block_and_post_to_settlement_layer(&mut self) -> Result<u64> {
{
let block = self.produce_new_block_with_mempool_transactions()?;
let block = self
.produce_new_block_with_mempool_transactions()
.context("Failed to produce new block with mempool transactions")?;
let (tx, msg_id) = self
.block_settlement_client
.create_inscribe_tx(&block)
.with_context(|| {
format!(
"Failed to create inscribe transaction for block with id {}",
block.header.block_id
)
})?;
self.last_bedrock_msg_id = msg_id.into();
match self
.block_settlement_client
.submit_block_to_bedrock(&block)
.submit_inscribe_tx_to_bedrock(tx)
.await
{
Ok(msg_id) => {
self.last_bedrock_msg_id = msg_id.into();
Ok(()) => {
info!("Posted block data to Bedrock, msg_id: {msg_id:?}");
}
Err(err) => {
@ -801,4 +812,35 @@ mod tests {
assert_eq!(sequencer.get_pending_blocks().unwrap().len(), 1);
}
#[tokio::test]
async fn test_last_bedrock_msg_id_updated_even_when_posting_fails() {
use crate::mock::{MockBlockSettlementClientWithError, MockIndexerClient};
let config = setup_sequencer_config();
let (mut sequencer, mempool_handle) = crate::SequencerCore::<
MockBlockSettlementClientWithError,
MockIndexerClient,
>::start_from_config(config)
.await;
// Store the initial last_bedrock_msg_id (should be genesis parent msg id)
let initial_msg_id = sequencer.last_bedrock_msg_id;
assert_eq!(initial_msg_id, [0; 32]);
// Add a transaction to the mempool
let tx = common::test_utils::produce_dummy_empty_transaction();
mempool_handle.push(tx).await.unwrap();
// Produce a block and post to settlement layer (which will fail)
let result = sequencer
.produce_new_block_and_post_to_settlement_layer()
.await;
// The method should succeed even though posting to Bedrock failed
assert!(result.is_ok());
// Verify that last_bedrock_msg_id was updated despite the posting failure
assert_ne!(sequencer.last_bedrock_msg_id, initial_msg_id);
}
}

View File

@ -1,6 +1,6 @@
use anyhow::Result;
use common::block::Block;
use logos_blockchain_core::mantle::ops::channel::{ChannelId, MsgId};
use anyhow::{Result, anyhow};
use bedrock_client::SignedMantleTx;
use logos_blockchain_core::mantle::ops::channel::ChannelId;
use logos_blockchain_key_management_system_service::keys::Ed25519Key;
use url::Url;
@ -34,8 +34,35 @@ impl BlockSettlementClientTrait for MockBlockSettlementClient {
&self.bedrock_signing_key
}
async fn submit_block_to_bedrock(&self, block: &Block) -> Result<MsgId> {
self.create_inscribe_tx(block).map(|(_, msg_id)| msg_id)
async fn submit_inscribe_tx_to_bedrock(&self, _tx: SignedMantleTx) -> Result<()> {
Ok(())
}
}
#[derive(Clone)]
pub struct MockBlockSettlementClientWithError {
bedrock_channel_id: ChannelId,
bedrock_signing_key: Ed25519Key,
}
impl BlockSettlementClientTrait for MockBlockSettlementClientWithError {
fn new(config: &BedrockConfig, bedrock_signing_key: Ed25519Key) -> Result<Self> {
Ok(Self {
bedrock_channel_id: config.channel_id,
bedrock_signing_key,
})
}
fn bedrock_channel_id(&self) -> ChannelId {
self.bedrock_channel_id
}
fn bedrock_signing_key(&self) -> &Ed25519Key {
&self.bedrock_signing_key
}
async fn submit_inscribe_tx_to_bedrock(&self, _tx: SignedMantleTx) -> Result<()> {
Err(anyhow!("Mock error"))
}
}

View File

@ -176,10 +176,19 @@ async fn retry_pending_blocks_loop(
info!("Resubmitting {} pending blocks", pending_blocks.len());
for block in &pending_blocks {
if let Err(e) = block_settlement_client.submit_block_to_bedrock(block).await {
// TODO: We could cache the inscribe tx for each pending block to avoid re-creating it
// on every retry.
let (tx, _msg_id) = block_settlement_client
.create_inscribe_tx(block)
.context("Failed to create inscribe tx for pending block")?;
if let Err(e) = block_settlement_client
.submit_inscribe_tx_to_bedrock(tx)
.await
{
warn!(
"Failed to resubmit block with id {} with error {}",
block.header.block_id, e
"Failed to resubmit block with id {} with error {e:#}",
block.header.block_id
);
}
}