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
drkclient 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.

DarkFi explorer main page

DarkFi explorer contracts page

DarkFi explorer stats page transactions per block graph

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.