Skip to content

Commit 4d504a0

Browse files
committed
tapfreighter: store package anchor tx conf state before proof transfer
This commit modifies the `ChainPorter` state machine to store the package state after the anchor transaction is confirmed, but before proof transfer is initiated. By ensuring the state is saved at this point, transfer change output coins remain spendable even if proof delivery fails.
1 parent ef90f31 commit 4d504a0

File tree

1 file changed

+74
-49
lines changed

1 file changed

+74
-49
lines changed

tapfreighter/chain_porter.go

Lines changed: 74 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,60 @@ func (p *ChainPorter) storeProofs(sendPkg *sendPackage) error {
612612
return nil
613613
}
614614

615+
// storePackageAnchorTxConf logs the on-chain confirmation of the transfer
616+
// anchor transaction for the given package.
617+
func (p *ChainPorter) storePackageAnchorTxConf(pkg *sendPackage) error {
618+
ctx, cancel := p.WithCtxQuitNoTimeout()
619+
defer cancel()
620+
621+
// Load passive asset proof files from archive.
622+
passiveAssetProofFiles := map[asset.ID][]*proof.AnnotatedProof{}
623+
for idx := range pkg.OutboundPkg.PassiveAssets {
624+
passivePkt := pkg.OutboundPkg.PassiveAssets[idx]
625+
passiveOut := passivePkt.Outputs[0]
626+
627+
proofLocator := proof.Locator{
628+
AssetID: fn.Ptr(passiveOut.Asset.ID()),
629+
ScriptKey: *passiveOut.ScriptKey.PubKey,
630+
OutPoint: fn.Ptr(passiveOut.ProofSuffix.OutPoint()),
631+
}
632+
proofFileBlob, err := p.cfg.ProofReader.FetchProof(
633+
ctx, proofLocator,
634+
)
635+
if err != nil {
636+
return fmt.Errorf("error fetching passive asset "+
637+
"proof file: %w", err)
638+
}
639+
640+
passiveAssetProofFiles[passiveOut.Asset.ID()] = append(
641+
passiveAssetProofFiles[passiveOut.Asset.ID()],
642+
&proof.AnnotatedProof{
643+
Locator: proofLocator,
644+
Blob: proofFileBlob,
645+
},
646+
)
647+
}
648+
649+
// At this point we have the confirmation signal, so we can mark the
650+
// parcel delivery as completed in the database.
651+
anchorTXID := pkg.OutboundPkg.AnchorTx.TxHash()
652+
anchorTxBlockHeight := int32(pkg.TransferTxConfEvent.BlockHeight)
653+
err := p.cfg.ExportLog.ConfirmParcelDelivery(ctx, &AssetConfirmEvent{
654+
AnchorTXID: anchorTXID,
655+
BlockHash: *pkg.TransferTxConfEvent.BlockHash,
656+
BlockHeight: anchorTxBlockHeight,
657+
TxIndex: int32(pkg.TransferTxConfEvent.TxIndex),
658+
FinalProofs: pkg.FinalProofs,
659+
PassiveAssetProofFiles: passiveAssetProofFiles,
660+
})
661+
if err != nil {
662+
return fmt.Errorf("unable to log parcel delivery "+
663+
"confirmation: %w", err)
664+
}
665+
666+
return nil
667+
}
668+
615669
// fetchInputProof fetches a proof for the given input from the proof archive.
616670
func (p *ChainPorter) fetchInputProof(ctx context.Context,
617671
input asset.PrevID) (*proof.File, error) {
@@ -716,8 +770,6 @@ func (p *ChainPorter) transferReceiverProof(pkg *sendPackage) error {
716770
ctx, cancel := p.WithCtxQuitNoTimeout()
717771
defer cancel()
718772

719-
anchorTXID := pkg.OutboundPkg.AnchorTx.TxHash()
720-
721773
deliver := func(ctx context.Context, out TransferOutput) error {
722774
key := out.ScriptKey.PubKey
723775

@@ -823,54 +875,14 @@ func (p *ChainPorter) transferReceiverProof(pkg *sendPackage) error {
823875
return fmt.Errorf("error delivering proof(s): %w", err)
824876
}
825877

826-
log.Infof("Marking parcel (txid=%v) as confirmed!",
878+
// At this point, the transfer is fully finalised and successful:
879+
// - The anchoring transaction has been confirmed on-chain.
880+
// - The proof(s) have been delivered to the receiver(s).
881+
// - The database has been updated to reflect the successful transfer.
882+
log.Infof("Parcel transfer is fully complete (anchor_txid=%v)",
827883
pkg.OutboundPkg.AnchorTx.TxHash())
828884

829-
// Load passive asset proof files from archive.
830-
passiveAssetProofFiles := map[asset.ID][]*proof.AnnotatedProof{}
831-
for idx := range pkg.OutboundPkg.PassiveAssets {
832-
passivePkt := pkg.OutboundPkg.PassiveAssets[idx]
833-
passiveOut := passivePkt.Outputs[0]
834-
835-
proofLocator := proof.Locator{
836-
AssetID: fn.Ptr(passiveOut.Asset.ID()),
837-
ScriptKey: *passiveOut.ScriptKey.PubKey,
838-
OutPoint: fn.Ptr(passiveOut.ProofSuffix.OutPoint()),
839-
}
840-
proofFileBlob, err := p.cfg.ProofReader.FetchProof(
841-
ctx, proofLocator,
842-
)
843-
if err != nil {
844-
return fmt.Errorf("error fetching passive asset "+
845-
"proof file: %w", err)
846-
}
847-
848-
passiveAssetProofFiles[passiveOut.Asset.ID()] = append(
849-
passiveAssetProofFiles[passiveOut.Asset.ID()],
850-
&proof.AnnotatedProof{
851-
Locator: proofLocator,
852-
Blob: proofFileBlob,
853-
},
854-
)
855-
}
856-
857-
// At this point we have the confirmation signal, so we can mark the
858-
// parcel delivery as completed in the database.
859-
err = p.cfg.ExportLog.ConfirmParcelDelivery(ctx, &AssetConfirmEvent{
860-
AnchorTXID: anchorTXID,
861-
BlockHash: *pkg.TransferTxConfEvent.BlockHash,
862-
BlockHeight: int32(pkg.TransferTxConfEvent.BlockHeight),
863-
TxIndex: int32(pkg.TransferTxConfEvent.TxIndex),
864-
FinalProofs: pkg.FinalProofs,
865-
PassiveAssetProofFiles: passiveAssetProofFiles,
866-
})
867-
if err != nil {
868-
return fmt.Errorf("unable to log parcel delivery "+
869-
"confirmation: %w", err)
870-
}
871-
872-
// If we've reached this point, then the parcel has been successfully
873-
// delivered. We'll send out the final notification.
885+
// Send out the final notification that the transfer is complete.
874886
p.publishSubscriberEvent(newAssetSendEvent(SendStateComplete, *pkg))
875887

876888
pkg.SendState = SendStateComplete
@@ -1225,7 +1237,20 @@ func (p *ChainPorter) stateStep(currentPkg sendPackage) (*sendPackage, error) {
12251237
// on to store the sender and receiver proofs in the proof archive.
12261238
case SendStateStoreProofs:
12271239
err := p.storeProofs(&currentPkg)
1228-
return &currentPkg, err
1240+
if err != nil {
1241+
return nil, fmt.Errorf("unable to store proofs: %w",
1242+
err)
1243+
}
1244+
1245+
// We'll now update the parcel state in storage to reflect that
1246+
// the transfer anchoring tx is confirmed on-chain.
1247+
err = p.storePackageAnchorTxConf(&currentPkg)
1248+
if err != nil {
1249+
return nil, fmt.Errorf("storing transfer anchor tx "+
1250+
"on-chain confirmation: %w", err)
1251+
}
1252+
1253+
return &currentPkg, nil
12291254

12301255
// At this point, the transfer transaction is confirmed on-chain, and
12311256
// we've stored the sender and receiver proofs in the proof archive.

0 commit comments

Comments
 (0)