Skip to content

Commit f3a3bff

Browse files
committed
single commit to bundle changes. draft
1 parent 614acdc commit f3a3bff

File tree

7 files changed

+441
-18
lines changed

7 files changed

+441
-18
lines changed

tapcfg/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,7 @@ func genServerConfig(cfg *Config, cfgLogger btclog.Logger,
425425
GroupVerifier: groupVerifier,
426426
IgnoreChecker: ignoreCheckerOpt,
427427
Wallet: walletAnchor,
428+
AnchorLister: assetStore,
428429
ChainParams: &tapChainParams,
429430
})
430431

tapdb/assets_store.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1320,6 +1320,87 @@ func (a *AssetStore) FetchManagedUTXOs(ctx context.Context) (
13201320
return managedUtxos, nil
13211321
}
13221322

1323+
// ListZeroValueAnchors returns the set of managed anchor UTXOs that only
1324+
// contain tombstone/burn commitments and therefore have zero effective asset
1325+
// value.
1326+
//
1327+
// NOTE: This implements the tapfreighter.ZeroValueAnchorLister interface.
1328+
func (a *AssetStore) ListZeroValueAnchors(ctx context.Context) (
1329+
[]*tapfreighter.ZeroValueAnchor, error) {
1330+
1331+
managedUtxos, err := a.FetchManagedUTXOs(ctx)
1332+
if err != nil {
1333+
return nil, err
1334+
}
1335+
1336+
now := a.clock.Now().UTC()
1337+
anchors := make([]*tapfreighter.ZeroValueAnchor, 0)
1338+
1339+
for _, utxo := range managedUtxos {
1340+
// Skip entries that are currently leased.
1341+
if len(utxo.LeaseOwner) != 0 {
1342+
if utxo.LeaseExpiry.IsZero() || !utxo.LeaseExpiry.Before(now) {
1343+
continue
1344+
}
1345+
}
1346+
1347+
anchorPointBytes, err := encodeOutpoint(utxo.OutPoint)
1348+
if err != nil {
1349+
return nil, err
1350+
}
1351+
1352+
filter := QueryAssetFilters{
1353+
AnchorPoint: anchorPointBytes,
1354+
Spent: sqlBool(false),
1355+
Leased: sqlBool(false),
1356+
Now: sql.NullTime{
1357+
Time: now,
1358+
Valid: true,
1359+
},
1360+
}
1361+
1362+
commitments, err := a.queryCommitments(ctx, filter)
1363+
switch {
1364+
case errors.Is(err, tapfreighter.ErrMatchingAssetsNotFound):
1365+
continue
1366+
case err != nil:
1367+
return nil, err
1368+
}
1369+
1370+
if len(commitments) == 0 {
1371+
continue
1372+
}
1373+
1374+
anchorCommitment := commitments[0].Commitment
1375+
assets := anchorCommitment.CommittedAssets()
1376+
if len(assets) == 0 {
1377+
continue
1378+
}
1379+
1380+
zeroValue := true
1381+
for _, asset := range assets {
1382+
if asset.Amount > 0 && !asset.IsBurn() {
1383+
zeroValue = false
1384+
break
1385+
}
1386+
}
1387+
1388+
if !zeroValue {
1389+
continue
1390+
}
1391+
1392+
anchors = append(anchors, &tapfreighter.ZeroValueAnchor{
1393+
OutPoint: utxo.OutPoint,
1394+
Value: utxo.OutputValue,
1395+
InternalKey: utxo.InternalKey,
1396+
Commitment: anchorCommitment,
1397+
TapscriptSibling: append([]byte(nil), utxo.TapscriptSibling...),
1398+
})
1399+
}
1400+
1401+
return anchors, nil
1402+
}
1403+
13231404
// FetchAssetProofsSizes fetches the sizes of the proofs in the db.
13241405
func (a *AssetStore) FetchAssetProofsSizes(
13251406
ctx context.Context) ([]AssetProofSize, error) {
@@ -2476,6 +2557,26 @@ func (a *AssetStore) LogPendingParcel(ctx context.Context,
24762557
}
24772558
}
24782559

2560+
for _, zeroAnchor := range spend.ZeroValueAnchors {
2561+
anchorPointBytes, err := encodeOutpoint(zeroAnchor)
2562+
if err != nil {
2563+
return err
2564+
}
2565+
2566+
err = q.UpdateUTXOLease(ctx, UpdateUTXOLease{
2567+
LeaseOwner: finalLeaseOwner[:],
2568+
LeaseExpiry: sql.NullTime{
2569+
Time: finalLeaseExpiry.UTC(),
2570+
Valid: true,
2571+
},
2572+
Outpoint: anchorPointBytes,
2573+
})
2574+
if err != nil {
2575+
return fmt.Errorf("unable to lease zero value "+
2576+
"anchor: %w", err)
2577+
}
2578+
}
2579+
24792580
// Then the passive assets.
24802581
if len(spend.PassiveAssets) > 0 {
24812582
if spend.PassiveAssetsAnchor == nil {

tapfreighter/chain_porter.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1589,7 +1589,7 @@ func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) {
15891589
"assets: %w", err)
15901590
}
15911591

