Skip to content

Commit 31e517e

Browse files
committed
universe: fix validation bug in batch of transfer leaves
In this commit we fix two bugs: 1. Before RegisterNewIssuanceBatch didn't properly attempt to look up the prev input for items in a batch. This caused the transfer leaf validation to fail, bailing the whole thing. 2. When validating items of a batch, inputs spent in the batch may have been produced within the batch itself. As a result, the look up for the local universe tree will fail, as we're still validating the batch. This is similar to needing to check for inputs spent in a block before assuming the input doesn't exist. To fix this, we make a new set of batch deps, then check that before we consult our local universe.
1 parent a7f70bf commit 31e517e

File tree

1 file changed

+61
-4
lines changed

1 file changed

+61
-4
lines changed

universe/base.go

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,9 @@ func (a *MintingArchive) RegisterIssuance(ctx context.Context, id Identifier,
198198
// Before we can validate a non-issuance proof we need to fetch the
199199
// previous asset snapshot (which is the proof verification result for
200200
// the previous/parent proof in the proof file).
201-
prevAssetSnapshot, err := a.getPrevAssetSnapshot(ctx, id, *newProof)
201+
prevAssetSnapshot, err := a.getPrevAssetSnapshot(
202+
ctx, id, *newProof, nil,
203+
)
202204
if err != nil {
203205
return nil, fmt.Errorf("unable to fetch previous asset "+
204206
"snapshot: %w", err)
@@ -280,6 +282,18 @@ func (a *MintingArchive) verifyIssuanceProof(ctx context.Context, id Identifier,
280282
return assetSnapshot, nil
281283
}
282284

285+
// extractBatchDeps constructs map from leaf key to asset in a batch. This is
286+
// useful for when we're validating an asset state transition in a batch, and
287+
// the input asset it depends on is created in the batch.
288+
func extractBatchDeps(batch []*IssuanceItem) map[UniverseKey]*asset.Asset {
289+
batchDeps := make(map[UniverseKey]*asset.Asset)
290+
for _, item := range batch {
291+
batchDeps[item.Key.UniverseKey()] = &item.Leaf.Proof.Asset
292+
}
293+
294+
return batchDeps
295+
}
296+
283297
// RegisterNewIssuanceBatch inserts a batch of new minting leaves within the
284298
// target universe tree (based on the ID), stored at the base key(s). We assume
285299
// the proofs within the batch have already been checked that they don't yet
@@ -329,13 +343,24 @@ func (a *MintingArchive) RegisterNewIssuanceBatch(ctx context.Context,
329343
}
330344
}
331345

346+
batchDeps := extractBatchDeps(items)
347+
332348
verifyBatch := func(batchItems []*IssuanceItem) error {
333349
err := fn.ParSlice(
334350
ctx, batchItems, func(ctx context.Context,
335351
i *IssuanceItem) error {
336352

353+
prevAssets, err := a.getPrevAssetSnapshot(
354+
ctx, i.ID, *i.Leaf.Proof, batchDeps,
355+
)
356+
if err != nil {
357+
return fmt.Errorf("unable to "+
358+
"fetch previous asset "+
359+
"snapshot: %w", err)
360+
}
361+
337362
assetSnapshot, err := a.verifyIssuanceProof(
338-
ctx, i.ID, i.Key, i.Leaf, nil,
363+
ctx, i.ID, i.Key, i.Leaf, prevAssets,
339364
)
340365
if err != nil {
341366
return err
@@ -397,10 +422,14 @@ func (a *MintingArchive) RegisterNewIssuanceBatch(ctx context.Context,
397422
return nil
398423
}
399424

425+
// UniverseKey represents the key used to locate an item within a universe.
426+
type UniverseKey [32]byte
427+
400428
// getPrevAssetSnapshot returns the previous asset snapshot for the passed
401429
// proof. If the proof is a genesis proof, then nil is returned.
402430
func (a *MintingArchive) getPrevAssetSnapshot(ctx context.Context,
403-
uniID Identifier, newProof proof.Proof) (*proof.AssetSnapshot, error) {
431+
uniID Identifier, newProof proof.Proof,
432+
batchAssets map[UniverseKey]*asset.Asset) (*proof.AssetSnapshot, error) {
404433

405434
// If this is a genesis proof, then there is no previous asset (and
406435
// therefore no previous asset snapshot).
@@ -433,9 +462,33 @@ func (a *MintingArchive) getPrevAssetSnapshot(ctx context.Context,
433462
ScriptKey: &prevScriptKey,
434463
}
435464

465+
// First, we'll check if the prev asset that we need is already amongst
466+
// the batch we have, if so then we won't have it in our universe tree
467+
// yet, so we'll return it directly.
468+
if batchAssets != nil {
469+
newAsset := newProof.Asset
470+
newScriptKey := newAsset.ScriptKey.PubKey.SerializeCompressed()
471+
472+
inputAsset, ok := batchAssets[prevLeafKey.UniverseKey()]
473+
if ok {
474+
475+
log.Debugf("script_key=%x spends item in batch, "+
476+
"universe_key=%x using batch input",
477+
newScriptKey, prevLeafKey.UniverseKey())
478+
479+
return &proof.AssetSnapshot{
480+
Asset: inputAsset,
481+
OutPoint: prevID.OutPoint,
482+
}, nil
483+
}
484+
}
485+
486+
// If we can't find the previous asset in the batch, then we'll query
487+
// our local universe.
436488
prevProofs, err := a.cfg.Multiverse.FetchProofLeaf(
437489
ctx, uniID, prevLeafKey,
438490
)
491+
439492
// If we've failed in finding the previous proof in the transfer
440493
// universe, we will try to find it in the issuance universe.
441494
if uniID.ProofType == ProofTypeTransfer &&
@@ -448,11 +501,15 @@ func (a *MintingArchive) getPrevAssetSnapshot(ctx context.Context,
448501
}
449502
if err != nil {
450503
return nil, fmt.Errorf("unable to fetch previous "+
451-
"proof: %v", err)
504+
"proof: %v, id=%v, leaf_key=%v, new_script_key=%x", err,
505+
spew.Sdump(uniID), spew.Sdump(prevLeafKey),
506+
newProof.Asset.ScriptKey.PubKey.SerializeCompressed())
452507
}
453508

454509
prevProof := prevProofs[0].Leaf.Proof
455510

511+
// TODO(roasbeef): need more than one snapshot, for inputs
512+
456513
// Construct minimal asset snapshot for previous asset.
457514
// This is a minimal the proof verification result for the
458515
// previous (input) asset. We know that it was already verified

0 commit comments

Comments
 (0)