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.
This commit is contained in:
Olaoluwa Osuntokun 2017-06-05 15:02:27 -07:00
parent fb4121ba6c
commit 66ba2e862c
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2
3 changed files with 60 additions and 26 deletions

@ -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)
}

@ -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)
}

@ -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)
})
}