-
Notifications
You must be signed in to change notification settings - Fork 132
Description
Background
After we confirm a transfer, and update proofs (only for the file based store), we'll attempt to upload any proofs (if needed) to a universe:
taproot-assets/tapfreighter/chain_porter.go
Lines 1165 to 1190 in fca9d11
// At this point, the transfer transaction is confirmed on-chain, and | |
// we've stored the sender and receiver proofs in the proof archive. | |
// We'll now attempt to transfer the receiver proof to the receiver. | |
case SendStateReceiverProofTransfer: | |
// We'll set the package state to complete early here so the | |
// main loop breaks out. We'll continue to attempt proof | |
// deliver in the background. | |
currentPkg.SendState = SendStateComplete | |
p.Wg.Add(1) | |
go func() { | |
defer p.Wg.Done() | |
err := p.transferReceiverProof(¤tPkg) | |
if err != nil { | |
log.Errorf("unable to transfer receiver "+ | |
"proof: %v", err) | |
p.publishSubscriberEvent(newAssetSendErrorEvent( | |
err, SendStateReceiverProofTransfer, | |
currentPkg, | |
)) | |
} | |
}() | |
return ¤tPkg, nil |
The issue here is that this is run in a goroutine, with the state step function returning immediately. Upon restart, we won't attempt to publish the proofs again. Even if we don't have any proofs, this is important, as only once we transfer all the proofs, do we call ConfirmParcelDelivery
: https://github.com/lightninglabs/taproot-assets/blob/main/tapfreighter/chain_porter.go#L795-L808.
The function name is slightly overloaded, but ConfirmParcelDelivery
is what will actually finalize the transfer by applying the pending outputs (make new assets w/ the relevant details):
taproot-assets/tapdb/assets_store.go
Lines 2816 to 2842 in fca9d11
// Since we define that a transfer can only move assets | |
// within the same asset ID, we can take any of the | |
// inputs as a template for the new asset, since the | |
// genesis and group key will be the same. We'll | |
// overwrite all other fields. | |
templateID := spentAssetIDs[0] | |
params := ApplyPendingOutput{ | |
ScriptKeyID: out.ScriptKeyID, | |
AnchorUtxoID: sqlInt64( | |
out.AnchorUtxoID, | |
), | |
Amount: out.Amount, | |
LockTime: out.LockTime, | |
RelativeLockTime: out.RelativeLockTime, | |
//nolint:lll | |
SplitCommitmentRootHash: out.SplitCommitmentRootHash, | |
SplitCommitmentRootValue: out.SplitCommitmentRootValue, | |
SpentAssetID: templateID, | |
Spent: isTombstone || isBurn, | |
AssetVersion: out.AssetVersion, | |
} | |
newAssetID, err := q.ApplyPendingOutput(ctx, params) | |
if err != nil { | |
return fmt.Errorf("unable to apply pending "+ | |
"output: %w", err) | |
} | |
Note that we do have the transfer log, and can resume from that, but this doesn't allow us to also execute this call back that finalizes the transfer.
One other relevant detail is that transferReceiverProof
sets the state to SendStateComplete
:
taproot-assets/tapfreighter/chain_porter.go
Line 814 in fca9d11
pkg.SendState = SendStateComplete |
taproot-assets/tapfreighter/chain_porter.go
Lines 1169 to 1172 in fca9d11
// We'll set the package state to complete early here so the | |
// main loop breaks out. We'll continue to attempt proof | |
// deliver in the background. | |
currentPkg.SendState = SendStateComplete |
Steps to reproduce
Update an itest to initiate a transfer, confirm, but then cause the proof transfer to fail.
Expected behavior
It should continue to both transmit the proofs, but also fix the logic above to call ConfirmParcelDelivery
as soon as we get the confirmation notification.
We should also remove the premature update of SendStateComplete
. This'll ensure that that case gets re-executed on start up until the proofs are actually sent.
With the above, we also need to read the parcels from disk, to relaunch the state machine for parcels that weren't finalized.
Actual behavior
On restart, lnd won't reattempt proof transfer. However, the proof files on disk are properly updated.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status