Skip to content

Commit e97827a

Browse files
authored
Merge pull request #1638 from lightninglabs/wip/supplycommit/seedling-db-delegation-key
tapdb: support delegation key and universe commitment flag in seedlings
2 parents ce81f8f + 01a3183 commit e97827a

File tree

9 files changed

+273
-95
lines changed

9 files changed

+273
-95
lines changed

tapdb/asset_minting.go

Lines changed: 104 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,45 @@ type OptionalSeedlingFields struct {
330330
GroupAnchorID sql.NullInt64
331331
}
332332

333+
// upsertDelegationKey inserts the given delegation key descriptor into
334+
// the internal_keys SQL table and returns its ID.
335+
func upsertDelegationKey(ctx context.Context, q PendingAssetStore,
336+
keyDescOpt fn.Option[keychain.KeyDescriptor]) (sql.NullInt64, error) {
337+
338+
var zero sql.NullInt64
339+
340+
// If the delegation key is not set, we can return a zero value.
341+
if keyDescOpt.IsNone() {
342+
return zero, nil
343+
}
344+
345+
// Unwrap the key descriptor to get the actual key.
346+
keyDesc, err := keyDescOpt.UnwrapOrErr(
347+
fmt.Errorf("delegation key is unexpectedly not set"),
348+
)
349+
if err != nil {
350+
return zero, err
351+
}
352+
353+
// Sanity check the key descriptor.
354+
if keyDesc.PubKey == nil {
355+
return zero, fmt.Errorf("delegation key pubkey is nil")
356+
}
357+
358+
// Insert the key descriptor into the internal_keys table.
359+
keyID, err := q.UpsertInternalKey(ctx, InternalKey{
360+
RawKey: keyDesc.PubKey.SerializeCompressed(),
361+
KeyFamily: int32(keyDesc.Family),
362+
KeyIndex: int32(keyDesc.Index),
363+
})
364+
if err != nil {
365+
return zero, fmt.Errorf("unable to insert internal key: %w",
366+
err)
367+
}
368+
369+
return sqlInt64(keyID), nil
370+
}
371+
333372
// insertMintAnchorTx inserts a mint anchor transaction into the database.
334373
func insertMintAnchorTx(ctx context.Context, q PendingAssetStore,
335374
anchorPackage tapgarden.FundedMintAnchorPsbt,
@@ -442,9 +481,10 @@ func (a *AssetMintingStore) CommitMintingBatch(ctx context.Context,
442481
// With our internal key inserted, we can now insert a new
443482
// batch which references the target internal key.
444483
if err := q.NewMintingBatch(ctx, MintingBatchInit{
445-
BatchID: batchID,
446-
HeightHint: int32(newBatch.HeightHint),
447-
CreationTimeUnix: newBatch.CreationTime.UTC(),
484+
BatchID: batchID,
485+
HeightHint: int32(newBatch.HeightHint),
486+
CreationTimeUnix: newBatch.CreationTime.UTC(),
487+
UniverseCommitments: newBatch.UniverseCommitments,
448488
}); err != nil {
449489
return fmt.Errorf("unable to insert minting "+
450490
"batch: %w", err)
@@ -510,6 +550,9 @@ func (a *AssetMintingStore) CommitMintingBatch(ctx context.Context,
510550
AssetSupply: int64(seedling.Amount),
511551
AssetMetaID: assetMetaID,
512552
EmissionEnabled: seedling.EnableEmission,
553+
554+
// nolint: lll
555+
UniverseCommitments: seedling.UniverseCommitments,
513556
}
514557

515558
scriptKeyID, err := upsertScriptKey(
@@ -544,6 +587,17 @@ func (a *AssetMintingStore) CommitMintingBatch(ctx context.Context,
544587
dbSeedling.GroupGenesisID = optionalDbIDs.GroupGenesisID
545588
dbSeedling.GroupAnchorID = optionalDbIDs.GroupAnchorID
546589

590+
// Upsert the seedling's delegation key if present.
591+
delegationKeyID, err := upsertDelegationKey(
592+
ctx, q, seedling.DelegationKey,
593+
)
594+
if err != nil {
595+
return fmt.Errorf("unable to insert "+
596+
"delegation key: %w", err)
597+
}
598+
599+
dbSeedling.DelegationKeyID = delegationKeyID
600+
547601
err = q.InsertAssetSeedling(ctx, dbSeedling)
548602
if err != nil {
549603
return err
@@ -641,6 +695,9 @@ func (a *AssetMintingStore) AddSeedlingsToBatch(ctx context.Context,
641695
AssetSupply: int64(seedling.Amount),
642696
AssetMetaID: assetMetaID,
643697
EmissionEnabled: seedling.EnableEmission,
698+
699+
// nolint: lll
700+
UniverseCommitments: seedling.UniverseCommitments,
644701
}
645702

646703
scriptKeyID, err := upsertScriptKey(
@@ -675,6 +732,18 @@ func (a *AssetMintingStore) AddSeedlingsToBatch(ctx context.Context,
675732
dbSeedling.GroupGenesisID = optionalDbIDs.GroupGenesisID
676733
dbSeedling.GroupAnchorID = optionalDbIDs.GroupAnchorID
677734

735+
// Handle delegation key: upsert to internal_keys and
736+
// reference in seedling.
737+
delegationKeyID, err := upsertDelegationKey(
738+
ctx, q, seedling.DelegationKey,
739+
)
740+
if err != nil {
741+
return fmt.Errorf("unable to insert "+
742+
"delegation key: %w", err)
743+
}
744+
745+
dbSeedling.DelegationKeyID = delegationKeyID
746+
678747
err = q.InsertAssetSeedlingIntoBatch(ctx, dbSeedling)
679748
if err != nil {
680749
return fmt.Errorf("unable to insert "+
@@ -724,7 +793,8 @@ func fetchAssetSeedlings(ctx context.Context, q PendingAssetStore,
724793
Amount: uint64(
725794
dbSeedling.AssetSupply,
726795
),
727-
EnableEmission: dbSeedling.EmissionEnabled,
796+
EnableEmission: dbSeedling.EmissionEnabled,
797+
UniverseCommitments: dbSeedling.UniverseCommitments,
728798
}
729799

730800
if dbSeedling.TweakedScriptKey != nil {
@@ -792,6 +862,31 @@ func fetchAssetSeedlings(ctx context.Context, q PendingAssetStore,
792862
}
793863
}
794864

865+
// If the seedling has a delegation key, we'll parse it and
866+
// store it in the seedling.
867+
if dbSeedling.DelegationKeyRaw != nil {
868+
delegationKeyPub, err := btcec.ParsePubKey(
869+
dbSeedling.DelegationKeyRaw,
870+
)
871+
if err != nil {
872+
return nil, err
873+
}
874+
875+
locator := keychain.KeyLocator{
876+
Index: extractSqlInt32[uint32](
877+
dbSeedling.DelegationKeyIndex,
878+
),
879+
Family: extractSqlInt32[keychain.KeyFamily](
880+
dbSeedling.DelegationKeyFam,
881+
),
882+
}
883+
884+
seedling.DelegationKey = fn.Some(keychain.KeyDescriptor{
885+
KeyLocator: locator,
886+
PubKey: delegationKeyPub,
887+
})
888+
}
889+
795890
if len(dbSeedling.GroupTapscriptRoot) != 0 {
796891
seedling.GroupTapscriptRoot = dbSeedling.
797892
GroupTapscriptRoot
@@ -1203,8 +1298,9 @@ func marshalMintingBatch(ctx context.Context, q PendingAssetStore,
12031298
},
12041299
PubKey: batchKey,
12051300
},
1206-
HeightHint: uint32(dbBatch.HeightHint),
1207-
CreationTime: dbBatch.CreationTimeUnix.UTC(),
1301+
HeightHint: uint32(dbBatch.HeightHint),
1302+
CreationTime: dbBatch.CreationTimeUnix.UTC(),
1303+
UniverseCommitments: dbBatch.UniverseCommitments,
12081304
}
12091305

12101306
batchState, err := tapgarden.NewBatchState(uint8(dbBatch.BatchState))
@@ -1336,12 +1432,9 @@ func marshalMintingBatch(ctx context.Context, q PendingAssetStore,
13361432
"genesis packet")
13371433
}
13381434

1339-
anchorOutputIndex := uint32(0)
1340-
if batch.GenesisPacket.ChangeOutputIndex == 0 {
1341-
anchorOutputIndex = 1
1342-
}
1435+
assetAnchorOutIdx := batch.GenesisPacket.AssetAnchorOutIdx
13431436
genesisTx := batch.GenesisPacket.Pkt.UnsignedTx
1344-
genesisScript := genesisTx.TxOut[anchorOutputIndex].PkScript
1437+
genesisScript := genesisTx.TxOut[assetAnchorOutIdx].PkScript
13451438
tapscriptSibling := batch.TapSibling()
13461439
batch.RootAssetCommitment, err = fetchAssetSprouts(
13471440
ctx, q, dbBatch.RawKey, tapscriptSibling, genesisScript,

tapdb/asset_minting_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ func assertBatchEqual(t *testing.T, a, b *tapgarden.MintingBatch) {
9393
t, &a.GenesisPacket.FundedPsbt, &b.GenesisPacket.FundedPsbt,
9494
)
9595
require.Equal(t, a.RootAssetCommitment, b.RootAssetCommitment)
96+
require.Equal(t, a.UniverseCommitments, b.UniverseCommitments)
9697
}
9798

9899
func assertSeedlingBatchLen(t *testing.T, batches []*tapgarden.MintingBatch,
@@ -608,6 +609,62 @@ func TestCommitMintingBatchSeedlings(t *testing.T) {
608609
assertSeedlingBatchLen(t, mintingBatches, 1, numSeedlings)
609610
}
610611

612+
// TestInsertFetchUniCommitBatch tests that we're able to properly write
613+
// and read a minting batch that has universe commitments enabled.
614+
func TestInsertFetchUniCommitBatch(t *testing.T) {
615+
t.Parallel()
616+
617+
ctx := context.Background()
618+
assetStore, _, _ := newAssetStore(t)
619+
620+
// Generate a batch with a single seedling, we will set the group
621+
// information later.
622+
batch := tapgarden.RandMintingBatch(
623+
t, tapgarden.WithTotalGroups([]int{1}),
624+
tapgarden.WithUniverseCommitments(true),
625+
)
626+
require.True(t, batch.UniverseCommitments)
627+
628+
// Generate the group genesis, persist it, and assign the group details
629+
// to the seedling.
630+
assetName := maps.Keys(batch.Seedlings)[0]
631+
assetType := batch.Seedlings[assetName].AssetType
632+
633+
privDesc, groupPriv := test.RandKeyDesc(t)
634+
randGenesis := asset.RandGenesis(t, assetType)
635+
_, _, group := storeGroupGenesis(
636+
t, ctx, randGenesis, nil, assetStore, privDesc, groupPriv,
637+
)
638+
639+
batch.Seedlings[assetName].GroupInfo = group
640+
641+
// Assert that the seedling is in the expected state.
642+
seedling := batch.Seedlings[assetName]
643+
require.True(t, seedling.UniverseCommitments)
644+
require.True(t, seedling.DelegationKey.IsSome())
645+
646+
// Commit the minting batch to the database.
647+
err := assetStore.CommitMintingBatch(ctx, batch)
648+
require.NoError(t, err)
649+
650+
// Fetch the same batch from the database.
651+
dbBatch, err := assetStore.FetchMintingBatch(
652+
ctx, batch.BatchKey.PubKey,
653+
)
654+
require.NoError(t, err)
655+
656+
// Assert that the batch is in the expected state.
657+
require.True(t, dbBatch.UniverseCommitments)
658+
659+
// Assert that the seedling is in the expected state.
660+
require.Len(t, dbBatch.Seedlings, 1)
661+
662+
dbSeedling := dbBatch.Seedlings[assetName]
663+
require.True(t, dbSeedling.UniverseCommitments)
664+
require.True(t, dbSeedling.DelegationKey.IsSome())
665+
require.Equal(t, dbSeedling.DelegationKey, seedling.DelegationKey)
666+
}
667+
611668
// seedlingsToAssetRoot maps a set of seedlings to an asset root.
612669
//
613670
// TODO(roasbeef): same func in tapgarden can just re-use?

tapdb/migrations.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const (
2424
// daemon.
2525
//
2626
// NOTE: This MUST be updated when a new migration is added.
27-
LatestMigrationVersion = 40
27+
LatestMigrationVersion = 41
2828
)
2929

3030
// DatabaseBackend is an interface that contains all methods our different

0 commit comments

Comments
 (0)