July 14, 2024
In this article, we use the technical preliminaries introduced in the last article to construct Babylon’s trustless and non-custodial Bitcoin staking contract step-by-step.
🔸For Bitcoin to be a legit staking asset, it should at least be slashable and lockable, have a slashable unbonding period, and be delegatable.
🔸Bitcoins are represented as UTXOs. A Bitcoin holder can have multiple UTXOs and specify different spending conditions on different UTXOs using BTC scripts.
🔸The Schnorr signature algorithm supported by BTC:
Now, let’s assume a staker named Alice wants to stake 1 Bitcoin. To do so, she should create a UTXO worth 1 Bitcoin and specify its spending conditions through a Bitcoin script. This is effectively a staking contract. We will construct it step-by-step and also version it for easy indexing.
To achieve locking, Alice can simply apply a time lock to her staking UTXO:
// Contract V0: adding a locking condition to Alice's staking UTXO
condition-1 (locking): time_lock = 1000 & alice_public_key
In the above contract, Alice locks her Bitcoin for 1000 Bitcoin blocks (about one week). Upon the expiration of the time lock, only Alice can spend it.
This simple condition provides a guaranteed exit for the staker. As long as the BTC network is live, Alice can get her 1 Bitcoin back even if everything else in the world goes offline.
To achieve slashing, Alice needs to create a Schnorr key pair as her EOTS key pair [eots_secret_key, eots_public_key]. She then creates a new spending condition in the staking contract, and put the eots_public_key there:
// Contract V1: adding naive slashing
condition-1 (locking): time_lock = 1000 & alice_public_key; OR
condition-2 (slashing): alice_eots_public_key
Alice can now use this EOTS key pair to provide attestations. For example, to cast her vote for a PoS chain-X at block height-1000:
Now, if Alice is malicous and wants to fork chain-X at height-1000, she will have to vote for another block-1000 using the same secret_nonce and eots_secret_key. Then whoever sees her both votes can decrypt her eots_secret_key and use condition-2 above to withdraw Alice’s 1 Bitcoin, effectively causing Alice to be slashed.
In general, the above contract and the pre-declaration requirement, enables an anti-equivocation mechanism. If Alice equivocates in any types of decision making, her 1 Bitcoin can be slashed.
“Wait a second, this mechanism is very buggy!” You may immediately recognize. Yes, it is just the beginning.
Let’s continue and fix it.
A big problem of contract V1 is that the slashing condition only requires Alice’s EOTS signature. Therefore, Alice can withdraw her 1 Bitcoin anytime via this condition to dodge slashing.
To solve this problem, we must ensure that when the staking UTXO is spent via the slashing condition, the staked Bitcoin can only be sent to a pre-defined burn address rather than to Alice. A sample burn address is 0000…0000; no one knows the corresponding secret key, so funds in this address are not spendable.
Remember the mom figure in Article-2 who ensured that Alice could only send her Bitcoin to her dad before the time lock expired? The same mechanism applies here.
// Contract V2
condition-1 (locking): time_lock = 1000 & alice_public_key; OR
condition-2 (slashing): alice_eots_public_key & covenant_committee_quorum
// Slashing transaction V0
inputs:
- input-1: the staking UTXO, spent using condition-2 above
outputs:
- output-1: value = 0.99 Bitcoin, owner = `0000...0000`
// Pre-approval V0: enforcing burning
//
// Covenant committee pre-signs the above slashing tx as its pre-approval
Here we add a covenant committee quorum to the slashing condition, which is an M-out-of-N multi-signature. This means the UTXO is only spendable if at least M out of a committee of N covenant members agree to it.
To enforce the burn address, the covenant committee will only sign a slashing transaction that sends Alice’s 1 Bitcoin to the pre-defined burn address. This way, Alice cannot withdraw her 1 Bitcoin and dodge slashing. For this reason, the members should be chosen from the entities that don’t want Alice to dodge slashing, such as the PoS systems’ foundations.
Indeed, since Alice’s EOTS key is required in the slashing condition, except for enforcing burn address, there is no way the covenant committee can act against Alice:
Thus, Alice does not need to trust the covenant committee at all. Even a compromised covenant committee cannot harm Alice. The only thing it can do is to help Alice withdraw her stake her way.
Delegation looks intuitive. Alice can put a third-party validator’s EOTS public key in the slashing condition. This way, if the validator is malicious, Alice can be slashed.
However, from Alice’s perspective, such delegation is not safe. The delegated validator and the covenant committee may collude and steal her Bitcoin.
This problem is simple to solve. Alice can play the mom figure in Article-2 against the validator and the covenant committee by adding her own ordinary public key (not an EOTS one) to the slashing condition, too. Alice will only pre-sign a slashing transaction that sends her 1 Bitcoin to the pre-defined burn address, leaving no way for the delegated validator and covenant committee to collude and steal her stake.
// Contract V3: enabling safe delegation
condition-1 (locking): time_lock = 1000 & alice_public_key; OR
condition-2 (slashing): alice_public_key & validator_eots_public_key & covenant_committee_quorum
// Slashing transaction V0
inputs:
- input-1: the staking UTXO, spent using condition-2 above
outputs:
- output-1: value = 0.99 Bitcoin, owner = `0000...0000`
// Pre-approval V1
//
// Alice pre-signs the slashing tx as her pre-approval.
//
// Covenant committee pre-signs the slashing tx as its pre-approval.
We have now completed the slashing condition of the staking contract. By involving three sets of signatures (staker, validator, covenant committee) in the slashing condition and asking the staker and covenant committee to only sign the slashing transaction that burns the stake, the system ensures that:
“Something still seems wrong…” You might feel.
Yes, you are sharp! There is a final piece of the puzzle: atomic slashing.
In the case of delegation, once Alice and the covenant committee give their pre-approval, the only signature needed to trigger the slashing condition is the delegated validator’s signature. If the validator goes rogue and has a beef with Alice, it can simply sign the slashing transaction and send it to BTC to get Alice slashed alone. No other stakes delegated to this validator will be affected.
Adaptor signature comes to the rescue. Alice can further encrypt her signature using the delegated validator’s EOTS public key and use the resulting adaptor signature as her pre-approval. This way, to target Alice, the validator will have to decrypt the adaptor signature using its own EOTS secret key, which will cause its EOTS secret key to be extracted. Once extracted, all the stakes delegated to it become slashable, including its self-delegated stake. Atomic slashing is thus achieved.
We can also request the covenant committee to sign the adaptor signature. This way, even if Alice is unable to generate adaptor signatures (e.g., due to a lack of wallet support), atomic slashing can still be achieved.
// Contract V3
condition-1 (locking): time_lock = 1000 & alice_public_key; OR
condition-2 (slashing): alice_public_key & validator_eots_public_key & covenant_committee_quorum
// Slashing transaction V0
inputs:
- input-1: the staking UTXO, spent using condition-2 above
outputs:
- output-1: value = 0.99 Bitcoin, owner = `0000...0000`
// Pre-approval V2: enforcing atomic slashing when delegate
//
// Alice's pre-approval is an adaptor signature of the slashing tx
// she generated using the validator's EOTS public key.
//
// Covenant committee pre-signs the slashing tx as its pre-approval.
Slashing the entire stake sounds scary. Luckily, many PoS systems support partial slashing, in which only a small portion of each stake is slashed, and the rest is returned to the staker.
The refund comes at a delay to mitigate the re-entry attack, which is most prominent when the slashing portion is small. More specifically, malicious stakers could use the slashing mechanism as the exit to get its fund back and then immediately re-enter the system as a staker and attack again. A refund delay will significantly slow down such attacks.
Enabling partial slashing is simple in Bitcoin staking. Instead of having only one output that sends the entire stake to burn, the slashing transaction can have two outputs: one sends a portion of the stake to the burn address and the other returns the rest to Alice with a pre-defined time lock.
Below is an example where the slashing percentage is 10% and the refud delay is 500 BTC blocks. 0.9 Bitcoin should be returned to Alice after 500 BTC blocks. The 0.01 Bitcoin surplus there is the transaction fee.
// Contract V3
condition-1 (locking): time_lock = 1000 & alice_public_key; OR
condition-2 (slashing): alice_public_key & validator_eots_public_key & covenant_committee_quorum
// Slashing transaction V1: enabling partial slashing
inputs:
- input-1: the staking UTXO, spent using condition-2 above
outputs:
- output-1: value = 0.09 Bitcoin, owner = `0000...0000`
- output-2: value = 0.9 Bitcoin,
conditions:
- condition-1: time_lock = 500 & alice_public_key
// Pre-approval V2
//
// Alice's pre-approval is an adaptor signature of the slashing tx
// she generated using the validator's EOTS public key.
//
// Covenant committee pre-signs the slashing tx as its pre-approval.
Restaking means Alice (or her delegated validator) uses her 1 Bitcoin to participate in multiple PoS systems at the same time. If Alice (or her delegated validator) attacks any one of them, she will be slashed and removed from all of them.
A naive solution is for Alice (or her delegated validator) to use the same EOTS key to participate in all the interested PoS systems. This achieves the restaking slashing goal without changing the staking contract. However, this is extremely unsafe and highly unrecommended by the validator community because the accidental leakage of one single secret key will jeopardise all the delegated stakes of this validator across all the PoS systems secured by it.
Therefore, every validator should use a different EOTS key for each different PoS system. For example, if three validators validate four PoS systems, then there should be 12 EOTS keys.
Then, for Alice, restaking becomes simply choosing one EOTS key per PoS system she wants to restake to. To achieve this, we only need to extend the EOTS key field in the staking contract from accepting one key to accepting a list of keys and specify that the EOTS condition is met as long as any one of the keys signs.
// Contract V4: Enabling restaking
condition-1 (locking): time_lock = 1000 & alice_public_key; OR
condition-2 (slashing): alice_public_key
& any signature from list [validator_eots_public_key]
& covenant_committee_quorum
// Slashing transaction V1
inputs:
- input-1: the staking UTXO, spent using condition-2 above
outputs:
- output-1: value = 0.09 Bitcoin, owner = `0000...0000`
- output-2: value = 0.9 Bitcoin,
conditions:
- condition-1: time_lock = 500 & alice_public_key
// Pre-approval V2
//
// Alice's pre-approval is an adaptor signature of the slashing tx
// she generated using the validator's EOTS public key.
//
// Covenant committee pre-signs the slashing tx as its pre-approval.
Article-2 explained that for stake unbonding to be secure, there should be an unbonding period, during which the stake must still be locked and slashable.
To achieve this, we need to turn Alice’s current stake (which has a long time lock) into a new stake (which has a shorter time lock equal to the unbonding time) and request Alice to pre-sign the corresponding new slashing transaction.
In addition, to enforce the above correct unbonding, we need to add a third spending condition (the unbonding condition) to the staking UTXO, which requires Alice’s and the covenant committee’s signatures.
More specifically:
// Contract V5: Enabling on-demand unbonding
condition-1 (locking): time_lock = 1000 & alice_public_key; OR
condition-2 (slashing): alice_public_key
& any signature from list [validator_eots_public_key]
& covenant_committee_quorum
condition-3 (unbonding): alice_public_key & covenant_committee_quorum
// Slashing transaction V1
inputs:
- input-1: the staking UTXO, spent using condition-2 above
outputs:
- output-1: value = 0.09 Bitcoin, owner = `0000...0000`
- output-2: value = 0.9 Bitcoin,
conditions:
- condition-1: time_lock = 500 & alice_public_key
// Pre-approval V2
//
// Alice's pre-approval is an adaptor signature of the slashing tx
// she generated using the validator's EOTS public key.
//
// Covenant committee pre-signs the slashing tx as its pre-approval.
// Unbonding transaction
inputs:
input-1: the staking UTXO, spent using condition-3 above
outputs:
output-1: value = 0.99 Bitcoin
conditions:
- condition-1 (locking): time_lock = 500 & alice_public_key; OR
- condition-2 (slashing): alice_public_key
& any signature from list [validator_eots_public_key]
& covenant_committee_quorum
// Slashing transaction of the unbonding transaction
inputs:
- input-1: the unbonding UTXO, spent using condition-2 above
outputs:
- output-1: value = 0.08 Bitcoin, owner = `0000...0000`
- output-2: value = 0.9 Bitcoin,
conditions:
- condition-1: time_lock = 500 & alice_public_key
Woohoo! This is super intense, but we made it! We constructed a contract that turns Bitcoin into a first-class staking asset with all the properties one could wish for a staking asset: trustless, self-custodial, lockable, slashable (including partial slashing and atomic slashing), delegatable, restakable, and on-demand unbondable!
Table of Contents
1. Recap
2. Locking
3. Slashing
3.1 Naïve Slashing
3.2 Enforcing Burning
3.3 Enabling Safe Delegation
3.4 Enforcing Atomic Slashing
3.5 Enabling Partial Slashing
4. Restaking
5. Secure On-Demand Unbonding
6. Summary
Copyright © 2018-2024 OKAYCOIN ALL RIGHT RESERVED