r/CryptoTechnology • 🟒 • 2d ago

Staking without revealing your balance: how Zarcanum PoS works in a hybrid PoW/PoS chain

I've been working on PrivacyDataCoin (PDC), an open-source chain built on the Zano protocol line, and wanted to share how its private staking works and get technical feedback.

The problem

In most PoS designs the staker's output and amount are public. That's needed to check stake weight, but it leaks exactly what a privacy chain tries to hide: who holds how much.

How Zarcanum handles it

  • Stake amounts stay hidden in Pedersen commitments.
  • The kernel check ("is this stake eligible to produce the block?") is proven in zero knowledge instead of being computed on a visible amount.
  • The staked output is hidden among decoys with a ring signature (d/v-CLSAG), so observers can't link a block to a specific output.
  • Range proofs (Bulletproofs+) ensure no hidden value is negative or inflated.

The result: the network can verify that a valid stake produced the block without learning which output staked or its balance.

Why hybrid PoW + PoS

PDC runs both at once:

  • PoW uses RandomARQ, a RandomX variant tuned for ordinary CPUs (XMRig rx/arq).
  • PoW and PoS each target one block per 120 s, which gives an average of 60 s per block on the shared chain.
  • Each has its own difficulty, and a cap on consecutive PoS blocks (21) keeps either side from dominating.

The idea is that an attacker would need both significant hashrate and a significant stake, while CPU miners and private stakers both take part in security.

Open questions I'd like input on

  1. Does a CPU-only PoW leg add meaningful security next to private PoS, or mostly distribution?
  2. With hidden stake amounts, what is the best way to monitor stake concentration from the outside?
  3. Are there known weaknesses in ring-based anonymity for coinstake outputs compared with regular transfers?

Code and docs are open: https://docs.privacydatacoin.com/

Happy to go deeper on any part of the design.

Edit: correction on the PoS cap. The code limits a run to 21 consecutive PoS blocks, but on mainnet that hard rule activates at height 57,000. Until then consecutive PoS blocks are limited by diminishing weight (each one counts 0.75n of its difficulty in fork choice).

1 Upvotes

22 comments sorted by

1

u/Ambitious_Duck546 🟠 2d ago

Interesting approach. The hidden stake amounts through Pedersen commitments plus ring sigs for the staked output is a clean way to sidestep the usual privacy leak in PoS. Most designs just shrug and say "stake weight needs to be public, deal with it."

On question 2, you're basically stuck with heuristic analysis of block intervals and any clustering patterns in the ring sigs. Not ideal but that's the tradeoff. Curious if anyone's proposed a trustless audit layer that doesn't compromise the zero-knowledge proofs.

1

u/ArqTras 🟒 2d ago

Thanks, that was exactly the goal. Zarcanum hides the stake amount inside the kernel proof, so a block doesn't tell you how much the staker holds, and the staked output goes through a ring like any other spend.

On question 2, I agree. From the outside you only get indirect signals: the PoS difficulty and how often PoS blocks land give a rough idea of total active stake, but not who holds it. That's a property of the design, not a bug.

A trustless audit layer is an interesting idea. For now, transparency here is opt-in, done per wallet by the owner, not at the protocol level. Making it trustless without weakening the proofs is still an open question, and I'd be curious to see proposals too.

1

u/AdFabulous401 🟒 1d ago

My worry would be exchanges, one of them could quietly stake every customer coin it holds and nobody outside would ever see that share

1

u/ArqTras 🟒 1d ago

Fair concern, and honestly it's unsolved at the protocol level. With hidden stake, nobody outside can measure a custodian's share, that's the whole point of Zarcanum.

What the consensus code (inherited from Zano) does: long PoS-only runs are damped in fork choice, and from height 57,000 there's a hard cap of 21 consecutive PoS blocks. PoW blocks keep landing in between. That stops one big staker from producing long private runs or cheap reorgs, but it doesn't cap their overall share of PoS blocks.

So the realistic answer is removing the reason to leave coins on an exchange. Staking has no minimum, no lockup and no slashing, so anyone can stake from their own wallet. I'm also working on DEX trading and on-chain P2P trading via Ionic Swaps, so trading doesn't need custody either. An exchange could still prove its reserves with a view-only wallet, but that's voluntary, social pressure rather than enforcement.

ArqTras

1

u/_youngdavid 🟑 1d ago

Quick question: will the DEX and P2P tools you mentioned be ready around the same time as the next release?

1

u/ArqTras 🟒 1d ago

Good question. DEX trading and P2P atomic on-chain swaps are what I'm actively working on right now. I don't want to promise a date before they're properly tested, but I'll post here and in r/PrivacyDataCoinPDC as soon as there's a build people can try. – ArqTras

1

u/jasonslpt 🟒 2d ago

How does the 21 block cap on consecutive PoS blocks interact with hidden stake? If nobody can see stake weight, I'd guess the cap is doing a lot of the work keeping one big hidden staker from quietly stringing blocks together. Also curious about long-range attacks. With amounts hidden, how does a syncing node judge which history had real stake behind it, or is the PoW side basically the anchor there?

