From 66ba2e862c98016b00e378beb6d4177789bd59ca Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 5 Jun 2017 15:02:27 -0700 Subject: [PATCH] channeldb: store the funding transaction broadcast height for channels This commit expands the field within the OpenChannel struct in order to start tracking the height that the funding transaction was initially broadcast. Other sub-systems within lnd can now use this data to give a more accurate height hint to the ChainNotifier, or to use during the funding workflow to decide if a channel should be forgotten after it fails to confirm for N blocks. --- channeldb/channel.go | 54 ++++++++++++++++++++++++--------------- channeldb/channel_test.go | 19 ++++++++++++-- channeldb/db.go | 13 +++++++--- 3 files changed, 60 insertions(+), 26 deletions(-) diff --git a/channeldb/channel.go b/channeldb/channel.go index 14e67690..e06c402d 100644 --- a/channeldb/channel.go +++ b/channeldb/channel.go @@ -70,7 +70,7 @@ var ( satReceivedPrefix = []byte("srp") commitFeePrefix = []byte("cfp") isPendingPrefix = []byte("pdg") - openHeightPrefix = []byte("open-height-prefix") + confInfoPrefix = []byte("conf-info") // chanIDKey stores the node, and channelID for an active channel. chanIDKey = []byte("cik") @@ -134,6 +134,12 @@ type OpenChannel struct { // marked open. OpeningHeight uint32 + // FundingBroadcastHeight is the height in which the funding + // transaction was broadcast. This value can be used by higher level + // sub-systems to determine if a channel is stale and/or should have + // been confirmed before a certain height. + FundingBroadcastHeight uint32 + // IdentityPub is the identity public key of the remote node this // channel has been established with. IdentityPub *btcec.PublicKey @@ -353,10 +359,12 @@ func (c *OpenChannel) fullSync(tx *bolt.Tx) error { // // TODO(roasbeef): addr param should eventually be a lnwire.NetAddress type // that includes service bits. -func (c *OpenChannel) SyncPending(addr *net.TCPAddr) error { +func (c *OpenChannel) SyncPending(addr *net.TCPAddr, pendingHeight uint32) error { c.Lock() defer c.Unlock() + c.FundingBroadcastHeight = pendingHeight + return c.Db.Update(func(tx *bolt.Tx) error { // First, sync all the persistent channel state to disk. if err := c.fullSync(tx); err != nil { @@ -1004,7 +1012,7 @@ func putOpenChannel(openChanBucket *bolt.Bucket, nodeChanBucket *bolt.Bucket, if err := putChanIsPending(openChanBucket, channel); err != nil { return err } - if err := putChanOpenHeight(openChanBucket, channel); err != nil { + if err := putChanConfInfo(openChanBucket, channel); err != nil { return err } if err := putChanCommitFee(openChanBucket, channel); err != nil { @@ -1097,7 +1105,7 @@ func fetchOpenChannel(openChanBucket *bolt.Bucket, nodeChanBucket *bolt.Bucket, if err = fetchChanIsPending(openChanBucket, channel); err != nil { return nil, err } - if err := fetchChanOpenHeight(openChanBucket, channel); err != nil { + if err := fetchChanConfInfo(openChanBucket, channel); err != nil { return nil, err } if err = fetchChanCommitFee(openChanBucket, channel); err != nil { @@ -1133,7 +1141,7 @@ func deleteOpenChannel(openChanBucket *bolt.Bucket, nodeChanBucket *bolt.Bucket, if err := deleteChanIsPending(openChanBucket, channelID); err != nil { return err } - if err := deleteChanOpenHeight(openChanBucket, channelID); err != nil { + if err := deleteChanConfInfo(openChanBucket, channelID); err != nil { return err } if err := deleteChanCommitFee(openChanBucket, channelID); err != nil { @@ -1503,41 +1511,45 @@ func fetchChanIsPending(openChanBucket *bolt.Bucket, channel *OpenChannel) error return nil } -func putChanOpenHeight(openChanBucket *bolt.Bucket, channel *OpenChannel) error { +func putChanConfInfo(openChanBucket *bolt.Bucket, channel *OpenChannel) error { var b bytes.Buffer if err := writeOutpoint(&b, channel.ChanID); err != nil { return err } - keyPrefix := make([]byte, 3+b.Len()) - copy(keyPrefix[3:], b.Bytes()) - copy(keyPrefix[:3], openHeightPrefix) + keyPrefix := make([]byte, len(confInfoPrefix)+b.Len()) + copy(keyPrefix[:len(confInfoPrefix)], confInfoPrefix) + copy(keyPrefix[len(confInfoPrefix):], b.Bytes()) + + // We store the conf info in the following format: broadcast || open. + var scratch [8]byte + byteOrder.PutUint32(scratch[:], channel.FundingBroadcastHeight) + byteOrder.PutUint32(scratch[4:], channel.OpeningHeight) - var scratch [4]byte - byteOrder.PutUint32(scratch[:], channel.OpeningHeight) return openChanBucket.Put(keyPrefix, scratch[:]) } -func fetchChanOpenHeight(openChanBucket *bolt.Bucket, channel *OpenChannel) error { +func fetchChanConfInfo(openChanBucket *bolt.Bucket, channel *OpenChannel) error { var b bytes.Buffer if err := writeOutpoint(&b, channel.ChanID); err != nil { return err } - keyPrefix := make([]byte, 3+b.Len()) - copy(keyPrefix[3:], b.Bytes()) - copy(keyPrefix[:3], openHeightPrefix) + keyPrefix := make([]byte, len(confInfoPrefix)+b.Len()) + copy(keyPrefix[:len(confInfoPrefix)], confInfoPrefix) + copy(keyPrefix[len(confInfoPrefix):], b.Bytes()) - openHeightBytes := openChanBucket.Get(keyPrefix) - channel.OpeningHeight = byteOrder.Uint32(openHeightBytes) + confInfoBytes := openChanBucket.Get(keyPrefix) + channel.FundingBroadcastHeight = byteOrder.Uint32(confInfoBytes[:4]) + channel.OpeningHeight = byteOrder.Uint32(confInfoBytes[4:]) return nil } -func deleteChanOpenHeight(openChanBucket *bolt.Bucket, chanID []byte) error { - keyPrefix := make([]byte, 3+len(chanID)) - copy(keyPrefix[3:], chanID) - copy(keyPrefix[:3], openHeightPrefix) +func deleteChanConfInfo(openChanBucket *bolt.Bucket, chanID []byte) error { + keyPrefix := make([]byte, len(confInfoPrefix)+len(chanID)) + copy(keyPrefix[:len(confInfoPrefix)], confInfoPrefix) + copy(keyPrefix[len(confInfoPrefix):], chanID) return openChanBucket.Delete(keyPrefix) } diff --git a/channeldb/channel_test.go b/channeldb/channel_test.go index 7484107f..3eef2717 100644 --- a/channeldb/channel_test.go +++ b/channeldb/channel_test.go @@ -654,7 +654,8 @@ func TestFetchPendingChannels(t *testing.T) { Port: 18555, } - if err := state.SyncPending(addr); err != nil { + const broadcastHeight = 99 + if err := state.SyncPending(addr, broadcastHeight); err != nil { t.Fatalf("unable to save and serialize channel state: %v", err) } @@ -668,6 +669,14 @@ func TestFetchPendingChannels(t *testing.T) { "got %v", 1, len(pendingChannels)) } + // The broadcast height of the pending channel should've been set + // properly. + if pendingChannels[0].FundingBroadcastHeight != broadcastHeight { + t.Fatalf("broadcast height mismatch: expected %v, got %v", + pendingChannels[0].FundingBroadcastHeight, + broadcastHeight) + } + const openHeight = 100 err = cdb.MarkChannelAsOpen(pendingChannels[0].ChanID, openHeight) if err != nil { @@ -684,6 +693,11 @@ func TestFetchPendingChannels(t *testing.T) { t.Fatalf("channel opening heights don't match: expected %v, "+ "got %v", openChans[0].OpeningHeight, openHeight) } + if openChans[0].FundingBroadcastHeight != broadcastHeight { + t.Fatalf("broadcast height mismatch: expected %v, got %v", + openChans[0].FundingBroadcastHeight, + broadcastHeight) + } pendingChannels, err = cdb.FetchPendingChannels() if err != nil { @@ -716,7 +730,8 @@ func TestFetchClosedChannels(t *testing.T) { IP: net.ParseIP("127.0.0.1"), Port: 18555, } - if err := state.SyncPending(addr); err != nil { + const broadcastHeight = 99 + if err := state.SyncPending(addr, broadcastHeight); err != nil { t.Fatalf("unable to save and serialize channel state: %v", err) } diff --git a/channeldb/db.go b/channeldb/db.go index 5b5d9f89..df236c27 100644 --- a/channeldb/db.go +++ b/channeldb/db.go @@ -391,10 +391,17 @@ func (d *DB) MarkChannelAsOpen(outpoint *wire.OutPoint, openHeight uint32) error // Finally, we'll also store the opening height for this // channel as well. - byteOrder.PutUint32(scratch, openHeight) - copy(keyPrefix[:3], openHeightPrefix) + confInfoKey := make([]byte, len(confInfoPrefix)+len(b.Bytes())) + copy(confInfoKey[:len(confInfoPrefix)], confInfoPrefix) + copy(confInfoKey[len(confInfoPrefix):], b.Bytes()) - return openChanBucket.Put(keyPrefix, scratch[:]) + confInfoBytes := openChanBucket.Get(confInfoKey) + infoCopy := make([]byte, len(confInfoBytes)) + copy(infoCopy[:], confInfoBytes) + + byteOrder.PutUint32(infoCopy[4:], openHeight) + + return openChanBucket.Put(confInfoKey, infoCopy) }) }