Skip to content

assert_eq!(bundle_block_height, block_height - 1) panics the net task when an M3 lands more than one block after the bundle #146

Description

@Coinelius

Repo: LayerTwo-Labs/coinshift-rs
File: lib/state/two_way_peg_data.rs:122, in connect_withdrawal_bundle_submitted

Summary

The assert requires a bundle's WithdrawalBundleSubmitted event to arrive exactly one sidechain block after the block that created the bundle. That only holds if the M3 reaches the mainchain in the immediately following block, which a sidechain operator generally cannot arrange.

M3 is a coinbase message. The block producer writes it from its own enforcer's database — BroadcastWithdrawalBundle is a local gRPC call that never propagates. Unlike M8, which is an ordinary mainchain transaction that any miner's enforcer picks up from the mempool, an M3 reaches the chain only when the sidechain operator themself mines a mainchain block. For an operator who is not the dominant miner, that is an arbitrary number of blocks later, and the assert fires.

Impact

The assert panics a tokio-rt-worker. The process does not exit, so systemd still reports the unit active, but the net task is gone. From that point the node:

  • has zero peers, permanently
  • never connects another tip — every subsequent BMM ends in net task error: Send new tip ready failed: send failed because receiver is gone
  • keeps broadcasting M8 bids that keep winning on L1, producing mainchain commitments for sidechain blocks it then drops

There is no crash, no restart, and no unhealthy status. The node looks fine and is silently dead.

Observed on eCash alphanet, slot 255

  • bundle created at sidechain height 11
  • M3 mined into mainchain block 996594 (2026-08-30 04:52 UTC), m6id f3c3a7d7819bf66d68ccaf56b4b51ae82d98a0113df5d730389d7fada4399785
  • by then the sidechain was at height 25, so block_height - 1 = 24 ≠ 11
Aug 30 05:22:04 coinshift_app: thread 'tokio-rt-worker' panicked at lib/state/two_way_peg_data.rs:122:9:
assertion `left == right` failed
  left: 11
 right: 24

Aug 30 05:38:21 ERROR coinshift::node::net_task: lib/node/net_task.rs:1405:
  Net task finished (before or during send) - channel receiver is gone.
Aug 30 05:38:21 ERROR coinshift_app::app: app/app.rs:1212:
  net task error: Send new tip ready failed: send failed because receiver is gone

The node then sat at height 25 for two days while its bidder kept winning slot-255 BMM in nearly every mainchain block from 996566 through 996672 — roughly 100 commitments for blocks that never connected.

Reproduction

  1. Run a sidechain node and create a withdrawal bundle.
  2. Do not mine the mainchain block immediately following the sidechain block that created it.
  3. Mine a later mainchain block carrying the M3.
  4. The net task panics and the node is stuck at that height for the life of the process.

Suggested fix

bundle_block_height is not read anywhere else in the function — every line after the assert uses block_height. Downgrading it to a warning is enough, and it also removes an underflow panic when block_height == 0:

if bundle_block_height != block_height.saturating_sub(1) {
    tracing::warn!(
        %bundle_block_height,
        %block_height,
        %m6id,
        "Withdrawal bundle submitted later than the block after the one that created it"
    );
}

If the one-block relationship is genuinely meant to be enforced, it should be a validation error returned to the caller rather than an assert on a worker thread. A panic there takes networking down for the rest of the process's life with no signal to the operator.

Version

main at 1b8d1d6 still carries the assert at line 122. Seen on 8dd148c (coinshift-rs 0.14.0-alpha) running against a bip300301_enforcer on eCash alphanet. Thunder and Truthcoin carry no equivalent assert in their two_way_peg_data.rs.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions