multi: introduce an option for resolutions
We don't always need the resolutions in the local force close summary so we make it an option.
This commit is contained in:
8 files changed
+196
-71
No files matched your search
+64
-16
@@ -7852,6 +7852,18 @@ type LocalForceCloseSummary struct {
|
||||
// commitment state.
|
||||
CloseTx *wire.MsgTx
|
||||
|
||||
// ChanSnapshot is a snapshot of the final state of the channel at the
|
||||
// time the summary was created.
|
||||
ChanSnapshot channeldb.ChannelSnapshot
|
||||
|
||||
// ContractResolutions contains all the data required for resolving the
|
||||
// different output types of a commitment transaction.
|
||||
ContractResolutions fn.Option[ContractResolutions]
|
||||
}
|
||||
|
||||
// ContractResolutions contains all the data required for resolving the
|
||||
// different output types of a commitment transaction.
|
||||
type ContractResolutions struct {
|
||||
// CommitResolution contains all the data required to sweep the output
|
||||
// to ourselves. Since this is our commitment transaction, we'll need
|
||||
// to wait a time delay before we can sweep the output.
|
||||
@@ -7860,19 +7872,38 @@ type LocalForceCloseSummary struct {
|
||||
// then this will be nil.
|
||||
CommitResolution *CommitOutputResolution
|
||||
|
||||
// HtlcResolutions contains all the data required to sweep any outgoing
|
||||
// HTLC's and incoming HTLc's we know the preimage to. For each of these
|
||||
// HTLC's, we'll need to go to the second level to sweep them fully.
|
||||
HtlcResolutions *HtlcResolutions
|
||||
|
||||
// ChanSnapshot is a snapshot of the final state of the channel at the
|
||||
// time the summary was created.
|
||||
ChanSnapshot channeldb.ChannelSnapshot
|
||||
|
||||
// AnchorResolution contains the data required to sweep the anchor
|
||||
// output. If the channel type doesn't include anchors, the value of
|
||||
// this field will be nil.
|
||||
AnchorResolution *AnchorResolution
|
||||
|
||||
// HtlcResolutions contains all the data required to sweep any outgoing
|
||||
// HTLC's and incoming HTLc's we know the preimage to. For each of these
|
||||
// HTLC's, we'll need to go to the second level to sweep them fully.
|
||||
HtlcResolutions *HtlcResolutions
|
||||
}
|
||||
|
||||
// ForceCloseOpt is a functional option argument for the ForceClose method.
|
||||
type ForceCloseOpt func(*forceCloseConfig)
|
||||
|
||||
// forceCloseConfig holds the configuration options for force closing a channel.
|
||||
type forceCloseConfig struct {
|
||||
// skipResolution if true will skip creating the contract resolutions
|
||||
// when generating the force close summary.
|
||||
skipResolution bool
|
||||
}
|
||||
|
||||
// defaultForceCloseConfig returns the default force close configuration.
|
||||
func defaultForceCloseConfig() *forceCloseConfig {
|
||||
return &forceCloseConfig{}
|
||||
}
|
||||
|
||||
// WithSkipContractResolutions creates an option to skip the contract
|
||||
// resolutions from the returned summary.
|
||||
func WithSkipContractResolutions() ForceCloseOpt {
|
||||
return func(cfg *forceCloseConfig) {
|
||||
cfg.skipResolution = true
|
||||
}
|
||||
}
|
||||
|
||||
// ForceClose executes a unilateral closure of the transaction at the current
|
||||
@@ -7883,10 +7914,17 @@ type LocalForceCloseSummary struct {
|
||||
// outputs within the commitment transaction.
|
||||
//
|
||||
// TODO(roasbeef): all methods need to abort if in dispute state
|
||||
func (lc *LightningChannel) ForceClose() (*LocalForceCloseSummary, error) {
|
||||
func (lc *LightningChannel) ForceClose(opts ...ForceCloseOpt) (
|
||||
*LocalForceCloseSummary, error) {
|
||||
|
||||
lc.Lock()
|
||||
defer lc.Unlock()
|
||||
|
||||
cfg := defaultForceCloseConfig()
|
||||
for _, opt := range opts {
|
||||
opt(cfg)
|
||||
}
|
||||
|
||||
// If we've detected local data loss for this channel, then we won't
|
||||
// allow a force close, as it may be the case that we have a dated
|
||||
// version of the commitment, or this is actually a channel shell.
|
||||
@@ -7901,6 +7939,14 @@ func (lc *LightningChannel) ForceClose() (*LocalForceCloseSummary, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cfg.skipResolution {
|
||||
return &LocalForceCloseSummary{
|
||||
ChanPoint: lc.channelState.FundingOutpoint,
|
||||
ChanSnapshot: *lc.channelState.Snapshot(),
|
||||
CloseTx: commitTx,
|
||||
}, nil
|
||||
}
|
||||
|
||||
localCommitment := lc.channelState.LocalCommitment
|
||||
summary, err := NewLocalForceCloseSummary(
|
||||
lc.channelState, lc.Signer, commitTx,
|
||||
@@ -8107,12 +8153,14 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel,
|
||||
}
|
||||
|
||||
return &LocalForceCloseSummary{
|
||||
ChanPoint: chanState.FundingOutpoint,
|
||||
CloseTx: commitTx,
|
||||
CommitResolution: commitResolution,
|
||||
HtlcResolutions: htlcResolutions,
|
||||
ChanSnapshot: *chanState.Snapshot(),
|
||||
AnchorResolution: anchorResolution,
|
||||
ChanPoint: chanState.FundingOutpoint,
|
||||
CloseTx: commitTx,
|
||||
ChanSnapshot: *chanState.Snapshot(),
|
||||
ContractResolutions: fn.Some(ContractResolutions{
|
||||
CommitResolution: commitResolution,
|
||||
HtlcResolutions: htlcResolutions,
|
||||
AnchorResolution: anchorResolution,
|
||||
}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
+21
-15
@@ -959,24 +959,26 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) {
|
||||
closeSummary, err := aliceChannel.ForceClose()
|
||||
require.NoError(t, err, "unable to force close channel")
|
||||
|
||||
resolutionsAlice := closeSummary.ContractResolutions.UnwrapOrFail(t)
|
||||
|
||||
// Alice should detect that she can sweep the outgoing HTLC after a
|
||||
// timeout, but also that she's able to sweep in incoming HTLC Bob sent
|
||||
// her.
|
||||
if len(closeSummary.HtlcResolutions.OutgoingHTLCs) != 1 {
|
||||
if len(resolutionsAlice.HtlcResolutions.OutgoingHTLCs) != 1 {
|
||||
t.Fatalf("alice out htlc resolutions not populated: expected %v "+
|
||||
"htlcs, got %v htlcs",
|
||||
1, len(closeSummary.HtlcResolutions.OutgoingHTLCs))
|
||||
1, len(resolutionsAlice.HtlcResolutions.OutgoingHTLCs))
|
||||
}
|
||||
if len(closeSummary.HtlcResolutions.IncomingHTLCs) != 1 {
|
||||
if len(resolutionsAlice.HtlcResolutions.IncomingHTLCs) != 1 {
|
||||
t.Fatalf("alice in htlc resolutions not populated: expected %v "+
|
||||
"htlcs, got %v htlcs",
|
||||
1, len(closeSummary.HtlcResolutions.IncomingHTLCs))
|
||||
1, len(resolutionsAlice.HtlcResolutions.IncomingHTLCs))
|
||||
}
|
||||
|
||||
// Verify the anchor resolutions for the anchor commitment format.
|
||||
if testCase.chanType.HasAnchors() {
|
||||
// Check the close summary resolution.
|
||||
anchorRes := closeSummary.AnchorResolution
|
||||
anchorRes := resolutionsAlice.AnchorResolution
|
||||
if anchorRes == nil {
|
||||
t.Fatal("expected anchor resolution")
|
||||
}
|
||||
@@ -1010,7 +1012,7 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) {
|
||||
|
||||
// The SelfOutputSignDesc should be non-nil since the output to-self is
|
||||
// non-dust.
|
||||
aliceCommitResolution := closeSummary.CommitResolution
|
||||
aliceCommitResolution := resolutionsAlice.CommitResolution
|
||||
if aliceCommitResolution == nil {
|
||||
t.Fatalf("alice fails to include to-self output in " +
|
||||
"ForceCloseSummary")
|
||||
@@ -1056,7 +1058,7 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) {
|
||||
// Next, we'll ensure that the second level HTLC transaction it itself
|
||||
// spendable, and also that the delivery output (with delay) itself has
|
||||
// a valid sign descriptor.
|
||||
htlcResolution := closeSummary.HtlcResolutions.OutgoingHTLCs[0]
|
||||
htlcResolution := resolutionsAlice.HtlcResolutions.OutgoingHTLCs[0]
|
||||
outHtlcIndex := htlcResolution.SignedTimeoutTx.TxIn[0].PreviousOutPoint.Index
|
||||
senderHtlcPkScript := closeSummary.CloseTx.TxOut[outHtlcIndex].PkScript
|
||||
|
||||
@@ -1140,7 +1142,7 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) {
|
||||
// We'll now perform similar set of checks to ensure that Alice is able
|
||||
// to sweep the output that Bob sent to her on-chain with knowledge of
|
||||
// the preimage.
|
||||
inHtlcResolution := closeSummary.HtlcResolutions.IncomingHTLCs[0]
|
||||
inHtlcResolution := resolutionsAlice.HtlcResolutions.IncomingHTLCs[0]
|
||||
inHtlcIndex := inHtlcResolution.SignedSuccessTx.TxIn[0].PreviousOutPoint.Index
|
||||
receiverHtlcScript := closeSummary.CloseTx.TxOut[inHtlcIndex].PkScript
|
||||
|
||||
@@ -1221,7 +1223,8 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) {
|
||||
// Check the same for Bob's ForceCloseSummary.
|
||||
closeSummary, err = bobChannel.ForceClose()
|
||||
require.NoError(t, err, "unable to force close channel")
|
||||
bobCommitResolution := closeSummary.CommitResolution
|
||||
resolutionsBob := closeSummary.ContractResolutions.UnwrapOrFail(t)
|
||||
bobCommitResolution := resolutionsBob.CommitResolution
|
||||
if bobCommitResolution == nil {
|
||||
t.Fatalf("bob fails to include to-self output in ForceCloseSummary")
|
||||
}
|
||||
@@ -1255,19 +1258,19 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) {
|
||||
// As we didn't add the preimage of Alice's HTLC to bob's preimage
|
||||
// cache, he should only detect that he can sweep only his outgoing
|
||||
// HTLC upon force close.
|
||||
if len(closeSummary.HtlcResolutions.OutgoingHTLCs) != 1 {
|
||||
if len(resolutionsBob.HtlcResolutions.OutgoingHTLCs) != 1 {
|
||||
t.Fatalf("alice out htlc resolutions not populated: expected %v "+
|
||||
"htlcs, got %v htlcs",
|
||||
1, len(closeSummary.HtlcResolutions.OutgoingHTLCs))
|
||||
1, len(resolutionsBob.HtlcResolutions.OutgoingHTLCs))
|
||||
}
|
||||
|
||||
// Bob should recognize that the incoming HTLC is there, but the
|
||||
// preimage should be empty as he doesn't have the knowledge required
|
||||
// to sweep it.
|
||||
if len(closeSummary.HtlcResolutions.IncomingHTLCs) != 1 {
|
||||
if len(resolutionsBob.HtlcResolutions.IncomingHTLCs) != 1 {
|
||||
t.Fatalf("bob in htlc resolutions not populated: expected %v "+
|
||||
"htlcs, got %v htlcs",
|
||||
1, len(closeSummary.HtlcResolutions.IncomingHTLCs))
|
||||
1, len(resolutionsBob.HtlcResolutions.IncomingHTLCs))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1326,9 +1329,11 @@ func TestForceCloseDustOutput(t *testing.T) {
|
||||
closeSummary, err := aliceChannel.ForceClose()
|
||||
require.NoError(t, err, "unable to force close channel")
|
||||
|
||||
resolutionsAlice := closeSummary.ContractResolutions.UnwrapOrFail(t)
|
||||
|
||||
// Alice's to-self output should still be in the commitment
|
||||
// transaction.
|
||||
commitResolution := closeSummary.CommitResolution
|
||||
commitResolution := resolutionsAlice.CommitResolution
|
||||
if commitResolution == nil {
|
||||
t.Fatalf("alice fails to include to-self output in " +
|
||||
"ForceCloseSummary")
|
||||
@@ -1362,10 +1367,11 @@ func TestForceCloseDustOutput(t *testing.T) {
|
||||
|
||||
closeSummary, err = bobChannel.ForceClose()
|
||||
require.NoError(t, err, "unable to force close channel")
|
||||
resolutionsBob := closeSummary.ContractResolutions.UnwrapOrFail(t)
|
||||
|
||||
// Bob's to-self output is below Bob's dust value and should be
|
||||
// reflected in the ForceCloseSummary.
|
||||
commitResolution = closeSummary.CommitResolution
|
||||
commitResolution = resolutionsBob.CommitResolution
|
||||
if commitResolution != nil {
|
||||
t.Fatalf("bob incorrectly includes to-self output in " +
|
||||
"ForceCloseSummary")
|
||||
|
||||
@@ -406,6 +406,8 @@ func testVectors(t *testing.T, chanType channeldb.ChannelType, test testCase) {
|
||||
hex.EncodeToString(txBytes.Bytes()),
|
||||
)
|
||||
|
||||
resolutions := forceCloseSum.ContractResolutions.UnwrapOrFail(t)
|
||||
|
||||
// Obtain the second level transactions that the local node's channel
|
||||
// state machine has produced. Store them in a map indexed by commit tx
|
||||
// output index. Also complete the second level transaction with the
|
||||
@@ -419,7 +421,8 @@ func testVectors(t *testing.T, chanType channeldb.ChannelType, test testCase) {
|
||||
secondLevelTxes[index] = tx
|
||||
}
|
||||
|
||||
for _, r := range forceCloseSum.HtlcResolutions.IncomingHTLCs {
|
||||
htlcResolutions := resolutions.HtlcResolutions
|
||||
for _, r := range htlcResolutions.IncomingHTLCs {
|
||||
successTx := r.SignedSuccessTx
|
||||
witnessScript := successTx.TxIn[0].Witness[4]
|
||||
var hash160 [20]byte
|
||||
@@ -428,7 +431,7 @@ func testVectors(t *testing.T, chanType channeldb.ChannelType, test testCase) {
|
||||
successTx.TxIn[0].Witness[3] = preimage[:]
|
||||
storeTx(r.HtlcPoint().Index, successTx)
|
||||
}
|
||||
for _, r := range forceCloseSum.HtlcResolutions.OutgoingHTLCs {
|
||||
for _, r := range htlcResolutions.OutgoingHTLCs {
|
||||
storeTx(r.HtlcPoint().Index, r.SignedTimeoutTx)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user