Skip to content

tapgarden: list batches correctly after asset transfer #992

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

Merged
merged 7 commits into from
Jul 10, 2024

Conversation

jharveyb
Copy link
Contributor

@jharveyb jharveyb commented Jul 4, 2024

Fixes #986 .

As detailed in #986, recent changes around how batches are created updated the way we store seedlings; specifically when we store script keys and group keys. Changes were made to how batches were displayed, and those changes broke the display of batches minted with tapd versions 0.3.3 or earlier.

Further investigation showed that the ListBatches RPC (and matching CLI command) displayed incorrect information once assets from a batch had been transferred.

To fix this, we can re-read the assets minted in a batch from their issuance proofs that we stored on disk when finalizing the batch.

@jharveyb jharveyb requested review from gijswijs and GeorgeTsagk July 4, 2024 01:58
@jharveyb jharveyb self-assigned this Jul 4, 2024
@Liongrass
Copy link
Contributor

I ran commit 2a6cea38c6ca692f36f4313db33ab1a4638c1ca1 as part of litd through modifying go.mod, created a batch with tapcli --tlscertpath ~/.lit/tls.cert --rpcserver=localhost:8443 --network=testnet assets mint --type normal --name beefbuxdontmint --supply 1000000 --decimal_display 3 --meta_bytes '{"hello":true}' --meta_type json --new_grouped_asset and then inspected the batch with tapcli --tlscertpath ~/.lit/tls.cert --rpcserver=localhost:8443 --network=testnet assets mint batches

As a result tapd crashes with the following errors:

2024-07-04 04:50:01.870 [DBG] RPCS: [/mintrpc.Mint/ListBatches] requested
2024-07-04 04:50:01.925 [DBG] GRDN: Stopping ChainCaretaker(02d35d233cf4113dac5b19f8d62a302abc0ef0bedd4103333613299a31ff23098a)
2024-07-04 04:50:01.926 [INF] GRDN: BatchCaretaker(02d35d233cf4113dac5b19f8d62a302abc0ef0bedd4103333613299a31ff23098a): Stopping
2024-07-04 04:50:01.926 [DBG] GRDN: Skipping TX confirmation, exiting
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x47b9fa]

goroutine 1559 [running]:
github.com/lightninglabs/taproot-assets/asset.ToSerialized(...)
	github.com/lightninglabs/[email protected]/asset/asset.go:73
github.com/lightninglabs/taproot-assets/tapgarden.fetchFinalizedBatch({0x3857be8, 0xc003858230}, {0x38577b8, 0xc0026786f0}, 0xc00244be00)
	github.com/lightninglabs/[email protected]/tapgarden/planter.go:775 +0x3f6
github.com/lightninglabs/taproot-assets/tapgarden.listBatches({0x3857be8, 0xc003858230}, {0x38754d0?, 0xc000468de0?}, {0x38577b8, 0xc0026786f0}, {0x383e260, 0x4d3fcc0}, {0x0, 0x0})
	github.com/lightninglabs/[email protected]/tapgarden/planter.go:896 +0x20a
github.com/lightninglabs/taproot-assets/tapgarden.(*ChainPlanter).gardener(0xc0007fc000)
	github.com/lightninglabs/[email protected]/tapgarden/planter.go:1199 +0x1397
created by github.com/lightninglabs/taproot-assets/tapgarden.(*ChainPlanter).Start.func1 in goroutine 1
	github.com/lightninglabs/[email protected]/tapgarden/planter.go:414 +0x67b

@dstadulis dstadulis added this to the v0.4 milestone Jul 8, 2024
Copy link
Member

@GeorgeTsagk GeorgeTsagk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good!
not sure if the CI error is a flake

@Roasbeef
Copy link
Member

Roasbeef commented Jul 9, 2024

I think we can just replace FetchAssetsForBatch all together. The issue is that today it'll join with the assets table, based on every genesis ID (asset ID) that was created in a batch. This'll end up returning duplicateed assets, as each time we do a transfer we insert another one.

Instead, I think we can follow this chain: batch genesis asset ref -> genesis point ref -> chain txn. Then with the chain txn of the batch, we can join on the assets table to find the precise asset that was created in this instance.

here's a version from Claude:

