diff --git a/chanbackup/single.go b/chanbackup/single.go index 1bfb49ca..c989bda9 100644 --- a/chanbackup/single.go +++ b/chanbackup/single.go @@ -66,6 +66,9 @@ type Single struct { // ShortChannelID encodes the exact location in the chain in which the // channel was initially confirmed. This includes: the block height, // transaction index, and the output within the target transaction. + // Channels that were not confirmed at the time of backup creation will + // have the funding TX broadcast height set as their block height in + // the ShortChannelID. ShortChannelID lnwire.ShortChannelID // RemoteNodePub is the identity public key of the remote node this @@ -126,11 +129,21 @@ func NewSingle(channel *channeldb.OpenChannel, // key. _, shaChainPoint := btcec.PrivKeyFromBytes(btcec.S256(), b.Bytes()) + // If a channel is unconfirmed, the block height of the ShortChannelID + // is zero. This will lead to problems when trying to restore that + // channel as the spend notifier would get a height hint of zero. + // To work around that problem, we add the channel broadcast height + // to the channel ID so we can use that as height hint on restore. + chanID := channel.ShortChanID() + if chanID.BlockHeight == 0 { + chanID.BlockHeight = channel.FundingBroadcastHeight + } + single := Single{ IsInitiator: channel.IsInitiator, ChainHash: channel.ChainHash, FundingOutpoint: channel.FundingOutpoint, - ShortChannelID: channel.ShortChannelID, + ShortChannelID: chanID, RemoteNodePub: channel.IdentityPub, Addresses: nodeAddrs, Capacity: channel.Capacity, diff --git a/chanbackup/single_test.go b/chanbackup/single_test.go index 776ce0bc..88e2f594 100644 --- a/chanbackup/single_test.go +++ b/chanbackup/single_test.go @@ -410,4 +410,44 @@ func TestSinglePackStaticChanBackups(t *testing.T) { } } +// TestSingleUnconfirmedChannel tests that unconfirmed channels get serialized +// correctly by encoding the funding broadcast height as block height of the +// short channel ID. +func TestSingleUnconfirmedChannel(t *testing.T) { + t.Parallel() + + var fundingBroadcastHeight = uint32(1234) + + // Let's create an open channel shell that contains all the information + // we need to create a static channel backup but simulate an + // unconfirmed channel by setting the block height to 0. + channel, err := genRandomOpenChannelShell() + if err != nil { + t.Fatalf("unable to gen open channel: %v", err) + } + channel.ShortChannelID.BlockHeight = 0 + channel.FundingBroadcastHeight = fundingBroadcastHeight + + singleChanBackup := NewSingle(channel, []net.Addr{addr1, addr2}) + keyRing := &mockKeyRing{} + + // Pack it and then unpack it again to make sure everything is written + // correctly, then check that the block height of the unpacked + // is the funding broadcast height we set before. + var b bytes.Buffer + if err := singleChanBackup.PackToWriter(&b, keyRing); err != nil { + t.Fatalf("unable to pack single: %v", err) + } + var unpackedSingle Single + err = unpackedSingle.UnpackFromReader(&b, keyRing) + if err != nil { + t.Fatalf("unable to unpack single: %v", err) + } + if unpackedSingle.ShortChannelID.BlockHeight != fundingBroadcastHeight { + t.Fatalf("invalid block height. got %d expected %d.", + unpackedSingle.ShortChannelID.BlockHeight, + fundingBroadcastHeight) + } +} + // TODO(roasbsef): fuzz parsing