Skip to content

Commit

Permalink
tapgarden: move batch sealing earlier to finalize
Browse files Browse the repository at this point in the history
  • Loading branch information
jharveyb committed Mar 26, 2024
1 parent 0d599bb commit b341ded
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 128 deletions.
186 changes: 65 additions & 121 deletions tapgarden/caretaker.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,15 +402,71 @@ func (b *BatchCaretaker) seedlingsToAssetSprouts(ctx context.Context,

newAssets := make([]*asset.Asset, 0, len(b.cfg.Batch.Seedlings))

// Seedlings that anchor a group may be referenced by other seedlings,
// and therefore need to be mapped to sprouts first so that we derive
// the initial tweaked group key early.
orderedSeedlings := SortSeedlings(maps.Values(b.cfg.Batch.Seedlings))
newGroups := make(map[string]*asset.AssetGroup, len(orderedSeedlings))
// separate grouped assets from ungrouped
groupedSeedlings, ungroupedSeedlings := filterSeedlingsWithGroup(
b.cfg.Batch.Seedlings,
)
groupedSeedlingCount := len(groupedSeedlings)

// load seedling asset groups and check for correct group count
seedlingGroups, err := b.cfg.Log.FetchSeedlingGroups(
ctx, genesisPoint, assetOutputIndex,
maps.Values(groupedSeedlings),
)
if err != nil {
return nil, err
}
seedlingGroupCount := len(seedlingGroups)

if groupedSeedlingCount != seedlingGroupCount {
return nil, fmt.Errorf("wrong number of grouped assets and "+
"asset groups: %d, %d", groupedSeedlingCount,
seedlingGroupCount)
}

for i := range seedlingGroups {
// check that asset group has a witness, and that the group
// has a matching seedling
seedlingGroup := seedlingGroups[i]
if len(seedlingGroup.GroupKey.Witness) == 0 {
return nil, fmt.Errorf("not all seedling groups have " +
"witnesses")
}

seedling, ok := groupedSeedlings[seedlingGroup.Tag]
if !ok {
groupTweakedKey := seedlingGroup.GroupKey.GroupPubKey.
SerializeCompressed()
return nil, fmt.Errorf("no seedling with tag matching "+
"group: %v, %x", seedlingGroup.Tag,
groupTweakedKey)
}

// build assets for grouped seedlings
var amount uint64
switch seedling.AssetType {
case asset.Normal:
amount = seedling.Amount
case asset.Collectible:
amount = 1
}

newAsset, err := asset.New(
*seedlingGroup.Genesis, amount, 0, 0,
seedling.ScriptKey, seedlingGroup.GroupKey,
asset.WithAssetVersion(seedling.AssetVersion),
)
if err != nil {
return nil, fmt.Errorf("unable to create new asset: %w",
err)
}

for _, seedlingName := range orderedSeedlings {
seedling := b.cfg.Batch.Seedlings[seedlingName]
newAssets = append(newAssets, newAsset)
}

// build assets for ungrouped seedlings
for seedlingName := range ungroupedSeedlings {
seedling := ungroupedSeedlings[seedlingName]
assetGen := asset.Genesis{
FirstPrevOut: genesisPoint,
Tag: seedling.AssetName,
Expand All @@ -425,138 +481,26 @@ func (b *BatchCaretaker) seedlingsToAssetSprouts(ctx context.Context,
assetGen.MetaHash = seedling.Meta.MetaHash()
}

var (
amount uint64
groupInfo *asset.AssetGroup
protoAsset *asset.Asset
sproutGroupKey *asset.GroupKey
err error
)

// Determine the amount for the actual asset.
var amount uint64
switch seedling.AssetType {
case asset.Normal:
amount = seedling.Amount
case asset.Collectible:
amount = 1
}

// If the seedling has a group key specified,
// that group key was validated earlier. We need to
// sign the new genesis with that group key.
if seedling.HasGroupKey() {
groupInfo = seedling.GroupInfo
}

// If the seedling has a group anchor specified, that anchor
// was validated earlier and the corresponding group has already
// been created. We need to look up the group key and sign
// the asset genesis with that key.
if seedling.GroupAnchor != nil {
groupInfo = newGroups[*seedling.GroupAnchor]
}

// If a group witness needs to be produced, then we will need a
// partially filled asset as part of the signing process.
if groupInfo != nil || seedling.EnableEmission {
protoAsset, err = asset.New(
assetGen, amount, 0, 0, seedling.ScriptKey,
nil,
asset.WithAssetVersion(seedling.AssetVersion),
)
if err != nil {
return nil, fmt.Errorf("unable to create "+
"asset for group key signing: %w", err)
}
}

if groupInfo != nil {
groupReq, err := asset.NewGroupKeyRequest(
groupInfo.GroupKey.RawKey, *groupInfo.Genesis,
protoAsset, nil,
)
if err != nil {
return nil, fmt.Errorf("unable to request "+
"asset group membership: %w", err)
}

genTx, err := groupReq.BuildGenesisTx(
b.cfg.GenTxBuilder,
)
if err != nil {
return nil, err
}

sproutGroupKey, err = asset.DeriveGroupKey(
b.cfg.GenSigner, *genTx, *groupReq,
)
if err != nil {
return nil, fmt.Errorf("unable to tweak group "+
"key: %w", err)
}
}

// If emission is enabled without a group key specified,
// then we'll need to generate another public key,
// then use that to derive the key group signature
// along with the tweaked key group.
if seedling.EnableEmission {
if seedling.GroupInternalKey == nil {
return nil, fmt.Errorf("unable to derive " +
"group key")
}

groupReq, err := asset.NewGroupKeyRequest(
*seedling.GroupInternalKey, assetGen,
protoAsset, nil,
)
if err != nil {
return nil, fmt.Errorf("unable to request "+
"asset group creation: %w", err)
}

genTx, err := groupReq.BuildGenesisTx(
b.cfg.GenTxBuilder,
)
if err != nil {
return nil, err
}

sproutGroupKey, err = asset.DeriveGroupKey(
b.cfg.GenSigner, *genTx, *groupReq,
)
if err != nil {
return nil, fmt.Errorf("unable to tweak group "+
"key: %w", err)
}

newGroups[seedlingName] = &asset.AssetGroup{
Genesis: &assetGen,
GroupKey: sproutGroupKey,
}
}

// With the necessary keys components assembled, we'll create
// the actual asset now.
newAsset, err := asset.New(
assetGen, amount, 0, 0, seedling.ScriptKey,
sproutGroupKey,
assetGen, amount, 0, 0, seedling.ScriptKey, nil,
asset.WithAssetVersion(seedling.AssetVersion),
)
if err != nil {
return nil, fmt.Errorf("unable to create new asset: %w",
err)
}

// Verify the group witness if present.
if sproutGroupKey != nil {
err := b.cfg.TxValidator.Execute(newAsset, nil, nil)
if err != nil {
return nil, fmt.Errorf("unable to verify "+
"asset group witness: %w", err)
}
}

newAssets = append(newAssets, newAsset)
}

Expand Down
15 changes: 8 additions & 7 deletions tapgarden/planter.go
Original file line number Diff line number Diff line change
Expand Up @@ -1183,23 +1183,24 @@ func (c *ChainPlanter) finalizeBatch(params FinalizeParams) (*BatchCaretaker,
}

// If the batch already has a funded TX, we can skip funding the batch.
if c.pendingBatch.GenesisPacket == nil {
fundParams := FundParams(params)

if !c.pendingBatch.IsFunded() {
// Fund the batch before starting the caretaker. If funding
// fails, we can't start a caretaker for the batch, so we'll
// clear the pending batch. The batch will exist on disk for
// the user to recreate it if necessary.
err = c.fundBatch(ctx, fundParams)
// TODO(jhb): Don't clear pending batch here
err = c.fundBatch(ctx, FundParams(params))
if err != nil {
c.pendingBatch = nil
return nil, err
}
}

// TODO(jhb): move batch sibling handling entirely to fundBatch, remove
// logic around sibling storage
// TODO(jhb): check for batch sealing
// TODO(jhb): follow-up PR: detect batches that were already sealed
err = c.sealBatch(ctx, SealParams{})
if err != nil {
return nil, err
}

// Now that the batch has been frozen on disk, we can update the batch
// state to frozen before launching a new caretaker state machine for
Expand Down

0 comments on commit b341ded

Please sign in to comment.