-- name: FetchAssetsForBatch :many
WITH batch_info AS (
    -- This CTE fetches the batch and its associated genesis point and anchor transaction
    SELECT 
        batches.batch_id,
        points.genesis_id,
        points.anchor_tx_id
    FROM asset_minting_batches batches
    JOIN internal_keys keys ON batches.batch_id = keys.key_id
    JOIN genesis_points points ON batches.genesis_id = points.genesis_id
    WHERE keys.raw_key = $1
)
SELECT 
    assets.version,
    script_keys.tweak,
    script_keys.tweaked_script_key,
    script_keys.declared_known AS script_key_declared_known,
    internal_keys.raw_key AS script_key_raw,
    internal_keys.key_family AS script_key_fam,
    internal_keys.key_index AS script_key_index,
    key_group_info_view.tapscript_root, 
    key_group_info_view.witness_stack, 
    key_group_info_view.tweaked_group_key,
    key_group_info_view.raw_key AS group_key_raw,
    key_group_info_view.key_family AS group_key_family,
    key_group_info_view.key_index AS group_key_index,
    assets.script_version,
    assets.amount,
    assets.lock_time,
    assets.relative_lock_time,
    assets.spent,
    genesis_info_view.asset_id,
    genesis_info_view.asset_tag,
    genesis_info_view.meta_hash, 
    genesis_info_view.meta_type,
    genesis_info_view.meta_blob,
    genesis_info_view.output_index AS genesis_output_index,
    genesis_info_view.asset_type,
    genesis_info_view.prev_out AS genesis_prev_out
FROM assets
JOIN genesis_info_view ON assets.genesis_id = genesis_info_view.gen_asset_id
LEFT JOIN key_group_info_view ON assets.genesis_id = key_group_info_view.gen_asset_id
JOIN script_keys ON assets.script_key_id = script_keys.script_key_id
JOIN internal_keys ON script_keys.internal_key_id = internal_keys.key_id
JOIN managed_utxos ON assets.anchor_utxo_id = managed_utxos.utxo_id
JOIN batch_info ON genesis_info_view.gen_asset_id = batch_info.genesis_id
WHERE managed_utxos.txn_id = batch_info.anchor_tx_id;

The last two lines there are most important:

JOIN batch_info ON genesis_info_view.gen_asset_id = batch_info.genesis_id
WHERE managed_utxos.txn_id = batch_info.anchor_tx_id;

We join with the batch info to line things up, then filter out for instances where the managed UTXO points to the same transaction as the one we made in the batch.

@jharveyb jharveyb force-pushed the marshal_batch_fixes branch from e3ca693 to e347419 Compare July 9, 2024 04:02
Copy link
Contributor

@gijswijs gijswijs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is looking real nice. Made some comments and asked some questions throughout.

@jharveyb
Copy link
Contributor Author

jharveyb commented Jul 9, 2024

I think we can just replace FetchAssetsForBatch all together. The issue is that today it'll join with the assets table, based on every genesis ID (asset ID) that was created in a batch. This'll end up returning duplicateed assets, as each time we do a transfer we insert another one.

Instead, I think we can follow this chain: batch genesis asset ref -> genesis point ref -> chain txn. Then with the chain txn of the batch, we can join on the assets table to find the precise asset that was created in this instance.

The last two lines there are most important:

JOIN batch_info ON genesis_info_view.gen_asset_id = batch_info.genesis_id
WHERE managed_utxos.txn_id = batch_info.anchor_tx_id;

We join with the batch info to line things up, then filter out for instances where the managed UTXO points to the same transaction as the one we made in the batch.

The query looks decent but @guggero caught an issue that would pop up as a result of how we handle passive assets.

At the end of the transfer logic, we re-anchor all passive assets:

