Skip to main content

Custody Tool Description

The majority of Chia Network Inc's prefarm is being held in a cold wallet, secured by a complex set of custodial rules. This document will describe the details of the custodial arrangement. A moderate level of technical proficiency is probably needed to understand the details. For a high-level overview of the custody wallet, see our blog post.

Other relevant documents:

Singleton Structure

The prefarm uses a singleton with the following features:

  1. Multisig -- required to perform actions on the singleton, where:
  • The total number of keys in the multisig is initially set to 5, which will be referred to as n for the rest of this document. n can be changed with a rekey (explained later)
  • Initially, 3 keys will be required to perform withdrawals and standard rekeys. This number will be referred to as m for the rest of this document. m can be thought of as the security level for the wallet. This variable can be modified to be as large as n. For the prefarm, it can be as small as 1, though other custodial wallets could set the minimum to a larger number
  1. Merkle root -- Chialisp puzzles representing the n keys are stored in a Merkle tree, where:
  • Puzzles representing every combination of keys, from 1 to m, are stored. If the keys are A, B, C, D and E, and m is 3, then the combinations to be stored are ABC, ABD, ABE, ACD, ACE, ADE, BCD, BCE, BDE, CDE, AB, AC, AD, AE, BC, BD, BE, CD, CE, DE, A, B, C, D and E
  • The Merkle root of this tree is curried (pre-committed) into the singleton
  • The Merkle root of a tree containing puzzles of all possible combinations for m + 1 is also curried into the singleton. This is required in case of a lock level increase (explained later). This root is recursive, in that it contains puzzles that have combinations for m + 2 committed to them, leading up to the level where m = n.
  • In order to spend a coin from this wallet, a node in the Merkle tree, along with a Proof of Inclusion, are required to be passed into the singleton's solution. The Proof of Inclusion must prove the node's existence in the current Merkle root in order for the spend to succeed
  • The Merkle tree is stored in multiple private locations. However, even if a copy is stolen, the thief will not gain access to the wallet because m digital signatures are still required (see below for a more detailed analysis)
  • The Merkle tree is generated deterministically, based on the n pubkeys. Therefore, if the Merkle tree is lost, it can be regenerated by using the n pubkeys
  1. Withdrawal Timelock -- This is a timelock on initiating a withdrawal, referred to as wt for the rest of this document. The value of wt is set upon the wallet's creation and can never be changed. It will be explained in detail below.

  2. Rekey Timelock -- This is a timelock on initiating a rekey, referred to as rt for the rest of this document. The value of rt is set upon the wallet's creation and can never be changed. It will be explained in detail below.


Singleton Settings

The singleton comes in two layers -- one permanent and one non-permanent.

Permanent layer

SettingPrefarm ValueDescription
wt30 daysWithdrawal Timelock -- When attempting to begin a withdrawal, this is the minimum number of seconds that must have elapsed since the last withdrawal, rekey or clawback.
pc90 daysPayment Clawback -- The minimum number of seconds that must elapse after initiating a withdrawal before the withdrawal can be completed. Clawbacks are possible during this window.
rt15 daysRekey Timelock -- When attempting to begin a standard rekey, this is the minimum number of seconds that must have elapsed since the last withdrawal, rekey or clawback. For a slow rekey, this amount gets added for each key less than m (in addition to the amount of time that would have been required in a standard rekey).
rc30 daysRekey Clawback -- The minimum number of seconds that must elapse after initiating a rekey before the rekey can be completed. Clawbacks are possible during this window.
sp45 daysSlow rekey Penalty -- This amount gets added to the Rekey Timelock when a slow rekey is being performed.

Non-permanent layer

SettingInitial
Value
Description
m3The initial number of pubkeys required to do a withdrawal or standard rekey
n5The maximum number of pubkeys required to do a withdrawal or standard rekey
pksA comma separated list of pubkey files that will control this money

Allowed Actions

Three separate actions are allowed on the wallet's singleton: withdrawals, rekeys (normal and slow) and lock level increases. Each of these actions will be discussed in detail in this section.

Withdrawal

This action removes money from the wallet. In order for a withdrawal to be initiated, exactly m of n signatures are required.

Two phases must be completed to perform a withdrawal: a withdrawal timelock and a drop coin.

Withdrawal Timelock

Even though this is called a timelock, it acts more like a gateway, which either allows or disallows the singleton to begin the withdrawal process. It has the following rules:

  • wt seconds must have elapsed since any actions (other than a lock level increase) have been performed on the singleton
  • The wt condition either has, or has not, been met
  • wt is part of the singleton's permanent layer, so it can never be modified

