Futuristic digital artwork featuring a glowing high-tech container labeled "PRT Honeypot" with robotic figures surrounding it, set against a natural landscape with mountains. Includes references to Cartesi, cybersecurity, and postmortem analysis, highlighting the intersection of blockchain, AI, and autonomous agents.

Honeypot is Dead, Long Live Honeypot

Tech/Oct 21, 2025/R&D Unit
Share:

A liveness bug froze the PRT dispute game; safety held, funds were Cartesi-owned, and the fix is now in.

Environment: Ethereum Mainnet — immutable, no admin/control keys

Rollup stack: Cartesi Rollup SDK · Cartesi Machine (rv64gc Linux) · App in C++ → RISC-V

App/VM initial hash: 0x615acc9fb8ae058d0e45c0d12fa10e1a6c9e645222c6fd94dfeda194ee427c14

Repos/Builds/Deployments (check wiki): https://github.com/cartesi/honeypot/

Core contracts:

TL;DR

This honeypot fulfilled its purpose: it revealed a liveness bug in the implementation of our PRT dispute game under real mainnet conditions while preserving safety and limiting impact to Cartesi-owned funds (≈ $1,000 CTSI).

  • We hit a liveness bug in the PRT dispute game during internal testing on mainnet.
  • Safety was preserved: no unauthorized withdrawals, no incorrect settlement.
  • Because this deployment is immutable and DaveConsensus allows only one active epoch, the stuck tournament cannot terminate, so the app is frozen and funds are locked forever (≈ $1,000 in CTSI, Cartesi Foundation funds only).
  • Root cause: a contract check for “does a match exist?” reused a content field (otherParent) as a sentinel. The dishonest claim used a Merkle subtree equal to 0x000…000, so the check incorrectly concluded the match didn’t exist and timing-out the dishonest side reverted.
  • Fix: decouple existence from content with a storage-backed phase flag, add property-based tests for zero-heavy trees, and add monitoring for “timeout revert” and “no actionable matches.”

This was an implementation bug in the PRT smart contracts; the algorithm as specified in the papers [1, 2] remains intact.

What happened

Honeypot runs the PRT (Permissionless Refereed Tournaments) fraud-proof system. PRT uses a chess-clock: if a claimant makes no move for 1 week, the other side can win by calling winMatchByTimeout.

  • We (Cartesi) intentionally submitted a fraudulent claim to test the game, 6 days after the tournament started, leaving this fraudulent claim with 1 day remaining before timeout.
  • The honest node responded with a bisect, which flipped the chess clock to the dishonest claim.
  • After 1 day with no action from the dishonest side, our watcher should have called winMatchByTimeout.
  • When simulated, the winMatchByTimeout call reverted with "match doesn't exist" error, despite the match clearly existing and being eligible for timeout. This prevented any valid move from progressing the tournament.

Because DaveConsensus manages exactly one disputed epoch at a time and opens the next epoch only after settlement, the dispute’s inability to terminate caused a permanent fail-stop for this deployment.

Impact

  • Funds affected:$1,000 worth of CTSI; Cartesi Foundation funds only.
  • Users: No third-party users were affected.
  • Safety: Preserved — no unauthorized withdrawal, no incorrect state settlement.
  • Liveness: Permanently lost for this deployment (freeze).
  • Blast radius: Confined to this honeypot instance.

Timeline (UTC + chain anchors)

Technical root cause (in brief)

PRT stores per-match state as:


struct State {
  Tree.Node otherParent; // bytes32
  Tree.Node leftNode; // bytes32
  Tree.Node rightNode; // bytes32
  uint256 runningLeafPosition;
  uint64 currentHeight;
  uint64 log2step; // const
  uint64 height; // const
}

The helper:


function exists(State memory state) internal pure returns (bool) {
    return !state.otherParent.isZero();
}

During a double bisect, otherParent is legitimately updated to a subtree from the opponent’s commitment. In our test, the dishonest claim populated its off-path child commitment with 0x000…000, a value that is syntactically admissible, though it does not correspond to any real execution trace. Our implementation mistakenly used otherParent == 0 as a sentinel for “match not created,” so calls gated by exists(...) (including winMatchByTimeout) reverted and the tournament could not terminate.

This is an implementation error (sentinel conflation) in the PRT smart contracts, not an algorithmic failure of PRT.

Why funds are locked but not lost

  • Immutable, no admin: this deployment has no multisig or escape hatches by design.
  • Single active epoch: DaveConsensus opens the next epoch only after settling the current one; the dispute cannot settle; therefore the app is permanently frozen.
  • Safety preserved: no function exists that could settle incorrectly or move funds without winning the dispute; the bug prevents termination, it does not grant a fraudulent win.

Fix & verification

Code change (principle): decouple existence from content.

  • Introduce a storage-backed phase flag (e.g., Uninitialized → Running → Resolved) or a separate created mapping set at creation and cleared on resolution.
  • Replace exists(State) with a storage read of phase == Running (or created[id] == true).
  • Audit all gates/call sites that previously relied on content values.

Testing:

  • Add a regression covering this incident sequence (honest proposal → two failed fraud attempts → valid fraud → honest bisect → timeout → winMatchByTimeout succeeds).
  • Property-based tests over Merkle trees (including zero-heavy trees); assert eventual termination and that timeout paths never revert when legal.

Monitoring:

  • Add dashboards/alerts for “no actionable matches > X hours”.

Next steps

  • Land the fix and tests in PRT, run a fresh audit pass on the dispute state machine and timeout logic.
  • Publish these artifacts and redeploy the Honeypot vNext after review.
  • Continue the bug-bounty, “financial benchmark” approach with staged increases as confidence grows.

On-chain references (full)

Subscribe to The Cartesi Newsletter

Subscribe to the newsletter to keep up with new episodes and Cartesi developments

More from Tech

Tech/Jun 19, 2025

Introducing PRT Honeypot: Cartesi’s First Rollup App with PRT Fraud-Proof System on Ethereum Mainnet

Cartesi’s PRT Honeypot is the first Rollup application equipped with the PRT fraud-proof system, putting the appchain stack to the test. It marks a key milestone toward decentralization and trustless security, aligned with L2BEAT’s standards.

Written By Marketing Unit

Tech/Nov 12, 2024

Powering the future of composable, next-gen dApps with Espresso

Discover the key benefits this collaboration will bring and explore some of the early projects already leveraging this integration to build truly innovative dApps.

Written By Marketing Unit

Tech/Oct 15, 2024

The Radically Simple Guide to: Developer Tools and Resources

Your go-to guide to understanding what Cartesi brings to developers.

Written By Marketing Unit

© 2025 The Cartesi Foundation. All rights reserved.