mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-04-28 03:00:18 -04:00
doc/spec/contract/vesting: formal definitions added
This commit is contained in:
@@ -102,6 +102,9 @@
|
||||
- [Concepts](spec/contract/deploy/concepts.md)
|
||||
- [Scheme](spec/contract/deploy/scheme.md)
|
||||
- [Vesting](spec/contract/vesting/vesting.md)
|
||||
- [Concepts](spec/contract/vesting/concepts.md)
|
||||
- [Model](spec/contract/vesting/model.md)
|
||||
- [Scheme](spec/contract/vesting/scheme.md)
|
||||
|
||||
# P2P API Tutorial
|
||||
|
||||
|
||||
60
doc/src/spec/contract/vesting/concepts.md
Normal file
60
doc/src/spec/contract/vesting/concepts.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# Concepts
|
||||
|
||||
The vesting process is divided in a few steps that are outlined below:
|
||||
|
||||
* **Vest:** a vesting configuration is submitted to the blockchain.
|
||||
* **Withdraw:** vestee can withdraw some amount of their vested coin.
|
||||
* **Forfeit:** vesting authority forfeits a specific vesting.
|
||||
* **Exec:** overlay function over `Withdraw` and `Forfeit`, used as the
|
||||
spend hook binding the coins to these contract calls.
|
||||
|
||||
## Vest
|
||||
|
||||
Vesting authority submits a vesting configuration on-chain, along with
|
||||
a chained transfer call burning the to-be-vested input coins, which
|
||||
must be for the same token, and minting a new coin with their total
|
||||
amount for a shared secret address, using the contracts' `Exec` call
|
||||
spend-hook, effectivelly becoming usable only withing the vesting
|
||||
contract context.
|
||||
|
||||
> Note: There is a limitation though; the vested coin can only be
|
||||
> controlled by a single address. To overcome this, when the vesting
|
||||
> authority creates the vested coin, instead of using the recipients
|
||||
> actual address, it generates a new random one, which is shared with
|
||||
> the vestee(in a safe off-chain manner), so the coin can be controlled
|
||||
> by both parties.
|
||||
|
||||
> Note: Since vesting requires a 1-1 vested coin to config matching, it
|
||||
> means that those coins can be tracked by the vesting configuration
|
||||
> bulla. It doesn't affect rest anonimity, since it's specific to the
|
||||
> vesting contract flow and no other information can be derived by it.
|
||||
|
||||
## Withdraw
|
||||
|
||||
After some time has passed, the vestee can withdraw some amount from
|
||||
their vested coin, which is done by a chained transfer call burning the
|
||||
existing vested coin and creating two output coins; one which will be a
|
||||
normal coin for a withdrawal address, and a second one representing the
|
||||
remaining balance using the spend hook of the vesting contract. This
|
||||
call is responsible to define the available-to-withdraw amount, along
|
||||
with ensuring the chained transfer call second output correctly
|
||||
represents the remaining balance vested coin. We don't care if that
|
||||
becomes a zero value coin, since it won't be usable anymore, and we
|
||||
want all our outputs to look the same, so the final withdrawl cannot be
|
||||
tracked.
|
||||
|
||||
> Note: A medatada leak exists though; the final withdrawl cannot be
|
||||
> conventionally tracked, since nobody but the parties knows when the
|
||||
> remaining balance coin becomes a zero value one, but someone tracking
|
||||
> all the withdrawls calls for a specific vesting configuration can
|
||||
> assume that the vesting has ended, if no other withdrawl is observed
|
||||
> after some time period. Still, they can't prove that the vesting has
|
||||
> concluded without access to the vesting information and/or the shared
|
||||
> secret address.
|
||||
|
||||
## Forfeit
|
||||
|
||||
With this call, a vesting authority is able to forfeit a specific
|
||||
vesting, by removing the vesting configuration, burning the remaining
|
||||
balance vested coin and mint a new one to a recipient address, using a
|
||||
chained transfer call.
|
||||
82
doc/src/spec/contract/vesting/model.md
Normal file
82
doc/src/spec/contract/vesting/model.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# Model
|
||||
|
||||
Let $\t{Bulla}$ be defined as in the section [Bulla Commitments][1].
|
||||
|
||||
Let $ℙₚ, 𝔽ₚ, \mathcal{X}, \mathcal{Y}, \t{𝔹⁶⁴2𝔽ₚ}$ be defined as in the
|
||||
section [Pallas and Vesta][2].
|
||||
|
||||
Let $\t{Coin}$ be defined as in the section [Coin][3].
|
||||
|
||||
## Vesting Configuration
|
||||
|
||||
The vesting configuration contains the main parameters that define the
|
||||
vesting operation:
|
||||
|
||||
* The vesting authority public key $VAPK$ controls the vesting
|
||||
configuration.
|
||||
* The vestee public key $VPK$ for withdrawls.
|
||||
* The shared secret public key $SPK$ controls who can use the vested
|
||||
coin.
|
||||
* Token $t$ is the token type of the to-be-vested input coins.
|
||||
* Total $T$ is the total amount of the to-be-vested input coins.
|
||||
* Cliff $C$ is the amount unlocked at the start blockwindow $S$.
|
||||
* Start $S$ and end $E$ are the blockwindows defining when vesting
|
||||
starts and ends.
|
||||
* Blockwindow value $V$ is the amount unlocked on each blockwindow.
|
||||
|
||||
Define the vesting configuration $VC$ params:
|
||||
$$ \begin{aligned}
|
||||
\t{Params}_\t{VC}.\t{VAPK} &∈ ℙₚ \\
|
||||
\t{Params}_\t{VC}.\t{VPK} &∈ ℙₚ \\
|
||||
\t{Params}_\t{VC}.\t{SPK} &∈ ℙₚ \\
|
||||
\t{Params}_\t{VC}.τ &∈ 𝔽ₚ \\
|
||||
\t{Params}_\t{VC}.T &∈ ℕ₆₄ \\
|
||||
\t{Params}_\t{VC}.C &∈ ℕ₆₄ \\
|
||||
\t{Params}_\t{VC}.S &∈ ℕ₆₄ \\
|
||||
\t{Params}_\t{VC}.E &∈ ℕ₆₄ \\
|
||||
\t{Params}_\t{VC}.V &∈ ℕ₆₄
|
||||
\end{aligned} $$
|
||||
|
||||
```rust
|
||||
TODO: add model definition path
|
||||
```
|
||||
|
||||
$$ \t{Bulla}_\t{VC} : \t{Params}_\t{VC} × 𝔽ₚ → 𝔽ₚ $$
|
||||
$$ \begin{aligned}
|
||||
\t{Bulla}_\t{VC}(p, b_\t{VC}) = \t{Bulla}( \\
|
||||
\mathcal{X}(p.\t{VAPK}), \mathcal{Y}(p.\t{VAPK}), \\
|
||||
\mathcal{X}(p.\t{VPK}), \mathcal{Y}(p.\t{VPK}), \\
|
||||
\mathcal{X}(p.\t{SPK}), \mathcal{Y}(p.\t{SPK}), \\
|
||||
p.τ, \\
|
||||
ℕ₆₄2𝔽ₚ(p.T), \\
|
||||
ℕ₆₄2𝔽ₚ(p.C), \\
|
||||
ℕ₆₄2𝔽ₚ(p.S), \\
|
||||
ℕ₆₄2𝔽ₚ(p.E), \\
|
||||
ℕ₆₄2𝔽ₚ(p.V), \\
|
||||
b_\t{VC} \\
|
||||
)
|
||||
\end{aligned} $$
|
||||
|
||||
> Note: Since a vesting configuration bulla is derived using a random
|
||||
> blinding factor, it's safe to use the same parameters to generate
|
||||
> different vesting configurations.
|
||||
|
||||
## Blockwindow
|
||||
|
||||
Time limits on vesting configurations are expressed in 1 day windows.
|
||||
Since proofs cannot guarantee which block they get into, we therefore
|
||||
must modulo the block height a certain number which we use in the
|
||||
proofs.
|
||||
|
||||
```rust
|
||||
TODO: add definition path
|
||||
```
|
||||
|
||||
which can be used like this:
|
||||
```rust
|
||||
TODO: add usage example path
|
||||
```
|
||||
|
||||
[1]: ../../crypto-schemes.md#bulla-commitments
|
||||
[2]: ../../crypto-schemes.md#pallas-and-vesta
|
||||
[3]: ../money/model.md#coin
|
||||
305
doc/src/spec/contract/vesting/scheme.md
Normal file
305
doc/src/spec/contract/vesting/scheme.md
Normal file
@@ -0,0 +1,305 @@
|
||||
# Scheme
|
||||
|
||||
<!-- toc -->
|
||||
|
||||
Let $\t{Params}_\t{VC}, \t{Bulla}_\t{VC}$ be defined as in
|
||||
[Vesting Configuration Model](model.md).
|
||||
|
||||
Let $\t{Coin}$ be defined as in the section [Coin][1].
|
||||
|
||||
Let $ℙₚ, 𝔽ₚ, \mathcal{X}, \mathcal{Y}, \t{𝔹⁶⁴2𝔽ₚ}$ be defined as in the
|
||||
section [Pallas and Vesta][2].
|
||||
|
||||
Let $t₀ = \t{BlockWindow} ∈ 𝔽ₚ$ be the current blockwindow as defined
|
||||
in [Blockwindow](model.md#blockwindow).
|
||||
|
||||
Let $\t{PoseidonHash}$ be defined as in the section
|
||||
[PoseidonHash Function](../../crypto-schemes.md#poseidonhash-function).
|
||||
|
||||
Let $\t{ElGamal.Encrypt}, \t{ElGamalEncNote}ₖ$ be defined as in the
|
||||
section [Verifiable In-Band Secret Distribution][3].
|
||||
|
||||
Denote the Vesting contract ID by $\t{CID}_\t{V} ∈ 𝔽ₚ$ and its `Exec`
|
||||
function spend hook by $\t{SH}_\t{V} ∈ 𝔽ₚ$.
|
||||
|
||||
## Vest
|
||||
|
||||
This function creates a vesting configuration bulla $ℬ_\t{VC}$. We
|
||||
commit to the vesting configuration params and then add the bulla to
|
||||
the set, along with the vested coin $\t{Coin}$ minted by the child
|
||||
`Money::transfer()` call. Each vesting configuration keeps track of its
|
||||
minted coins, to ensure that only those can be burned in next actions,
|
||||
creating a sequence of coins, enabling the contract to keep track of
|
||||
remaining balances anonymously. Additionally, we verify the minted
|
||||
vesting coin is encrypted for the configuration shared secret key,
|
||||
ensuring both parties have access to it.
|
||||
|
||||
* Wallet builder: `TODO: add client path`
|
||||
* WASM VM code: `TODO: add entrypoint path`
|
||||
* ZK proof: `TODO: add proof path`
|
||||
|
||||
### Function Params
|
||||
|
||||
Define the vest function params
|
||||
$$ \begin{aligned}
|
||||
ℬ_\t{VC} &∈ \t{im}(\t{Bulla}_\t{VC}) \\
|
||||
\t{SPK} &∈ ℙₚ
|
||||
\end{aligned} $$
|
||||
|
||||
```rust
|
||||
TODO: Add call params path
|
||||
```
|
||||
|
||||
### Contract Statement
|
||||
|
||||
**Vesting configuration bulla uniqueness**   whether $ℬ_\t{VC}$
|
||||
already exists. If yes then fail.
|
||||
|
||||
Let there be a prover auxiliary witness inputs:
|
||||
$$ \begin{aligned}
|
||||
VAx &∈ 𝔽ₚ \\
|
||||
VPK &∈ 𝔽ₚ \\
|
||||
Sx &∈ 𝔽ₚ \\
|
||||
τ &∈ 𝔽ₚ \\
|
||||
T &∈ ℕ₆₄ \\
|
||||
C &∈ ℕ₆₄ \\
|
||||
S &∈ ℕ₆₄ \\
|
||||
E &∈ ℕ₆₄ \\
|
||||
V &∈ ℕ₆₄ \\
|
||||
b_\t{VC} &∈ 𝔽ₚ \\
|
||||
b_\t{Coin} &∈ 𝔽ₚ
|
||||
\end{aligned} $$
|
||||
|
||||
Attach a proof $π$ such that the following relations hold:
|
||||
|
||||
**Proof that start blockwindow is greater than current blockwindow**
|
||||
  $S > t₀$.
|
||||
|
||||
**Proof that end blockwindow is greater than start blockwindow**  
|
||||
$E > S$.
|
||||
|
||||
**Proof that total is greater than cliff**   $T >= C$.
|
||||
|
||||
**Proof that blockwindow value is valid**   $T == (E - S) * V +
|
||||
C$.
|
||||
|
||||
**Proof of vesting authority public key ownership**   $\t{VAPK} =
|
||||
\t{DerivePubKey}(VAx)$.
|
||||
|
||||
**Proof of shared secret public key ownership**   $\t{SPK} =
|
||||
\t{DerivePubKey}(Sx)$.
|
||||
|
||||
**Vesting configuration bulla integrity**   $ℬ =
|
||||
\t{Bulla}_\t{VC}(\mathcal{X}(p.\t{VAPK}), \mathcal{Y}(p.\t{VAPK}),
|
||||
\mathcal{X}(p.\t{VPK}), \mathcal{Y}(p.\t{VPK}),
|
||||
\mathcal{X}(p.\t{SPK}), \mathcal{Y}(p.\t{SPK}), t, T, C, S, E, V,
|
||||
b_\t{VC})$
|
||||
|
||||
**Minted vested coin integrity**   $Coin =
|
||||
\t{PoseidonHash}(\mathcal{X}(p.\t{SPK}), \mathcal{Y}(p.\t{SPK}), T, t,
|
||||
\t{CID}_\t{V}, \t{SH}_\t{V}, ℬ, b_\t{Coin})$
|
||||
|
||||
**Verifiable vested coin note encryption**  
|
||||
let $𝐧 = (c.v, c.τ, c.\t{SH}, c.\t{UD}, c.n)$, and verify
|
||||
$a = \t{ElGamal}.\t{Encrypt}(𝐧, \t{esk}, d.\t{SPK})$.
|
||||
|
||||
### Signatures
|
||||
|
||||
There should be a single signature attached, which uses
|
||||
$\t{SPK}$ as the signature public key.
|
||||
|
||||
## Withdraw
|
||||
|
||||
This function enables the vestee to withdraw the corresponding unlocked
|
||||
value up to that blockwindow. The child `Money::transfer()` call must
|
||||
contain a single input, the vested coin we burn, and two outputs. The
|
||||
first one being the withdrawed one while the second one is the
|
||||
remaining vested balance coin. Both coins values are verified by the
|
||||
vesting configuration rules, and we store the second one as the current
|
||||
vested coin, to burn in next actions. Additionally, we verify the
|
||||
second/vested coin is encrypted for the configuration shared secret
|
||||
key, ensuring both parties have access to it.
|
||||
|
||||
* Wallet builder: `TODO: add client path`
|
||||
* WASM VM code: `TODO: add entrypoint path`
|
||||
* ZK proof: `TODO: add proof path`
|
||||
|
||||
### Function Params
|
||||
|
||||
Define the withdraw function params
|
||||
$$ \begin{aligned}
|
||||
ℬ_\t{VC} &∈ \t{im}(\t{Bulla}_\t{VC}) \\
|
||||
\t{SPK} &∈ ℙₚ
|
||||
\end{aligned} $$
|
||||
|
||||
```rust
|
||||
TODO: Add call params path
|
||||
```
|
||||
|
||||
### Contract Statement
|
||||
|
||||
**Vesting configuration bulla existance**   whether $ℬ_\t{VC}$
|
||||
exists. If no then fail.
|
||||
|
||||
**Burned vested coin existance**   whether the burned coin
|
||||
$\t{BCoin}$ matches the vesting configuration record one. If no then
|
||||
fail.
|
||||
|
||||
Let there be a prover auxiliary witness inputs:
|
||||
$$ \begin{aligned}
|
||||
VAPK &∈ 𝔽ₚ \\
|
||||
Vx &∈ 𝔽ₚ \\
|
||||
Sx &∈ 𝔽ₚ \\
|
||||
τ &∈ 𝔽ₚ \\
|
||||
T &∈ ℕ₆₄ \\
|
||||
C &∈ ℕ₆₄ \\
|
||||
S &∈ ℕ₆₄ \\
|
||||
E &∈ ℕ₆₄ \\
|
||||
V &∈ ℕ₆₄ \\
|
||||
b_\t{VC} &∈ 𝔽ₚ \\
|
||||
Bv &∈ ℕ₆₄ \\
|
||||
b_\t{BCoin} &∈ 𝔽ₚ \\
|
||||
x_c &∈ 𝔽ₚ \\
|
||||
Cv &∈ ℕ₆₄ \\
|
||||
b_\t{Coin} &∈ 𝔽ₚ
|
||||
\end{aligned} $$
|
||||
|
||||
Attach a proof $π$ such that the following relations hold:
|
||||
|
||||
**Proof of vestee public key ownership**   $\t{VPK} =
|
||||
\t{DerivePubKey}(Vx)$.
|
||||
|
||||
**Proof of shared secret public key ownership**   $\t{SPK} =
|
||||
\t{DerivePubKey}(Sx)$.
|
||||
|
||||
**Vesting configuration bulla integrity**   $ℬ =
|
||||
\t{Bulla}_\t{VC}(\mathcal{X}(p.\t{VAPK}), \mathcal{Y}(p.\t{VAPK}),
|
||||
\mathcal{X}(p.\t{VPK}), \mathcal{Y}(p.\t{VPK}),
|
||||
\mathcal{X}(p.\t{SPK}), \mathcal{Y}(p.\t{SPK}), t, T, C, Cb, S, E, V,
|
||||
b_\t{VC})$
|
||||
|
||||
**Proof that current blockwindow is greater than start blockwindow**
|
||||
  $t₀ >= S$.
|
||||
|
||||
TODO: cond_select statement to pick current or end blockwindow
|
||||
|
||||
**Proof of withdraw amount correctness**  
|
||||
$$ \begin{aligned}
|
||||
CurrentBlockwindow = CondSelect(BlockwindowCond, t₀, E); \\
|
||||
BlockwindowsPassed = CurrentBlockwindow - S; \\
|
||||
Available = (BlockwindowsPassed * V) + C; \\
|
||||
Withdrawn = T - Bv; \\
|
||||
WithdrawlCoinValue = Available - Withdrawn; \\
|
||||
VestingChangeValue = T - (Withdrawn + WithdrawlCoinValue);
|
||||
\end{aligned} $$
|
||||
|
||||
Verify the child `Money::transfer()` call correctnes:
|
||||
|
||||
**Burned vested coin integrity**   $BCoin =
|
||||
\t{PoseidonHash}(\mathcal{X}(p.\t{SPK}), \mathcal{Y}(p.\t{SPK}),
|
||||
Bv, t, \t{CID}_\t{V}, \t{SH}_\t{V}, ℬ, b_\t{Coin})$
|
||||
|
||||
**Burned vested coin nullifier integrity**   $\cN =
|
||||
\t{PoseidonHash}(x_c, BCoin)$
|
||||
|
||||
**Minted vested coin integrity**   $Coin =
|
||||
\t{PoseidonHash}(\mathcal{X}(p.\t{SPK}), \mathcal{Y}(p.\t{SPK}),
|
||||
VestingChangeValue, t, \t{CID}_\t{V}, \t{SH}_\t{V}, ℬ, b_\t{Coin})$
|
||||
|
||||
**Verifiable vested coin note encryption**  
|
||||
let $𝐧 = (c.v, c.τ, c.\t{SH}, c.\t{UD}, c.n)$, and verify
|
||||
$a = \t{ElGamal}.\t{Encrypt}(𝐧, \t{esk}, d.\t{SPK})$.
|
||||
|
||||
### Signatures
|
||||
|
||||
There should be a single signature attached, which uses
|
||||
$\t{SPK}$ as the signature public key.
|
||||
|
||||
## Forfeit
|
||||
|
||||
This function enables the vesting authority to forfeit a vesting
|
||||
configuration, withdrawing the rest of vested value. The child
|
||||
`Money::transfer()` call must containg a single input, the vested coin
|
||||
we burn, and a single output, the newlly minted coin. Both coins values
|
||||
are verified by the vesting configuration rules, and we remove the
|
||||
vesting configuration bulla $ℬ_\t{VC}$ entry from the set.
|
||||
|
||||
* Wallet builder: `TODO: add client path`
|
||||
* WASM VM code: `TODO: add entrypoint path`
|
||||
* ZK proof: `TODO: add proof path`
|
||||
|
||||
### Function Params
|
||||
|
||||
Define the vest function params
|
||||
$$ \begin{aligned}
|
||||
ℬ_\t{VC} &∈ \t{im}(\t{Bulla}_\t{VC}) \\
|
||||
\t{SPK} &∈ ℙₚ
|
||||
\end{aligned} $$
|
||||
|
||||
```rust
|
||||
TODO: Add call params path
|
||||
```
|
||||
|
||||
### Contract Statement
|
||||
|
||||
**Vesting configuration bulla existance**   whether $ℬ_\t{VC}$
|
||||
exists. If no then fail.
|
||||
|
||||
**Burned vested coin existance**   whether the burned coin
|
||||
$\t{BCoin}$ matches the vesting configuration record one. If no then
|
||||
fail.
|
||||
|
||||
Let there be a prover auxiliary witness inputs:
|
||||
$$ \begin{aligned}
|
||||
VAx &∈ 𝔽ₚ \\
|
||||
VPK &∈ 𝔽ₚ \\
|
||||
Sx &∈ 𝔽ₚ \\
|
||||
τ &∈ 𝔽ₚ \\
|
||||
T &∈ ℕ₆₄ \\
|
||||
C &∈ ℕ₆₄ \\
|
||||
S &∈ ℕ₆₄ \\
|
||||
E &∈ ℕ₆₄ \\
|
||||
V &∈ ℕ₆₄ \\
|
||||
b_\t{VC} &∈ 𝔽ₚ
|
||||
Bv &∈ ℕ₆₄ \\
|
||||
b_\t{BCoin} &∈ 𝔽ₚ \\
|
||||
x_c &∈ 𝔽ₚ
|
||||
\end{aligned} $$
|
||||
|
||||
Attach a proof $π$ such that the following relations hold:
|
||||
|
||||
**Proof of vesting authority public key ownership**   $\t{VAPK} =
|
||||
\t{DerivePubKey}(VAx)$.
|
||||
|
||||
**Proof of shared secret public key ownership**   $\t{SPK} =
|
||||
\t{DerivePubKey}(Sx)$.
|
||||
|
||||
**Vesting configuration bulla integrity**   $ℬ =
|
||||
\t{Bulla}_\t{VC}(\mathcal{X}(p.\t{VAPK}), \mathcal{Y}(p.\t{VAPK}),
|
||||
\mathcal{X}(p.\t{VPK}), \mathcal{Y}(p.\t{VPK}),
|
||||
\mathcal{X}(p.\t{SPK}), \mathcal{Y}(p.\t{SPK}), t, T, C, S, E, V,
|
||||
b_\t{VC})$
|
||||
|
||||
**Proof of forfeit amount correctness**   $ForfeitValue = T - Bv$
|
||||
|
||||
Verify the child `Money::transfer()` call correctnes:
|
||||
|
||||
**Burned vested coin integrity**   $BCoin =
|
||||
\t{PoseidonHash}(\mathcal{X}(p.\t{SPK}), \mathcal{Y}(p.\t{SPK}),
|
||||
ForfeitValue, t, \t{CID}_\t{V}, \t{SH}_\t{V}, ℬ, b_\t{Coin})$
|
||||
|
||||
**Burned vested coin nullifier integrity**   $\cN =
|
||||
\t{PoseidonHash}(x_c, BCoin)$
|
||||
|
||||
**Minted coin integrity**   $Coin =
|
||||
\t{PoseidonHash}(\mathcal{X}(p.\t{SPK}), \mathcal{Y}(p.\t{SPK}),
|
||||
ForfeitValue, t, \t{CID}_\t{V}, \t{SH}_\t{V}, ℬ, b_\t{Coin})$
|
||||
|
||||
### Signatures
|
||||
|
||||
There should be a single signature attached, which uses
|
||||
$\t{SPK}$ as the signature public key.
|
||||
|
||||
[1]: ../money/model.md#coin
|
||||
[2]: ../../crypto-schemes.md#pallas-and-vesta
|
||||
[3]: ../../crypto-schemes.md#verifiable-in-band-secret-distribution
|
||||
@@ -1,67 +1,48 @@
|
||||
# Vesting
|
||||
|
||||
```
|
||||
status: draft
|
||||
```
|
||||
|
||||
## Abstract
|
||||
|
||||
We want to create a fully anonymous vesting contract, in which all the vesting information is private.
|
||||
What we need, is a vesting authority, which initially submits coins to be vested, along with the vesting
|
||||
configuration. When this happens, all input coins, which must be for the same token, get burned, and a new
|
||||
coin is minted for the vested public key, which uses the spend hook of the vesting contract, effectivelly
|
||||
becoming usable only withing the vesting contract context. After some time has passed, the vestee can
|
||||
withdraw some coins, which is done by burning the existing vested coin, and creates two output coins;
|
||||
one representing the remaining balance using the spend hook of the vesting contract and a second one
|
||||
which will be a normal coin for a withdrawal address(DAOs can be recipients too).
|
||||
This contract implements fully anonymous vesting, in which all the
|
||||
vesting information is private. Anyone can become a vesting authority,
|
||||
submitting coins to-be-vested for another user(or a DAO), the vestee.
|
||||
After some time has passed, the vestee can withdraw a chunk of the
|
||||
vested coin value. The vesting authority is also able to forfeit a
|
||||
vesting at any time, retrieving the remaining vested coin balance.
|
||||
|
||||
The vesting authority must also be able to forfeit the configured vesting. There is a limitation though;
|
||||
the vested coin can only be controlled by a single address. To overcome this, when the vesting authority
|
||||
creates the vested coin, instead of using the recipients actual address, it generates a new random one,
|
||||
which is shared with the vestee(in a safe off-chain manner), so the coin can be controlled by both parties.
|
||||
When the vestee withdraws some funds, the new remaining balance token must use the same shared secret key.
|
||||
We don't care if that becomes a zero value coin, since it won't be usable anymore, and we want all our
|
||||
outputs to look the same, so the final withdrawl cannot be tracked.
|
||||
|
||||
## Vesting configuration
|
||||
|
||||
The vesting contract uses 1 day block windows as its time measurement, similar to the DAO contract.
|
||||
|
||||
The configuration structure contains the following:
|
||||
1. auth_public_x: The vesting autority public key X coord
|
||||
2. auth_public_y: The vesting autority public key Y coord
|
||||
3. shared_public_x: The shared vestee public key X coord
|
||||
4. shared_public_y: The shared vestee public key Y coord
|
||||
5. cliff: Amount unlocked at the cliff timestamp.
|
||||
6. cliff_window: Vesting contract cliff block window.
|
||||
7. start_window: Block window when the tokens start vesting.
|
||||
8. end_window: Block window when all the tokens are fully vested.
|
||||
|
||||
The above information get hashed by poseidon to produce the VestingBulla, which is used as the on-chain
|
||||
identifier of this specific configuration.
|
||||
|
||||
## Contract calls
|
||||
|
||||
TODO: describe all the checks for each call
|
||||
|
||||
### Vest
|
||||
Vesting authority submits a vesting configuration on-chain, burns the to-be-vested input coins and mints
|
||||
a new coin with their total amount for the shared secret address, using the contracts' spend-hook.
|
||||
|
||||
### Withdraw
|
||||
This call is responsible to define the available to withdraw amount, along with checking the first output
|
||||
correctly represents the shared secret and uses contract spend-hook. In this call, input coin value
|
||||
commitment must match the addition of the output coin value commitments. Also we check the next call is a
|
||||
normal money transfer call, which executes the actual burn and mint functions.
|
||||
|
||||
### ExecWithdraw
|
||||
This is an overlay function, acting as the parent of a Withdraw and money Transfer calls combination, to
|
||||
bound them together into a single atomic action, and define the input spendhook that must be used in the
|
||||
transfer call.
|
||||
|
||||
### Forfeit
|
||||
With this call, the vest autority is able to forfeit a specific vesting, by removing the configuration,
|
||||
burning the existing vest coin and mint a new one to a recipient address, using a normal money transfer.
|
||||
Input and output coin values must be identical.
|
||||
|
||||
### ExecForfeit
|
||||
This is an overlay function, acting as the parent of a Forfeit and money Transfer calls combination, to
|
||||
bound them together into a single atomic action, and define the input spendhook that must be used in the
|
||||
transfer call.
|
||||
- [Concepts](concepts.md)
|
||||
- [Model](model.md)
|
||||
- [Scheme](scheme.md)
|
||||
|
||||
> Open questions:
|
||||
> 1. Do we need a separate cliff time? If its set thats the start time
|
||||
> so no real need to keep them both we can assume start == cliff.
|
||||
> 2. Is using the shared key for signatures safe and needed?
|
||||
> 3. Should vesting configurations be grouped by authority so is easier
|
||||
> UX to manage them?
|
||||
> 4. Is the vested coin encryption verification formula correct?
|
||||
> 5. Do we need to check both coins in withdraw transfer in the proof or
|
||||
> its fine since transfer itself enforces them?
|
||||
> 6. Vesting requires 1-1 vested coin to config matching, which means
|
||||
> vested coin is trackable as they are used during the vesting process.
|
||||
> Does that break any anonymity properties? Withdrawed coins cannot be
|
||||
> tracked, just the vested coin.
|
||||
> 7. We need to figure out a way to handle withdrawls after end
|
||||
> blockwindow has passed. We can use `cond_select` where both prover
|
||||
> and verifier pass the condition checl `current >= end` and in the
|
||||
> proof we pick current blockwindow or end blockwindow based on that.
|
||||
> But this require the verifier to know the end blockwindow, unless we
|
||||
> find a way the condition check can be done without revealing it.
|
||||
> Another option is to have an explicit `WithdrawAfterEnd` to withdraw
|
||||
> remaining balance after end blockwindow has passed. We already have
|
||||
> the metadata leak of ending tracking assumption, so perhaps its
|
||||
> worthy to sacrifice it.
|
||||
> 8. Withdrawl calcs correctness? They can also be simplified further
|
||||
> for proof optimization.
|
||||
> 9. All calls use the same parameters. Unless we need something in any
|
||||
> of them they will be the same structure in the final code.
|
||||
> 10. Do we need to check both coins in forfeit transfer in the proof or
|
||||
> its fine since transfer itself enforces them?
|
||||
|
||||
Reference in New Issue
Block a user