Skip to content

Commit 423306a

Browse files
committed
tapdb: extend UnspentPrecommits to include remote issuers
Update method SupplyCommitMachine.UnspentPrecommits to return pre-commitment outputs generated by remote issuer nodes, in addition to those from the local node.
1 parent 98edcb4 commit 423306a

File tree

8 files changed

+131
-29
lines changed

8 files changed

+131
-29
lines changed

tapdb/supply_commit.go

Lines changed: 99 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"time"
1111

1212
"github.com/btcsuite/btcd/btcec/v2"
13+
"github.com/btcsuite/btcd/btcec/v2/schnorr"
1314
"github.com/btcsuite/btcd/chaincfg/chainhash"
1415
"github.com/btcsuite/btcd/wire"
1516
"github.com/lightninglabs/taproot-assets/asset"
@@ -20,14 +21,21 @@ import (
2021
"github.com/lightninglabs/taproot-assets/universe/supplycommit"
2122
"github.com/lightninglabs/taproot-assets/universe/supplyverifier"
2223
lfn "github.com/lightningnetwork/lnd/fn/v2"
24+
"github.com/lightningnetwork/lnd/keychain"
2325
"github.com/lightningnetwork/lnd/lnutils"
2426
)
2527

2628
type (
2729
// UnspentMintPreCommits is an alias for the sqlc type representing an
28-
// unspent supply pre-commitment row.
30+
// unspent supply pre-commitment row where the local node was the
31+
// issuer.
2932
UnspentMintPreCommits = sqlc.FetchUnspentMintSupplyPreCommitsRow
3033

34+
// UnspentPreCommits is an alias for the sqlc type representing an
35+
// unspent supply pre-commitment row where a remote node was the
36+
// issuer.
37+
UnspentPreCommits = sqlc.FetchUnspentSupplyPreCommitsRow
38+
3139
// SupplyCommit is an alias for the sqlc type.
3240
SupplyCommit = sqlc.FetchSupplyCommitRow
3341

@@ -109,6 +117,12 @@ type SupplyCommitStore interface {
109117
FetchUnspentMintSupplyPreCommits(ctx context.Context,
110118
groupKey []byte) ([]UnspentMintPreCommits, error)
111119

120+
// FetchUnspentSupplyPreCommits fetches all unspent supply
121+
// pre-commitments for the specified asset group key where a remote
122+
// node was the issuer.
123+
FetchUnspentSupplyPreCommits(ctx context.Context,
124+
groupKey []byte) ([]UnspentPreCommits, error)
125+
112126
// FetchSupplyCommit fetches the latest confirmed supply commitment for
113127
// a given group key.
114128
FetchSupplyCommit(ctx context.Context,
@@ -267,7 +281,8 @@ func NewSupplyCommitMachine(db BatchedSupplyCommitStore) *SupplyCommitMachine {
267281
// asset spec. The asset spec will only specify a group key, and not also an
268282
// asset ID.
269283
func (s *SupplyCommitMachine) UnspentPrecommits(ctx context.Context,
270-
assetSpec asset.Specifier) lfn.Result[supplycommit.PreCommits] {
284+
assetSpec asset.Specifier,
285+
localIssuerOnly bool) lfn.Result[supplycommit.PreCommits] {
271286

272287
groupKey := assetSpec.UnwrapGroupKeyToPtr()
273288
if groupKey == nil {
@@ -278,23 +293,25 @@ func (s *SupplyCommitMachine) UnspentPrecommits(ctx context.Context,
278293
var preCommits supplycommit.PreCommits
279294
readTx := ReadTxOption()
280295
dbErr := s.db.ExecTx(ctx, readTx, func(db SupplyCommitStore) error {
281-
rows, err := db.FetchUnspentMintSupplyPreCommits(
296+
mintRows, err := db.FetchUnspentMintSupplyPreCommits(
282297
ctx, groupKeyBytes,
283298
)
284-
if err != nil {
285-
// It's okay if there are no unspent pre-commits.
286-
if errors.Is(err, sql.ErrNoRows) {
287-
return nil
288-
}
289-
return fmt.Errorf("error fetching unspent "+
290-
"precommits: %w", err)
299+
switch {
300+
case errors.Is(err, sql.ErrNoRows):
301+
// No unspent pre-commits minted by this local node
302+
// exist for this group key. Proceed to query for
303+
// pre-commits from other issuers.
304+
305+
case err != nil:
306+
return fmt.Errorf("failed to fetch unspent local node "+
307+
"issued pre-commit outputs: %w", err)
291308
}
292309

293310
// For each pre-commitment, parse the internal key and group
294311
// key, and assemble the final struct as needed by the
295312
// interface.
296-
preCommits = make(supplycommit.PreCommits, 0, len(rows))
297-
for _, row := range rows {
313+
preCommits = make(supplycommit.PreCommits, 0, len(mintRows))
314+
for _, row := range mintRows {
298315
internalKey, err := parseInternalKey(row.InternalKey)
299316
if err != nil {
300317
return fmt.Errorf("failed to parse "+
@@ -326,6 +343,76 @@ func (s *SupplyCommitMachine) UnspentPrecommits(ctx context.Context,
326343
preCommits = append(preCommits, preCommit)
327344
}
328345

346+
// If any pre-commits were found where we acted as the issuer,
347+
// return early and skip querying for pre-commits from other
348+
// issuers. Also return early if the caller explicitly requested
349+
// only pre-commits issued by the local node.
350+
if len(preCommits) > 0 || localIssuerOnly {
351+
return nil
352+
}
353+
354+
// No pre-commits found where we were the issuer. So now
355+
// we'll query for pre-commits from other issuers.
356+
rows, err := db.FetchUnspentSupplyPreCommits(
357+
ctx, schnorr.SerializePubKey(groupKey),
358+
)
359+
switch {
360+
case errors.Is(err, sql.ErrNoRows):
361+
// No unspent pre-commits minted by peer issuer nodes
362+
// exist for this group key. Return early.
363+
return nil
364+
365+
case err != nil:
366+
return fmt.Errorf("failed to fetch unspent remote "+
367+
"node issued pre-commit outputs: %w", err)
368+
}
369+
370+
// Parse rows into pre-commitment structs.
371+
for _, row := range rows {
372+
pubKey, err := btcec.ParsePubKey(row.TaprootInternalKey)
373+
if err != nil {
374+
return fmt.Errorf("failed to parse internal "+
375+
"raw key: %w", err)
376+
}
377+
378+
internalKey := keychain.KeyDescriptor{
379+
PubKey: pubKey,
380+
}
381+
382+
groupPubKey, err := schnorr.ParsePubKey(row.GroupKey)
383+
if err != nil {
384+
return fmt.Errorf("error parsing group key: %w",
385+
err)
386+
}
387+
388+
var mintingTx wire.MsgTx
389+
err = mintingTx.Deserialize(bytes.NewReader(row.RawTx))
390+
if err != nil {
391+
return fmt.Errorf("error deserializing "+
392+
"minting tx: %w", err)
393+
}
394+
395+
var outpoint wire.OutPoint
396+
err = readOutPoint(
397+
bytes.NewReader(row.Outpoint), 0, 0, &outpoint,
398+
)
399+
if err != nil {
400+
return fmt.Errorf("%w: %w", ErrReadOutpoint,
401+
err)
402+
}
403+
404+
preCommit := supplycommit.PreCommitment{
405+
BlockHeight: uint32(
406+
row.BlockHeight.Int32,
407+
),
408+
MintingTxn: &mintingTx,
409+
OutIdx: outpoint.Index,
410+
InternalKey: internalKey,
411+
GroupPubKey: *groupPubKey,
412+
}
413+
preCommits = append(preCommits, preCommit)
414+
}
415+
329416
return nil
330417
})
331418
if dbErr != nil {

tapdb/supply_commit_test.go

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,7 +1705,9 @@ func TestSupplyCommitApplyStateTransition(t *testing.T) {
17051705

17061706
// Verify we have all three unspent pre-commitments before the
17071707
// transition.
1708-
precommitsRes := h.commitMachine.UnspentPrecommits(h.ctx, h.assetSpec)
1708+
precommitsRes := h.commitMachine.UnspentPrecommits(
1709+
h.ctx, h.assetSpec, true,
1710+
)
17091711
precommits, err := precommitsRes.Unpack()
17101712
require.NoError(t, err)
17111713
require.Len(
@@ -1730,7 +1732,9 @@ func TestSupplyCommitApplyStateTransition(t *testing.T) {
17301732
// After the first transition, only the two pre-commitments that were
17311733
// included in the transaction inputs should be marked as spent.
17321734
// The extra pre-commitment should remain unspent.
1733-
precommitsRes = h.commitMachine.UnspentPrecommits(h.ctx, h.assetSpec)
1735+
precommitsRes = h.commitMachine.UnspentPrecommits(
1736+
h.ctx, h.assetSpec, true,
1737+
)
17341738
precommits, err = precommitsRes.Unpack()
17351739
require.NoError(t, err)
17361740
require.Len(
@@ -1760,7 +1764,9 @@ func TestSupplyCommitApplyStateTransition(t *testing.T) {
17601764
)
17611765

17621766
// Verify we have the extra one from before plus the new one.
1763-
precommitsRes = h.commitMachine.UnspentPrecommits(h.ctx, h.assetSpec)
1767+
precommitsRes = h.commitMachine.UnspentPrecommits(
1768+
h.ctx, h.assetSpec, true,
1769+
)
17641770
precommits, err = precommitsRes.Unpack()
17651771
require.NoError(t, err)
17661772
require.Len(
@@ -1783,7 +1789,9 @@ func TestSupplyCommitApplyStateTransition(t *testing.T) {
17831789

17841790
// After the second transition, the new pre-commitment should also be
17851791
// spent. Finally, verify that no unspent pre-commitments remain.
1786-
precommitsRes = h.commitMachine.UnspentPrecommits(h.ctx, h.assetSpec)
1792+
precommitsRes = h.commitMachine.UnspentPrecommits(
1793+
h.ctx, h.assetSpec, true,
1794+
)
17871795
precommits, err = precommitsRes.Unpack()
17881796
require.NoError(t, err)
17891797
require.Empty(
@@ -1805,7 +1813,7 @@ func TestSupplyCommitUnspentPrecommits(t *testing.T) {
18051813
)
18061814

18071815
// To start with, we shouldn't have any precommits.
1808-
precommitsRes := h.commitMachine.UnspentPrecommits(h.ctx, spec)
1816+
precommitsRes := h.commitMachine.UnspentPrecommits(h.ctx, spec, true)
18091817
precommits, err := precommitsRes.Unpack()
18101818
require.NoError(t, err)
18111819
require.Empty(t, precommits)
@@ -1819,7 +1827,7 @@ func TestSupplyCommitUnspentPrecommits(t *testing.T) {
18191827
)
18201828

18211829
// At this point, we should find a single pre commitment on disk.
1822-
precommitsRes = h.commitMachine.UnspentPrecommits(h.ctx, spec)
1830+
precommitsRes = h.commitMachine.UnspentPrecommits(h.ctx, spec, true)
18231831
precommits, err = precommitsRes.Unpack()
18241832
require.NoError(t, err)
18251833
require.Len(t, precommits, 1)
@@ -1839,7 +1847,7 @@ func TestSupplyCommitUnspentPrecommits(t *testing.T) {
18391847
)
18401848

18411849
// We should now find two pre-commitments.
1842-
precommitsRes = h.commitMachine.UnspentPrecommits(h.ctx, spec)
1850+
precommitsRes = h.commitMachine.UnspentPrecommits(h.ctx, spec, true)
18431851
precommits, err = precommitsRes.Unpack()
18441852
require.NoError(t, err)
18451853
require.Len(t, precommits, 2)
@@ -1861,7 +1869,7 @@ func TestSupplyCommitUnspentPrecommits(t *testing.T) {
18611869

18621870
// As the transaction was confirmed above, we should now only have a
18631871
// single pre commitment on disk.
1864-
precommitsRes = h.commitMachine.UnspentPrecommits(h.ctx, spec)
1872+
precommitsRes = h.commitMachine.UnspentPrecommits(h.ctx, spec, true)
18651873
precommits, err = precommitsRes.Unpack()
18661874
require.NoError(t, err)
18671875
require.Len(t, precommits, 1)
@@ -1872,14 +1880,18 @@ func TestSupplyCommitUnspentPrecommits(t *testing.T) {
18721880
otherSpec := asset.NewSpecifierOptionalGroupPubKey(
18731881
asset.RandID(t), otherGroupKey,
18741882
)
1875-
precommitsRes = h.commitMachine.UnspentPrecommits(h.ctx, otherSpec)
1883+
precommitsRes = h.commitMachine.UnspentPrecommits(
1884+
h.ctx, otherSpec, true,
1885+
)
18761886
precommits, err = precommitsRes.Unpack()
18771887
require.NoError(t, err)
18781888
require.Empty(t, precommits)
18791889

18801890
// Finally, trying with a missing group key should yield an error.
18811891
emptySpec := asset.NewSpecifierOptionalGroupKey(asset.RandID(t), nil)
1882-
precommitsRes = h.commitMachine.UnspentPrecommits(h.ctx, emptySpec)
1892+
precommitsRes = h.commitMachine.UnspentPrecommits(
1893+
h.ctx, emptySpec, true,
1894+
)
18831895
require.ErrorIs(t, precommitsRes.Err(), ErrMissingGroupKey)
18841896
}
18851897

universe/supplycommit/env.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -645,8 +645,8 @@ type CommitmentTracker interface {
645645
// UnspentPrecommits returns the set of unspent pre-commitments for a
646646
// given asset spec. The asset spec will only specify a group key, and
647647
// not also an asset ID.
648-
UnspentPrecommits(ctx context.Context,
649-
assetSpec asset.Specifier) lfn.Result[PreCommits]
648+
UnspentPrecommits(ctx context.Context, assetSpec asset.Specifier,
649+
localIssuerOnly bool) lfn.Result[PreCommits]
650650

651651
// SupplyCommit returns the root commitment for a given asset spec. From
652652
// the PoV of the chain, this is a singleton instance.

universe/supplycommit/mock.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,10 @@ type mockCommitmentTracker struct {
6666
}
6767

6868
func (m *mockCommitmentTracker) UnspentPrecommits(ctx context.Context,
69-
assetSpec asset.Specifier) lfn.Result[PreCommits] {
69+
assetSpec asset.Specifier,
70+
localIssuerOnly bool) lfn.Result[PreCommits] {
7071

71-
args := m.Called(ctx, assetSpec)
72+
args := m.Called(ctx, assetSpec, localIssuerOnly)
7273
return args.Get(0).(lfn.Result[PreCommits])
7374
}
7475

universe/supplycommit/state_machine_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,7 @@ func (h *supplyCommitTestHarness) expectTreeFetches() {
397397
func (h *supplyCommitTestHarness) expectCommitmentFetches() {
398398
h.mockCommits.On(
399399
"UnspentPrecommits", mock.Anything, mock.Anything,
400+
mock.Anything,
400401
).Return(
401402
lfn.Ok[PreCommits](nil),
402403
).Once()

universe/supplycommit/transitions.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -719,7 +719,7 @@ func (c *CommitTxCreateState) ProcessEvent(event Event,
719719
// commitments, the current supply root (which may not exist),
720720
// and the new supply root.
721721
preCommits, err := env.Commitments.UnspentPrecommits(
722-
ctx, env.AssetSpec,
722+
ctx, env.AssetSpec, true,
723723
).Unpack()
724724
if err != nil {
725725
return nil, fmt.Errorf("unable to fetch "+

universe/supplyverifier/env.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ type SupplyCommitView interface {
3030
// UnspentPrecommits returns the set of unspent pre-commitments for a
3131
// given asset spec.
3232
UnspentPrecommits(ctx context.Context,
33-
assetSpec asset.Specifier) lfn.Result[supplycommit.PreCommits]
33+
assetSpec asset.Specifier,
34+
localIssuerOnly bool) lfn.Result[supplycommit.PreCommits]
3435

3536
// SupplyCommit returns the latest supply commitment for a given asset
3637
// spec.

universe/supplyverifier/verifier.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ func (v *Verifier) ensurePrecommitsSpent(ctx context.Context,
9797

9898
// Fetch all unspent pre-commitment outputs for the asset group.
9999
allPreCommits, err := v.cfg.SupplyCommitView.UnspentPrecommits(
100-
ctx, assetSpec,
100+
ctx, assetSpec, false,
101101
).Unpack()
102102
if err != nil {
103103
return fmt.Errorf("unable to fetch unspent pre-commitments: %w",

0 commit comments

Comments
 (0)