stacks-network / sbtc

Repo containing sbtc
GNU General Public License v3.0
211 stars 3 forks source link

[Design]: sBTC Deposit UTXO Binary Format #30

Closed AshtonStephens closed 5 months ago

AshtonStephens commented 5 months ago

Completing the issue description and arriving at a conclusion is the deliverable of this issue.

Design - sBTC Deposit UTXO Binary Format

This ticket holds the design of the BTC Deposit UTXO Binary Format and how it fits into sBTC-v1.

1. Summary

To deposit funds into sBTC according to the deposit flow defined in #15, a Bitcoin holder creates a deposit request. A deposit request in sBTC is a Bitcoin transaction which contains a deposit UTXO. This transaction should be accompanied by a deposit proof sent to Signers, to allow the signers to consume the UTXO. The exact format of the deposit UTXO and the content of the deposit proof is defined in this ticket.

2. Context & Purpose

The purpose of defining a deposit UTXO format is to allow any sBTC client to create valid deposit requests, and any sBTC Signer implementation to properly implement deposit UTXO consumption which is a vital part of the current deposit flow.

To support the deposit flow, the deposit UTXO format must define a type of UTXO which can be:

  1. Consumed by Signers.
    • The Signers are collectively capable of producing BIP-340-compliant Schnorr signatures.
    • When the UTXO consumed, it should reveal the sBTC recipient address.
  2. If the deposit is rejected, can be reclaimed by the Bitcoin holder.

Relevant Research Discussions Deposit flow https://github.com/Trust-Machines/sbtc-v1/issues/15

External Resources

3. Design

3.1 Proposed Component Design

The deposit UTXO is a SegWit version 1 output (BIP-341). This means that the scriptPubKey is a 33 byte long sequence with a leading version byte and a following witness program as illustrated below.

Deposit UTXO scriptPubKey:

0              1                 33
| version_byte | witness_program |

To fulfill the requirements on the deposit UTXO, the witness program must have two general conditions and no public key spend condition. The general conditions should be

  1. Accept condition. A condition allowing Signers to consume the UTXO with their aggregate key and while doing so revealing a sbtc_payload from the user on the chain.
  2. Reclaim condition. A condition allowing the BTC holder to reclaim the UTXO if the deposit is rejected.

Witness program

More specifically, the witness program is the x coordinate of a secp256k1 curve point $$Q = P + hash_{TapTweak}(p || k),$$ where

$$k = hash{TapBranch}(\text{min}(h{leaf}(sa), h{leaf}(sr)) || \text{max}(h{leaf}(sa), h{leaf}(s_r))),$$

where $h_{leaf}$ is the tapleaf hash defined as

$$h{leaf}(s) = hash{TapLeaf}(v || \text{compact size}(\text{size of s}) || s).$$

Requirements of the spend conditions

The spend conditions are BIP-342 compliant tapscripts, with additional requirements on their content. The accept condition $s_a$ must be of the form

<sbtc_payload> DROP <signers_pubkey> CHECKSIGVERIFY

where the sbtc_payload contains the following data

0               8                                          159
|---------------|-------------------------------------------|
   max_fee             recipient_address

where max_fee is the maximum fee the BTC holder would like to pay for the deposit, and the recipient_address is the address which should be minted the sBTC, encoded as a Clarity value as defined in SIP-005.

The reclaim condition can be any spend condition as long as it is initiated with a timelock

<lock_time> CHECKSEQUENCEVERIFY <reclaim script>

The timelock allow Signers to choose to reject transactions if there is a risk that a user may attempt reclaim the deposit UTXO in the same block as the sBTC transaction which would consume the same UTXO, which would increase the risk of the sBTC transaction not being included in a block.

Deposit proof

Once the deposit UTXO has been accepted in a Bitcoin block, the Signers can consume it and accept the deposit. However, in order to do so they must receive the following information:

These items together form the deposit proof.