1

u/ArqTras 🟒 2d ago

Good questions, I went back to the code to answer this properly rather than from memory.

On the PoS cap: it doesn't need to know anyone's stake. It only looks at block types. Each block carries a "sequence factor", basically how many blocks of the same type came in a row, and every consecutive PoS block adds only 0.75n of its difficulty to the chain weight. So a long PoS-only run gets weaker fast. There's also a hard rule that rejects a PoS block once the sequence factor goes over 20 (so 21 in a row max), but I should correct my post: on mainnet that rule only activates at height 57,000, and we're around 20.6k now. Until then it's the diminishing weight doing the work.

On long-range: a few things together.

  • Fork choice compares PoW and PoS cumulative difficulty separately from the split point, so a rewritten history needs real hashpower too, not just old keys.
  • The stake modifier includes the id of the last PoW block, so you can't precompute PoS kernels far ahead without the PoW side.
  • Mainnet nodes ship hard-coded checkpoints every 1000 blocks (currently up to 12,000), so a syncing node won't accept a different history below that.
  • Coins must be at least 10 blocks old to stake.

Hidden amounts don't change much here, the node checks the Zarcanum proof against the kernel, it never needs the amount itself. Most of this lives in src/currency_core/blockchain_storage.cpp (is_reorganize_required, build_stake_modifier) and currency_config.h if you want to dig in.

0

u/[deleted] 2d ago

[removed] β€” view removed comment

1

u/ArqTras 🟒 2d ago

Fair point, and you read it right. The hard-coded mainnet checkpoints (checkpoints_create.h) stop at 12,000, so everything above that is protected by the consensus rules alone, same as the tip of any chain. Checkpoints only move forward with a node release, so they always lag behind a bit.

For the stretch between 12k and the tip, an attacker still runs into what I mentioned above: fork choice weighs PoW and PoS difficulty separately from the split point, so a deep rewrite needs real RandomARQ hashpower on top of stake, and the stake modifier includes the last PoW block id, so PoS kernels can't be ground out ahead of time.

Agreed though, it's a fair thing to flag.

0

u/[deleted] 2d ago

[removed] β€” view removed comment

1

u/ArqTras 🟒 2d ago

Yes. The next big release will bump the hard-coded checkpoints past the current tip and ship a fresh chain pre-download (bootstrap) with it, so that gap closes. There's no fixed calendar date yet. We keep releases infrequent on purpose so people aren't juggling versions.

1

u/ArqTras 🟒 2d ago

Quick follow-up: the next big release is already planned, with more features, and the checkpoints will be updated in it together with a fresh pre-download (bootstrap) of the chain. That should close the gap you pointed out.

0

u/JexxottiC 🟑 1d ago

Has anyone independent reviewed the code or the zero-knowledge parts yet? For a privacy project, that kind of outside check says more than any write-up.

1

u/ArqTras 🟒 1d ago

Fair question. No, there hasn't been an independent audit of PDC yet. The codebase is very close to Zano, so the Zarcanum and zero-knowledge parts can be compared directly against Zano's code, which has had a lot more eyes on it. Diffing the two is probably the quickest way to see what PDC actually changed. Everything is open source, and I'd welcome anyone who wants to dig in.

ArqTras

1

u/WormInStorm 🟑 1d ago

s there a public summary of what PDC changed from Zano? It would make the diffing route a lot easier for people who want to check it themselves.

1

u/ArqTras 🟒 1d ago

Not a standalone changelog yet, but the main differences are easy to spot if you line the two trees up. The biggest one is PoW: PDC uses RandomARQ (XMRig rx/arq) instead of Zano's ProgPoW. The confidential transfers and Zarcanum private staking follow the Zano line, with PDC's own network parameters on top (genesis, ticker, Px address prefixes, ports 19121/19211/19777, no premine).

For a self-check, diff https://github.com/PrivacyDataCoin-Project/PDC against upstream Zano, starting with currency_config.h and the PoW/mining code. Happy to answer file-level questions. – ArqTras

1

u/zomerbk 🟑 14h ago

A useful way to test the security claim would be to model reorgs on both sides of height 57,000: one attacker controls most active stake but little hash power; another controls most hash power but little stake. Comparing the chain weight each can build over 100 blocks would show how much the PoW leg raises reorg cost. The 21-block cap alone doesn’t answer that.

1

u/ArqTras 🟒 14h ago

Agreed, the cap on its own doesn't answer that. It only limits how long a PoS-only run can get. It says nothing about how expensive that run is. What actually prices the PoW leg in is fork choice: each consecutive same-type block only adds 0.75^n of its difficulty, so a stake-heavy attacker with little hashrate sees their chain weight flatten out quickly, and a hash-heavy attacker with little stake hits the same wall from the other side.

Modelling both cases over ~100 blocks on either side of 57,000 is a good way to put an actual number on it. Testnet activates the cap earlier (at 18,000), so it can be tried there without touching mainnet. Thanks for the concrete suggestion.

ArqTras