If the withdrawal timelock condition has not been met, then a withdrawal may not be initiated. If it has been met, then a withdrawal may be initiated. The next phase is the drop coin.

Drop Coin

Upon entering this phase, the singleton creates a drop coin of the amount of XCH to be withdrawn. A drop coin has several rules:

  • It has a curried XCH payout address, to where the money is flagged to be withdrawn
  • It has a payment clawback timelock, referred to as pc for the rest of this document
  • Because the drop coin cannot be spent for pc seconds, it functions as a permissionless escrow
  • The drop coin contains the same Merkle root that was used in the singleton
  • At any point before the drop coin has been spent, clawback is allowed. Clawback has the following features:
    • It cancels the withdrawal and returns the money to the custodial wallet's singleton. It uses p2_singleton to accomplish this, so there is further action needed for the funds to be absorbed into the singleton
    • It requires m of n signatures
    • The m signatures don't have to be the same as the ones that initiated the withdrawal. In other words, different people could claw back the withdrawal than those who initiated it
  • After pc seconds, if a clawback has not been performed, completion becomes possible. Completion has the following features:
    • It completes the withdrawal to a pre-specified XCH address
    • Anyone is allowed to perform a completion
    • Even though a completion can be performed by anyone, it is secure because the withdrawal address is not changeable

Rekey

This action changes the keys associated with the wallet's singleton.

k keys are required for a rekey, where:

  • For a standard rekey, k = m
  • For a slow rekey, 1 <= k < m. (By default, a slow rekey could be performed with just one key.)

Either type of rekey can also modify m and/or n if desired. We'll discuss the circumstances where each type of rekey will be performed later in this section.

Two phases must be completed to perform a rekey, an initiation timelock and a drop coin.

Initiation

Before a rekey can begin, a certain amount of time must have elapsed since the last action (other than a lock level increase) was performed on the singleton. This is a de facto gateway; the time condition either has, or has not been met.

The amount of time before the rekey can begin depends on the number of keys used. In order to calculate this time, several factors must be considered:

  • Start by calculating x, where x = m - k + 1
    • If x is 1, then it's a standard rekey (k = m)
    • If x > 1, then it's a slow rekey. x represents the gap between the k keys being used with this rekey and m + 1 (the number of keys required for a lock level increase, as explained below)
  • For a standard rekey, the initiation timelock duration is rt. As discussed above, rt is stored in the singleton and can never be changed
  • For a slow rekey, a time penalty sp seconds is automatically applied. The value of sp also can never be changed
  • The duration of the initiation timelock of a slow rekey is sp + rt*x seconds. In other words, a penalty of rt seconds gets added for each key less than m

The following table illustrates a few examples of initiation timelock lengths, for various values of m and k. For this table, rt is set to 15 days and sp is set to 45 days (these are both denominated in seconds). The table assumes that n (5) and the minimum k (1) have not been modified from their default values:

mkDaysComment
3315Standard rekey, no penalty
3275Slow rekey, sp day penalty + 2 * standard rt days
3190Slow rekey, sp day penalty + 3 * rt days
1115This is a case where, after a prior rekey, m was reduced to 1. There is no penalty, even with a single key
5390In this case, a lock level increase has been performed, so 5 of 5 keys are required to avoid a penalty
51120This is the longest possible initiation timelock duration when n is 5 and the minimum k is 1. In this case, m has been increased to 5, and 1 key is being used for the rekey
.........Other combinations for m and k are possible, but not shown here

If the singleton has been modified within the timelock's required number of seconds, then the initiation timelock condition has not been met, and a rekey may not be initiated. If the initiation timelock condition has been met, then a withdrawal may be initiated. The next phase is the drop coin. k is carried over to this phase.

Drop Coin

Upon entering this phase, the singleton creates a drop coin with 0 value. This drop coin has several rules:

  • It has a hard-coded timelock, hereafter referred to as rc
  • The same Merkle root that was used in the singleton is curried into the drop coin
  • A new Merkle root is also curried into the drop coin. After the rekey has completed, this will become the Merkle root of the puzzles representing the new keys. Therefore, the new keys, along with the new Merkle tree, must have been generated before the drop coin was created
  • At any point before the drop coin has been spent, clawback is allowed. Clawback has the following features:
    • It cancels the rekey; the custodial wallet's singleton is left unmodified
    • It requires k signatures (the number of keys that initiated the rekey)
    • The k signatures don't have to be the same as the ones that initiated the rekey. In other words, different people could claw back the rekey than those who initiated it
  • After rc seconds, if a clawback has not been performed, completion becomes possible. Completion has the following features:
    • It spends the drop coin, which creates a puzzle announcement for the singleton to use
    • It also spends the singleton, which asserts the puzzle announcement from the drop coin, and recreates itself with the new Merkle root curried in
    • m and/or n could be set to different numbers in the new singleton
    • Anyone is allowed to perform the completion
    • Even though the completion can be performed by anyone, it is secure because the new Merkle root has already been committed to

