Slushie is a protocol for private transactions within pallet-contracts
-compliant networks. It allows users to carry out private balance transfers,
thanks to advanced cryptography.
Slushie has the following functionality:
- Deposit money to the contract. This can be done in a single transaction with a fixed
amount (denoted by
N
), which has been set up during initialization (deposit size
) - Withdraw money from the contract. The
N
tokens are withdrawn through a Relayer with sending fee (denoted byf
) tokens as a fee to the Relayer address (denoted byt
) andN - f
tokens to the recipient address (denoted byA
). The withdrawal transaction is initiated by the Relayer and it pays the transaction fee that is covered byf
.
During initialization, the contract accepts N
as a parameter. Also, the contract creates a Merkle tree of height 20.
Merkle tree features:
- each non-leaf node hashes its 2 children with Poseidon Hash (denoted by
H
) - initialized with all zero leaves where each equals
blake2x256("slushie")
- stores the last 100 root values in the history array.
- for the latest Merkle tree, stores the values of nodes on the path from the last added leaf to the root that is necessary to compute the next root.
To deposit, a user:
- Generate two random 32-bit unsigned numbers nullifier (denoted by
k
), randomness (denoted byr
), and computes commitment (denoted byC
) such thatC = H(k || r)
(already implemented in CLI tool) - Send transaction with
N
tokens to contract with dataC
interpreted as 32 bytes array (for now, using polkadot.js)
If the tree is not full, the contract accepts the transaction, inserts C
into the tree as a new non-zero leaf and recalculates the path from the last added value and the latest root. The previous root is added to the history array. Also, the contract emits a "Deposited" event, which includes C
that will be used for finding the leaf index of C
(denoted by l
), computing Merkle opening (value of sister nodes on the way from leaf l
to the root R
, denoted by O(l)
) and Merkle path (path from R
to l
, denoted by p(l)
).
To withdraw a user:
- Select an
A
andf
value such thatf ≤ N
- Select an
R
among the stored ones in the Merkle tree history and computeO(l)
,p(l)
(in progress in CLI tool) - Compute nullifier hash (denoted by
h
)h = H(k)
(already implemented in CLI tool) - Generate proof (denoted by
P
) (already implemented in CLI tool) - Send a request to Relayer supplying transaction data
R
,h
,A
,f
,t
,P
. Then the Relayer makes a Withdrawal transaction to contract with supplied data (in progress)
The contract verifies the proof and uniqueness of the nullifier hash to guarantee that proof has not appeared before. If verification succeeds, it sends N − f
to A
and f
to the t
and adds h
to the list of nullifier hashes.
For proof generating and verification, Slushie uses the zero-knowledge proof scheme called PLONK.
PLONK circuit has such inputs: Private:
k
r
O(l)
p(l)
Public:
R
h
A
t
f
A
, t
, f
are included in the circuit to guarantee that provided inputs for generating proof equal to provided inputs for verification.
Also for generating proof and verification PLONK uses Public Parameters (denoted by pp
) which later will be generated during the trusted setup ceremony, but for now, it is hardcoded in the file.
Proof generation function use pp
, l
, R
, O(l)
, k
, r
, A
, t
, f
. It computes p(l)
using l
and then using pp
, Public and Private inputs, generates a proof and serialized it.
In general, the circuit has such main constraints:
A
,t
,f
are the same for generating and verifying- calculated in circuit
H(k)
, which for calculation used provided secretk
, equals to publich
- calculated in circuit
R
, which for calculation used provided secretp(l)
,O(l)
,k
,r
, equals to publicR
Proof verification function will use pp
, R
, A
, t
, f
. Using pp
and Public inputs, verify proof and then return true
in a successful case, otherwise, return false
.
Slushie provides the helper CLI tool that can be used with polkadot.js to gain access to all Slushie features. The short list of features:
- Commitment generation
- Getting leaf index
l
using commitmentC
(Command in progress) - Getting root
R
for leaf indexl
(Command in progress) - Generate Merkle opening
O(l)
forl
(Command in progress) - Proof generation using
l
,R
,O(l)
,k
,r
Slushie is currently implemented as an ink!-based smart-contract, prover library, wasm wrapper on prover library, and the CLI tool to generate proofs in the off-chain context.
Slushie uses plonk
as the ZKP system and poseidon252
as the
Pedersen hash.
Build and deploy as a normal ink!
contract.
Test normally with cargo test
.
However, tests can take some time due to proof generation. To decrease running time you can use cargo test -r --features parallel
Integration tests using polkadot.js
and substrate-contracts-node
However, tests take a long time due to proof generation through wasm.
More about integration tests here
At the moment, Slushie does not have a trusted setup.
SRS (Public Parameters) was generated using a random number generator. This method is used for testing and exploration. In this way, knowing these random values would allow anyone to generate invalid proofs which verifiers would accept.
Slushie has trusted setup ceremony in future plans.
This project was made with ❤️🔥 by 4IRE with support from Web3 Foundation