DarkFi Testnet v0.3-r1 Alpha Reset

DarkFi v0.3-r1 Alpha Hardening Reset

Introducing DarkFi Testnet v0.3-r2 Alpha Hardening Reset.

After our first Alpha Hardening Reset had been running for a week, we received additional protocol exploit reports from our friends at P2Pool and an independent security researcher through informal audits. We also upgraded the underlying halo2 engine powering our smart contracts in response to the Orchard vulnerability reported by the Zcash Open Development Lab.

The impact of recent LLM releases on these security fixes cannot be overstated. LLMs assisted in identifying several of the vulnerabilities disclosed here. DarkFi is fortunate to be preparing for release during a new golden age of LLM assisted security research. Every bug found today strengthens the protocol for tomorrow.

Here's a detailed summary of the findings and fixes:

1. Under-constrained SMT position decomposition (Severity: Critical)

DarkFi provides a Sparse Merkle Tree (SMT) gadget for zero-knowledge proofs. This is used to prove that a nullifier (representing a spent coin) is not part of the tree, effectively proving that a coin has not been spent. The nullifier must map to an empty leaf in the SMT.

The gadget walks the tree using a 255-bit decomposition of the nullifier as the leaf position. This decomposition was under-constrained: the circuit only required the bits to be boolean and sum to the nullifier modulo p, but did not enforce the canonical (< p) decomposition. Since the Pallas modulus p is a 255-bit prime, for any nullifier N, the integer N + p also fits in 255 bits and reduces to N (mod p). This means bits(N+p) is an equally valid witness that walks to a different leaf, one that is always empty.

A malicious prover could supply bits(N+p) with the authentication path of that empty leaf. The recomputed root equals the genuine root, allowing the circuit to falsely certify that a nullifier is absent when it actually exists in the SMT.

The DAO vote and propose circuits were the only in-circuit consumers of this gadget, meaning a malicious user could repeatedly vote or create proposals using already spent governance coins.

The Money contract is unaffected, as it checks nullifiers host-side and does not use this gadget in-circuit.

Fix: SMT position decomposition is now constrained to be canonical (< p).

2. Unbounded fork proposal timestamps (Severity: High)

Block difficulty is computed based on the timestamps of its sequence. An attacker could spread their fork sequence block timestamps arbitrarily far into the future, collapsing difficulty and enabling cheap mining of a very long sequence. When proposed, this sequence could outrank the honest chain, forcing a reorg and invalidating honest miners' blocks extending that sequence (unless they also adjusted their timestamps).

This was possible due to a missing timestamp upper bound check on fork proposals, requiring manual miner intervention to reset nodes to the honest chain.

Fix: Block and proposal timestamps are now strictly upper-bounded in all code paths to ensure they remain within accepted limits.

3. DAO money transfer auth call positional check (Severity: High)

A holder of a DAO's exec key could create a malicious transaction providing their own coins as a decoy to pay the proposal transfer while draining the treasury. This was possible due to an erroneous check in the auth call: it did not verify that the transfer call being checked was the actual one verified by its parent exec call.

The auth call would verify the decoy call as valid (since it pays the proposal amount), while the exec call would validate the overall transaction, including the actual transfer draining the treasury with valid DAO tokens, allowing the exploit to succeed.

Fix: The DAO money transfer auth call now explicitly retrieves the transfer call from the parent exec call, binding them together.

4. DAO proposal input reuse (Severity: High)

Our previous fix enforced input uniqueness, but did not fully resolve the issue, a more sophisticated exploit remained possible.

Proposals accept input coins verified against a recent state snapshot, but did not require all inputs to be verified against the same snapshot. A malicious user could create an input proof, transfer the coin to themselves, create a new input proof using the updated snapshot, and repeat this process. By aggregating these transfers and proofs created after the snapshot limit, they could surpass the proposer threshold using the same coins.

Fix: All input proofs must now be generated using the same snapshot, ensuring they all refer to the same state.

Network reset

These exploits, along with other minor fixes, are detailed in the DarkFi repo.

Since the underlying protocol engine has changed, we've reset the network to test the new version cleanly, without technical debt from previous runs.

Node Operators:

  • Remove previous node database artifacts.
  • Update your nodes. The drk client will automatically reset to the new block sequence while preserving your generated keys.
  • Deployed contracts must be redeployed.

DarkFi Testnet v0.3-r1 alpha continued to see great adoption, with hashrate reaching 44.70 KH/s and 18 anonymous smart contracts deployed before the reset.

overview

DarkFi explorer main page

contracts

DarkFi explorer contracts page

tx_per_block_graph

DarkFi explorer stats page transactions per block graph

tx_per_block

DarkFi explorer stats page tables

Enter the Dark Forest, with even more security!

Each hardening reset demonstrates the value of open source development and public testing. The issues identified through community review were discovered early, responsibly reported, and promptly fixed, strengthening the protocol before wider deployment.

We would like to thank the P2Pool team and the independent researcher for their audits and responsible disclosures, as well as everyone who continues to participate by running nodes, mining blocks, deploying contracts, and providing feedback. Every finding, contribution, and test helps move DarkFi closer to a secure and resilient network.

Want to contribute? Join us.

About the release: This is the testnet code of the DarkFi network. It is being provided _as is_. We encourage everyone to try out the testnet and find bugs. Please send us feedback on IRC or via Codeberg. To be clear: no guarantee, representation or warranty is being made, express or implied, as to the safety or correctness of the testnet code. No assurances are given the testnet will work as intended and users may experience delays, failures, errors, omissions, financial loss or loss of transmitted information. By electing to use the testnet you are doing so at your own risk. Neither the authors and contributors to the testnet code nor any member of the DarkFi community shall be held responsible or liable in any manner. You hereby release them from all liability or responsibility for any loss of whatsoever due to the foregoing factors. We do not condone illegal or unethical behaviour. Nothing in this post should be construed as investment advice or legal advice. All testnet code is licensed under AGPL3.
Back to top
Back to dark.fi