Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Nakamoto] stack-stx burn op requires a signing key and signature #4285

Closed
jcnelson opened this issue Jan 24, 2024 · 7 comments · Fixed by #4412
Closed

[Nakamoto] stack-stx burn op requires a signing key and signature #4285

jcnelson opened this issue Jan 24, 2024 · 7 comments · Fixed by #4412

Comments

@jcnelson
Copy link
Member

The Stacks-on-Bitcoin stack-stx transaction needs to encode a signing key and a signature as well as the existing arguments. Right now, the OP_RETURN is as follows:

0      2  3                             19           20
|------|--|-----------------------------|------------|
magic  op         uSTX to lock (u128)     cycles (u8)

We can expand this to include the signing key, but not the signature:

0      2  3                             19           20                   53
|------|--|-----------------------------|------------|---------------------|
magic  op         uSTX to lock (u128)     cycles (u8)    signing key

What I was thinking is that in order to stack STX, you'd have to create a third output in its PreStxOp that is the hash160 of the signing key, and then spend it in the StackStxOp. We already do this in order to prove ownership of the STX -- the StackStxOp spends the second output of the PreStxOp and thereby proves that the second output of the PreStxOp is tx-sender. So, the PreStxOp for StacksStxOp would manifest as follows:

 PreStxOp                                                      StackStxOp

.-------------------.-------------------.                      .-------------------------------------.------------------.
|                   | OP_RETURN ...     |          .---------> | <tx-sender sig> <tx-sender pubkey>  | OP_RETURN ...    |
|                   |-------------------|   spend  |           |-------------------------------------|------------------|
| (funding inputs)  | tx-sender UTXO    |  --------*   .-----> | <signer sig> <signer pubkey>        | PoX reward addr  |
|                   |-------------------|   spend      |       |-------------------------------------|------------------|
|                   | signing key UTXO  |  ------------*       |        (funding inputs)             | (change outputs) |
*-------------------*-------------------*                      *-------------------------------------*------------------*
@zone117x
Copy link
Member

zone117x commented Jan 25, 2024

Can the signer-key be omitted since it can be recovered from the signer sig?

Although I suppose that still wouldn't fit in OP_RETURN (85 bytes total?)

@hstove
Copy link
Contributor

hstove commented Jan 25, 2024

What about if we stack it into new outputs:

  • OP_RETURN with signing key added
  • Stacking rewards address
  • p2wsh: [3 bytes] [first 32 bytes of signature] [checksum]
  • p2wsh: [1 byte addr version] [recovery byte acting as witness version] [next 32 bytes of signature] [checksum]

I'm not sure how viable it is to take advantage of the witness program version as recovery byte. Seems like only 0 and 1 are used but it's reserved for 0-16.

Edit: Or the simpler approach is to append the signature's recovery byte to OP_RETURN:

0      2  3                             19           20                    53            54
|------|--|-----------------------------|------------|---------------------|-------------|
magic  op         uSTX to lock (u128)     cycles (u8)    signing key        recovery byte

And then the last two outputs being:

|addr|wit |    |32 byte signature chunk         |checksum
|0x10|0x00|0x00|--------------------------------|----

@hstove
Copy link
Contributor

hstove commented Jan 25, 2024

Following Jude's idea of spending a UTXO to prove ownership of the signing key, if we want to follow that approach, should the spend of the "signing key UTXO" be a signature that doesn't just prove ownership of the signing key (ie p2pkh), but a signature over the hash of the reward cycle and the stacker (via p2sh)? I suppose that's not necessary, because the signature couldn't be re-used if it's signing the UTXO.

It's also interesting because it supports hardware wallets. You could construct this transaction in a PSBT and add partial signatures before broadcasting it.

However, if we didn't take the approach of my first paragraph (signing over the SIP18 message hash), then we have the challenge of not being able to re-construct the stack-stx contract call from just the burn op. referencing this code in blocks.rs. If I'm following correctly, we'd have to do something like expose a private stack-stx-from-burn-op transaction.

@jcnelson
Copy link
Member Author

@hstove I'm personally fine with either approach. However, if you add this to the SIP, you should expect some pushback because creating unspendable witness UTXOs constitutes "UTXO pollution" (which I think is BS because (1) UTXO pollution being a problem is ipso facto a consequence of a bad UTXO index design on Bitcoin's part, and (2) Bitcoin miners who actually care about this can simply run a Stacks node and prune these UTXOs from their nodes, but these aren't universally held opinions).

@hstove
Copy link
Contributor

hstove commented Jan 26, 2024

Yeah that's a very fair point. IMO the approach you described of PreStx spending the signing key UTXO is more elegant and I really like the fact that it's easy to use a wallet to sign the Signer Key UTXO. The only downside I see is what I mentioned around re-constructing the stack-stx payload, but (I think) this can be worked around with something like (define-private (stack-stx-from-burn-op)).

One recommended tweak to your approach would be to make a new Signing Key UTXO back as change in the StackStxOp transaction. This way it's much easier to "chain" further down the road, and you don't have to first create a new Signer Key UTXO every time. This doesn't need to be in the spec, it can just be the standard way these transactions are made.

A different tweak would be to not require a fixed UTXO->output signing flow, as you have in your diagram. Unless I'm mistaken, that just adds some complexity around managing balances of your two "addresses" (stx-sender and signer-key). If it was more of an AnyOneCanPay (not exactly sure if that's the right type) approach, then the Stacker can maintain a fixed dust balance in either address, and use the other one to pay fees.

@hstove
Copy link
Contributor

hstove commented Feb 21, 2024

Note: once the PR for signer key authorizations is merged, we can simply add the 33-byte signer key to the OP_RETURN

@8marz8
Copy link
Contributor

8marz8 commented Feb 22, 2024

I have started making some changes in this branch since #4277 is merged...my changes are subject to correction/completion as I gain more understanding...for now I've mostly followed similar logic/pattern from #4277

@hstove hstove linked a pull request Feb 22, 2024 that will close this issue
@hstove hstove self-assigned this Mar 18, 2024
@hstove hstove added the 2.5 label Mar 19, 2024
@saralab saralab closed this as completed Mar 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

5 participants