Skip to content

Commit 08e0ea2

Browse files
committed
tapgarden: implement SealBatch
1 parent a60f7aa commit 08e0ea2

File tree

1 file changed

+87
-26
lines changed

1 file changed

+87
-26
lines changed

tapgarden/planter.go

Lines changed: 87 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,9 +1000,32 @@ func (c *ChainPlanter) gardener() {
10001000

10011001
req.Resolve(c.pendingBatch)
10021002

1003-
// TODO(jhb): follow-up PR: Implement SealBatch command
10041003
case reqTypeSealBatch:
1005-
req.Error(fmt.Errorf("not yet implemented"))
1004+
if c.pendingBatch == nil {
1005+
req.Error(fmt.Errorf("no pending batch"))
1006+
break
1007+
}
1008+
1009+
sealReqParams, err :=
1010+
typedParam[SealParams](req)
1011+
if err != nil {
1012+
req.Error(fmt.Errorf("bad seal "+
1013+
"params: %w", err))
1014+
break
1015+
}
1016+
1017+
ctx, cancel := c.WithCtxQuit()
1018+
sealedBatch, err := c.sealBatch(
1019+
ctx, *sealReqParams,
1020+
)
1021+
cancel()
1022+
if err != nil {
1023+
req.Error(fmt.Errorf("unable to seal"+
1024+
"minting batch: %w", err))
1025+
break
1026+
}
1027+
1028+
req.Resolve(sealedBatch)
10061029

10071030
case reqTypeFinalizeBatch:
10081031
if c.pendingBatch == nil {
@@ -1192,27 +1215,24 @@ func (c *ChainPlanter) fundBatch(ctx context.Context, params FundParams) error {
11921215
// possible if they are not provided. After all asset group witnesses have been
11931216
// validated, they are saved to disk to be used by the caretaker during batch
11941217
// finalization.
1195-
func (c *ChainPlanter) sealBatch(ctx context.Context, _ SealParams) error {
1218+
func (c *ChainPlanter) sealBatch(ctx context.Context,
1219+
params SealParams) (*MintingBatch, error) {
11961220
// A batch should exist with 1+ seedlings and be funded before being
11971221
// sealed.
1198-
if c.pendingBatch == nil {
1199-
return fmt.Errorf("no pending batch")
1200-
}
1201-
1202-
if len(c.pendingBatch.Seedlings) == 0 {
1203-
return fmt.Errorf("no seedlings in batch")
1222+
if !c.pendingBatch.HasSeedlings() {
1223+
return nil, fmt.Errorf("no seedlings in batch")
12041224
}
12051225

12061226
if !c.pendingBatch.IsFunded() {
1207-
return fmt.Errorf("batch is not funded")
1227+
return nil, fmt.Errorf("batch is not funded")
12081228
}
12091229

12101230
// Filter the batch seedlings to only consider those that will become
12111231
// grouped assets. If there are no such seedlings, then there is nothing
12121232
// to seal and no action is needed.
12131233
groupSeedlings, _ := filterSeedlingsWithGroup(c.pendingBatch.Seedlings)
12141234
if len(groupSeedlings) == 0 {
1215-
return nil
1235+
return c.pendingBatch, nil
12161236
}
12171237

12181238
// Before we can build the group key requests for each seedling, we must
@@ -1229,23 +1249,61 @@ func (c *ChainPlanter) sealBatch(ctx context.Context, _ SealParams) error {
12291249
// Construct the group key requests and group virtual TXs for each
12301250
// seedling. With these we can verify provided asset group witnesses,
12311251
// or attempt to derive asset group witnesses if needed.
1232-
groupReqs, genTXs, err := c.buildGroupReqs(
1233-
genesisPoint, anchorOutputIndex, groupSeedlings,
1252+
groupReqs, genTXs, err := buildGroupReqs(
1253+
genesisPoint, anchorOutputIndex, c.cfg.GenTxBuilder,
1254+
groupSeedlings,
12341255
)
12351256
if err != nil {
1236-
return fmt.Errorf("unable to build group requests: %w", err)
1257+
return nil, fmt.Errorf("unable to build group requests: "+
1258+
"%w", err)
1259+
}
1260+
1261+
// TODO: check for previous batch sealing
1262+
// Each provided asset group witness must have a corresponding seedling
1263+
// in the current batch.
1264+
seedlingAssetIDs := fn.NewSet(
1265+
fn.Map(groupReqs, func(req asset.GroupKeyRequest) asset.ID {
1266+
return req.NewAsset.ID()
1267+
})...,
1268+
)
1269+
externalWitnesses := make(map[asset.ID]groupSeal)
1270+
for _, seal := range params.GroupWitnesses {
1271+
if !seedlingAssetIDs.Contains(seal.GroupMember) {
1272+
return nil, fmt.Errorf("witness has no matching "+
1273+
"seedling: %v", seal)
1274+
}
1275+
externalWitnesses[seal.GroupMember] = seal
12371276
}
12381277

12391278
assetGroups := make([]*asset.AssetGroup, 0, len(groupReqs))
12401279
for i := 0; i < len(groupReqs); i++ {
1241-
// Derive the asset group witness.
1242-
groupKey, err := asset.DeriveGroupKey(
1243-
c.cfg.GenSigner, genTXs[i], groupReqs[i], nil,
1280+
var (
1281+
groupKey *asset.GroupKey
1282+
err error
12441283
)
1245-
if err != nil {
1246-
return err
1247-
}
12481284

1285+
reqAssetID := groupReqs[i].NewAsset.ID()
1286+
groupWitness, ok := externalWitnesses[reqAssetID]
1287+
1288+
switch {
1289+
case ok:
1290+
// Set the provided witness; it will be validated below.
1291+
groupKey = &asset.GroupKey{
1292+
RawKey: groupReqs[i].RawKey,
1293+
GroupPubKey: genTXs[i].TweakedKey,
1294+
TapscriptRoot: groupReqs[i].TapscriptRoot,
1295+
Witness: groupWitness.GroupWitness,
1296+
}
1297+
1298+
default:
1299+
// Derive the asset group witness.
1300+
groupKey, err = asset.DeriveGroupKey(
1301+
c.cfg.GenSigner, genTXs[i], groupReqs[i], nil,
1302+
)
1303+
if err != nil {
1304+
return nil, err
1305+
}
1306+
}
12491307
// Recreate the asset with the populated group key and validate
12501308
// the asset group witness.
12511309
protoAsset := groupReqs[i].NewAsset
@@ -1256,12 +1314,12 @@ func (c *ChainPlanter) sealBatch(ctx context.Context, _ SealParams) error {
12561314
asset.WithAssetVersion(protoAsset.Version),
12571315
)
12581316
if err != nil {
1259-
return err
1317+
return nil, err
12601318
}
12611319

12621320
err = c.cfg.TxValidator.Execute(groupedAsset, nil, nil)
12631321
if err != nil {
1264-
return fmt.Errorf("unable to verify asset "+
1322+
return nil, fmt.Errorf("unable to verify asset "+
12651323
"group witness: %w", err)
12661324
}
12671325

@@ -1277,10 +1335,11 @@ func (c *ChainPlanter) sealBatch(ctx context.Context, _ SealParams) error {
12771335
// to disk.
12781336
err = c.cfg.Log.AddSeedlingGroups(ctx, genesisPoint, assetGroups)
12791337
if err != nil {
1280-
return fmt.Errorf("unable to write seedling groups: %w", err)
1338+
return nil, fmt.Errorf("unable to write seedling groups: "+
1339+
"%w", err)
12811340
}
12821341

1283-
return nil
1342+
return c.pendingBatch, nil
12841343
}
12851344

12861345
// finalizeBatch creates a new caretaker for the batch and starts it.
@@ -1336,8 +1395,10 @@ func (c *ChainPlanter) finalizeBatch(params FinalizeParams) (*BatchCaretaker,
13361395
}
13371396
}
13381397

1339-
// TODO(jhb): follow-up PR: detect batches that were already sealed
1340-
err = c.sealBatch(ctx, SealParams{})
1398+
// If the batch needs to be sealed, we'll use the default behavior for
1399+
// generating asset group witnesses. Any custom behavior requires
1400+
// calling SealBatch() explicitly, before batch finalization.
1401+
_, err = c.sealBatch(ctx, SealParams{})
13411402
if err != nil {
13421403
return nil, err
13431404
}

0 commit comments

Comments
 (0)