err = a.reAnchorPassiveAssets(

Which involves updating the asset witness and the anchor UTXO:

-- name: ReAnchorPassiveAssets :exec

So after a single reanchoring, if we filtered entries in the assets table on the anchor_utxo_id of the batch, we would have no matches.

@jharveyb
Copy link
Contributor Author

jharveyb commented Jul 9, 2024

I ran commit 2a6cea38c6ca692f36f4313db33ab1a4638c1ca1 as part of litd through modifying go.mod, created a batch with tapcli --tlscertpath ~/.lit/tls.cert --rpcserver=localhost:8443 --network=testnet assets mint --type normal --name beefbuxdontmint --supply 1000000 --decimal_display 3 --meta_bytes '{"hello":true}' --meta_type json --new_grouped_asset and then inspected the batch with tapcli --tlscertpath ~/.lit/tls.cert --rpcserver=localhost:8443 --network=testnet assets mint batches

With the latest push, I can successfully list batches from this DB + set of proof files, with the earliest batch dating to October 2023.

Reading assets from the proof files means that we don't get unserialized info like the group internal key or the tap tweak for the script key, so I'd like to add some logic to fetch that from the DB (we never delete entries in those tables, so that info should be present regardless of future transfers).

The group anchor field is also unpopulated, but that should also be doable given the seedlings we're already fetching.

@Roasbeef
Copy link
Member

Roasbeef commented Jul 9, 2024

Test timeout, will rerun:

panic: test timed out after 20m0s
running tests:
	TestBatchedAssetIssuance (19m58s)
	TestBatchedAssetIssuance/minting_with_cancellation (19m44s)

goroutine 2157 [running]:
testing.(*M).startAlarm.func1()
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:2366 +0x265
created by time.goFunc
	/opt/hostedtoolcache/go/1.22.3/x64/src/time/sleep.go:177 +0x45

goroutine 1 [chan receive, 19 minutes]:
testing.(*T).Run(0xc00016f040, {0x26ac6c8, 0x18}, 0x27f6668)
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:1750 +0x851
testing.runTests.func1(0xc00016f040)
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:2161 +0x86
testing.tRunner(0xc00016f040, 0xc00005bb10)
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:1689 +0x21f
testing.runTests(0xc000118888, {0x3899420, 0x8, 0x8}, {0xc00005bbb8?, 0xc00005bc00?, 0x38c9700?})
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:2159 +0x8bf
testing.(*M).Run(0xc0003554a0)
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:2027 +0xf18
main.main()
	_testmain.go:63 +0x2be

goroutine 8 [chan receive, 20 minutes]:
testing.(*T).Parallel(0xc00016f1e0)
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:1483 +0x525
github.com/lightninglabs/taproot-assets/tapgarden.TestWatchProofs(0xc00016f1e0)
	/home/runner/work/taproot-assets/taproot-assets/tapgarden/re-org_watcher_test.go:111 +0x3f
testing.tRunner(0xc00016f1e0, 0x27f6658)
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:1689 +0x21f
created by testing.(*T).Run in goroutine 1
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:1742 +0x826

goroutine 9 [chan receive, 20 minutes]:
testing.(*T).Parallel(0xc00016f380)
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:1483 +0x525
github.com/lightninglabs/taproot-assets/tapgarden_test.TestCustodianNewAddr(0xc00016f380)
	/home/runner/work/taproot-assets/taproot-assets/tapgarden/custodian_test.go:508 +0x3f
testing.tRunner(0xc00016f380, 0x27f6678)
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:1689 +0x21f
created by testing.(*T).Run in goroutine 1
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:1742 +0x826

goroutine 10 [chan receive, 20 minutes]:
testing.(*T).Parallel(0xc00016f520)
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:1483 +0x525
github.com/lightninglabs/taproot-assets/tapgarden_test.TestBookAssetSyncer(0xc00016f520)
	/home/runner/work/taproot-assets/taproot-assets/tapgarden/custodian_test.go:550 +0x3f
testing.tRunner(0xc00016f520, 0x27f6670)
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:1689 +0x21f
created by testing.(*T).Run in goroutine 1
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:1742 +0x826

goroutine 244 [chan receive, 19 minutes]:
testing.(*T).Parallel(0xc0000e61a0)
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:1483 +0x525
github.com/lightninglabs/taproot-assets/tapgarden_test.TestTransactionConfirmedOnly(0xc0000e61a0)
	/home/runner/work/taproot-assets/taproot-assets/tapgarden/custodian_test.go:673 +0x27
testing.tRunner(0xc0000e61a0, 0x27f6688)
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:1689 +0x21f
created by testing.(*T).Run in goroutine 1
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:1742 +0x826

goroutine 1227 [select, 19 minutes]:
github.com/lightninglabs/taproot-assets/tapgarden.(*ChainPlanter).gardener(0xc00036a140)
	/home/runner/work/taproot-assets/taproot-assets/tapgarden/planter.go:1118 +0x370
created by github.com/lightninglabs/taproot-assets/tapgarden.(*ChainPlanter).Start.func1 in goroutine 1221
	/home/runner/work/taproot-assets/taproot-assets/tapgarden/planter.go:414 +0x9cb

goroutine 1095 [select, 19 minutes]:
github.com/lightninglabs/taproot-assets/tapgarden.(*ChainPlanter).gardener(0xc00036a8c0)
	/home/runner/work/taproot-assets/taproot-assets/tapgarden/planter.go:1118 +0x370
created by github.com/lightninglabs/taproot-assets/tapgarden.(*ChainPlanter).Start.func1 in goroutine 684
	/home/runner/work/taproot-assets/taproot-assets/tapgarden/planter.go:414 +0x9cb

goroutine 1143 [select, 19 minutes]:
database/sql.(*DB).connectionOpener(0xc000128410, {0x2a5fef0, 0xc000f43950})
	/opt/hostedtoolcache/go/1.22.3/x64/src/database/sql/sql.go:1246 +0xed
created by database/sql.OpenDB in goroutine 610
	/opt/hostedtoolcache/go/1.22.3/x64/src/database/sql/sql.go:824 +0x2d4

goroutine 593 [chan receive, 19 minutes]:
testing.(*T).Parallel(0xc000368340)
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:1483 +0x525
github.com/lightninglabs/taproot-assets/tapgarden_test.TestAddrMatchesAsset(0xc000368340)
	/home/runner/work/taproot-assets/taproot-assets/tapgarden/custodian_test.go:810 +0x3f
testing.tRunner(0xc000368340, 0x27f6660)
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:1689 +0x21f
created by testing.(*T).Run in goroutine 1
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:1742 +0x826

goroutine 1679 [select, 19 minutes]:
github.com/lightninglabs/taproot-assets/tapgarden.(*ChainPlanter).cancelMintingBatch(0xc0002c4000, {0x2a5fef0, 0xc0002b4fa0}, 0xc0000d4c80)
	/home/runner/work/taproot-assets/taproot-assets/tapgarden/planter.go:1072 +0x495
github.com/lightninglabs/taproot-assets/tapgarden.(*ChainPlanter).gardener(0xc0002c4000)
	/home/runner/work/taproot-assets/taproot-assets/tapgarden/planter.go:1332 +0x1b45
created by github.com/lightninglabs/taproot-assets/tapgarden.(*ChainPlanter).Start.func1 in goroutine 1673
	/home/runner/work/taproot-assets/taproot-assets/tapgarden/planter.go:414 +0x9cb

goroutine 1614 [select, 11 minutes]:
database/sql.(*DB).connectionCleaner(0xc000976680, 0x8bb2c97000)
	/opt/hostedtoolcache/go/1.22.3/x64/src/database/sql/sql.go:1089 +0x129
created by database/sql.(*DB).startCleanerLocked in goroutine 610
	/opt/hostedtoolcache/go/1.22.3/x64/src/database/sql/sql.go:1076 +0x268

goroutine 1144 [select, 11 minutes]:
database/sql.(*DB).connectionCleaner(0xc000128410, 0x8bb2c97000)
	/opt/hostedtoolcache/go/1.22.3/x64/src/database/sql/sql.go:1089 +0x129
created by database/sql.(*DB).startCleanerLocked in goroutine 610
	/opt/hostedtoolcache/go/1.22.3/x64/src/database/sql/sql.go:1076 +0x268

goroutine 1673 [chan receive, 19 minutes]:
github.com/lightninglabs/taproot-assets/tapgarden.(*ChainPlanter).CancelBatch(0xc0002c4000)
	/home/runner/work/taproot-assets/taproot-assets/tapgarden/planter.go:1786 +0x25a
github.com/lightninglabs/taproot-assets/tapgarden_test.(*mintingTestHarness).cancelMintingBatch(0xc0004bc400, 0x0)
	/home/runner/work/taproot-assets/taproot-assets/tapgarden/planter_test.go:518 +0x67
github.com/lightninglabs/taproot-assets/tapgarden_test.testMintingCancelFinalize(0xc0004bc400)
	/home/runner/work/taproot-assets/taproot-assets/tapgarden/planter_test.go:1292 +0x872
github.com/lightninglabs/taproot-assets/tapgarden_test.TestBatchedAssetIssuance.func1(0xc000368000)
	/home/runner/work/taproot-assets/taproot-assets/tapgarden/planter_test.go:1830 +0x56
testing.tRunner(0xc000368000, 0xc000928030)
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:1689 +0x21f
created by testing.(*T).Run in goroutine 610
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:1742 +0x826

goroutine 1613 [select, 19 minutes]:
database/sql.(*DB).connectionOpener(0xc000976680, {0x2a5fef0, 0xc0009be960})
	/opt/hostedtoolcache/go/1.22.3/x64/src/database/sql/sql.go:1246 +0xed
created by database/sql.OpenDB in goroutine 610
	/opt/hostedtoolcache/go/1.22.3/x64/src/database/sql/sql.go:824 +0x2d4

goroutine 789 [chan receive, 19 minutes]:
github.com/lightninglabs/taproot-assets/tapgarden.(*ChainPlanter).FinalizeBatch(0xc000186140, {{0x0, 0x0}, {0x0, {0x0, 0x0}}})
	/home/runner/work/taproot-assets/taproot-assets/tapgarden/planter.go:1775 +0x389
github.com/lightninglabs/taproot-assets/tapgarden_test.(*mintingTestHarness).finalizeBatch.func1()
	/home/runner/work/taproot-assets/taproot-assets/tapgarden/planter_test.go:302 +0x18b
created by github.com/lightninglabs/taproot-assets/tapgarden_test.(*mintingTestHarness).finalizeBatch in goroutine 684
	/home/runner/work/taproot-assets/taproot-assets/tapgarden/planter_test.go:290 +0x18b

goroutine 612 [select, 11 minutes]:
database/sql.(*DB).connectionCleaner(0xc000789380, 0x8bb2c97000)
	/opt/hostedtoolcache/go/1.22.3/x64/src/database/sql/sql.go:1089 +0x129
created by database/sql.(*DB).startCleanerLocked in goroutine 610
	/opt/hostedtoolcache/go/1.22.3/x64/src/database/sql/sql.go:1076 +0x268

goroutine 611 [select, 19 minutes]:
database/sql.(*DB).connectionOpener(0xc000789380, {0x2a5fef0, 0xc000f42820})
	/opt/hostedtoolcache/go/1.22.3/x64/src/database/sql/sql.go:1246 +0xed
created by database/sql.OpenDB in goroutine 610
	/opt/hostedtoolcache/go/1.22.3/x64/src/database/sql/sql.go:824 +0x2d4

goroutine 610 [chan receive, 19 minutes]:
testing.(*T).Run(0xc0003684e0, {0x26ae15d, 0x19}, 0xc000928030)
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:1750 +0x851
github.com/lightninglabs/taproot-assets/tapgarden_test.TestBatchedAssetIssuance(0xc0003684e0)
	/home/runner/work/taproot-assets/taproot-assets/tapgarden/planter_test.go:[182](https://github.com/lightninglabs/taproot-assets/actions/runs/9850764001/job/27196510871?pr=992#step:4:183)8 +0x5f
testing.tRunner(0xc0003684e0, 0x27f6668)
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:1689 +0x21f
created by testing.(*T).Run in goroutine 1
	/opt/hostedtoolcache/go/1.22.3/x64/src/testing/testing.go:1742 +0x826
FAIL	github.com/lightninglabs/taproot-assets/tapgarden	1200.193s

@jharveyb jharveyb force-pushed the marshal_batch_fixes branch from e347419 to a8c7639 Compare July 9, 2024 23:03
jharveyb added 7 commits July 9, 2024 20:23
In this commit, we change the batch marshalling behavior for finalized
batches. We return the batch seedlings, as these are not mutated once
an asset from a batch is spent. The next commit adds the means to fetch
fully populated assets for a finalized batch.
In this commit, we add a new method to proof.Archiver to support
querying for proofs with only the asset ID and anchor outpoint. This
is useful when fetching issuance proofs in order to display genesis
assets, as assets in the DB can be mutated if they are reanchored as
passive assets (participate in a transfer as change). This is only
implemented for the FileArchiver, as we can use proof file paths to
efficiently fetch only the issuance proofs.
In this commit, we update listBatches to fetch assets from finalized
batches via the proof file archiver instead of the DB. Given the batch
seedlings, we fetch issuance proofs and decode the asset in its genesis
state. We also verify that we can recompute the genesis output script
with the fetched assets.
@jharveyb jharveyb force-pushed the marshal_batch_fixes branch from a8c7639 to 62b431e Compare July 10, 2024 00:28
@jharveyb
Copy link
Contributor Author

Addressed all comments + added some extra querying to fill out the script key and group key info. No further changes planned.

Copy link
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 💃🏾


// TODO(jhb): Does funding guarantee that minting TXs always have
// exactly two outputs? If not this func should be fallible.
if genesisPkt.ChangeOutputIndex == 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the current FundPSBT call, yeah: we'll have the target output and a change output. Once we expose batch asset chan funding though, this'll change.

// If a proof cannot be found, then ErrProofNotFound should be returned.
//
// NOTE: This implements the Archiver interface.
func (f *FileArchiver) FetchIssuanceProof(ctx context.Context, id asset.ID,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't this just use the universe archive only? The only thing you need to query for the issuance proof is the asset ID: https://github.com/lightninglabs/taproot-assets/blob/main/universe/interface.go#L329-L336

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed offline, and for maximal backwards compat, the file store may be the best option here as it existed before the universe store did.

@Roasbeef Roasbeef merged commit 5236dea into main Jul 10, 2024
16 checks passed
@guggero guggero deleted the marshal_batch_fixes branch July 10, 2024 10:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: ✅ Done
Development

Successfully merging this pull request may close these issues.

tapgarden: minting batch display for pre-0.3.3 batches
6 participants