taproot-assets icon indicating copy to clipboard operation
taproot-assets copied to clipboard

universe - [feature]: finish implementation of on chain universe commitments

Open Roasbeef opened this issue 1 year ago • 9 comments

Is your feature request related to a problem? Please describe.

Today we don't have a commitment maintained by an issuer on chain that can be used to authenticate new issuance, as outlined in the BIP.

Describe the solution you'd like

Revamp and review the section of the BIP listed above.

In addition, we want to expand the scope of the chain commitment to include 3 commitemtns:

  • issued
  • burned
  • ignore

Issued is the root of the normal issuance universe tree we maintain today.

Burned is a new root that stores any relevant burning transaction as state transition proofs.

Ignore is another new root for outputs that should be ignored, meaning if an asset being verified has the specified asset outpoint/scriptkey in it's history, then it should be ignored.

Some very early interface definitions and structs can be found here which should prove to be useful: https://github.com/lightninglabs/taproot-assets/blob/420f2467a6770a9c6042159dcd4c3ce194db25c2/universe/interface.go#L661-L696.

In particular, we'll need to decide on the finer details of the "canonical issuance" scheme, which as proposed, would reuse the asset_group_key as an on-chain taproot public key, in order to tie the off-chain asset group key to an on-chain UTXO.

With this, the total amount outstanding is: issued - burned - ignore.

Additional context

Such a feature can be used to authenticate any supply level information, useful for transparency pages involving bridges.

Roasbeef avatar Aug 20 '24 00:08 Roasbeef

@ffranr this is the issue related to replicating "gettxoutsetinfo" type behavior in tapd

dstadulis avatar Sep 23 '24 18:09 dstadulis

Writing back offline discussion:

The design as outlined in the BIP linked above has an issue with a circular dependency. Tl;dr:

  • The proofs for any issuance event can only be created once the anchor TX for the issuance is confirmed.
  • The canonical universe commitment should have the proofs for issuance events as leaves in an MS-SMT.
  • Therefore, any commitment that includes an issuance event as a leaf must be anchored after the issuance event.
  • This also means that the commitment update must happen in a TX separate from the issuance itself.

This presents a new question of how to link issuance events to the chain of canonical universe commitments.

One pattern mentioned in the BIP that could be preserved is using the tweaked_group_key as the internal key for the canonical universe commitments. In that case, updating the canonical commitment requires access to the same keys that were used to create the asset group and authorize re-issuance (which is desirable).

In that case, I'm not sure if we need to have an on-chain link between issuance events and the canonical commitment chain at all. One argument in favor of that link is that you could easily discover all the relevant keys for leaves in the issuance section of the canonical commitment.

I'm also not sure what constraints we actually need to apply to issuance events that would be related to a canonical commitment.

E.x. should minting batches only contain the single grouped asset (re)issuance to be compatible with the canonical commitments? What if we want to reissue assets for multiple groups in one batch? Or batch in ungrouped assets?

Here are some sketches of ways to link the anchor TX for an issuance to a canonical commitment TX chain:

canonical_commitment_draft

This shows 4 TXs, with TX 1 in the top left, TX 2 in the top right, and TX 3 in the bottom left. TX 1 is an initial issuance event, TX 2 is a TX anchored in a later block that includes the first canonical commitment root, and TX 3 is what a possible second canonical commitment would be.

The transition rule being shown is to mint, and then begin the canonical commitment in a TX that also re-anchors the minted asset. This proves that the commitment maintainer is also the asset issuer by spending the anchor output for the batch. Future commitment updates would also re-anchor future issuances.

The second pattern is shown by TX 1 and the TX in the bottom right, TX 4. Here, instead of re-anchoring the minted assets, we create the issuance anchoring TX with a bitcoin-only change output where the internal key is the tweaked_group_key. This change output can be spent when starting the canonical commitment chain, and prove control of the tweaked_group_key.

jharveyb avatar Nov 06 '24 03:11 jharveyb

I think that in general here we need to keep a PSBT flow in mind.

I lean toward the 'bottom right TX 4' approach for spending, using the asset group key. This method effectively ties the group key's ownership to minting events, which I find compelling. However, it seems to me that the universe commitment transaction will also require support for a cold wallet PSBT signing process.

ffranr avatar Nov 19 '24 21:11 ffranr

The design as outlined in the BIP linked above has an issue with a circular dependency. Tl;dr:

This makes sense, that draft is from before the proof format was at concrete as it is now.

One pattern mentioned in the BIP that could be preserved is using the tweaked_group_key as the internal key for the canonical universe commitments. In that case, updating the canonical commitment requires access to the same keys that were used to create the asset group and authorize re-issuance (which is desirable).

Yep, the other idea I was playing around with was to have a NUMs key, but then replicate the leaves from the group key with the leaves of this output public key. This would allow for any threshold or multi-sig scripts to exist on the Bitcoin level as well. We can also opt to reuse the raw group key and also replicate all the leaves. In the context of the old design, it wouldn't be the same top level tweaked group key, as it would also contain the new unified universe commitment.

Roasbeef avatar Nov 20 '24 01:11 Roasbeef

