stacks-network / sbtc

Repo containing sbtc
GNU General Public License v3.0
130 stars 2 forks source link

[Design]: Stacks native multisig wallets for Signer operations #59

Closed hstove closed 1 day ago

hstove commented 2 months ago

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

Design - Authorizing signer actions in Clarity

This ticket holds the design of how signer operations are authorized in Clarity and how it fits into sBTC v1.

1. Summary

There are two options for authorizing signer operations in Clarity:

  1. Including a list of ECDSA signatures in function payloads
  2. Using a Stacks-native multisig address composed of the signer's public keys

Previously, we had mainly discussed using a list of ECDSA signatures as a means of authorizing signer operations. The purpose of this ticket is to evaluate the possibility of using a Stacks-native multisig wallet to authorize signer operations.

Using a native multisig Stacks wallet simplifies much of the Clarity code needed to authorize transactions. It also has significantly lower execution cost.

2. Context & Purpose

There are a few Clarity operations that may only be executed by the signers, such as:

As discussed previously, Clarity is not able to verify Schnoor signatures, which means we can't use WSTS in Clarity.

In previous discussions we landed on using a list of ECDSA signatures to authorize operations. While this solution should be perfectly functional, it adds significant overhead to Clarity transactions. To verify these signatures, you need to make expensive Clarity function calls. Additionally, verifying a list of signatures has a lot of room for error, because you need to make sure you're properly authorizing every single aspect of a transaction's payload.

Using a native multisig wallet simplifies this authorization quite a bit. Because the tx-sender is the multi-sig itself (using the same N-of-M rules used on the Bitcoin side), it's implicit that every single argument in a transaction's payload is authorized.

To simplify, if using a native multi-sig address, the only check needed to be done in Clarity is against tx-sender. In Clarity, the checks needed to be done will be significantly larger.

Relevant Research Discussions

Can we verify Schnoor signatures in Clarity? Stacks tx sponsorship

External Resources

Stacks-core PR to remove order-dependent signatures in Stacks multisig SIP for improved multisig experience

3. Design

3.1 Proposed Component Design

Generating the multi-sig transaction

Instead of authorizing signers using a list of ECDSA signatures, signers instead sign the Stacks transaction directly, using their ECDSA key.

When a Stacks operation is ready to be built, the coordinator constructs a Stacks contract-call transaction. This transaction is unsigned, but the auth field is specified to be a multi-sig with N-of-M signatures.

The coordinator sends this unsigned TX to each signer. Each signer generates their signature over the transaction payload and sends their signature back to the coordinator. The coordinator concatenates these signatures into the Stacks transaction's authorization field.

Finally, the transaction is sent to the sponsoring server. The sponsoring server verifies that the transaction, sponsors it, and broadcasts the transaction to the Stacks chain.

Verifying the multisig in Clarity

Clarity has the principal-construct? function, which allows turning public keys into a principal type. Given that Clarity has access to the set of the signer's public keys, Clarity contracts can dynamically determine and validate the tx-sender of a given transaction. It's also likely that this principal can be "pre-computed" when the set of signer keys is written into Clarity state.

This principal that is equal to the signer multi-sig can be referred to as the signer-principal or signer-stacks-address.

When sBTC contracts need to authorize the sender of a signer operation, the contracts only need to validate that tx-sender is equal to signer-principal.

3.1.1 Design Diagram

3.1.2 Considerations & Alternatives

On the "old" Stacks multisig format

Prior to epoch 3.0, native multisig on Stacks was really hard to work with. The primary reason for this challenge was that signatures needed to be gathered sequentially. If Alice and Bob had a 2-of-2, first Alice would have to sign, and then Bob would have to sign "on top" of Alice's signature.

Fortunately, the specification for Stacks multisig accounts has been addressed in a SIP and will be implemented before epoch 3.0.

Downsides vs using a list of ECDSA signatures

One downside of using native multisig, as opposed to a list of signatures, is that it "locks in" the signers to using a specific Stacks address. If operations were verified with a list of signatures, it's possible that any Stacks address could make operations like withdrawal-accept. It's likely that we wouldn't want to take that approach, though, due to potential challenges around having multiple addresses submit these operations at the same time.

3.2 Areas of Ambiguity


Closing Checklist

setzeus commented 2 months ago

Neat! I didn't realize the SIP was that far along. I'm aware that the current UX of multi-sigs is v limited but if it's upgraded soon this would certainly be a great use-case.

setzeus commented 2 months ago

I don't think I follow the logic around selecting the 'signer-principal.' Or, mainly not sure why we'd need it. If the contract holds all public keys then we can map from keys to principals & check that tx-sender exists in the list of principals?

Can you expand a bit more on the 'signer-principal'?

hstove commented 2 months ago

@jferrant I think the "how rotating keys works" is more related to other tickets. I think there's also a good argument to say that this ticket is irrelevant, as we each signer make individual transactions. This is more about "for functions like withdrawal-accept, how do we verify that the signers voted on it"

hstove commented 2 months ago

I've written proof-of-concept code that shows we can take a list of pubkeys and generate a Stacks multisig principal in Clarity:

https://gist.github.com/hstove/04e785422b8b22caad15b7d2dccef989

hstove commented 2 months ago

I'd love to get more opinions on this, but now that I've dug into the code more, I'm feeling pretty confident that we can (and should) use the approach described in this doc.

Here are some relevant links for signing a Stacks multisig transaction:

https://github.com/fess-v/stacks-blockchain/blob/fa950324fbeea1b5de24dc9c0707b272bb5d7dd8/stackslib/src/chainstate/stacks/transaction.rs#L860

https://github.com/fess-v/stacks-blockchain/blob/fa950324fbeea1b5de24dc9c0707b272bb5d7dd8/stackslib/src/chainstate/stacks/transaction.rs#L917

test demonstrating signing a Stacks multisig transaction