unstake.md 4.6 KB

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:

{{#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 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()

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()

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 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:

{{#include ../../../../src/contract/money/src/model.rs:ConsensusUnstakeUpdate}}

Consensus::process_update()

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()

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()

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:

{{#include ../../../../src/contract/money/src/model.rs:MoneyUnstakeUpdate}}

Money::process_update()

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.