Merge pull request #3789 from cfromknecht/coop-close-rpc-status
republish force and coop closes on startup
This commit is contained in:
8 files changed
+269
-65
No files matched your search
+69
-13
@@ -69,9 +69,13 @@ var (
|
||||
// remote peer during a channel sync in case we have lost channel state.
|
||||
dataLossCommitPointKey = []byte("data-loss-commit-point-key")
|
||||
|
||||
// closingTxKey points to a the closing tx that we broadcasted when
|
||||
// moving the channel to state CommitBroadcasted.
|
||||
closingTxKey = []byte("closing-tx-key")
|
||||
// forceCloseTxKey points to a the unilateral closing tx that we
|
||||
// broadcasted when moving the channel to state CommitBroadcasted.
|
||||
forceCloseTxKey = []byte("closing-tx-key")
|
||||
|
||||
// coopCloseTxKey points to a the cooperative closing tx that we
|
||||
// broadcasted when moving the channel to state CoopBroadcasted.
|
||||
coopCloseTxKey = []byte("coop-closing-tx-key")
|
||||
|
||||
// commitDiffKey stores the current pending commitment state we've
|
||||
// extended to the remote party (if any). Each time we propose a new
|
||||
@@ -382,6 +386,10 @@ var (
|
||||
// has been restored, and doesn't have all the fields a typical channel
|
||||
// will have.
|
||||
ChanStatusRestored ChannelStatus = 1 << 3
|
||||
|
||||
// ChanStatusCoopBroadcasted indicates that a cooperative close for this
|
||||
// channel has been broadcasted.
|
||||
ChanStatusCoopBroadcasted ChannelStatus = 1 << 4
|
||||
)
|
||||
|
||||
// chanStatusStrings maps a ChannelStatus to a human friendly string that
|
||||
@@ -392,6 +400,7 @@ var chanStatusStrings = map[ChannelStatus]string{
|
||||
ChanStatusCommitBroadcasted: "ChanStatusCommitBroadcasted",
|
||||
ChanStatusLocalDataLoss: "ChanStatusLocalDataLoss",
|
||||
ChanStatusRestored: "ChanStatusRestored",
|
||||
ChanStatusCoopBroadcasted: "ChanStatusCoopBroadcasted",
|
||||
}
|
||||
|
||||
// orderedChanStatusFlags is an in-order list of all that channel status flags.
|
||||
@@ -401,6 +410,7 @@ var orderedChanStatusFlags = []ChannelStatus{
|
||||
ChanStatusCommitBroadcasted,
|
||||
ChanStatusLocalDataLoss,
|
||||
ChanStatusRestored,
|
||||
ChanStatusCoopBroadcasted,
|
||||
}
|
||||
|
||||
// String returns a human-readable representation of the ChannelStatus.
|
||||
@@ -951,24 +961,65 @@ func (c *OpenChannel) isBorked(chanBucket *bbolt.Bucket) (bool, error) {
|
||||
// republish this tx at startup to ensure propagation, and we should still
|
||||
// handle the case where a different tx actually hits the chain.
|
||||
func (c *OpenChannel) MarkCommitmentBroadcasted(closeTx *wire.MsgTx) error {
|
||||
return c.markBroadcasted(
|
||||
ChanStatusCommitBroadcasted, forceCloseTxKey, closeTx,
|
||||
)
|
||||
}
|
||||
|
||||
// MarkCoopBroadcasted marks the channel to indicate that a cooperative close
|
||||
// transaction has been broadcast, either our own or the remote, and that we
|
||||
// should wach the chain for it to confirm before taking further action. It
|
||||
// takes as argument a cooperative close tx that could appear on chain, and
|
||||
// should be rebroadcast upon startup. This is only used to republish and ensure
|
||||
// propagation, and we should still handle the case where a different tx
|
||||
// actually hits the chain.
|
||||
func (c *OpenChannel) MarkCoopBroadcasted(closeTx *wire.MsgTx) error {
|
||||
return c.markBroadcasted(
|
||||
ChanStatusCoopBroadcasted, coopCloseTxKey, closeTx,
|
||||
)
|
||||
}
|
||||
|
||||
// markBroadcasted is a helper function which modifies the channel status of the
|
||||
// receiving channel and inserts a close transaction under the requested key,
|
||||
// which should specify either a coop or force close.
|
||||
func (c *OpenChannel) markBroadcasted(status ChannelStatus, key []byte,
|
||||
closeTx *wire.MsgTx) error {
|
||||
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
var b bytes.Buffer
|
||||
if err := WriteElement(&b, closeTx); err != nil {
|
||||
return err
|
||||
// If a closing tx is provided, we'll generate a closure to write the
|
||||
// transaction in the appropriate bucket under the given key.
|
||||
var putClosingTx func(*bbolt.Bucket) error
|
||||
if closeTx != nil {
|
||||
var b bytes.Buffer
|
||||
if err := WriteElement(&b, closeTx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
putClosingTx = func(chanBucket *bbolt.Bucket) error {
|
||||
return chanBucket.Put(key, b.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
putClosingTx := func(chanBucket *bbolt.Bucket) error {
|
||||
return chanBucket.Put(closingTxKey, b.Bytes())
|
||||
}
|
||||
|
||||
return c.putChanStatus(ChanStatusCommitBroadcasted, putClosingTx)
|
||||
return c.putChanStatus(status, putClosingTx)
|
||||
}
|
||||
|
||||
// BroadcastedCommitment retrieves the stored closing tx set during
|
||||
// BroadcastedCommitment retrieves the stored unilateral closing tx set during
|
||||
// MarkCommitmentBroadcasted. If not found ErrNoCloseTx is returned.
|
||||
func (c *OpenChannel) BroadcastedCommitment() (*wire.MsgTx, error) {
|
||||
return c.getClosingTx(forceCloseTxKey)
|
||||
}
|
||||
|
||||
// BroadcastedCooperative retrieves the stored cooperative closing tx set during
|
||||
// MarkCoopBroadcasted. If not found ErrNoCloseTx is returned.
|
||||
func (c *OpenChannel) BroadcastedCooperative() (*wire.MsgTx, error) {
|
||||
return c.getClosingTx(coopCloseTxKey)
|
||||
}
|
||||
|
||||
// getClosingTx is a helper method which returns the stored closing transaction
|
||||
// for key. The caller should use either the force or coop closing keys.
|
||||
func (c *OpenChannel) getClosingTx(key []byte) (*wire.MsgTx, error) {
|
||||
var closeTx *wire.MsgTx
|
||||
|
||||
err := c.Db.View(func(tx *bbolt.Tx) error {
|
||||
@@ -983,7 +1034,7 @@ func (c *OpenChannel) BroadcastedCommitment() (*wire.MsgTx, error) {
|
||||
return err
|
||||
}
|
||||
|
||||
bs := chanBucket.Get(closingTxKey)
|
||||
bs := chanBucket.Get(key)
|
||||
if bs == nil {
|
||||
return ErrNoCloseTx
|
||||
}
|
||||
@@ -1025,6 +1076,11 @@ func (c *OpenChannel) putChanStatus(status ChannelStatus,
|
||||
}
|
||||
|
||||
for _, f := range fs {
|
||||
// Skip execution of nil closures.
|
||||
if f == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := f(chanBucket); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -992,9 +992,28 @@ func TestFetchWaitingCloseChannels(t *testing.T) {
|
||||
PreviousOutPoint: channel.FundingOutpoint,
|
||||
},
|
||||
)
|
||||
|
||||
if err := channel.MarkCommitmentBroadcasted(closeTx); err != nil {
|
||||
t.Fatalf("unable to mark commitment broadcast: %v", err)
|
||||
}
|
||||
|
||||
// Now try to marking a coop close with a nil tx. This should
|
||||
// succeed, but it shouldn't exit when queried.
|
||||
if err = channel.MarkCoopBroadcasted(nil); err != nil {
|
||||
t.Fatalf("unable to mark nil coop broadcast: %v", err)
|
||||
}
|
||||
_, err := channel.BroadcastedCooperative()
|
||||
if err != ErrNoCloseTx {
|
||||
t.Fatalf("expected no closing tx error, got: %v", err)
|
||||
}
|
||||
|
||||
// Finally, modify the close tx deterministically and also mark
|
||||
// it as coop closed. Later we will test that distinct
|
||||
// transactions are returned for both coop and force closes.
|
||||
closeTx.TxIn[0].PreviousOutPoint.Index ^= 1
|
||||
if err := channel.MarkCoopBroadcasted(closeTx); err != nil {
|
||||
t.Fatalf("unable to mark coop broadcast: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Now, we'll fetch all the channels waiting to be closed from the
|
||||
@@ -1004,7 +1023,7 @@ func TestFetchWaitingCloseChannels(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("unable to fetch all waiting close channels: %v", err)
|
||||
}
|
||||
if len(waitingCloseChannels) != 2 {
|
||||
if len(waitingCloseChannels) != numChannels {
|
||||
t.Fatalf("expected %d channels waiting to be closed, got %d", 2,
|
||||
len(waitingCloseChannels))
|
||||
}
|
||||
@@ -1018,17 +1037,31 @@ func TestFetchWaitingCloseChannels(t *testing.T) {
|
||||
channel.FundingOutpoint)
|
||||
}
|
||||
|
||||
// Finally, make sure we can retrieve the closing tx for the
|
||||
// channel.
|
||||
closeTx, err := channel.BroadcastedCommitment()
|
||||
chanPoint := channel.FundingOutpoint
|
||||
|
||||
// Assert that the force close transaction is retrievable.
|
||||
forceCloseTx, err := channel.BroadcastedCommitment()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to retrieve commitment: %v", err)
|
||||
}
|
||||
|
||||
if closeTx.TxIn[0].PreviousOutPoint != channel.FundingOutpoint {
|
||||
if forceCloseTx.TxIn[0].PreviousOutPoint != chanPoint {
|
||||
t.Fatalf("expected outpoint %v, got %v",
|
||||
channel.FundingOutpoint,
|
||||
closeTx.TxIn[0].PreviousOutPoint)
|
||||
chanPoint,
|
||||
forceCloseTx.TxIn[0].PreviousOutPoint)
|
||||
}
|
||||
|
||||
// Assert that the coop close transaction is retrievable.
|
||||
coopCloseTx, err := channel.BroadcastedCooperative()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to retrieve coop close: %v", err)
|
||||
}
|
||||
|
||||
chanPoint.Index ^= 1
|
||||
if coopCloseTx.TxIn[0].PreviousOutPoint != chanPoint {
|
||||
t.Fatalf("expected outpoint %v, got %v",
|
||||
chanPoint,
|
||||
coopCloseTx.TxIn[0].PreviousOutPoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user