Преглед изворни кода

spec: add money contract with money transfer

zero пре 2 година
родитељ
комит
9ec277abee
4 измењених фајлова са 166 додато и 1 уклоњено
  1. 1 0
      doc/src/SUMMARY.md
  2. 160 0
      doc/src/spec/contracts/money.md
  3. 2 0
      src/contract/money/src/lib.rs
  4. 3 1
      src/contract/money/src/model.rs

+ 1 - 0
doc/src/SUMMARY.md

@@ -83,6 +83,7 @@
     - [Concepts](spec/contracts/dao/concepts.md)
     - [Model](spec/contracts/dao/model.md)
     - [Contract](spec/contracts/dao/contract.md)
+  - [Money](spec/contracts/money.md)
 
 # P2P API Tutorial
 

+ 160 - 0
doc/src/spec/contracts/money.md

@@ -0,0 +1,160 @@
+# Money Contract Specification
+
+The _Money_ contract implements network fees, token transfers,
+atomic swaps, token minting and freezing, and staking/unstaking of
+PoS consensus tokens.
+
+The functions/entrypoints provided by this smart contract are:
+```rust
+{{#include ../../../../src/contract/money/src/lib.rs:money-function}}
+```
+
+## `MoneyFunction::TransferV1`
+
+
+### ZK proofs
+
+#### `Mint_V1`
+
+Using the `Mint_V1` circuit, we are able to create outputs
+in our UTXO set. It is used along with the `Burn_V1` circuit in
+`MoneyFunction::TransferV1` where we perform a payment to some address
+on the network.
+
+**Circuit witnesses:**
+
+* $P$ - Public key of the recipient which goes into the coin commitment (pallas curve point)
+* $v$ - Value of the coin commitment (unsigned 64-bit integer)
+* $t$ - Token ID of the coin commitment (pallas base field element)
+* $s$ - Unique serial number of the coin commitment (pallas base field element)
+* $h$ - Spend hook, allows composing this ZK proof to invoke other contracts (pallas base field element)
+* $u$ - Data passed from this coin to the invoked contract (pallas base field element)
+* $v_{\text{blind}}$ - Random blinding factor for a Pedersen commitment to $v$ (pallas scalar field element)
+* $t_{\text{blind}}$ - Random blinding factor for a commitment to $t$ (pallas base field element)
+
+**Circuit public inputs:**
+
+* $C$ - Coin commitment
+* $V$ - Pedersen commitment to $v$
+* $T$ - Token ID commitment
+
+**Circuit:**
+
+$$ C = \text{Poseidon}(P || v || t || s || h || u) $$
+$$ \text{RangeCheck}(64, v) $$
+$$ V = vG + v_{\text{blind}}H $$
+$$ T = \text{Poseidon}(t || t_{\text{blind}}) $$
+
+$G$ and $H$ are constant well-known generators that are in the codebase
+as `VALUE_COMMIT_VALUE` and `VALUE_COMMIT_RANDOM`:
+
+
+* `src/sdk/src/crypto/constants/fixed_bases/value_commit_v.rs`
+* `src/sdk/src/crypto/constants/fixed_bases/value_commit_r.rs`
+
+### `Burn_V1`
+
+Using the `Burn_V1` circuit, we are able to create inputs in
+our UTXO set. It is used along with the `Mint_V1` circuit in
+`MoneyFunction::TransferV1` where we perform a payment to some address
+on the network.
+
+**Circuit witnesses:**
+
+* $v$ - Value of the coin being spent (unsigned 64-bit integer)
+* $t$ - Token ID of the coin being spent (pallas curve base field element)
+* $v_{\text{blind}}$ - Random blinding factor for a Pedersen commitment to $v$ (pallas scalar field element)
+* $t_{\text{blind}}$ - Random blinding factor for a commitment to $t$ (pallas base field element)
+* $s$ - Unique serial number of the coin commitment (pallas base field element)
+* $h$ - Spend hook, allows composing this ZK proof to invoke other contracts (pallas base field element)
+* $u$ - Data passed from this coin to the invoked contract (pallas base field element)
+* $u_{\text{blind}}$ - Blinding factor for encrypting $u$ (pallas base field element)
+* $x$ - Secret key used to derive $N$ (nullifier) and $P$ (public key) from the coin $C$ (pallas base field element)
+* $l$ - Leaf position of $C$ in the Merkle tree of all coin commitments (unsigned 32-bit integer)
+* $p$ - Merkle path to the coin $C$ in the Merkle tree (array of 32 pallas base field elements)
+* $z$ - Secret key used to derive public key for the tx signature $Z$
+
+**Circuit public inputs:**
+
+* $N$ - Published nullifier to prevent double spending
+* $V$ - Pedersen commitment to $v$
+* $T$ - Token ID commitment
+* $R$ - Merkle root calculated from $l$ and $p$
+* $U$ - Commitment to $u$
+* $h$ - Spend hook
+* $Z$ - Public key derived from $z$ used for transaction signing
+
+**Circuit:**
+
+$$ N = \text{Poseidon}(x || s) $$
+$$ V = vG + v_{\text{blind}}H $$
+$$ T = \text{Poseidon}(t || t_{\text{blind}}) $$
+$$ P = xK $$
+$$ C = \text{Poseidon}(P || v || t || s || h || u) $$
+$$ C' = \text{ZeroCond}(v, C) $$
+$$ R = \text{MerkleRoot}(l, p, C') $$
+$$ U = \text{Poseidon}(u, u_{\text{blind}}) $$
+$$ Z = zK $$
+
+$G$ and $H$ are the same generators used in `Mint_V1`, $K$ is the
+generator in the codebase known as `NULLIFIER_K`:
+
+* `src/sdk/src/crypto/constants/fixed_bases/nullifier_k.rs`
+
+`ZeroCond` is a conditional selection: `f(a, b) = if a == 0 {a} else {b}`.
+We use this because the Merkle tree is instantiated with a fake coin of
+value 0 and so we're able to produce dummy inputs of value 0.
+
+## Contract call creation
+
+Assuming a coin $C$ exists on the blockchain on leaf position $l$ and
+does not have a corresponding published nullifier $N$, it can be spent.
+To create the necessary proofs, Alice uses the known values of her
+coin $C$ and picks other values that are needed to create a new coin
+$C'$ that will be minted to Bob after $C$ is spent.
+
+**Values for `Burn_V1`:**
+
+1. Alice picks a random element $z$ from $F_p$ to use as the secret key
+   in order to sign the transaction.
+2. Alice picks a random element $v_{\text{blind}}$ from $F_q$ to use
+   as the blinding factor for $V$.
+3. Alice picks a random element $t_{\text{blind}}$ from $F_p$ to use
+   as the blinding factor for $T$.
+4. Alice creates the `Burn_V1` ZK proof using the existing known values
+   of her coin $C$ and the values picked above.
+
+**Values for `Mint_V1`:**
+
+1. Alice picks a random element $s$ from $F_p$ to use as a unique serial
+   number for the new coin $C'$.
+2. Alice optionally chooses a contract ID to use as $h$ or uses `ZERO`
+   if $h$ does not have to call another contract.
+3. Alice optionally chooses necessary data for $u$ or uses `ZERO`
+   if no data has to be passed.
+4. Alice chooses the corresponding $v_{\text{blind}}$ to be able to
+   enforce the Pedersen commitment correctness ($\infty + V - V'$ has
+   to evaluate to $\infty$)
+5. Alice creates the `Mint_V1` ZK proof using the existing known values
+   and the values picked above.
+
+After creating the proofs, Alice builds a transaction containing a
+number of inputs that were created with `Burn_V1` and a number of
+outputs created with `Mint_V1`.
+
+```rust
+{{#include ../../../../src/contract/money/src/model.rs:money-params}}
+```
+
+This gets encoded into the `Transaction` format and the transaction is
+signed with a Schnorr signature scheme using the $z$ secret key chosen
+in `Burn_V1`.
+
+## Contract call execution
+
+For `MoneyFunction::TransferV1`, we have the following functions, in
+order:
+
+* [`money_transfer_get_metadata_v1`](https://github.com/darkrenaissance/darkfi/blob/master/src/contract/money/src/entrypoint/transfer_v1.rs#L42)
+* [`money_transfer_process_instruction_v1`](https://github.com/darkrenaissance/darkfi/blob/master/src/contract/money/src/entrypoint/transfer_v1.rs#L106)
+* [`money_transfer_process_update_v1`](https://github.com/darkrenaissance/darkfi/blob/master/src/contract/money/src/entrypoint/transfer_v1.rs#L258)

+ 2 - 0
src/contract/money/src/lib.rs

@@ -23,6 +23,7 @@ use darkfi_sdk::error::ContractError;
 
 /// Functions available in the contract
 #[repr(u8)]
+// ANCHOR: money-function
 pub enum MoneyFunction {
     FeeV1 = 0x00,
     GenesisMintV1 = 0x01,
@@ -34,6 +35,7 @@ pub enum MoneyFunction {
     UnstakeV1 = 0x07,
     PoWRewardV1 = 0x08,
 }
+// ANCHOR_END: money-function
 
 impl TryFrom<u8> for MoneyFunction {
     type Error = ContractError;

+ 3 - 1
src/contract/money/src/model.rs

@@ -205,8 +205,9 @@ pub struct MoneyFeeUpdateV1 {
     pub fee: u64,
 }
 
-/// Parameters for `Money::Transfer` and `Money::OtcSwap`
 #[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
+// ANCHOR: money-params
+/// Parameters for `Money::Transfer` and `Money::OtcSwap`
 pub struct MoneyTransferParamsV1 {
     /// Clear inputs
     pub clear_inputs: Vec<ClearInput>,
@@ -215,6 +216,7 @@ pub struct MoneyTransferParamsV1 {
     /// Anonymous outputs
     pub outputs: Vec<Output>,
 }
+// ANCHOR_END: money-params
 
 /// State update for `Money::Transfer` and `Money::OtcSwap`
 #[derive(Clone, Debug, SerialEncodable, SerialDecodable)]