@@ -888,6 +888,7 @@ func (p *ChainPorter) storePackageAnchorTxConf(pkg *sendPackage) error {
888888 TxIndex : int32 (pkg .TransferTxConfEvent .TxIndex ),
889889 FinalProofs : pkg .FinalProofs ,
890890 PassiveAssetProofFiles : passiveAssetProofFiles ,
891+ ZeroValueInputs : pkg .ZeroValueInputs ,
891892 }, burns )
892893 if err != nil {
893894 return fmt .Errorf ("unable to log parcel delivery " +
@@ -1248,9 +1249,8 @@ func (p *ChainPorter) importLocalAddresses(ctx context.Context,
12481249 for idx := range parcel .Outputs {
12491250 out := & parcel .Outputs [idx ]
12501251
1251- // Skip non-local outputs, those are going to a receiver outside
1252- // of this daemon.
1253- if ! out .ScriptKeyLocal {
1252+ // Determine if the output should be imported into the wallet.
1253+ if ! out .IsSpendable () {
12541254 continue
12551255 }
12561256
@@ -1267,23 +1267,30 @@ func (p *ChainPorter) importLocalAddresses(ctx context.Context,
12671267 return err
12681268 }
12691269
1270+ log .Infof ("Importing anchor output key for output %d " +
1271+ "(isTombstone=%v, isBurn=%v): outpoint=%v, key=%x" ,
1272+ idx , out .IsTombstone (), out .IsBurn (),
1273+ out .Anchor .OutPoint ,
1274+ anchorOutputKey .SerializeCompressed ())
1275+
12701276 // Before we broadcast the transaction to the network, we'll
12711277 // import the new anchor output into the wallet so it watches
12721278 // it for spends and also takes account of the BTC we used in
12731279 // the transfer.
12741280 _ , err = p .cfg .Wallet .ImportTaprootOutput (ctx , anchorOutputKey )
1275- switch {
1276- case err == nil :
1277- break
1278-
1279- // On restart, we'll get an error that the output has already
1280- // been added to the wallet, so we'll catch this now and move
1281- // along if so.
1282- case strings .Contains (err .Error (), "already exists" ):
1283- break
1281+ if err != nil {
1282+ // On restart, we'll get an error that the output has
1283+ // already been added to the wallet, so we'll catch this
1284+ // now and move along if so.
1285+ if strings .Contains (err .Error (), "already exists" ) {
1286+ log .Tracef ("Anchor output key already exists " +
1287+ "(outpoint=%v): %w" ,
1288+ out .Anchor .OutPoint , err )
1289+ continue
1290+ }
12841291
1285- default :
1286- return err
1292+ return fmt . Errorf ( "unable to import anchor output " +
1293+ "key: %w" , err )
12871294 }
12881295 }
12891296
@@ -1446,6 +1453,7 @@ func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) {
14461453
14471454 currentPkg .VirtualPackets = fundSendRes .VPackets
14481455 currentPkg .InputCommitments = fundSendRes .InputCommitments
1456+ currentPkg .ZeroValueInputs = fundSendRes .ZeroValueInputs
14491457
14501458 currentPkg .SendState = SendStateVirtualSign
14511459
@@ -1591,9 +1599,10 @@ func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) {
15911599
15921600 anchorTx , err := wallet .AnchorVirtualTransactions (
15931601 ctx , & AnchorVTxnsParams {
1594- FeeRate : feeRate ,
1595- ActivePackets : currentPkg .VirtualPackets ,
1596- PassivePackets : currentPkg .PassiveAssets ,
1602+ FeeRate : feeRate ,
1603+ ActivePackets : currentPkg .VirtualPackets ,
1604+ PassivePackets : currentPkg .PassiveAssets ,
1605+ ZeroValueInputs : currentPkg .ZeroValueInputs ,
15971606 },
15981607 )
15991608 if err != nil {
@@ -1695,8 +1704,8 @@ func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) {
16951704 parcel , err := ConvertToTransfer (
16961705 currentHeight , currentPkg .VirtualPackets ,
16971706 currentPkg .AnchorTx , currentPkg .PassiveAssets ,
1698- isLocalKey , currentPkg .Label ,
1699- currentPkg .SkipAnchorTxBroadcast ,
1707+ currentPkg .ZeroValueInputs , isLocalKey ,
1708+ currentPkg .Label , currentPkg . SkipAnchorTxBroadcast ,
17001709 )
17011710 if err != nil {
17021711 p .unlockInputs (ctx , & currentPkg )
@@ -1715,6 +1724,8 @@ func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) {
17151724 // Write the parcel to disk as a pending parcel. This step also
17161725 // records the transfer details (e.g., reference to the anchor
17171726 // transaction ID, transfer outputs and inputs) to the database.
1727+ // This will also extend the leases for both asset inputs and
1728+ // zero-value UTXOs to prevent them from being used elsewhere.
17181729 err = p .cfg .ExportLog .LogPendingParcel (
17191730 ctx , parcel , defaultWalletLeaseIdentifier ,
17201731 time .Now ().Add (defaultBroadcastCoinLeaseDuration ),
@@ -1883,18 +1894,33 @@ func (p *ChainPorter) unlockInputs(ctx context.Context, pkg *sendPackage) {
18831894 // sanity-check that we have known input commitments to unlock, since
18841895 // that might not always be the case (for example if another party
18851896 // contributes inputs).
1886- if pkg .SendState < SendStateStorePreBroadcast &&
1887- len (pkg .InputCommitments ) > 0 {
1897+ // Also unlock any zero-value UTXOs that were leased for this package.
1898+ if pkg .SendState < SendStateStorePreBroadcast {
1899+ // Gather all outpoints to unlock in a single array
1900+ var outpoints []wire.OutPoint
18881901
1902+ // Add input commitment outpoints
18891903 for prevID := range pkg .InputCommitments {
18901904 log .Debugf ("Unlocking input %v" , prevID .OutPoint )
1905+ outpoints = append (outpoints , prevID .OutPoint )
1906+ }
1907+
1908+ // Add zero-value inputs
1909+ zeroValueOutpoints := fn .Map (
1910+ pkg .ZeroValueInputs ,
1911+ func (z * ZeroValueInput ) wire.OutPoint {
1912+ return z .OutPoint
1913+ },
1914+ )
1915+ outpoints = append (outpoints , zeroValueOutpoints ... )
18911916
1917+ // Release all coins in a single call
1918+ if len (outpoints ) > 0 {
18921919 err := p .cfg .AssetWallet .ReleaseCoins (
1893- ctx , prevID . OutPoint ,
1920+ ctx , outpoints ... ,
18941921 )
18951922 if err != nil {
1896- log .Warnf ("Unable to unlock input %v: %v" ,
1897- prevID .OutPoint , err )
1923+ log .Warnf ("Unable to unlock inputs: %v" , err )
18981924 }
18991925 }
19001926 }
0 commit comments