Note that because rc is hard-coded, a second rekey can't overtake a rekey that has already been initiated. If rekey A is in progress and someone attempts rekey B, then A will succeed and B will fail.

Lock Level Increase

This action increases the wallet's security (m) by 1. It has the following features:

  • m + 1 keys are required to perform a lock level increase
  • The effect is immediate -- there is no timelock
  • The keys themselves don't change
  • This action is secure because the new Merkle root has been pre-committed
  • In running this action, the singleton is spent and a new copy is created, with a new Merkle root curried in. This new root is taken from a Merkle tree containing every possible combination of keys from the new m down to 1
  • This action automatically invalidates all outstanding rekey attempts because the Merkle root has changed in the new copy of the singleton
  • This action does not invalidate outstanding withdrawal attempts

When are Normal Rekey, Slow Rekey and Lock Level Increase needed?

Normally, each of the keys will be in a Secure state. This means that the original owner still possesses the key, and no adversaries have gained access to it.

Keys have three other possible states:

  • Sniffed -- An adversary has gained access to the key. The owner still has a copy
  • Stolen -- An adversary has gained access to the key. The owner no longer has a copy
  • Lost -- The owner has lost the key. Nobody else has gained access to it

If a sufficient number of keys are sniffed, stolen or lost, there are three potential catastrophic consequences:

  • Deadlocked -- The owner and adversary each attempt to rekey and claw back those attempts. Neither side obtains the funds until the other side gives up
  • Drained -- An adversary is able to steal the funds
  • Bricked -- Nobody is able to access the funds

Any time one or more keys have been sniffed, stolen or lost, a rekey will be performed if possible.

The Merkle tree is needed to perform withdrawals, rekeys and lock level increases. Multiple copies of the tree will be kept in private locations. The assumption is that if any of the keys are sniffed, stolen or lost, the Merkle tree has been sniffed.

The following table lists the action/consequence, given the current value of m and the state of the keys:


mKeys SniffedKeys StolenKeys Lost
30-2: normal rekey
3: lock level increase to 4, normal rekey
4: lock level increase to 5, normal rekey
5: deadlocked
0-2: normal rekey
3-5: drained
0-2: normal rekey
3-4: slow rekey
5: bricked
40-3: normal rekey
4: lock level increase to 5, normal rekey
5: deadlocked
0-1: normal rekey
2: slow rekey
3-5: drained
0-1: normal rekey
2-4: slow rekey
5: bricked
50-4: normal rekey
5: deadlocked
0: normal rekey
1-2: slow rekey
3-5: drained
0: normal rekey
1-4: slow rekey
5: bricked

Source Code

The source code for the custody solution is in the internal-custody GitHub repository.

There are two configuration files, one public (for observers) and one private.

Public Configuration

An observer can track the prefarm's configuration information from prefarm_info.py, which contains the following variables:

  • launcher_id: bytes32 -- This is pre-set; the user cannot change it
  • puzzle_root: bytes32 -- This is pre-set; the user cannot change it
  • withdrawal_timelock: uint64 -- For how long must the singleton remain unspent in order to initiate a withdrawal?
  • payment_clawback_period: uint64 -- How much time do signers have to claw back a payment?
  • rekey_clawback_period: uint64 -- How much time do signers have to claw back a rekey?
  • slow_rekey_timelock: uint64 -- What is the penalty P that is applied to the singleton timelock when initiating a slow rekey?
  • rekey_increments: uint64 -- What is the timelock increment for using fewer keys?

Private Configuration

The necessary information to spend the prefarm is located in puzzle_root_construction.py. This information is considered private. However, if an attacker obtained this information, it would still be insufficient to spend the prefarm because valid signatures would be required. However, the Merkle tree would be considered sniffed, so a rekey would be required.

This code contains the following variables:

  • prefarm_info: PrefarmInfo -- The info from the public file
  • pubkey_list: List[G1Element] -- What are the set of pubkeys?
  • required_pubkeys: uint32 -- How many keys are required for payments and standard rekeys (m initially is 3 for the prefarm)?
  • maximum_pubkeys: uint32 -- What is the maximum lock level (n is 5 for the prefarm)?
  • minimum_pubkeys: uint32 -- What is the minimum number of keys required for a slow rekey (1 for the prefarm, but could be higher if so desired).
  • next_root: Optional[bytes32] -- What will the root key be in the event of a rekey?
  • filter_proofs: ProofType
  • leaf_proofs: ProofType