1592-
anchorTx, err := wallet.AnchorVirtualTransactions(
1592+
anchorTx, sweptAnchors, err := wallet.AnchorVirtualTransactions(
15931593
ctx, &AnchorVTxnsParams{
15941594
FeeRate: feeRate,
15951595
ActivePackets: currentPkg.VirtualPackets,
@@ -1608,6 +1608,7 @@ func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) {
16081608
// signing process with a copy to avoid clearing the info on
16091609
// finalization.
16101610
currentPkg.AnchorTx = anchorTx
1611+
currentPkg.ZeroValueAnchors = sweptAnchors
16111612

16121613
// For the final validation, we need to also supply the assets
16131614
// that were committed to the input tree but pruned because they
@@ -1695,7 +1696,7 @@ func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) {
16951696
parcel, err := ConvertToTransfer(
16961697
currentHeight, currentPkg.VirtualPackets,
16971698
currentPkg.AnchorTx, currentPkg.PassiveAssets,
1698-
isLocalKey, currentPkg.Label,
1699+
currentPkg.ZeroValueAnchors, isLocalKey, currentPkg.Label,
16991700
currentPkg.SkipAnchorTxBroadcast,
17001701
)
17011702
if err != nil {

tapfreighter/interface.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,12 @@ type CoinSelector interface {
192192
maxVersion commitment.TapCommitmentVersion,
193193
) ([]*AnchoredCommitment, error)
194194

195+
// LeaseCoins leases/locks/reserves coins for the given lease owner
196+
// until the given expiry. This is used to prevent multiple concurrent
197+
// coin selection attempts from selecting the same coin(s).
198+
LeaseCoins(ctx context.Context, leaseOwner [32]byte, expiry time.Time,
199+
utxoOutpoints ...wire.OutPoint) error
200+
195201
// ReleaseCoins releases/unlocks coins that were previously leased and
196202
// makes them available for coin selection again.
197203
ReleaseCoins(ctx context.Context, utxoOutpoints ...wire.OutPoint) error
@@ -244,6 +250,34 @@ type Anchor struct {
244250
PkScript []byte
245251
}
246252

253+
// ZeroValueAnchor describes a managed anchor UTXO whose commitment only holds
254+
// tombstones/burns and therefore carries zero effective asset value.
255+
type ZeroValueAnchor struct {
256+
// OutPoint is the BTC outpoint of the anchor UTXO.
257+
OutPoint wire.OutPoint
258+
259+
// Value is the BTC value locked in the anchor output.
260+
Value btcutil.Amount
261+
262+
// InternalKey is the internal key that anchors the commitment in the
263+
// outpoint.
264+
InternalKey keychain.KeyDescriptor
265+
266+
// Commitment is the full Taproot Asset commitment anchored at the
267+
// outpoint.
268+
Commitment *commitment.TapCommitment
269+
270+
// TapscriptSibling is the serialized tapscript sibling preimage for the
271+
// anchor output (if present).
272+
TapscriptSibling []byte
273+
}
274+
275+
// ZeroValueAnchorLister lists managed anchor UTXOs whose asset commitments
276+
// carry no spendable value.
277+
type ZeroValueAnchorLister interface {
278+
ListZeroValueAnchors(ctx context.Context) ([]*ZeroValueAnchor, error)
279+
}
280+
247281
// OutputIdentifier is a key that can be used to uniquely identify a transfer
248282
// output.
249283
type OutputIdentifier [32]byte
@@ -467,6 +501,10 @@ type OutboundParcel struct {
467501
// during the parcel confirmation process.
468502
PassiveAssets []*tappsbt.VPacket
469503

504+
// ZeroValueAnchors lists the tombstone/burn anchors that were swept as
505+
// additional BTC inputs when constructing the anchor transaction.
506+
ZeroValueAnchors []wire.OutPoint
507+
470508
// PassiveAssetsAnchor is the anchor point for the passive assets. This
471509
// might be a distinct anchor from any active transfer in case the
472510
// active transfers don't create any change going back to us.
@@ -498,6 +536,7 @@ func (o *OutboundParcel) Copy() *OutboundParcel {
498536
ChainFees: o.ChainFees,
499537
Inputs: fn.CopySlice(o.Inputs),
500538
Outputs: fn.CopySlice(o.Outputs),
539+
ZeroValueAnchors: fn.CopySlice(o.ZeroValueAnchors),
501540
Label: o.Label,
502541
SkipAnchorTxBroadcast: o.SkipAnchorTxBroadcast,
503542
}

tapfreighter/parcel.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,10 @@ type sendPackage struct {
494494
// PassiveAssets is the data used in re-anchoring passive assets.
495495
PassiveAssets []*tappsbt.VPacket
496496

497+
// ZeroValueAnchors keeps track of tombstone/burn anchors that were added
498+
// as extra BTC inputs to the anchor transaction for garbage collection.
499+
ZeroValueAnchors []wire.OutPoint
500+
497501
// Parcel is the asset transfer request that kicked off this transfer.
498502
Parcel Parcel
499503

@@ -546,6 +550,7 @@ type sendPackage struct {
546550
// they were already committed at.
547551
func ConvertToTransfer(currentHeight uint32, activeTransfers []*tappsbt.VPacket,
548552
anchorTx *tapsend.AnchorTransaction, passiveAssets []*tappsbt.VPacket,
553+
zeroValueAnchors []wire.OutPoint,
549554
isLocalKey func(asset.ScriptKey) (bool, error), label string,
550555
skipAnchorTxBroadcast bool) (*OutboundParcel, error) {
551556

@@ -584,6 +589,7 @@ func ConvertToTransfer(currentHeight uint32, activeTransfers []*tappsbt.VPacket,
584589
),
585590
PassiveAssets: passiveAssets,
586591
PassiveAssetsAnchor: passiveAssetAnchor,
592+
ZeroValueAnchors: fn.CopySlice(zeroValueAnchors),
587593
Label: label,
588594
SkipAnchorTxBroadcast: skipAnchorTxBroadcast,
589595
}

0 commit comments

Comments
 (0)