In that case, I'm not sure if we need to have an on-chain link between issuance events and the canonical commitment chain at all. One argument in favor of that link is that you could easily discover all the relevant keys for leaves in the issuance section of the canonical commitment.

The original design goal here was that: light clients (or anyone really) can watch a special on-chain UTXO and know that when that was spent, a minting transaction also occurred. I think we can still retain that (first input of minting txn is always the prior UTXO), but we still need to wrangle with the circular dep interaction.

Roasbeef avatar Nov 20 '24 01:11 Roasbeef

I think you're correct that in order to handle this circular dep, we actually need at least two transactions. One to mint the assets (can replicate the group key on the Bitcoin level to ensure identical script satisfiability), and another to update the on-chain unified commitment.

So to summarize you propose two patterns to resolve this:

  1. Mint, then re-anchor with the updated commitment.
    • First you mint, from that special output (spending the last output used for the commitment).
    • Then you spend the newly created transactions commit to the new unified commitment in a change output.
  2. Mint as normal, but create a change output re-using the group key.
    • After the minting transaction confirms, you spend a change output in that transaction in a transaction that also spends the prior unified commitment (?).
      • So then the commitment chain is a transaction change that spends a change output from every minting transaction.

Roasbeef avatar Nov 20 '24 01:11 Roasbeef

Reflecting a bit, I think option 2 is what I was going after with the original draft idea.

Here's a single shot Claude diagram that captures my current mental model

%%{init: {
    'theme': 'base',
    'themeVariables': {
        'fontFamily': 'arial',
        'fontSize': '16px',
        'primaryTextColor': '#000000',
        'lineColor': '#999999'  
    }
}}%%

graph LR
    %% First Mint Transaction
    MT1[Mint Transaction 1] --> |output 1| NA1[New Assets 1]
    MT1 --> |output 2| CP1[Commitment Placeholder 1]
    
    %% First Commitment Update
    CP1 --> CU1[Commitment Update Tx 1]
    CU1 --> CO1[Commitment Output 1]
    
    %% Second Mint Transaction
    Input2[Any Input] --> MT2[Mint Transaction 2]
    MT2 --> |output 1| NA2[New Assets 2]
    MT2 --> |output 2| CP2[Change Output]
    
    %% Second Commitment Update
    CP2 --> CU2[Commitment Update Tx 2]
    CO1 --> CU2
    CU2 --> CO2[Commitment Output 2]
    
    %% Third Mint Transaction (to show pattern)
    Input3[Any Input] --> MT3[Mint Transaction 3]
    MT3 --> |output 1| NA3[New Assets 3]
    MT3 --> |output 2| CP3[Change Output]
    
    %% Third Commitment Update
    CP3 --> CU3[Commitment Update Tx 3]
    CO2 --> CU3
    CU3 --> CO3[Commitment Output 3]
    
    %% Styling with darker colors and better contrast
    classDef mint fill:#f9f,stroke:#333,stroke-width:2px,color:#000
    classDef commitment fill:#bbf,stroke:#333,stroke-width:2px,color:#000
    classDef assets fill:#bfb,stroke:#333,stroke-width:2px,color:#000
    linkStyle default stroke:#999,stroke-width:1.5px
    
    class MT1,MT2,MT3 mint
    class CU1,CU2,CU3,CO1,CO2,CO3,CP1,CP2,CP3 commitment
    class NA1,NA2,NA3 assets

So you can mint from anywhere, but then create that special change output. You then MUST spend that change output with the updated commitment. This is interesting, as I think it lets an existing grouped asset opt into these rules, assuming a flexible verifier.

Don't think it's necessary, but we could go a step further and use Bitcoin scripts to force that the owner spends that output in time. It can be similar to the anchor outputs in LN, you can spend yourself for N blocks, but afterwards anyone can.

One other idea is that this new change output created can commit to some of the essential details of the mint (so can't do the merkle proof due to circular dep, but can add the script key and amounts, etc). Then this way we also force a partial reveal there that some of the information is updated, before we can commit to the full minting proof in the next transaction.

Roasbeef avatar Nov 20 '24 02:11 Roasbeef

mint from anywhere, but then create that special change output. You then MUST spend that change output with the updated commitment.

What would be the defined behavior for when the special change output is created but not included?

dstadulis avatar Nov 23 '24 23:11 dstadulis

Few things to solidify:

  1. What script is used in the change address created in the minting transaction? * What are the rules that govern spending of this output?
  2. What are the semantics of the new field added to the genesis proof to opt into this behavior? * One key requirement is that we use a staged roll out. So first an optional bit, where if the rules aren't 100% followed, there's no protocol violation. Then a require bit, that mandates the new rules.
    • Ideally it's also possible to do some sort of catch up proof (you spend the change outputs on chain to retroactively create the commitment chain).
  3. The exact commitment structure and location of the unified commitment.

Roasbeef avatar Dec 02 '24 17:12 Roasbeef

Closing as the system is in place for the issuer, with issues tracking the missing gaps for the verifier.

Roasbeef avatar Sep 10 '25 00:09 Roasbeef