@@ -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