Overview
The Proof of Validator (PoV) scheme aims to differentiate stable validators from unstable ones within a network. Stable validators are required to prove they hold a minimum amount of stake and prove the age of their staked coins regularly. This scheme provides Sybil protection and ensures that only validators meeting the stability criteria can participate as the Mixnet and Data Availability nodes.
For representing every eligible coin for the Proof of Stake (PoS), nodes maintain in parallel a value representing the entire set of eligible coins through a Merkle root.
For the PoV, the node gives as input one coin, proving that it belongs to the set of eligible coins using the root of the MMR containing the note commitment, deriving the note peer ID from the note nullifier secret key, and verifying that the stake is enough. During this process, the coin is split afterward to enable the freezing of the stake and to propose a slashing mechanism.
Requirements
- Minimum Stake: Validators must prove they have bonded at least X units of stake. This value is a public input meaning it can be dynamically adjusted.
- Minimum Coin Age: Validators must prove their staked coin has been held for a minimum period A. In the first iteration A is 2 epochs.
- Avoid multiple identities: Coins must be staked by only one node at a time.
Protocol
Firstly, coins must privately contain information that could lead to a unique node identity. For example, something derived from their IP address or public key. That’s important to prevent Sybil attacks as the coin fields are private and only known by their owner. The PoV must guarantee that each coin is used by only one node at a time to prevent Sybil attacks. In this first version, we will derive the Peed ID from the nullifier secret key of the coin. By doing so, the derived peer ID no longer depends on the nonce, allowing coins to evolve (as long as their value remains unchanged) without altering the identity of their holder. This also eliminates the need for any modifications to the ledger representation, such as adding new fields or altering the state field. In this first version, the peer ID will be derived exactly like the nullifier but using a different domain separator.
We can also suppose that outside of the proof, the nodes verifying the PoV maintain a set of coins eligible for PoS that can be used either by the PoV or by the Proof of Leadership (PoL). Because eligible coins are either old enough or updated by a PoL/PoV. This can simply be a Merkle root or any set commitment.
For security, it’s essential to verify that the coin used in the PoV is not already spent. To do that, we need to compute the coin nullifier. For privacy reasons, we then evolve the coins modifying their nonce as in the PoL. However, before evolving the coin, we split its value between a staking coin that will be locked during the validity of the PoV and a change coin that is evolved and returned to the validator by directly including it in the global set and in the eligible set of the current epoch. If the validator misbehaves during its validator period, the validator can be slashed (in this case the evolved commitment of the staking note is erased). At the end of the validator period, the commitment is included in the eligible set and in the global set.
The proof verification can be seen as a classic UTXO transaction verified with a dedicated key.
Proof
A validator must prove that they own one coin such that its value is greater than X.
Public Inputs
- The Merkle root of the set of eligible coins.
- The threshold X.
Private Inputs
- All the fields of the coin used to produce the proof (such that its value exceeds X).
- The Merkle path of the coins.
Public Outputs
- Identity of the validator (the Peer_ID).
- The nullifier of the input coin (to verify that the coin wasn’t spent).
- The updated commitment of the staking coin and of the changing coin.
Checked constraints
- The inputted coin’s value is greater than X.
- The coin commitment belongs to the set commitment.
- The computation of the nullifier of the input coin, the split and the evolution of the staking and changing coins.
Limitation of the first version
- A user must merge their coins when the threshold X increases dynamically. They must then wait 2 epochs for the merged coin to be eligible again.
In the future, we could for example create a new transaction type to merge coins without losing their age (if every field is coherent).
Circuit results (old)
This circuit isn’t completely up to date due to uncertainties in the ledger representation but is close to the described protocol demonstrating feasibility and efficiency. The table below summarizes our first result using two different zk-friendly hash functions.
circuits | constraints | snarkJS proof | Tachyon CPU proof | proving key | witness generation |
---|---|---|---|---|---|
Poseidon2 | 8,899 | 1.523 s | 445 ms | 8.0 MB | 8 ms |
Anemoi | 4,789 | 1.259 s | 260 ms | 4.6 MB | 14 ms |
In the testnet, we will favor Poseidon2 as it’s more studied and used in the industry.
Integration
What we need
- A stable list of validators.
- A way of addressing blobs to specific sets of times. When a transaction (with a blob) is sent, it’s supposed to be dispersed as well. The set of validators when that was dispersed needs to be tracked. As we may have to keep track of evolving sets of validators for different blob sampling at different times. Basically answering: Who has what and when?
- When an epoch ends, we get the randomness and a stable set of validators from the previous epoch: a stable list of validators is a must-have from the mixnet perspective.
- We need a set of peer identification, addresses of the peer and the proof that it has a threshold of stake.
Proposal
PoVs take as input the root of the set of eligible coins. These coins are simultaneously used by the private proof-of-stake mechanism, meaning the root changes with each block due to an evolution mechanism of PoLs. Using the same set of eligible coins guarantees that only coins of a certain age can be used to become a validator. PoVs are only valid for two epochs following their inclusion. A new proof must be produced before each epoch in which a validator wishes to participate.
To bind coins to a unique validator, we propose to derive the peer ID from the coin itself. This allows for the verification of identity solely through the peer ID, meaning that the coin indirectly stores this information and we don’t need to change the coin with the peer address. The PoV then proves that a peer ID meets the minimum stake requirement and links the validator’s current address as a public input in the proof. Since public inputs are also verified to ensure the proof’s correctness, this mechanism securely associates the address with the validator. Additionally, since PoVs are only valid for two epochs, this provides sufficient protection for peers, allowing them to change their address frequently without needing to modify their coins. This ensures both security and flexibility for validators.
PoVs reveal the nullifiers of the consumed coin to ensure that it hasn’t been previously spent. To prevent information leakage about the value of the coins or their owner, the coin is split between a change note and a staked note and then evolved as in PoLs, updating the coin commitments. The published nullifier and change note commitment are then added to the global state, and the change note commitment is also added to the set of eligible coins. The stake note commitment is stored with the peer ID and its address for the time of validity of the PoV.
Since we need a stable list of validators, this list must remain unchanged throughout an entire epoch. To achieve this, consensus must be reached on the included PoVs at the beginning of each epoch. When an epoch begins, the validator list consists of peer IDs that were proven valid by PoVs included in the blocks of the last 2 epochs. To reach consensus on the validator list, PoVs that were included late in each epoch and may not have been fully confirmed/received by all nodes will be discarded. For example, if n slots are required for finalization and for having all connections ready, PoVs included in the last n slots of an epoch will not be used in forming the validator list. The value of n is currently the maximum between the finalization time defined as the finalization of the snapshot of the nonce for each epoch in Cryptarchia, which is \frac{3k}{f}, and the time needed to have all connections ready. This ensures that the list remains stable and prevents any late modifications. We propose making the proofs for the last n blocks invalid, encouraging nodes not to include PoV in them.
The process followed by the nodes a PoV is inserted in the blockchain is summarized in the following diagram:
Answering initial questions
- What is the sync period? Since the finality period is currently long enough, we assume that the nodes have sufficient time to synchronize. However, in a future scenario where the finality period becomes shorter and is no longer sufficient for synchronization, we would then define n as the minimum time required by the network to achieve synchronization.
- Who has what and when? Everything would be easily accessible on-chain looking into the history of PoV.