Let $T$ be a transaction on the DarkFi network. Each transaction consists of multiple ordered contract calls:
$$ T = [C_1, …, C_n] $$
Associate with each contract call an operator $fC =$
contract_function. Each contract consists of arbitrary data which
is interpreted by the contract. So for example sending money to a
person, the transaction $T = [C_1]$ has a single call with $fC_1 =$
Money::Transfer. To enforce a transaction fee, we can add another
call to Money::Fee and now our transaction $T$ would have two calls:
$[C_1, C_2]$.
To move money from a DAO's treasury, we can build a transaction $T = [C_1, C_2, C_3]$ where:
Money::FeeMoney::TransferDAO::ExecThis illustrates the concept of chaining function calls together in a single transaction.
Money::TransferDenote the call data here simply by $C$. Since payments on DarkFi use the Sapling UTXO model, there are $n$ inputs $I_i$ and $m$ outputs $O_j$ in $C$. There are also $\pi_n$ input burn zero-knowledge proofs, and $\mu_m$ output mint zero-knowledge proofs.
Each input $I_i$ contains a nullifier $N_i$ which is deterministically generated from the previous output's (the output which is being spent) serial code $\rho_i$ and secret key $x_i$. The ZK burn proof $\pi_i$ states:
Outputs $O_j$ contain the public coin commitment $C_j$, a proof of their construction $\mu_j$, and corresponding value/token commitments. The unlinkability property comes from only the nullifier $N$ being revealed in inputs (while $C$ is hidden), while the coin $C$ appears in outputs (but without nullifiers). Since there is a deterministic derivation of nullifiers from $C$, you cannot double spend coins.
The ZK mint proof is simpler and consists of proving the correct construction of $C$ and the corresponding value/token commitments.
To hide amounts, both proofs export value commitments on the coin amounts. They use a commitment function with a homomorphic property:
$$ \phi : \mathbb{F} \rightarrow E $$ $$ \phi(x + y) = \phi(x) + \phi(y) $$
So to check value is preserved across inputs and outputs, it's merely sufficient to check: $$ \sum_{u_i \in U} \phi(ui) = \sum{v_j \in V} \phi(v_j) $$
DAO::ExecEarlier we mentioned that bullas/coins can contain arbitrary metadata (indicated by …). This allows us to construct the concept of protocol owned liquidity. Inside the coin we can store metadata that is checked for correctness by subsequent contract calls within the same transaction. Take for example $T = [C_1, C_2]$ mentioned earlier. We have:
Money::TransferDAO::ExecNow the contract $C_2$ will use the encrypted DAO value exported from $C_1$ in its ZK proof when attempting to debit money from the DAO treasury. This enables secure separation of contracts and also enables composability in the anonymous smart contract context.
The DAO proof states:
By sending money to the DAO's treasury, you add metadata into the coin which when spent requires additional contract calls to be present in the transaction $T$. These additional calls then enforce additional restrictions on the structure and data of $T$ such as is specified above.