Skip to content

Commit

Permalink
feat(pending): pending block sealed instead of re-added to mempool (#468
Browse files Browse the repository at this point in the history
)
  • Loading branch information
Trantorian1 authored Jan 22, 2025
1 parent 6bd2e3b commit 88ed67c
Show file tree
Hide file tree
Showing 10 changed files with 1,068 additions and 262 deletions.
1,070 changes: 1,045 additions & 25 deletions crates/madara/client/block_production/src/lib.rs

Large diffs are not rendered by default.

This file was deleted.

6 changes: 6 additions & 0 deletions crates/madara/client/db/src/block_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,12 @@ impl MadaraBackend {
Ok(res)
}

#[tracing::instrument(skip(self), fields(module = "BlockDB"))]
pub fn has_pending_block(&self) -> Result<bool> {
let col = self.db.get_column(Column::BlockStorageMeta);
Ok(self.db.get_cf(&col, ROW_PENDING_STATE_UPDATE)?.is_some())
}

#[tracing::instrument(skip(self), fields(module = "BlockDB"))]
pub fn get_pending_block_state_update(&self) -> Result<StateDiff> {
let col = self.db.get_column(Column::BlockStorageMeta);
Expand Down
4 changes: 0 additions & 4 deletions crates/madara/client/db/src/storage_updates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,9 @@ impl MadaraBackend {
};

let task_contract_db = || {
// let nonces_from_deployed =
// state_diff.deployed_contracts.iter().map(|&DeployedContractItem { address, .. }| (address, Felt::ZERO));

let nonces_from_updates =
state_diff.nonces.into_iter().map(|NonceUpdate { contract_address, nonce }| (contract_address, nonce));

// let nonce_map: HashMap<Felt, Felt> = nonces_from_deployed.chain(nonces_from_updates).collect(); // set nonce to zero when contract deployed
let nonce_map: HashMap<Felt, Felt> = nonces_from_updates.collect();

let contract_class_updates_replaced = state_diff
Expand Down
22 changes: 12 additions & 10 deletions crates/madara/client/devnet/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,8 +311,9 @@ mod tests {
let importer = Arc::new(BlockImporter::new(Arc::clone(&backend), None).unwrap());

tracing::debug!("{:?}", block.state_diff);
tokio::runtime::Runtime::new()
.unwrap()
let runtime = tokio::runtime::Runtime::new().unwrap();

runtime
.block_on(
importer.add_block(
block,
Expand All @@ -335,14 +336,15 @@ mod tests {
let mempool = Arc::new(Mempool::new(Arc::clone(&backend), Arc::clone(&l1_data_provider), mempool_limits));
let metrics = BlockProductionMetrics::register();

let block_production = BlockProductionTask::new(
Arc::clone(&backend),
Arc::clone(&importer),
Arc::clone(&mempool),
Arc::new(metrics),
Arc::clone(&l1_data_provider),
)
.unwrap();
let block_production = runtime
.block_on(BlockProductionTask::new(
Arc::clone(&backend),
Arc::clone(&importer),
Arc::clone(&mempool),
Arc::new(metrics),
Arc::clone(&l1_data_provider),
))
.unwrap();

DevnetForTesting { backend, contracts, block_production, mempool }
}
Expand Down
17 changes: 0 additions & 17 deletions crates/madara/client/mempool/src/inner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -767,23 +767,6 @@ impl MempoolInner {
}
}

// This is called by the block production when loading the pending block
// from db
pub fn insert_txs(
&mut self,
txs: impl IntoIterator<Item = MempoolTransaction>,
force: bool,
) -> Result<(), TxInsertionError> {
for tx in txs {
// Transactions are marked as ready as they were already included
// into the pending block
let nonce = tx.nonce;
let nonce_next = tx.nonce_next;
self.insert_tx(tx, force, true, NonceInfo::ready(nonce, nonce_next))?;
}
Ok(())
}

/// Returns true if [MempoolInner] has the transaction at a contract address
/// and [Nonce] in the ready queue.
pub fn nonce_is_ready(&self, sender_address: Felt, nonce: Nonce) -> bool {
Expand Down
37 changes: 2 additions & 35 deletions crates/madara/client/mempool/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,6 @@ pub trait MempoolProvider: Send + Sync {
txs: VecDeque<MempoolTransaction>,
consumed_txs: Vec<MempoolTransaction>,
) -> Result<(), MempoolError>;
fn txs_insert_no_validation(&self, txs: Vec<MempoolTransaction>, force: bool) -> Result<(), MempoolError>
where
Self: Sized;
fn chain_id(&self) -> Felt;
}

Expand Down Expand Up @@ -154,7 +151,8 @@ impl Mempool {
let pending_block_info = if let Some(block) = self.backend.get_block_info(&DbBlockId::Pending)? {
block
} else {
// No current pending block, we'll make an unsaved empty one for the sake of validating this tx.
// No current pending block, we'll make an unsaved empty one for
// the sake of validating this tx.
let parent_block_hash = self
.backend
.get_block_hash(&BlockId::Tag(BlockTag::Latest))?
Expand Down Expand Up @@ -486,37 +484,6 @@ impl MempoolProvider for Mempool {
Ok(())
}

/// This is called by the block production task to re-add transaction from
/// the pending block back into the mempool
#[tracing::instrument(skip(self, txs), fields(module = "Mempool"))]
fn txs_insert_no_validation(&self, txs: Vec<MempoolTransaction>, force: bool) -> Result<(), MempoolError> {
let mut nonce_cache = self.nonce_cache.write().expect("Poisoned lock");

for tx in &txs {
// Theoretically we should not have to invalidate the nonce cache
// here as this function should ONLY be called when adding the
// pending block back into the mempool from the db. However I am
// afraid someone will end up using this incorrectly so I am adding
// this here.
nonce_cache.remove(&tx.contract_address());

// Save to db. Transactions are marked as ready since they were
// already previously included into the pending block
let nonce_info = NonceInfo::ready(tx.nonce, tx.nonce_next);
let saved_tx = blockifier_to_saved_tx(&tx.tx, tx.arrived_at);
self.backend.save_mempool_transaction(
&saved_tx,
tx.tx_hash().to_felt(),
&tx.converted_class,
&nonce_info,
)?;
}

let mut inner = self.inner.write().expect("Poisoned lock");
inner.insert_txs(txs, force)?;

Ok(())
}
fn chain_id(&self) -> Felt {
Felt::from_bytes_be_slice(format!("{}", self.backend.chain_config().chain_id).as_bytes())
}
Expand Down
3 changes: 2 additions & 1 deletion crates/madara/node/src/service/block_production.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ impl Service for BlockProductionService {
Arc::clone(mempool),
Arc::clone(metrics),
Arc::clone(l1_data_provider),
)?;
)
.await?;

runner.service_loop(move |ctx| block_production_task.block_production_task(ctx));

Expand Down
2 changes: 1 addition & 1 deletion crates/madara/primitives/block/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ impl From<MadaraBlock> for MadaraMaybePendingBlock {

/// Visited segments are the class segments that are visited during the execution of the block.
/// This info is an input of SNOS and used for proving.
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[derive(Clone, Default, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct VisitedSegments(pub Vec<VisitedSegmentEntry>);

#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
Expand Down
78 changes: 0 additions & 78 deletions crates/madara/tests/src/devnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,81 +159,3 @@ async fn madara_devnet_mempool_saving() {
)
.await;
}

#[rstest]
#[tokio::test]
async fn madara_devnet_continue_pending() {
let _ = tracing_subscriber::fmt().with_test_writer().try_init();

let cmd_builder = MadaraCmdBuilder::new().args([
"--devnet",
"--no-l1-sync",
"--gas-price",
"0",
// never produce blocks but produce pending txs
"--chain-config-path",
"test_devnet.yaml",
"--chain-config-override",
"block_time=5min,pending_block_update_time=500ms",
]);
let mut node = cmd_builder.clone().run();
node.wait_for_ready().await;

let chain_id = node.json_rpc().chain_id().await.unwrap();

let signer = LocalWallet::from_signing_key(SigningKey::from_secret_scalar(ACCOUNT_SECRET));
let mut account =
SingleOwnerAccount::new(node.json_rpc(), signer, ACCOUNT_ADDRESS, chain_id, ExecutionEncoding::New);
account.set_block_id(BlockId::Tag(BlockTag::Pending));

let res = account
.execute_v3(vec![Call {
to: ERC20_STRK_CONTRACT_ADDRESS,
selector: starknet_keccak(b"transfer"),
calldata: vec![ACCOUNT_ADDRESS, 15.into(), Felt::ZERO],
}])
.send()
.await
.unwrap();

wait_for_cond(
|| async {
let receipt = node.json_rpc().get_transaction_receipt(res.transaction_hash).await?;
assert_eq!(receipt.block, ReceiptBlock::Pending);
Ok(())
},
Duration::from_millis(500),
60,
)
.await;

drop(node);

// tx should appear in saved pending block

let cmd_builder = cmd_builder.args([
"--devnet",
"--no-l1-sync",
"--gas-price",
"0",
// never produce blocks but produce pending txs
"--chain-config-path",
"test_devnet.yaml",
"--chain-config-override",
"block_time=5min,pending_block_update_time=500ms",
]);
let mut node = cmd_builder.clone().run();
node.wait_for_ready().await;

// should find receipt
wait_for_cond(
|| async {
let receipt = node.json_rpc().get_transaction_receipt(res.transaction_hash).await?;
assert_eq!(receipt.block, ReceiptBlock::Pending);
Ok(())
},
Duration::from_millis(500),
60,
)
.await;
}

0 comments on commit 88ed67c

Please sign in to comment.