channeldb+funding: track opening height using MarkChannelAsOpen

This commit modifies the OpenChannel structure on-disk to also track
that opening height of a channel. This change is being made in order to
make and more light client friendly. A follow up commit will modify
several areas of the codebase to use this new functionality.
This commit is contained in:
Olaoluwa Osuntokun 2017-05-10 16:44:40 -07:00
parent 373a1192ce
commit a5113c2439
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2
4 changed files with 96 additions and 17 deletions

@ -70,6 +70,7 @@ var (
satReceivedPrefix = []byte("srp")
netFeesPrefix = []byte("ntp")
isPendingPrefix = []byte("pdg")
openHeightPrefix = []byte("open-height-prefix")
// chanIDKey stores the node, and channelID for an active channel.
chanIDKey = []byte("cik")
@ -129,6 +130,10 @@ const (
// to an on-disk log, which can then subsequently be queried in order to
// "time-travel" to a prior state.
type OpenChannel struct {
// OpeningHeight is the height in which this channel was officially
// marked open.
OpeningHeight uint32
// IdentityPub is the identity public key of the remote node this
// channel has been established with.
IdentityPub *btcec.PublicKey
@ -356,17 +361,18 @@ func (c *OpenChannel) SyncPending(addr *net.TCPAddr) error {
return err
}
// If a LinkNode for this identity public key already exsits, then
// we can exit early.
// If a LinkNode for this identity public key already exists,
// then we can exit early.
nodePub := c.IdentityPub.SerializeCompressed()
if nodeInfoBucket.Get(nodePub) != nil {
return nil
}
// Next, we need to establish a (possibly) new LinkNode
// relationship for this channel. The LinkNode metadata contains
// reachability, up-time, and service bits related information.
// TODO(roasbeef): net info shuld be in lnwire.NetAddress
// relationship for this channel. The LinkNode metadata
// contains reachability, up-time, and service bits related
// information.
// TODO(roasbeef): net info should be in lnwire.NetAddress
linkNode := c.Db.NewLinkNode(wire.MainNet, c.IdentityPub, addr)
return putLinkNode(nodeInfoBucket, linkNode)
@ -966,6 +972,9 @@ func putOpenChannel(openChanBucket *bolt.Bucket, nodeChanBucket *bolt.Bucket,
if err := putChanIsPending(openChanBucket, channel); err != nil {
return err
}
if err := putChanOpenHeight(openChanBucket, channel); err != nil {
return err
}
// Next, write out the fields of the channel update less frequently.
if err := putChannelIDs(nodeChanBucket, channel); err != nil {
@ -1053,6 +1062,9 @@ 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 {
return nil, err
}
return channel, nil
}
@ -1083,6 +1095,9 @@ func deleteOpenChannel(openChanBucket *bolt.Bucket, nodeChanBucket *bolt.Bucket,
if err := deleteChanIsPending(openChanBucket, channelID); err != nil {
return err
}
if err := deleteChanOpenHeight(openChanBucket, channelID); err != nil {
return err
}
// Finally, delete all the fields directly within the node's channel
// bucket.
@ -1447,6 +1462,44 @@ func fetchChanIsPending(openChanBucket *bolt.Bucket, channel *OpenChannel) error
return nil
}
func putChanOpenHeight(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)
var scratch [4]byte
byteOrder.PutUint32(scratch[:], channel.OpeningHeight)
return openChanBucket.Put(keyPrefix, scratch[:])
}
func fetchChanOpenHeight(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)
openHeightBytes := openChanBucket.Get(keyPrefix)
channel.OpeningHeight = byteOrder.Uint32(openHeightBytes)
return nil
}
func deleteChanOpenHeight(openChanBucket *bolt.Bucket, chanID []byte) error {
keyPrefix := make([]byte, 3+len(chanID))
copy(keyPrefix[3:], chanID)
copy(keyPrefix[:3], openHeightPrefix)
return openChanBucket.Delete(keyPrefix)
}
func putChannelIDs(nodeChanBucket *bolt.Bucket, channel *OpenChannel) error {
// TODO(roasbeef): just pass in chanID everywhere for puts
var b bytes.Buffer

@ -666,10 +666,23 @@ func TestFetchPendingChannels(t *testing.T) {
"got %v", 1, len(pendingChannels))
}
if err := cdb.MarkChannelAsOpen(pendingChannels[0].ChanID); err != nil {
const openHeight = 100
err = cdb.MarkChannelAsOpen(pendingChannels[0].ChanID, openHeight)
if err != nil {
t.Fatalf("unable to mark channel as open: %v", err)
}
// Next, we'll re-fetch the channel to ensure that the open height was
// properly set.
openChans, err := cdb.FetchAllChannels()
if err != nil {
t.Fatalf("unable to fetch channels: %v", err)
}
if openChans[0].OpeningHeight != openHeight {
t.Fatalf("channel opening heights don't match: expected %v, "+
"got %v", openChans[0].OpeningHeight, openHeight)
}
pendingChannels, err = cdb.FetchPendingChannels()
if err != nil {
t.Fatalf("unable to list pending channels: %v", err)
@ -707,7 +720,8 @@ func TestFetchClosedChannels(t *testing.T) {
// Next, simulate the confirmation of the channel by marking it as
// pending within the database.
if err := cdb.MarkChannelAsOpen(state.ChanID); err != nil {
const openHeight = 100
if err := cdb.MarkChannelAsOpen(state.ChanID, openHeight); err != nil {
t.Fatalf("unable to mark channel as open: %v", err)
}

@ -344,6 +344,7 @@ func fetchChannels(d *DB, pendingOnly bool) ([]*OpenChannel, error) {
return fmt.Errorf("unable to read channel for "+
"node_key=%x: %v", k, err)
}
// TODO(roasbeef): simplify
if pendingOnly {
for _, channel := range nodeChannels {
if channel.IsPending {
@ -361,16 +362,17 @@ func fetchChannels(d *DB, pendingOnly bool) ([]*OpenChannel, error) {
}
// MarkChannelAsOpen records the finalization of the funding process and marks
// a channel as available for use.
func (d *DB) MarkChannelAsOpen(outpoint *wire.OutPoint) error {
// a channel as available for use. Additionally the height in which this
// channel as opened will also be recorded within the database.
func (d *DB) MarkChannelAsOpen(outpoint *wire.OutPoint, openHeight uint32) error {
return d.Update(func(tx *bolt.Tx) error {
openChanBucket := tx.Bucket(openChannelBucket)
if openChanBucket == nil {
return ErrNoActiveChannels
}
// Generate the database key, which will consist of the IsPending
// prefix followed by the channel's outpoint.
// Generate the database key, which will consist of the
// IsPending prefix followed by the channel's outpoint.
var b bytes.Buffer
if err := writeOutpoint(&b, outpoint); err != nil {
return err
@ -379,11 +381,20 @@ func (d *DB) MarkChannelAsOpen(outpoint *wire.OutPoint) error {
copy(keyPrefix[3:], b.Bytes())
copy(keyPrefix[:3], isPendingPrefix)
// For the database value, store a zero, since the channel is no
// longer pending.
scratch := make([]byte, 2)
byteOrder.PutUint16(scratch, uint16(0))
return openChanBucket.Put(keyPrefix, scratch)
// For the database value, store a zero, since the channel is
// no longer pending.
scratch := make([]byte, 4)
byteOrder.PutUint16(scratch[:2], uint16(0))
if err := openChanBucket.Put(keyPrefix, scratch[:2]); err != nil {
return err
}
// Finally, we'll also store the opening height for this
// channel as well.
byteOrder.PutUint32(scratch, openHeight)
copy(keyPrefix[:3], openHeightPrefix)
return openChanBucket.Put(keyPrefix, scratch[:])
})
}

@ -922,7 +922,8 @@ func (f *fundingManager) waitForFundingConfirmation(completeChan *channeldb.Open
// Now that the channel has been fully confirmed, we'll mark it as open
// within the database.
completeChan.IsPending = false
err = f.cfg.Wallet.ChannelDB.MarkChannelAsOpen(&fundingPoint)
err = f.cfg.Wallet.ChannelDB.MarkChannelAsOpen(&fundingPoint,
confDetails.BlockHeight)
if err != nil {
fndgLog.Errorf("error setting channel pending flag to false: "+
"%v", err)