fix: all block transactions stored in the block tree for migration#1122
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughBlock migration now includes per-block transaction data: BlockMigratedEvent and migrate/send paths accept and propagate Arc<BlockTransactions); mempool migration and validation consume transactions from the event instead of querying the mempool; BlockTree and domain model store and validate per-block BlockTransactions. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant BlockTree as BlockTreeService
participant Mempool as MempoolService
participant DB as Database
participant Validator as Validator/Consumer
BlockTree->>BlockTree: extract BlockTransactions from cache
BlockTree->>Mempool: emit BlockMigratedEvent(block_header, transactions)
Mempool->>Mempool: process event.transactions (commitment, submit, publish)
Mempool->>DB: insert commitment records and data-tx headers/metadata
Mempool->>Mempool: cleanup mempool entries using in-event tx IDs
Mempool->>Validator: notify downstream components of migrated block (with transactions)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
crates/actors/src/mempool_service/facade.rs (1)
276-286:⚠️ Potential issue | 🟡 Minor
migrate_blocksends emptyBlockTransactions— code path appears unused but should be addressed.The facade's
migrate_blocksendsBlockTransactions::default()in theBlockMigratedEvent. The handler (handle_block_migratedin lifecycle.rs) readsevent.transactionsto extract commitment transactions (line 836-840), so empty transactions would skip that processing step.However, this facade method is not called from any production code paths. Real migrations use the
block_tree_serviceandblock_index_serviceimplementations directly, not the facade. The only usage is in the test mock (crates/p2p/src/tests/util.rs).Consider either removing this unused method from the trait, or documenting why empty transactions are acceptable here (e.g., if this is a legacy fallback path that should never be used with real transactions).
crates/actors/src/block_tree_service.rs (1)
454-479:⚠️ Potential issue | 🟠 MajorDon’t fall back to empty transactions during migration.
Silently defaulting to empty transactions can reintroduce missing‑tx migrations if the cache lookup fails. Treat this as an error instead.
✅ Suggested fix
- let transactions = { - let cache = self.cache.read().expect("poisoned lock"); - cache - .blocks - .get(&block_to_migrate.block_hash) - .map(|meta| meta.transactions.clone()) - .unwrap_or_default() - }; + let transactions = { + let cache = self.cache.read().expect("poisoned lock"); + cache + .blocks + .get(&block_to_migrate.block_hash) + .map(|meta| meta.transactions.clone()) + .ok_or_else(|| eyre::eyre!( + "missing block metadata for migration block {}", + block_to_migrate.block_hash + ))? + };
🤖 Fix all issues with AI agents
In `@crates/chain/tests/api/hardfork_tests.rs`:
- Around line 777-781: The test currently awaits genesis_node.post_data_tx(...)
but drops the Result, which can hide submission failures; change the calls to
propagate errors by using the `?` operator on the awaited Result (e.g.,
`genesis_node.post_data_tx(anchor, data.into_bytes(), signer).await?`) or
otherwise return a Result from the test and propagate errors; update both
occurrences of genesis_node.post_data_tx(...) (around the current block and the
similar block at the other location) so failures surface immediately instead of
being silently ignored.
In `@crates/domain/src/models/block_tree.rs`:
- Around line 417-452: The data-tx validation currently compares the union of
all ledger tx IDs which allows mismatches across ledgers; change it to validate
per-ledger equality: for each ledger in block.data_ledgers compute expected_ids
from ledger.tx_ids and fetch the corresponding entry in transactions.data_txs
(use the ledger's identifier/key that maps into transactions.data_txs), then
ensure the HashSet of expected IDs equals the HashSet of actual IDs for that
ledger; also ensure there are no extra ledger entries in transactions.data_txs
(i.e., check that transactions.data_txs.keys() == block.data_ledgers.keys()).
Update the block_tree.rs checks around expected_data_ids/actual_data_ids to
iterate per-ledger and use eyre::ensure! per ledger with a clear error message
including the block hash and ledger identifier.
819359f to
e5f1237
Compare
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In `@crates/actors/src/mempool_service/lifecycle.rs`:
- Around line 847-858: The current flow removes commitment transactions from
mempool via mempool_state.remove_commitment_txs(...) before persisting them with
irys_db.update_eyre(...) and insert_commitment_tx(...), which risks losing them
if the DB commit fails; change the ordering so you first persist all
commitment_txs inside irys_db.update_eyre (calling insert_commitment_tx for
each) and only after that succeeds call
mempool_state.remove_commitment_txs(...), or alternatively wrap the DB commit in
a try and on failure re-insert the removed txs via mempool_state.add or a
rollback helper; update references in this block (remove_commitment_txs,
insert_commitment_tx, irys_db.update_eyre, mempool_state) accordingly so
removals happen only after a successful DB persist.
- Around line 889-892: The loop is unnecessarily cloning publish_txs; since
publish_tx_ids is already computed above and publish_txs isn't used afterward,
change the iteration inside the self.irys_db.update_eyre closure from for mut
header in publish_txs.clone() to consuming iteration (e.g., for mut header in
publish_txs.into_iter()) so the vector is moved instead of cloned, keeping the
surrounding update_eyre(...) and mut header usage intact.
In `@crates/chain/tests/api/hardfork_tests.rs`:
- Around line 1078-1101: After the second peer restart, add the same DB
integrity checks you ran in Step 7 (the block index hash checks, PoA chunk
validations and any DB content assertions) for the restarted peer_node: after
peer_node.wait_until_height(final_height, ...).await? and before
peer_node.stop().await, call the existing helper(s) used earlier (e.g. the
functions that validated block index hashes, PoA chunks and DB contents in Step
7—reuse those exact function names) to spot-check the restarted node’s database
contents and ensure they match expected state.
- Line 799: The subtraction pre_activation_height - block_migration_depth as u64
can underflow; change the calculation that sets pre_aurora_migrated_height to
use saturating_sub (e.g.,
pre_activation_height.saturating_sub(block_migration_depth as u64)) or add a
guard that ensures block_migration_depth as u64 <= pre_activation_height before
subtracting, and apply the same fix to the other occurrence mentioned (the
analogous subtraction at the second spot around line 918) so both use saturating
arithmetic or a proper check.
| self.mempool_state | ||
| .remove_commitment_txs(commitments.values().map(CommitmentTransaction::id)) | ||
| .remove_commitment_txs(commitment_txs.iter().map(CommitmentTransaction::id)) | ||
| .await; | ||
|
|
||
| // stage 1: insert commitment transactions into database | ||
| self.irys_db.update_eyre(|tx| { | ||
| for commitment_tx in commitments.values() { | ||
| for commitment_tx in commitment_txs { | ||
| // Insert the commitment transaction in to the db, perform migration | ||
| insert_commitment_tx(tx, commitment_tx)?; | ||
| } | ||
| Ok(()) | ||
| })?; |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Mempool removal precedes the DB commit — pre-existing risk, not introduced here.
If insert_commitment_tx fails (line 855 propagates via ?), the commitment transactions have already been removed from the mempool (line 847–849) but are not yet persisted to the database. The transactions remain available via event.transactions (and the block tree), so a retry of the migration event should recover them. Just flagging for awareness — this ordering existed before and is not new to this PR.
🤖 Prompt for AI Agents
In `@crates/actors/src/mempool_service/lifecycle.rs` around lines 847 - 858, The
current flow removes commitment transactions from mempool via
mempool_state.remove_commitment_txs(...) before persisting them with
irys_db.update_eyre(...) and insert_commitment_tx(...), which risks losing them
if the DB commit fails; change the ordering so you first persist all
commitment_txs inside irys_db.update_eyre (calling insert_commitment_tx for
each) and only after that succeeds call
mempool_state.remove_commitment_txs(...), or alternatively wrap the DB commit in
a try and on failure re-insert the removed txs via mempool_state.add or a
rollback helper; update references in this block (remove_commitment_txs,
insert_commitment_tx, irys_db.update_eyre, mempool_state) accordingly so
removals happen only after a successful DB persist.
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/actors/src/block_tree_service.rs (1)
261-284: 🧹 Nitpick | 🔵 TrivialMinor:
all_txsVec andall_txs_mapHashMap duplicate the same data.Both structures are built from the same
submit_txsandpublish_txs. The comment on line 277 acknowledges this as a TODO. No action needed now, but this is a candidate for a future clean-up whenblock_indexis migrated to useHashMap.
🤖 Fix all issues with AI agents
In `@crates/actors/src/block_tree_service.rs`:
- Around line 452-461: The code currently uses unwrap_or_default() when reading
transactions from cache.blocks for each block_to_migrate, which silently returns
an empty BlockTransactions on a cache miss; replace this silent fallback with a
loud failure: change the lookup to unwrap the Option with expect (or otherwise
return/propagate an Err) so that a missing cache entry for
block_to_migrate.block_hash fails immediately with a clear message including the
block_hash and context (e.g., in the block migration function where
self.cache.read(), cache.blocks.get(&block_to_migrate.block_hash) and
.map(|meta| meta.transactions.clone()) are used).
In `@crates/domain/src/models/block_tree.rs`:
- Around line 2583-2616: The helper mock_transactions_for_block duplicates
integer-to-enum mapping for data ledgers; replace the manual match with
DataLedger::try_from(data_ledger.ledger_id) (the same approach used in
add_common) and skip entries on Err, so ledger conversion is consistent with
production code and avoids hardcoded IDs; update use sites in
mock_transactions_for_block to check the Result and only insert when Ok(ledger).
- Around line 1482-1510: The load_data_transactions function currently drops
missing transactions returned as None from tx_header_by_txid, which can hide DB
corruption for blocks inserted into the cache (start block) without add_common
validation; update load_data_transactions to detect when tx_header_by_txid
returns None for any tx_id and either (a) return an Err/eyre::Report describing
the missing tx and include the tx_id and DataLedger, or (b) at minimum log a
clear warning with the tx_id and ledger and then return an Err so callers
(including the start block insertion path) fail-fast; modify any callers
expecting Ok(HashMap...) accordingly and ensure references to
load_data_transactions, tx_header_by_txid, and the start-block insertion code
path are updated to handle the error.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
crates/domain/src/models/block_tree.rs (1)
1457-1479:⚠️ Potential issue | 🟠 MajorInconsistent error handling:
load_commitment_transactionssilently drops missing txs whileload_data_transactionsfails.
load_data_transactions(line 1498–1507) returns anErrwhen a header-referenced tx is absent from the DB, butload_commitment_transactions(line 1470–1474) silently skips it. For the start block (lines 275, 283–296), commitment transactions are loaded here and inserted directly into the cache without going throughadd_commonvalidation, so a missing commitment tx would go undetected.Consider aligning the two functions — either return an error or at least log a warning.
🐛 Proposed fix
for tx_id in &commitment_tx_ids { if let Some(header) = commitment_tx_by_txid(&db_tx, tx_id).expect("to retrieve tx header from db") { txs.push(header); + } else { + return Err(eyre::eyre!( + "Missing commitment transaction in DB: tx_id={}, \ + This may indicate DB corruption.", + tx_id + )); } }crates/actors/src/block_tree_service.rs (1)
470-483: 🧹 Nitpick | 🔵 TrivialAvoid cloning
BlockTransactionsby wrapping inArcearlier.
transactions.clone()on line 472 duplicates the entireBlockTransactions(which containsVecs of transaction headers). Since both theBlockMigratedEventandsend_block_migration_messageonly need a shared reference, you can create theArconce and reuse it.♻️ Proposed fix
+ let arc_transactions = Arc::new(transactions); + // NOTE: order of events is very important! block migration event // writes chunks to db, which is expected by `send_block_migration_message`. let block_migrated_event = BlockMigratedEvent { block: Arc::clone(&block_to_migrate), - transactions: Arc::new(transactions.clone()), + transactions: Arc::clone(&arc_transactions), }; if let Err(e) = self .service_senders .block_migrated_events .send(block_migrated_event) { debug!("No reorg subscribers: {:?}", e); } let block_hash = block_to_migrate.block_hash; let block_height = block_to_migrate.height; - self.send_block_migration_message(block_to_migrate, &transactions) + self.send_block_migration_message(block_to_migrate, &arc_transactions) .await
🤖 Fix all issues with AI agents
In `@crates/actors/src/mempool_service/lifecycle.rs`:
- Around line 870-877: The loop over submit_txs currently swallows
insert_tx_header errors (logging and continuing) which can leave the DB in a
partially-migrated state; change the behavior in the update_eyre closure so any
failure in insert_tx_header aborts the operation and causes the DB transaction
to roll back — e.g., replace the error-logging-only branch in the for header in
&submit_txs loop with logic that propagates the error (use ? or return Err(...))
up from the closure so the transaction does not commit after a failed insert.
| for header in &submit_txs { | ||
| if let Err(err) = insert_tx_header(tx, header) { | ||
| error!( | ||
| "Could not insert transaction header - txid: {} err: {}", | ||
| header.id, err | ||
| ); | ||
| } | ||
| } |
There was a problem hiding this comment.
Submit tx insertion logs errors but continues — verify this is intentional.
If insert_tx_header fails for a submit tx (line 871), the error is logged but the loop continues. This means a partially-failed migration could leave the DB in an inconsistent state (some submit txs written, others not), yet the overall update_eyre closure returns Ok(()). If any insert fails, the DB transaction will still commit the successful ones.
For submit transactions that are referenced by the block header, a single failure likely indicates a serious issue. Consider propagating the error to abort the DB transaction.
🔧 Proposed fix
self.irys_db.update_eyre(|tx| {
for header in &submit_txs {
- if let Err(err) = insert_tx_header(tx, header) {
- error!(
- "Could not insert transaction header - txid: {} err: {}",
- header.id, err
- );
- }
+ insert_tx_header(tx, header)?;
}
Ok(())
})?;🤖 Prompt for AI Agents
In `@crates/actors/src/mempool_service/lifecycle.rs` around lines 870 - 877, The
loop over submit_txs currently swallows insert_tx_header errors (logging and
continuing) which can leave the DB in a partially-migrated state; change the
behavior in the update_eyre closure so any failure in insert_tx_header aborts
the operation and causes the DB transaction to roll back — e.g., replace the
error-logging-only branch in the for header in &submit_txs loop with logic that
propagates the error (use ? or return Err(...)) up from the closure so the
transaction does not commit after a failed insert.
There was a problem hiding this comment.
^, this seems like it should be a hard error
There was a problem hiding this comment.
@JesseTheRobot Agreed! Submit transactions are an integral part of the migrated block's state, so if any insertion fails, the entire migration should abort to maintain consistency. This aligns with the pattern elsewhere in the codebase where critical invariant violations cause the operation to fail rather than partially succeed.
The proposed fix of replacing the error-logging branch with insert_tx_header(tx, header)?; will ensure the database transaction rolls back on any failure, preventing partial migrations.
Would you like me to implement this change (along with the same fix for publish txs a few lines below), or will you handle it?
🧠 Learnings used
Learnt from: DanMacDonald
Repo: Irys-xyz/irys PR: 1104
File: crates/actors/src/mempool_service/data_txs.rs:31-37
Timestamp: 2026-01-24T02:18:04.459Z
Learning: In `crates/actors/src/mempool_service/data_txs.rs`, the precheck for DataLedger intentionally rejects OneYear and ThirtyDay ledgers during the staged rollout. Downstream match arms for these ledgers are placeholders for future implementation and should not be flagged as unreachable during this development phase.
Learnt from: glottologist
Repo: Irys-xyz/irys PR: 1090
File: crates/actors/src/supply_state_calculator.rs:121-130
Timestamp: 2026-01-20T13:27:23.880Z
Learning: In the supply state backfill logic (crates/actors/src/supply_state_calculator.rs), if the backfill process encounters failures such as batch count mismatches, the code should panic rather than return an error, as these represent critical invariant violations.
| // Check commitment transactions | ||
| let expected_commitment_ids: HashSet<H256> = | ||
| block.get_commitment_ledger_tx_ids().into_iter().collect(); | ||
| let actual_commitment_ids: HashSet<H256> = transactions |
There was a problem hiding this comment.
iirc the we have almost identical logic as part of the block body validation - it might make sense to unify the logic
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
crates/actors/src/block_tree_service.rs (2)
258-280:⚠️ Potential issue | 🟡 MinorStale doc comment references mempool/database access.
Line 264 still mentions "if any mempool/database access fails", but the method no longer performs mempool lookups. Minor doc inconsistency.
📝 Suggested doc fix
/// Errors - /// Returns an error if the block header cannot be fetched or if any mempool/database access fails. + /// Returns an error if the block migration or chunk migration message fails to send.
475-488: 🧹 Nitpick | 🔵 TrivialConsider wrapping in
Arcbefore cloning to avoid a redundant deep copy.
transactionsis cloned on line 477 into theArcfor the event, then borrowed on line 488. You could wrap it inArconce and reuse it for both, avoiding one deep clone.♻️ Proposed refactor
+ let transactions = Arc::new(transactions); + let block_migrated_event = BlockMigratedEvent { block: Arc::clone(&block_to_migrate), - transactions: Arc::new(transactions.clone()), + transactions: Arc::clone(&transactions), }; if let Err(e) = self .service_senders @@ -486,7 +488,7 @@ let block_hash = block_to_migrate.block_hash; let block_height = block_to_migrate.height; - self.send_block_migration_message(block_to_migrate, &transactions) + self.send_block_migration_message(block_to_migrate, &transactions) .awaitThis requires
send_block_migration_messageto accept&BlockTransactions(which it already does — theArcauto-derefs).crates/chain/tests/validation/mod.rs (1)
60-79: 🧹 Nitpick | 🔵 TrivialVerify that empty
BlockTransactionsis intentional for this path.
send_block_to_block_validationsends directly to the validation service with emptyBlockTransactions. This is used only byheavy_block_validation_discards_a_block_if_its_too_old(line 1161), where the goal is testing cancellation — not transaction validation. If validation ever starts checking transactions in this path, this would silently break.A brief comment explaining the intentional empty transactions would help future readers.
🤖 Fix all issues with AI agents
In `@crates/chain/tests/api/hardfork_tests.rs`:
- Around line 839-845: Add a brief clarifying comment around the
stop/sleep/activation sequence: explain that the sleep after calling
genesis_node.stop().await is to ensure the node has fully stopped before
sampling activation_timestamp (the variable activation_timestamp = now_secs()),
and note that the test assumes blocks mined by mine_block may have timestamps
equal to activation_timestamp and that Aurora activation treats timestamps >=
activation_timestamp as active; optionally mention increasing the sleep if you
want a larger gap.
- Around line 866-893: The V1 rejection check currently reuses the same signer
that just posted a V2 commitment (see v2_signer_idx, signers, create_stake_tx,
TxVersion::V2/V1 inside the epoch loop), which is correct but unclear; either
(a) reserve a dedicated "rejection probe" signer from the signers array and use
that signer when creating the V1 probe tx so intent is explicit, or (b) add a
short clarifying comment above the V1 probe explaining that reusing the same
signer is intentional because the rejection happens at mempool validation;
update references to v2_signer_idx/signers accordingly to avoid off-by-one
signing mix-ups.
- Around line 1065-1072: The manual reconstruction of IrysNodeTest is
unnecessary—replace the explicit IrysNodeTest { node_ctx: (), cfg:
stopped_peer.cfg.clone(), temp_dir: stopped_peer.temp_dir, name:
stopped_peer.name }.start().await with calling start() directly on the
stopped_peer returned by stop(); in other words, use let peer_node =
stopped_peer.start().await; so you rely on the start() method on the
stopped_peer (IrysNodeTest<()>) rather than rebuilding the struct.
| // Step 4: Stop Both Nodes, Activate Aurora, Restart | ||
| let mut stopped_genesis = genesis_node.stop().await; | ||
|
|
||
| tokio::time::sleep(Duration::from_secs(1)).await; | ||
|
|
||
| // Set activation in the past so all new blocks are post-activation | ||
| let activation_timestamp = now_secs(); |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Verify the 1-second sleep is sufficient for the activation timestamp to be "in the past".
Line 842 sleeps for 1 second, then line 845 captures now_secs() as the activation timestamp. This means activation_timestamp equals the current time after the sleep. New blocks mined after this point will have timestamps ≥ activation_timestamp, so they should be post-activation. However, if mine_block produces a block with a timestamp exactly equal to activation_timestamp, the Aurora activation check needs to treat >= as active. This is likely the case (the test passes), but worth a brief comment clarifying the intent of the sleep — it appears to ensure genesis has fully stopped before capturing the timestamp rather than creating a gap.
🤖 Prompt for AI Agents
In `@crates/chain/tests/api/hardfork_tests.rs` around lines 839 - 845, Add a brief
clarifying comment around the stop/sleep/activation sequence: explain that the
sleep after calling genesis_node.stop().await is to ensure the node has fully
stopped before sampling activation_timestamp (the variable activation_timestamp
= now_secs()), and note that the test assumes blocks mined by mine_block may
have timestamps equal to activation_timestamp and that Aurora activation treats
timestamps >= activation_timestamp as active; optionally mention increasing the
sleep if you want a larger gap.
| let mut v2_signer_idx = NUM_EPOCHS_BEFORE_ACTIVATION * NUM_BLOCKS_IN_EPOCH; | ||
| for epoch in 0..NUM_EPOCHS_AFTER_ACTIVATION { | ||
| for block_in_epoch in 0..NUM_BLOCKS_IN_EPOCH { | ||
| let anchor = genesis_node.get_anchor().await?; | ||
| let signer = &signers[v2_signer_idx]; | ||
|
|
||
| // Post V2 commitment (V1 would be rejected now) | ||
| let v2_tx = create_stake_tx(&genesis_node, signer, TxVersion::V2).await; | ||
| genesis_node.post_commitment_tx(&v2_tx).await?; | ||
|
|
||
| // Verify V1 is rejected after activation (only check once per epoch) | ||
| if block_in_epoch == 0 { | ||
| let v1_tx = create_stake_tx(&genesis_node, signer, TxVersion::V1).await; | ||
| assert!( | ||
| genesis_node.post_commitment_tx(&v1_tx).await.is_err(), | ||
| "V1 should be rejected after Aurora activation" | ||
| ); | ||
| } | ||
|
|
||
| // Post data tx | ||
| let data = format!("post-aurora-epoch{}-block{}", epoch, block_in_epoch); | ||
| genesis_node | ||
| .post_data_tx(anchor, data.into_bytes(), signer) | ||
| .await; | ||
|
|
||
| genesis_node.mine_block().await?; | ||
| v2_signer_idx += 1; | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
V1 rejection check reuses the same signer that just posted a V2 tx — this is fine but could be clearer.
At line 878, create_stake_tx(&genesis_node, signer, TxVersion::V1) uses the same signer that already posted a V2 commitment in the same loop iteration (line 873). Since the V1 tx is expected to be rejected at the mempool (never reaching state), this works. However, a dedicated "rejection probe" signer (or a comment) would make the intent more obvious and avoid any future confusion if mempool validation ever considers per-signer state before version checks.
🤖 Prompt for AI Agents
In `@crates/chain/tests/api/hardfork_tests.rs` around lines 866 - 893, The V1
rejection check currently reuses the same signer that just posted a V2
commitment (see v2_signer_idx, signers, create_stake_tx, TxVersion::V2/V1 inside
the epoch loop), which is correct but unclear; either (a) reserve a dedicated
"rejection probe" signer from the signers array and use that signer when
creating the V1 probe tx so intent is explicit, or (b) add a short clarifying
comment above the V1 probe explaining that reusing the same signer is
intentional because the rejection happens at mempool validation; update
references to v2_signer_idx/signers accordingly to avoid off-by-one signing
mix-ups.
| let peer_node = IrysNodeTest { | ||
| node_ctx: (), | ||
| cfg: stopped_peer.cfg.clone(), | ||
| temp_dir: stopped_peer.temp_dir, | ||
| name: stopped_peer.name, | ||
| } | ||
| .start() | ||
| .await; |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, let's find the stop() method implementation
rg -n 'fn stop\(self\)' crates/chain/tests/api/hardfork_tests.rs -A 15 | head -50
# Also check IrysNodeTest struct definition
rg -n 'struct IrysNodeTest' crates/chain/tests/api/ -A 15Repository: Irys-xyz/irys
Length of output: 39
🏁 Script executed:
#!/bin/bash
# Let's read the beginning of the test file to see imports and structure
head -100 crates/chain/tests/api/hardfork_tests.rs | cat -n
# Search for IrysNodeTest definition across the entire codebase
fd . crates/chain/tests/ -name '*.rs' | head -20Repository: Irys-xyz/irys
Length of output: 4039
🏁 Script executed:
#!/bin/bash
# Find files that might contain IrysNodeTest definition
find crates/chain/tests -name '*.rs' -type f
# Search for IrysNodeTest definition
rg -n 'struct IrysNodeTest' crates/chain/tests/ -A 10
# Search for stop() method definition
rg -n 'fn stop\(' crates/chain/tests/ -B 2 -A 10Repository: Irys-xyz/irys
Length of output: 6442
🏁 Script executed:
#!/bin/bash
# Check for start() method implementations on IrysNodeTest
rg -n 'pub.*fn start\(' crates/chain/tests/utils.rs -B 2 -A 15 | head -100
# Check the exact code at lines 965 and 1065-1072 in the test file
sed -n '960,970p' crates/chain/tests/api/hardfork_tests.rs | cat -n
sed -n '1060,1075p' crates/chain/tests/api/hardfork_tests.rs | cat -nRepository: Irys-xyz/irys
Length of output: 1736
Remove manual construction — call .start() directly on stopped peer for consistency.
In Step 6 (line 965), the peer is restarted via stopped_peer.start(). The stop() method already returns IrysNodeTest<()>, which has a .start() method available. The manual construction of IrysNodeTest { node_ctx: (), cfg: stopped_peer.cfg.clone(), ... } at lines 1065-1072 is unnecessary and inconsistent with the earlier usage. Simplify to: let peer_node = stopped_peer.start().await;
🤖 Prompt for AI Agents
In `@crates/chain/tests/api/hardfork_tests.rs` around lines 1065 - 1072, The
manual reconstruction of IrysNodeTest is unnecessary—replace the explicit
IrysNodeTest { node_ctx: (), cfg: stopped_peer.cfg.clone(), temp_dir:
stopped_peer.temp_dir, name: stopped_peer.name }.start().await with calling
start() directly on the stopped_peer returned by stop(); in other words, use let
peer_node = stopped_peer.start().await; so you rely on the start() method on the
stopped_peer (IrysNodeTest<()>) rather than rebuilding the struct.
Describe the changes
Issue experienced on testnet: a commitment tx was not migrated to the database because it was not present in the mempool. The mempool had rejected the commitment tx from the block pool service because it deemed that the tx should be of commitment type v2, even tho the node was syncing historical blocks.
EmaMismatcherrors - The worst case for an unfixed restarted node is that it rejects valid blocks due to EmaMismatch and falls behind. It wouldn't produce invalid blocks or cause other nodes to reject its blocks - it would just get "stuck" rejecting the chain tip.causing transaction validation failures.
Checklist
Summary by CodeRabbit
New Features
Bug Fixes
Tests