Skip to content

Commit 349f609

Browse files
jharveybguggero
authored andcommitted
tapdb: fetch altLeaves during input coin selection
1 parent f484f8d commit 349f609

File tree

4 files changed

+110
-28
lines changed

4 files changed

+110
-28
lines changed

tapdb/assets_store.go

Lines changed: 81 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1409,30 +1409,9 @@ func (a *AssetStore) FetchProof(ctx context.Context,
14091409

14101410
readOpts := NewAssetStoreReadTx()
14111411
dbErr := a.db.ExecTx(ctx, &readOpts, func(q ActiveAssetsStore) error {
1412-
assetProofs, err := q.FetchAssetProof(ctx, args)
1413-
if err != nil {
1414-
return fmt.Errorf("unable to fetch asset proof: %w",
1415-
err)
1416-
}
1417-
1418-
switch {
1419-
// We have no proof for this script key.
1420-
case len(assetProofs) == 0:
1421-
return proof.ErrProofNotFound
1422-
1423-
// If the query without the outpoint returns exactly one proof
1424-
// then we're fine. If there actually are multiple proofs, we
1425-
// require the user to specify the outpoint as well.
1426-
case len(assetProofs) == 1:
1427-
diskProof = assetProofs[0].ProofFile
1428-
1429-
return nil
1430-
1431-
// User needs to specify the outpoint as well, since we have
1432-
// multiple proofs for this script key.
1433-
default:
1434-
return proof.ErrMultipleProofs
1435-
}
1412+
var err error
1413+
diskProof, err = fetchProof(ctx, q, args)
1414+
return err
14361415
})
14371416
switch {
14381417
case errors.Is(dbErr, sql.ErrNoRows):
@@ -1444,6 +1423,34 @@ func (a *AssetStore) FetchProof(ctx context.Context,
14441423
return diskProof, nil
14451424
}
14461425

1426+
// fetchProof is a wrapper around the FetchAssetProof query that enforces that
1427+
// a proof is only returned if exactly one matching proof was found.
1428+
func fetchProof(ctx context.Context, q ActiveAssetsStore,
1429+
args sqlc.FetchAssetProofParams) (proof.Blob, error) {
1430+
1431+
assetProofs, err := q.FetchAssetProof(ctx, args)
1432+
if err != nil {
1433+
return nil, fmt.Errorf("unable to fetch asset proof: %w", err)
1434+
}
1435+
1436+
switch {
1437+
// We have no proof for this script key.
1438+
case len(assetProofs) == 0:
1439+
return nil, proof.ErrProofNotFound
1440+
1441+
// If the query without the outpoint returns exactly one proof
1442+
// then we're fine. If there actually are multiple proofs, we
1443+
// require the user to specify the outpoint as well.
1444+
case len(assetProofs) == 1:
1445+
return assetProofs[0].ProofFile, nil
1446+
1447+
// User needs to specify the outpoint as well, since we have
1448+
// multiple proofs for this script key.
1449+
default:
1450+
return nil, proof.ErrMultipleProofs
1451+
}
1452+
}
1453+
14471454
// locatorToProofQuery turns a proof locator into a FetchAssetProof query
14481455
// struct.
14491456
func locatorToProofQuery(locator proof.Locator) (FetchAssetProof, error) {
@@ -2084,8 +2091,12 @@ func (a *AssetStore) queryCommitments(ctx context.Context,
20842091
chainAnchorToAssets = make(
20852092
map[wire.OutPoint][]*asset.ChainAsset,
20862093
)
2087-
anchorPoints = make(map[wire.OutPoint]AnchorPoint)
2088-
err error
2094+
anchorPoints = make(map[wire.OutPoint]AnchorPoint)
2095+
anchorAltLeaves = make(
2096+
map[wire.OutPoint][]asset.AltLeaf[asset.Asset],
2097+
)
2098+
matchingAssetProofs = make(map[wire.OutPoint]proof.Blob)
2099+
err error
20892100
)
20902101

20912102
readOpts := NewAssetStoreReadTx()
@@ -2145,6 +2156,31 @@ func (a *AssetStore) queryCommitments(ctx context.Context,
21452156
}
21462157

21472158
anchorPoints[anchorPoint] = anchorUTXO
2159+
2160+
// TODO(jhb): replace full proof fetch with
2161+
// outpoint -> alt leaf table / index
2162+
// We also need to fetch the input proof here, in order
2163+
// to fetch any committed alt leaves.
2164+
assetLoc := proof.Locator{
2165+
AssetID: fn.Ptr(matchingAsset.ID()),
2166+
ScriptKey: *matchingAsset.ScriptKey.PubKey,
2167+
OutPoint: &matchingAsset.AnchorOutpoint,
2168+
}
2169+
proofArgs, err := locatorToProofQuery(assetLoc)
2170+
if err != nil {
2171+
return err
2172+
}
2173+
2174+
var assetProof proof.Blob
2175+
assetProof, err = fetchProof(ctx, q, proofArgs)
2176+
switch {
2177+
case errors.Is(err, sql.ErrNoRows):
2178+
return proof.ErrProofNotFound
2179+
case err != nil:
2180+
return err
2181+
}
2182+
2183+
matchingAssetProofs[anchorPoint] = assetProof
21482184
}
21492185

21502186
return nil
@@ -2153,6 +2189,17 @@ func (a *AssetStore) queryCommitments(ctx context.Context,
21532189
return nil, dbErr
21542190
}
21552191

2192+
for anchorPoint, rawProof := range matchingAssetProofs {
2193+
lastProof, err := rawProof.AsSingleProof()
2194+
if err != nil {
2195+
return nil, err
2196+
}
2197+
2198+
anchorAltLeaves[anchorPoint] = append(
2199+
anchorAltLeaves[anchorPoint], lastProof.AltLeaves...,
2200+
)
2201+
}
2202+
21562203
// Our final query wants the complete Taproot Asset commitment for each
21572204
// of the managed UTXOs. Some of the assets that match our query might
21582205
// actually be in the same Taproot Asset commitment, so we'll collect
@@ -2164,6 +2211,7 @@ func (a *AssetStore) queryCommitments(ctx context.Context,
21642211
anchorPoint := anchorPoint
21652212
anchorUTXO := anchorPoints[anchorPoint]
21662213
anchoredAssets := chainAnchorToAssets[anchorPoint]
2214+
anchoredAltLeaves := anchorAltLeaves[anchorPoint]
21672215

21682216
// Fetch the asset leaves from each chain asset, and then
21692217
// build a Taproot Asset commitment from this set of assets.
@@ -2199,6 +2247,13 @@ func (a *AssetStore) queryCommitments(ctx context.Context,
21992247
return nil, err
22002248
}
22012249

2250+
// The reconstructed commitment must also include any alt leaves
2251+
// included in the original commitment.
2252+
err = tapCommitment.MergeAltLeaves(anchoredAltLeaves)
2253+
if err != nil {
2254+
return nil, err
2255+
}
2256+
22022257
// Verify that the constructed Taproot Asset commitment matches
22032258
// the commitment root stored in the managed UTXO.
22042259
commitmentRoot := tapCommitment.TapscriptRoot(nil)

tapdb/assets_store_test.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,28 @@ func (a *assetGenerator) genAssets(t *testing.T, assetStore *AssetStore,
618618
height := a.anchorPointsToHeights[desc.anchorPoint]
619619
tapCommitment := anchorPointsToTapCommitments[desc.anchorPoint]
620620

621-
err := assetStore.importAssetFromProof(
621+
// Encode a minimal proof so we have a valid proof blob to
622+
// store.
623+
assetProof := proof.Proof{}
624+
assetProof.AnchorTx = *anchorPoint
625+
assetProof.BlockHeight = height
626+
627+
txMerkleProof, err := proof.NewTxMerkleProof(
628+
[]*wire.MsgTx{anchorPoint}, 0,
629+
)
630+
require.NoError(t, err)
631+
632+
assetProof.TxMerkleProof = *txMerkleProof
633+
assetProof.Asset = *newAsset
634+
assetProof.InclusionProof = proof.TaprootProof{
635+
OutputIndex: 0,
636+
InternalKey: test.RandPubKey(t),
637+
}
638+
639+
proofBlob, err := proof.EncodeAsProofFile(&assetProof)
640+
require.NoError(t, err)
641+
642+
err = assetStore.importAssetFromProof(
622643
ctx, assetStore.db, &proof.AnnotatedProof{
623644
AssetSnapshot: &proof.AssetSnapshot{
624645
AnchorTx: anchorPoint,
@@ -627,7 +648,7 @@ func (a *assetGenerator) genAssets(t *testing.T, assetStore *AssetStore,
627648
ScriptRoot: tapCommitment,
628649
AnchorBlockHeight: height,
629650
},
630-
Blob: bytes.Repeat([]byte{1}, 100),
651+
Blob: proofBlob,
631652
},
632653
)
633654
require.NoError(t, err)

tapdb/sqlutils_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ func (d *DbHandler) AddRandomAssetProof(t *testing.T) (*asset.Asset,
5656
// Next, we'll make a new random asset that also has a few inputs with
5757
// dummy witness information.
5858
testAsset := randAsset(t)
59+
testAltLeaves := asset.ToAltLeaves(asset.RandAltLeaves(t, true))
5960

6061
assetRoot, err := commitment.NewAssetCommitment(testAsset)
6162
require.NoError(t, err)
@@ -66,6 +67,9 @@ func (d *DbHandler) AddRandomAssetProof(t *testing.T) (*asset.Asset,
6667
)
6768
require.NoError(t, err)
6869

70+
err = taprootAssetRoot.MergeAltLeaves(testAltLeaves)
71+
require.NoError(t, err)
72+
6973
// With our asset created, we can now create the AnnotatedProof we use
7074
// to import assets into the database.
7175
var blockHash chainhash.Hash
@@ -87,6 +91,7 @@ func (d *DbHandler) AddRandomAssetProof(t *testing.T) (*asset.Asset,
8791

8892
// Generate a random proof and encode it into a proof blob.
8993
testProof := randProof(t, testAsset)
94+
testProof.AltLeaves = testAltLeaves
9095

9196
var proofBlobBuffer bytes.Buffer
9297
err = testProof.Encode(&proofBlobBuffer)

tapdb/universe_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ func randProof(t *testing.T, argAsset *asset.Asset) *proof.Proof {
184184
InclusionProof: proof.TaprootProof{
185185
InternalKey: test.RandPubKey(t),
186186
},
187+
AltLeaves: asset.ToAltLeaves(asset.RandAltLeaves(t, true)),
187188
}
188189
}
189190

0 commit comments

Comments
 (0)