Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mint with external asset group PSBT signer #1210

Closed
wants to merge 3 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 129 additions & 33 deletions tapgarden/planter.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"github.com/lightninglabs/taproot-assets/tapscript"
"github.com/lightninglabs/taproot-assets/tapsend"
"github.com/lightninglabs/taproot-assets/universe"
lfn "github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"golang.org/x/exp/maps"
)
Expand Down Expand Up @@ -765,6 +766,13 @@
}
}

// Verify consistency: ensure the number of group key requests matches
// the number of group VM transactions.
if len(groupReqs) != len(genTXs) {
return nil, nil, fmt.Errorf("mismatched number of group " +
"requests and virtual TXs")
}

return groupReqs, genTXs, nil
}

Expand Down Expand Up @@ -1052,11 +1060,6 @@
"requests: %w", err)
}

if len(groupReqs) != len(genTXs) {
return nil, fmt.Errorf("mismatched number of group " +
"requests and virtual TXs")
}

// Copy existing seedlngs into the unsealed seedling map; we'll
// clear the batch seedlings after adding group information.
currentBatch.UnsealedSeedlings = make(
Expand Down Expand Up @@ -1527,30 +1530,17 @@
return nil
}

// sealBatch will verify that each grouped asset in the pending batch has an
// asset group witness, and will attempt to create asset group witnesses when
// possible if they are not provided. After all asset group witnesses have been
// validated, they are saved to disk to be used by the caretaker during batch
// finalization.
func (c *ChainPlanter) sealBatch(ctx context.Context, params SealParams,
workingBatch *MintingBatch) (*MintingBatch, error) {

// A batch should exist with 1+ seedlings and be funded before being
// sealed.
if !workingBatch.HasSeedlings() {
return nil, fmt.Errorf("no seedlings in batch")
}

if !workingBatch.IsFunded() {
return nil, fmt.Errorf("batch is not funded")
}
// isBatchSealed returns true if the minting batch has been sealed; otherwise,
// it returns false.
func (c *ChainPlanter) isBatchSealed(ctx context.Context,
workingBatch *MintingBatch) lfn.Result[bool] {

// Filter the batch seedlings to only consider those that will become
// grouped assets. If there are no such seedlings, then there is nothing
// to seal and no action is needed.
groupSeedlings, _ := filterSeedlingsWithGroup(workingBatch.Seedlings)
if len(groupSeedlings) == 0 {
return workingBatch, nil
return lfn.Ok[bool](true)
}

// Before we can build the group key requests for each seedling, we must
Expand All @@ -1575,31 +1565,137 @@
existingGroups, err := c.cfg.Log.FetchSeedlingGroups(
ctx, genesisPoint, anchorOutputIndex, singleSeedling,
)

switch {
case len(existingGroups) != 0:
return nil, ErrBatchAlreadySealed
case err != nil:
if err != nil {
// The only expected error is for a missing asset genesis.
if !errors.Is(err, ErrNoGenesis) {
return nil, err
return lfn.Err[bool](err)
}
}

if len(existingGroups) != 0 {
// Asset genesis already stored on disk therefore the batch is
// already sealed.
return lfn.Ok[bool](true)
}

// If we reach this point then the batch hasn't been sealed.
return lfn.Ok[bool](false)
}

type SeedlingExternalSealPkg struct {
Psbt psbt.Packet

AssetGroupPubKey btcec.PublicKey
}

func (c *ChainPlanter) querySealBatchPsbts(ctx context.Context,

Check failure on line 1591 in tapgarden/planter.go

View workflow job for this annotation

GitHub Actions / Lint check

func `(*ChainPlanter).querySealBatchPsbts` is unused (unused)
workingBatch *MintingBatch) ([]SeedlingExternalSealPkg, error) {

// Check if the batch meets the requirements for sealing.
if !workingBatch.HasSeedlings() {
return nil, fmt.Errorf("no seedlings in batch")
}

if !workingBatch.IsFunded() {
return nil, fmt.Errorf("batch is not funded")
}

// Return early if the batch is already sealed.
isSealed, err := c.isBatchSealed(ctx, workingBatch).Unpack()
if err != nil {
return nil, fmt.Errorf("failed to inspect batch seal status: "+
"%w", err)
}

if isSealed {
return nil, ErrBatchAlreadySealed
}

genesisPoint := extractGenesisOutpoint(
workingBatch.GenesisPacket.Pkt.UnsignedTx,
)
anchorOutputIndex := extractAnchorOutputIndex(
workingBatch.GenesisPacket,
)
groupSeedlings, _ := filterSeedlingsWithGroup(workingBatch.Seedlings)

// Construct the group key requests and group virtual TXs for each
// seedling. With these we can verify provided asset group witnesses,
// or attempt to derive asset group witnesses if needed.
groupReqs, genTXs, err := buildGroupReqs(
_, genTXs, err := buildGroupReqs(
genesisPoint, anchorOutputIndex, c.cfg.GenTxBuilder,
groupSeedlings,
)
if err != nil {
return nil, fmt.Errorf("unable to build group requests: "+
"%w", err)
}
if len(groupReqs) != len(genTXs) {
return nil, fmt.Errorf("mismatched number of group requests " +
"and virtual TXs")

var res []SeedlingExternalSealPkg
for idx := range genTXs {
genTX := genTXs[idx]

psbtPkg, err := psbt.NewFromUnsignedTx(&genTX.Tx)
if err != nil {
return nil, fmt.Errorf("unable to create psbt from "+
"unsigned group witness VM tx: %w", err)
}

res = append(res, SeedlingExternalSealPkg{
Psbt: *psbtPkg,
AssetGroupPubKey: genTX.TweakedKey,
})
}

return res, nil
}

// sealBatch will verify that each grouped asset in the pending batch has an
// asset group witness, and will attempt to create asset group witnesses when
// possible if they are not provided. After all asset group witnesses have been
// validated, they are saved to disk to be used by the caretaker during batch
// finalization.
func (c *ChainPlanter) sealBatch(ctx context.Context, params SealParams,
workingBatch *MintingBatch) (*MintingBatch, error) {

// Check if the batch meets the requirements for sealing.
if !workingBatch.HasSeedlings() {
return nil, fmt.Errorf("no seedlings in batch")
}

if !workingBatch.IsFunded() {
return nil, fmt.Errorf("batch is not funded")
}

// Return early if the batch is already sealed.
isSealed, err := c.isBatchSealed(ctx, workingBatch).Unpack()
if err != nil {
return nil, fmt.Errorf("failed to inspect batch seal status: "+
"%w", err)
}

if isSealed {
return nil, ErrBatchAlreadySealed
}

genesisPoint := extractGenesisOutpoint(
workingBatch.GenesisPacket.Pkt.UnsignedTx,
)
anchorOutputIndex := extractAnchorOutputIndex(
workingBatch.GenesisPacket,
)
groupSeedlings, _ := filterSeedlingsWithGroup(workingBatch.Seedlings)

// Construct the group key requests and group virtual TXs for each
// seedling. With these we can verify provided asset group witnesses,
// or attempt to derive asset group witnesses if needed.
groupReqs, genTXs, err := buildGroupReqs(
genesisPoint, anchorOutputIndex, c.cfg.GenTxBuilder,
groupSeedlings,
)
if err != nil {
return nil, fmt.Errorf("unable to build group requests: "+
"%w", err)
}

// Each provided group witness must have a corresponding seedling in the
Expand Down
Loading