mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-04-28 03:00:18 -04:00
doc/arch: removed consensus contract related stuff
This commit is contained in:
@@ -32,11 +32,6 @@
|
||||
- [Anonymous assets](arch/anonymous_assets.md)
|
||||
- [Blockchain](arch/blockchain.md)
|
||||
- [Consensus](arch/consensus.md)
|
||||
- [GenesisStake](arch/consensus/genesis_stake.md)
|
||||
- [Stake](arch/consensus/stake.md)
|
||||
- [Proposal](arch/consensus/proposal.md)
|
||||
- [UnstakeRequest](arch/consensus/unstake_request.md)
|
||||
- [Unstake](arch/consensus/unstake.md)
|
||||
- [Transactions](arch/tx_lifetime.md)
|
||||
- [Smart Contracts](arch/smart_contracts.md)
|
||||
- [Bridge](arch/bridge.md)
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
# Consensus
|
||||
|
||||
To understand how the consensus smart contract works and how anonymous
|
||||
staking and unstaking is achieved, read the following chapters:
|
||||
|
||||
* [Genesis stake](consensus/genesis_stake.md)
|
||||
* [Stake](consensus/stake.md)
|
||||
* [Proposal](consensus/proposal.md)
|
||||
* [Unstake request](consensus/unstake_request.md)
|
||||
* [Unstake](consensus/unstake.md)
|
||||
|
||||
This section of the book describes how nodes participating in the DarkFi
|
||||
blockchain achieve consensus.
|
||||
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
Genesis stake
|
||||
=============
|
||||
|
||||
The `Consensus::GenesisStake` function is used for bootstrapping the
|
||||
Proof of Stake (PoS) network. Using this, we are able to create an
|
||||
initial staking coin that participates in consensus and is able to
|
||||
propose blocks. We can gather any number of these calls/transactions
|
||||
and hardcode them into a constant genesis block, so anyone is able
|
||||
to deterministically reproduce the genesis block and begin syncing
|
||||
the blockchain.
|
||||
|
||||
The parameters to execute this function are a single clear input,
|
||||
and a single anonymous output:
|
||||
|
||||
```rust,no_run,no_playground
|
||||
{{#include ../../../../src/contract/consensus/src/model.rs:ConsensusGenesisStakeParams}}
|
||||
```
|
||||
|
||||
For transparency, we use a clear input in order to show how many
|
||||
tokens are initially minted at genesis, and an anonymous output
|
||||
in order to anonymise the staker.
|
||||
|
||||
The ZK proof we use to prove the minting of the anonymous output
|
||||
is the `ConsensusMint_V1` circuit:
|
||||
|
||||
```
|
||||
{{#include ../../../../src/contract/consensus/proof/consensus_mint_v1.zk}}
|
||||
```
|
||||
|
||||
Important to note here is that in the case of genesis, this mint will
|
||||
have `epoch` set to 0 (zero) in order for these stakers to be able to
|
||||
immediately propose blocks without a grace period in order to advance
|
||||
the blockchain.
|
||||
|
||||
## Contract logic
|
||||
|
||||
### [`get_metadata()`](https://github.com/darkrenaissance/darkfi/blob/master/src/contract/consensus/src/entrypoint/genesis_stake_v1.rs#L39)
|
||||
|
||||
In the `consensus_genesis_stake_get_metadata_v1` function, we gather
|
||||
the public key used to verify the transaction signature from the clear
|
||||
input, and we extract the necessary public inputs that go into the
|
||||
`ConsensusMint_V1` proof verification.
|
||||
|
||||
### [`process_instruction()`](https://github.com/darkrenaissance/darkfi/blob/master/src/contract/consensus/src/entrypoint/genesis_stake_v1.rs#L73)
|
||||
|
||||
In the `consensus_genesis_stake_process_instruction_v1` function, we
|
||||
perform the state transition. We enforce that:
|
||||
|
||||
* The verifying slot for this function is actually the genesis slot (0)
|
||||
* The _token ID_ from the clear input is the native network token
|
||||
* The output coin was not already seen in the set of staked or unstaked coins
|
||||
* The value commitments in the clear input and anon output match
|
||||
|
||||
If these checks pass, we create a state update with the output coin:
|
||||
|
||||
```rust,no_run,no_playground
|
||||
{{#include ../../../../src/contract/consensus/src/model.rs:ConsensusGenesisStakeUpdate}}
|
||||
```
|
||||
|
||||
### [`process_update()`](https://github.com/darkrenaissance/darkfi/blob/master/src/contract/consensus/src/entrypoint/stake_v1.rs#L176)
|
||||
|
||||
For the state update, we use the `consensus_stake_process_update_v1`
|
||||
function. This will simply take the state update produced by
|
||||
`consensus_genesis_stake_process_instruction_v1` and add the coin to
|
||||
the set of seen coins in the consensus state, and append it to the
|
||||
Merkle tree of coins in the consensus Merkle tree of coins.
|
||||
@@ -1,70 +0,0 @@
|
||||
Proposal
|
||||
========
|
||||
|
||||
The `Consensus::Proposal` function is used whenever a consensus
|
||||
participant is able to produce a winning proof and wants to prove
|
||||
they're the current consensus leader and are eligible to propose a
|
||||
block. By itself, this smart contract has nothing to do with blocks
|
||||
themself, it is up to the leader to choose which transactions to
|
||||
include in the block they're proposing. The `Consensus::Proposal`
|
||||
function simply serves as a way to verify that the block proposer is
|
||||
indeed an eligible leader.
|
||||
|
||||
The parameters to execute this function are 1 anonymous input and 1
|
||||
anonymous output, and other necessary metadata. Essentially we burn
|
||||
the winning coin, and mint a new one in order to compete in further
|
||||
slots. Every time a proposer wins the leader election, they have to
|
||||
burn their competing coin, prove they're the winner, and then mint
|
||||
a new coin that includes the block reward and is eligible to compete
|
||||
in upcoming future slots.
|
||||
|
||||
|
||||
```rust,no_run,no_playground
|
||||
{{#include ../../../../src/contract/consensus/src/model.rs:ConsensusProposalParams}}
|
||||
```
|
||||
|
||||
The ZK proof we use for this is a single circuit,
|
||||
`ConsensusProposal_V1`:
|
||||
|
||||
```
|
||||
{{#include ../../../../src/contract/consensus/proof/consensus_proposal_v1.zk}}
|
||||
```
|
||||
|
||||
## Contract logic
|
||||
|
||||
### [`get_metadata()`](https://github.com/darkrenaissance/darkfi/blob/master/src/contract/consensus/src/entrypoint/proposal_v1.rs#L46)
|
||||
|
||||
In the `consensus_proposal_get_metadata_v1` function, we gather
|
||||
the necessary metadata that we use to verify the ZK proof and the
|
||||
transaction signature. Inside this function, we also verify the
|
||||
VRF proof executed by the proposer using a deterministic input and
|
||||
the proposer's revealed public key. This public key is derived from
|
||||
the input (burned) coin in ZK and is also used to sign the entire
|
||||
transaction.
|
||||
|
||||
### [`process_instruction()`](https://github.com/darkrenaissance/darkfi/blob/master/src/contract/consensus/src/entrypoint/proposal_v1.rs#L167)
|
||||
|
||||
In the `consensus_proposal_process_instruction_v1` function, we
|
||||
perform the state transition. We enforce that:
|
||||
|
||||
* The timelock of the burned coin has passed and the coin is eligible to compete
|
||||
* The Merkle inclusion proof of the burned coin is valid
|
||||
* The revealed nullifier of the burned coin has not been seen before
|
||||
* The value commitments match, this is done as `input+reward=output`
|
||||
* The newly minted coin was not seen before
|
||||
|
||||
If these checks pass, we create a state update with the burned
|
||||
nullifier and the minted coin:
|
||||
|
||||
```rust,no_run,no_playground
|
||||
{{#include ../../../../src/contract/consensus/src/model.rs:ConsensusProposalUpdate}}
|
||||
```
|
||||
|
||||
### [`process_update()`](https://github.com/darkrenaissance/darkfi/blob/master/src/contract/consensus/src/entrypoint/proposal_v1.rs#L252)
|
||||
|
||||
For the state update, we use the `consensus_proposal_process_update_v1`
|
||||
function. This takes the state update produced by
|
||||
`consensus_proposal_process_instruction_v1` and appends the new
|
||||
nullifier to the set of seen nullifiers, adds the minted coin to the
|
||||
set of coins and appends it to the Merkle tree of all coins in the
|
||||
consensus state.
|
||||
@@ -1,119 +0,0 @@
|
||||
Stake
|
||||
=====
|
||||
|
||||
The `Money::Stake` and `Consensus::Stake` functions are used in order
|
||||
to apply to become eligible for participation in the block proposal
|
||||
process, commonly known as Consensus.
|
||||
|
||||
The _Stake_ transaction consists of two contract calls, calling the
|
||||
above mentioned functions. The parameters, respectively, are:
|
||||
|
||||
```rust,no_run,no_playground
|
||||
{{#include ../../../../src/contract/money/src/model.rs:MoneyStakeParams}}
|
||||
|
||||
{{#include ../../../../src/contract/money/src/model.rs:ConsensusStakeParams}}
|
||||
```
|
||||
|
||||
These two contract calls need to happen atomically, meaning they
|
||||
should be part of a single transaction being executed on the network.
|
||||
On a high level, what is happening in the _stake_ process is burning
|
||||
a coin in the state of _Money_ and minting a coin in the state of
|
||||
_Consensus_ in order to start being able to participate in consensus
|
||||
and propose blocks.
|
||||
|
||||
The contract calls execute in sequence:
|
||||
|
||||
1. `Money::Stake`
|
||||
2. `Consensus::Stake`
|
||||
|
||||
The ZK proof we use to prove burning of the coin in _Money_ is the
|
||||
`Burn_V1` circuit:
|
||||
|
||||
```
|
||||
{{#include ../../../../src/contract/money/proof/burn_v1.zk}}
|
||||
```
|
||||
|
||||
The ZK proof we use to prove minting of the coin in _Consensus_ is the
|
||||
`ConsensusMint_V1` circuit:
|
||||
|
||||
```
|
||||
{{#include ../../../../src/contract/consensus/proof/consensus_mint_v1.zk}}
|
||||
```
|
||||
|
||||
## Contract logic
|
||||
|
||||
### [`Money::get_metadata()`](https://github.com/darkrenaissance/darkfi/blob/master/src/contract/money/src/entrypoint/stake_v1.rs#L40)
|
||||
|
||||
In the `money_stake_get_metadata_v1` function, we gather the input
|
||||
pubkey for signature verification, and extract necessary public inputs
|
||||
for verifying the money burn ZK proof.
|
||||
|
||||
### [`Money::process_instruction()`](https://github.com/darkrenaissance/darkfi/blob/master/src/contract/money/src/entrypoint/stake_v1.rs#L87)
|
||||
|
||||
In the `money_stake_process_instruction_v1` function, we perform the
|
||||
state transition. We enforce that:
|
||||
|
||||
* The input `spend_hook` is 0 (zero) (for now we don't have protocol-owned stake)
|
||||
* The input _token ID_ corresponds to the native network token (the commitment blind is revealed in the params)
|
||||
* The input coin Merkle inclusion proof is valid
|
||||
* The input nullifier was not published before
|
||||
* The next `call_idx` is a call to the `Consensus::StakeV1` function
|
||||
* The input in the params to the next function is the same as the current input
|
||||
|
||||
If these checks pass, we create a state update with the revealed
|
||||
_nullifier_:
|
||||
|
||||
```rust,no_run,no_playground
|
||||
{{#include ../../../../src/contract/money/src/model.rs:MoneyStakeUpdate}}
|
||||
```
|
||||
|
||||
### [`Money::process_update()`](https://github.com/darkrenaissance/darkfi/blob/master/src/contract/money/src/entrypoint/stake_v1.rs#L169)
|
||||
|
||||
For the _Money_ state update, we use the
|
||||
`money_stake_process_update_v1` function. This will simply append
|
||||
the revealed _nullifier_ to the existing set of nullifiers in order
|
||||
to prevent double-spending.
|
||||
|
||||
After the `Money::Stake` state transition has passed, we move on to
|
||||
executing the `Consensus::Stake` state transition. This is supposed
|
||||
to mint the new coin in the _Consensus_ state.
|
||||
|
||||
### [`Consensus::get_metadata()`](https://github.com/darkrenaissance/darkfi/blob/master/src/contract/consensus/src/entrypoint/stake_v1.rs#L41)
|
||||
|
||||
In `consensus_stake_get_metadata_v1` we grab the current epoch of
|
||||
the slot where we're executing this contract call and use it as one
|
||||
of the public inputs for the ZK proof of minting the new coin. This
|
||||
essentially serves as a timelock where we can enforce a grace period
|
||||
for this staked coin before it is able to start proposing blocks. More
|
||||
information on this can be found in the [Proposal](proposal.md) page.
|
||||
Additionally we extract the coin and the value commitment to use as
|
||||
the proof's public inputs.
|
||||
|
||||
### [`Consensus::process_instruction()`](https://github.com/darkrenaissance/darkfi/blob/master/src/contract/consensus/src/entrypoint/stake_v1.rs#L75)
|
||||
|
||||
In `consensus_stake_process_instruction_v1` we perform the state
|
||||
transition. We enforce that:
|
||||
|
||||
* The previous `call_idx` is a call to `Money::StakeV1`
|
||||
* The `Input` from the current call is the same as the `Input` from
|
||||
the previous call (essentially copying it)
|
||||
* The value commitments in the `Input` and `ConsensusOutput` match
|
||||
* The `Input` coin's Merkle inclusion proof is valid in the _Money_ state
|
||||
* The input's _nullifier_ is revealed and exists in the _Money_ state
|
||||
* The `ConsensusOutput` coin hasn't existed in the _Consensus_ state before
|
||||
* The `ConsensusOutput` coin hasn't existed in the _Unstaked Consensus_ state before
|
||||
|
||||
If these checks pass we create a state update with the minted coin
|
||||
that is now considered staked in _Consensus_:
|
||||
|
||||
```rust,no_run,no_playground
|
||||
{{#include ../../../../src/contract/money/src/model.rs:ConsensusStakeUpdate}}
|
||||
```
|
||||
|
||||
### [`Consensus::process_update()`](https://github.com/darkrenaissance/darkfi/blob/master/src/contract/consensus/src/entrypoint/stake_v1.rs#L176)
|
||||
|
||||
For the state update, we use the `consensus_stake_process_update_v1`
|
||||
function. This takes the coin from the `ConsensusOutput` and adds
|
||||
it to the set of staked coins, and appends it to the Merkle tree of
|
||||
staked coins so participants are able to create inclusion proofs in
|
||||
the future.
|
||||
@@ -1,112 +0,0 @@
|
||||
Unstake
|
||||
=======
|
||||
|
||||
The `Consensus::Unstake` and `Money::Unstake` functions are used in
|
||||
order to fully exit from the consensus participation and move back
|
||||
the staked funds into the _Money_ state.
|
||||
|
||||
The _Unstake_ transaction consists of two contract calls, calling the
|
||||
above mentioned functions. The parameters, respectively, are:
|
||||
|
||||
```rust,no_run,no_playground
|
||||
{{#include ../../../../src/contract/money/src/model.rs:ConsensusUnstakeParams}}
|
||||
|
||||
{{#include ../../../../src/contract/money/src/model.rs:MoneyUnstakeParams}}
|
||||
```
|
||||
|
||||
These two contract calls need to happen atomically, meaning they should
|
||||
be part of a single transaction being executed on the network. On a
|
||||
high level, what is happening in the _unstake_ process is burning the
|
||||
coin previously created through [`UnstakeRequest`](unstake_request.md)
|
||||
in the _Consensus_ state and minting a new coin in the _Money_ state
|
||||
where it can then again be used for other functionality outside
|
||||
of consensus.
|
||||
|
||||
The contract calls execute in sequence:
|
||||
|
||||
1. `Consensus::Unstake`
|
||||
2. `Money::Unstake`
|
||||
|
||||
The ZK proof we use to prove burning of the coin in _Consensus_ is the
|
||||
`ConsensusBurn_V1` circuit:
|
||||
|
||||
```
|
||||
{{#include ../../../../src/contract/consensus/proof/consensus_burn_v1.zk}}
|
||||
```
|
||||
|
||||
The ZK proof we use to prove minting of the coin in _Money_ is the
|
||||
`Mint_V1` circuit:
|
||||
|
||||
```
|
||||
{{#include ../../../../src/contract/money/proof/mint_v1.zk}}
|
||||
```
|
||||
|
||||
## Contract logic
|
||||
|
||||
### [`Consensus::get_metadata()`](https://github.com/darkrenaissance/darkfi/blob/master/src/contract/consensus/src/entrypoint/unstake_v1.rs#L39)
|
||||
|
||||
In the `consensus_unstake_get_metadata_v1` function, we gather the
|
||||
public inputs necessary to verify the `ConsensusBurn_V1` ZK proof,
|
||||
and additionally the public key used to verify the transaction
|
||||
signature. This pubkey is also derived and enforced in ZK.
|
||||
|
||||
### [`Consensus::process_instruction()`](https://github.com/darkrenaissance/darkfi/blob/master/src/contract/consensus/src/entrypoint/unstake_v1.rs#L84)
|
||||
|
||||
For the _Consensus_ state transition, we use the
|
||||
`consensus_unstake_process_instruction_v1` function. We enforce that:
|
||||
|
||||
* The next `call_idx` is a call to the `Money::UnstakeV1` function
|
||||
* The input in the params to the next function is the same as current input
|
||||
* The timelock from [`UnstakeRequest`](unstake_request.md) has expired
|
||||
* The input coin Merkle inclusion proof is valid
|
||||
* The input nullifier was not published before
|
||||
|
||||
If these checks pass, we create a state update with the revealed
|
||||
_nullifier_:
|
||||
|
||||
```rust,no_run,no_playground
|
||||
{{#include ../../../../src/contract/money/src/model.rs:ConsensusUnstakeUpdate}}
|
||||
```
|
||||
|
||||
### [`Consensus::process_update()`](https://github.com/darkrenaissance/darkfi/blob/master/src/contract/consensus/src/entrypoint/unstake_v1.rs#L169)
|
||||
|
||||
For the _Consensus_ state update, we use the
|
||||
`consensus_unstake_process_update_v1` function. This will simply
|
||||
append the revealed _nullifier_ to the existing set of nullifiers in
|
||||
order to prevent double-spending.
|
||||
|
||||
After the `Consensus::Unstake` state transition has passed, we move on
|
||||
to executing the `Money::Unstake` state transition. This is supposed
|
||||
to mint the new coin in the _Money_ state.
|
||||
|
||||
### [`Money::get_metadata()`](https://github.com/darkrenaissance/darkfi/blob/master/src/contract/money/src/entrypoint/unstake_v1.rs#L41)
|
||||
|
||||
In the `money_unstake_get_metadata_v1` function, we gather the public
|
||||
inputs necessary to verify the `Mint_V1` ZK proof. It is not necessary
|
||||
to grab any public keys for signature verification, as they're already
|
||||
collected in `Consensus::get_metadata()`.
|
||||
|
||||
### [`Money::process_instruction()`](https://github.com/darkrenaissance/darkfi/blob/master/src/contract/money/src/entrypoint/unstake_v1.rs#L79)
|
||||
|
||||
In the `money_unstake_process_instruction_v1` function, we perform
|
||||
the state transition. We enforce that:
|
||||
|
||||
* The previous `call_idx` is a call to the `Consensus::UnstakeV1` function
|
||||
* The token pedersen commitment is a commitment to the native network token
|
||||
* The value pedersen commitments in the input and output match
|
||||
* The input coin Merkle inclusion proof is valid for _Consensus_
|
||||
* The input nullifier was published in _Consensus_
|
||||
* The output coin was not seen before in the set of coins in _Money_
|
||||
|
||||
If these checks pass, we create a state update with the revealed
|
||||
minted coin:
|
||||
|
||||
```rust,no_run,no_playground
|
||||
{{#include ../../../../src/contract/money/src/model.rs:MoneyUnstakeUpdate}}
|
||||
```
|
||||
|
||||
### [`Money::process_update()`](https://github.com/darkrenaissance/darkfi/blob/master/src/contract/money/src/entrypoint/unstake_v1.rs#L194)
|
||||
|
||||
In `money_unstake_process_update_v1` we simply append the newly minted
|
||||
coin to the set of seen coins in _Money_, and we add it to the Merkle
|
||||
tree of coins in _Money_ so further inclusion proofs can be validated.
|
||||
@@ -1,74 +0,0 @@
|
||||
Unstake request
|
||||
===============
|
||||
|
||||
The `Consensus::UnstakeRequest` function is used when a consensus
|
||||
participant wants to exit participation and plans to unstake their
|
||||
staked coin. What the user is essentially doing here is burning
|
||||
their coin they have been using for consensus participation,
|
||||
and minting a new coin that isn't able to compete anymore, and is
|
||||
timelocked for a predefined amount of time. This new coin then has to
|
||||
wait until the timelock is expired, and then it can be used in the
|
||||
[`Unstake`](unstake.md) function in order to be redeemed back into
|
||||
the _Money_ state.
|
||||
|
||||
The parameters to execute this function are 1 anonymous input and 1
|
||||
anonymous output:
|
||||
|
||||
```rust,no_run,no_playground
|
||||
{{#include ../../../../src/contract/consensus/src/model.rs:ConsensusUnstakeRequestParams}}
|
||||
```
|
||||
|
||||
In this function, we have two ZK proofs, `ConsensusBurn_V1` and
|
||||
`ConsensusMint_V1`:
|
||||
|
||||
```
|
||||
{{#include ../../../../src/contract/consensus/proof/consensus_burn_v1.zk}}
|
||||
```
|
||||
|
||||
```
|
||||
{{#include ../../../../src/contract/consensus/proof/consensus_mint_v1.zk}}
|
||||
```
|
||||
|
||||
## Contract logic
|
||||
|
||||
### [`get_metadata()`](https://github.com/darkrenaissance/darkfi/blob/master/src/contract/consensus/src/entrypoint/unstake_request_v1.rs#L43)
|
||||
|
||||
In the `consensus_unstake_request_get_metadata_v1` function, we gather
|
||||
the public inputs necessary to verify the given ZK proofs. It's pretty
|
||||
straightforward, and more or less the same as other `get_metadata`
|
||||
functions in this smart contract.
|
||||
|
||||
### [`process_instruction()`](https://github.com/darkrenaissance/darkfi/blob/master/src/contract/consensus/src/entrypoint/unstake_request_v1.rs#L99)
|
||||
|
||||
We perform the state transition in
|
||||
`consensus_unstake_request_process_instruction_v1`. We enforce that:
|
||||
|
||||
* The timelock of the burned coin has passed and the coin is eligible for unstaking
|
||||
* The Merkle inclusion proof of the burned coin is valid
|
||||
* The revealed nullifier of the burned coin has not been seen before
|
||||
* The input and output value commitments are the same
|
||||
* The output/minted coin has not been seen before
|
||||
|
||||
When this is done, and everything passes, we create a state update
|
||||
with the burned nullifier and the minted coin. Here we use the same
|
||||
parameters like we do in [`Proposal`](proposal.md) - a nullifier and
|
||||
a coin:
|
||||
|
||||
```rust,no_run,no_playground
|
||||
{{#include ../../../../src/contract/consensus/src/model.rs:ConsensusProposalUpdate}}
|
||||
```
|
||||
|
||||
### [`process_update()`](https://github.com/darkrenaissance/darkfi/blob/master/src/contract/consensus/src/entrypoint/unstake_request_v1.rs#L174)
|
||||
|
||||
For the state update, we use the
|
||||
`consensus_unstake_request_process_update_v1`
|
||||
function. This takes the state update produced by
|
||||
`consensus_unstake_request_process_instruction_v1`. With it, we
|
||||
append the revealed nullifier to the set of seen nullifiers. The
|
||||
minted _coin_, in this case however, does _not_ get added to the
|
||||
Merkle tree of staked coins. Instead, we add it to the Merkle tree
|
||||
of **unstaked** coins where it lives in a separate state. By doing
|
||||
this, we essentially disallow the new coin to compete in consensus
|
||||
again because in that state it does not exist. It only exists in the
|
||||
unstaked state, and as such can only be operated with other functions
|
||||
that actually read from this state - namely [`Unstake`](unstake.md)
|
||||
Reference in New Issue
Block a user