3.1.1 Design Diagram

3.1.2 Considerations & Alternatives

One alternative consideration is to not put any requirements on the reclaim spend path. This would allow more freedom in how users construct and handle reclaims, but it would also open up the possibility of a user intentionally "blocking out" sBTC transactions by reclaiming their deposit UTXO shortly after making a deposit request.

3.2 Areas of Ambiguity

We have not yet concluded if we want to encode max_fee in the sbtc_payload or if we'd rather take the fee of the deposit UTXO as an implicit max fee (possibly doubling it).


Closing Checklist

setzeus commented 5 months ago

Superb work here, exciting to read. A few thoughts as I'm reading:

  1. For the accept condition s(a) it might help readability to add the push_ops for the sbtc_payload & signers_pubkey (so I believe OP_160 (0xa0) & OP_21 (0x33))
  2. For the reclaim condition have we decided how long until eligible reclaim? I believe last time we decided on ~150 blocks?
hstove commented 5 months ago
<block_height> CHECKLOCKTIMEVERIFY <reclaim script>

This is really the only part I'd push back on. In general <blocks_amount> CHECK_SEQUENCE_VERIFY enacts the same functional behavior with less downsides. For example, with a fixed block height, the depositor has to do some math to figure out when their TX will get confirmed. They could set the lock time to 500 blocks from now, but then their transaction doesn't get confirmed for 5 blocks. Now, the effective lock time is 495 blocks.

Additionally, CHECK_SEQUENCE_VERIFY is a lot easier to verify, because it can just be a fixed sequence of bytes (ie blocks_amount+op_code)

netrome commented 5 months ago

Superb work here, exciting to read. A few thoughts as I'm reading:

1. For the accept condition s(a) it might help readability to add the push_ops for the sbtc_payload & signers_pubkey (so I believe OP_160 (0xa0) & OP_21 (0x33))

2. For the reclaim condition have we decided how long until eligible reclaim? I believe last time we decided on ~150 blocks?
  1. The push ops differ based on the payload size right? Because the sbtc_payload has variadic length, so the actual push_op can vary. Or is there a good convention already on how to write this? I'm used to the explicit push op being omitted when writing scripts, but I also think being explicit helps if we can do that with variadic push codes.

  2. I would let this be up to the Signers to decide, but if we want to decide on anything specific we can. I think last time, this had to be a very long time to prevent double spends because sBTC was minted before the deposit request was claimed by the signers. This time, we can have very low reclaim times.

netrome commented 5 months ago
<block_height> CHECKLOCKTIMEVERIFY <reclaim script>

This is really the only part I'd push back on. In general <blocks_amount> CHECK_SEQUENCE_VERIFY enacts the same functional behavior with less downsides. For example, with a fixed block height, the depositor has to do some math to figure out when their TX will get confirmed. They could set the lock time to 500 blocks from now, but then their transaction doesn't get confirmed for 5 blocks. Now, the effective lock time is 495 blocks.

Additionally, CHECK_SEQUENCE_VERIFY is a lot easier to verify, because it can just be a fixed sequence of bytes (ie blocks_amount+op_code)

Fair point, agree. I'll update.

AshtonStephens commented 5 months ago

Tentatively closing but we might revisit.

AshtonStephens commented 5 months ago

On allowing an arbitrary redeem script in order to validate that we have all the spend paths we'd need to know EXACTLY what the redeem script is, do I have that right? We can't check against it being a wildcard? @xoloki

xoloki commented 5 months ago

Yeah @AshtonStephens we need the full script tree, otherwise they could have a sneaky spend path. We can calculate the merkle root from the full tree, and verify that the tree is correct

netrome commented 5 months ago

And in our case, the full script tree is determined by the two scripts $s_a$ and $s_r$. We need both of these to be known. However, $s_r$ can take any form as long as it is initiated with a time lock as specified. The remainder of the script do not need any additional validation from the Deposit API or sBTC signers.