Skip to content

Commit 1660f9c

Browse files
authored
Merge pull request #1537 from lightninglabs/backward-compat-revert
tapchannel: don't break backward compatibility
2 parents 95af368 + 0700523 commit 1660f9c

File tree

3 files changed

+149
-4
lines changed

3 files changed

+149
-4
lines changed

tapchannel/aux_closer.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func NewAuxChanCloser(cfg AuxChanCloserCfg) *AuxChanCloser {
104104
// createCloseAlloc is a helper function that creates an allocation for an asset
105105
// close. This does not set a script key, as the script key will be set for each
106106
// packet after the coins have been distributed.
107-
func createCloseAlloc(isLocal bool, outputSum uint64,
107+
func createCloseAlloc(isLocal, isInitiator bool, outputSum uint64,
108108
shutdownMsg tapchannelmsg.AuxShutdownMsg) (*tapsend.Allocation, error) {
109109

110110
// The sort pkScript for the allocation will just be the internal key,
@@ -146,6 +146,7 @@ func createCloseAlloc(isLocal bool, outputSum uint64,
146146

147147
return tapsend.CommitAllocationToRemote
148148
}(),
149+
SplitRoot: isInitiator,
149150
InternalKey: shutdownMsg.AssetInternalKey.Val,
150151
GenScriptKey: scriptKeyGen,
151152
Amount: outputSum,
@@ -306,7 +307,7 @@ func (a *AuxChanCloser) AuxCloseOutputs(
306307
remoteSum := fn.Reduce(commitState.RemoteAssets.Val.Outputs, sumAmounts)
307308
if localSum > 0 {
308309
localAlloc, err = createCloseAlloc(
309-
true, localSum, localShutdown,
310+
true, desc.Initiator, localSum, localShutdown,
310311
)
311312
if err != nil {
312313
return none, err
@@ -318,7 +319,7 @@ func (a *AuxChanCloser) AuxCloseOutputs(
318319
}
319320
if remoteSum > 0 {
320321
remoteAlloc, err = createCloseAlloc(
321-
false, remoteSum, remoteShutdown,
322+
false, !desc.Initiator, remoteSum, remoteShutdown,
322323
)
323324
if err != nil {
324325
return none, err

tapchannel/commitment.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -714,7 +714,10 @@ func CreateAllocations(chanState lnwallet.AuxChanState, ourBalance,
714714
initiatorAssetBalance)
715715

716716
// Next, we add the HTLC outputs, using this helper function to
717-
// distinguish between incoming and outgoing HTLCs.
717+
// distinguish between incoming and outgoing HTLCs. The haveHtlcSplit
718+
// boolean is used to store if one of the HTLCs has already been chosen
719+
// to be the split root (only the very first HTLC might be chosen).
720+
var haveHtlcSplitRoot bool
718721
addHtlc := func(htlc *DecodedDescriptor, isIncoming bool) error {
719722
htlcScript, err := lnwallet.GenTaprootHtlcScript(
720723
isIncoming, whoseCommit, htlc.Timeout, htlc.RHash,
@@ -732,6 +735,21 @@ func CreateAllocations(chanState lnwallet.AuxChanState, ourBalance,
732735
"sibling: %w", err)
733736
}
734737

738+
// We should always just have a single split root, which
739+
// normally is the initiator's balance. However, if the
740+
// initiator has no balance, then we choose the very first HTLC
741+
// in the list to be the split root. If there are no HTLCs, then
742+
// all the balance is on the receiver side and we don't need a
743+
// split root.
744+
shouldHouseSplitRoot := initiatorAssetBalance == 0 &&
745+
!haveHtlcSplitRoot
746+
747+
// Make sure we only select the very first HTLC that pays to the
748+
// initiator.
749+
if shouldHouseSplitRoot {
750+
haveHtlcSplitRoot = true
751+
}
752+
735753
allocType := tapsend.CommitAllocationHtlcOutgoing
736754
if isIncoming {
737755
allocType = tapsend.CommitAllocationHtlcIncoming
@@ -779,6 +797,7 @@ func CreateAllocations(chanState lnwallet.AuxChanState, ourBalance,
779797
Type: allocType,
780798
Amount: rfqmsg.Sum(htlc.AssetBalances),
781799
AssetVersion: asset.V1,
800+
SplitRoot: shouldHouseSplitRoot,
782801
BtcAmount: htlc.Amount.ToSatoshis(),
783802
InternalKey: htlcTree.InternalKey,
784803
NonAssetLeaves: sibling,
@@ -982,6 +1001,7 @@ func addCommitmentOutputs(chanType channeldb.ChannelType, localChanCfg,
9821001
Type: tapsend.CommitAllocationToLocal,
9831002
Amount: ourAssetBalance,
9841003
AssetVersion: asset.V1,
1004+
SplitRoot: initiator,
9851005
BtcAmount: ourBalance,
9861006
InternalKey: toLocalTree.InternalKey,
9871007
NonAssetLeaves: sibling,
@@ -1044,6 +1064,7 @@ func addCommitmentOutputs(chanType channeldb.ChannelType, localChanCfg,
10441064
Type: tapsend.CommitAllocationToRemote,
10451065
Amount: theirAssetBalance,
10461066
AssetVersion: asset.V1,
1067+
SplitRoot: !initiator,
10471068
BtcAmount: theirBalance,
10481069
InternalKey: toRemoteTree.InternalKey,
10491070
NonAssetLeaves: sibling,

tapsend/allocation_test.go

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,129 @@ func TestDistributeCoins(t *testing.T) {
877877
},
878878
},
879879
},
880+
{
881+
// This tests the backward compatibility case where the
882+
// split root output is defined explicitly, even for an
883+
// interactive packet (which is the case for channels).
884+
name: "single asset, distributed to three outputs",
885+
inputs: []*proof.Proof{
886+
makeProof(t, assetID1Tranche1),
887+
},
888+
interactive: true,
889+
//nolint:lll
890+
allocations: []*Allocation{
891+
{
892+
Type: CommitAllocationHtlcOutgoing,
893+
Amount: 50,
894+
OutputIndex: 2,
895+
},
896+
{
897+
Type: CommitAllocationToLocal,
898+
Amount: 20,
899+
OutputIndex: 3,
900+
},
901+
{
902+
Type: CommitAllocationToRemote,
903+
Amount: 30,
904+
OutputIndex: 4,
905+
SplitRoot: true,
906+
},
907+
},
908+
expectedInputs: map[asset.ID][]asset.ScriptKey{
909+
assetID1.ID(): {
910+
assetID1Tranche1.ScriptKey,
911+
},
912+
},
913+
expectedOutputs: map[asset.ID][]*tappsbt.VOutput{
914+
assetID1.ID(): {
915+
{
916+
Amount: 50,
917+
Type: simple,
918+
Interactive: true,
919+
AnchorOutputIndex: 2,
920+
},
921+
{
922+
Amount: 20,
923+
Type: simple,
924+
Interactive: true,
925+
AnchorOutputIndex: 3,
926+
},
927+
{
928+
Amount: 30,
929+
Type: split,
930+
Interactive: true,
931+
AnchorOutputIndex: 4,
932+
},
933+
},
934+
},
935+
},
936+
{
937+
// This shows that for channels with multiple asset IDs,
938+
// defining the split root output might not be enough,
939+
// if some assets aren't even distributed to that
940+
// output. So the fallback for a packet that doesn't
941+
// get an exact split root output is to just use the
942+
// first output as the split root.
943+
name: "multiple allocations, split root defined on " +
944+
"output that gets full value",
945+
inputs: []*proof.Proof{
946+
makeProof(t, assetID4Tranche1),
947+
makeProof(t, assetID5Tranche1),
948+
},
949+
interactive: true,
950+
//nolint:lll
951+
allocations: []*Allocation{
952+
{
953+
Type: CommitAllocationHtlcOutgoing,
954+
Amount: 5000,
955+
OutputIndex: 2,
956+
},
957+
{
958+
Type: CommitAllocationToLocal,
959+
Amount: 20000,
960+
OutputIndex: 3,
961+
},
962+
{
963+
Type: CommitAllocationToRemote,
964+
Amount: 25000,
965+
OutputIndex: 4,
966+
SplitRoot: true,
967+
},
968+
},
969+
vPktVersion: tappsbt.V1,
970+
expectedInputs: map[asset.ID][]asset.ScriptKey{
971+
assetID4.ID(): {
972+
assetID4Tranche1.ScriptKey,
973+
},
974+
assetID5.ID(): {
975+
assetID5Tranche1.ScriptKey,
976+
},
977+
},
978+
expectedOutputs: map[asset.ID][]*tappsbt.VOutput{
979+
assetID4.ID(): {
980+
{
981+
Amount: 5000,
982+
Type: split,
983+
Interactive: true,
984+
AnchorOutputIndex: 2,
985+
},
986+
{
987+
Amount: 20000,
988+
Type: simple,
989+
Interactive: true,
990+
AnchorOutputIndex: 3,
991+
},
992+
},
993+
assetID5.ID(): {
994+
{
995+
Amount: 25000,
996+
Type: simple,
997+
Interactive: true,
998+
AnchorOutputIndex: 4,
999+
},
1000+
},
1001+
},
1002+
},
8801003
}
8811004

8821005
for _, tc := range testCases {

0 commit comments

Comments
 (0)