From 320bed7e6bb530ce2b0fa68a10eb535c5d7deb55 Mon Sep 17 00:00:00 2001 From: bryanvu Date: Sun, 30 Apr 2017 22:53:54 -0700 Subject: [PATCH] lnwallet: add CommitFee field to OpenChannel In order to cleanly handle shutdowns and restarts during state machine operation, the fee for the current commitment transaction must be persisted. This allows the fee to be reapplied when the current state is reloaded. --- channeldb/channel.go | 75 ++++++++++++++++++++++++++++++++++++++++- lnwallet/channel.go | 21 ++++++++---- lnwallet/reservation.go | 1 + 3 files changed, 90 insertions(+), 7 deletions(-) diff --git a/channeldb/channel.go b/channeldb/channel.go index 7792407e..b276704e 100644 --- a/channeldb/channel.go +++ b/channeldb/channel.go @@ -68,7 +68,7 @@ var ( updatePrefix = []byte("uup") satSentPrefix = []byte("ssp") satReceivedPrefix = []byte("srp") - netFeesPrefix = []byte("ntp") + commitFeePrefix = []byte("cfp") isPendingPrefix = []byte("pdg") openHeightPrefix = []byte("open-height-prefix") @@ -181,6 +181,13 @@ type OpenChannel struct { // channel directly spendable by the remote node. TheirBalance btcutil.Amount + // CommitFee is the amount calculated to be paid in fees for the + // current set of commitment transactions. The fee amount is + // persisted with the channel in order to allow the fee amount to be + // removed and recalculated with each channel state update, including + // updates that happen after a system restart. + CommitFee btcutil.Amount + // OurCommitKey is the latest version of the commitment state, // broadcast able by us. OurCommitTx *wire.MsgTx @@ -410,6 +417,7 @@ func (c *OpenChannel) UpdateCommitment(newCommitment *wire.MsgTx, c.TheirBalance = delta.RemoteBalance c.NumUpdates = delta.UpdateNum c.Htlcs = delta.Htlcs + c.CommitFee = delta.CommitFee // First we'll write out the current latest dynamic channel // state: the current channel balance, the number of updates, @@ -424,6 +432,9 @@ func (c *OpenChannel) UpdateCommitment(newCommitment *wire.MsgTx, if err := putChanNumUpdates(chanBucket, c); err != nil { return err } + if err := putChanCommitFee(chanBucket, c); err != nil { + return err + } if err := putChanCommitTxns(nodeChanBucket, c); err != nil { return err } @@ -492,6 +503,10 @@ type ChannelDelta struct { // update number. RemoteBalance btcutil.Amount + // CommitFee is the fee that has been subtracted from the channel + // initiator's balance at this point in the commitment chain. + CommitFee btcutil.Amount + // UpdateNum is the update number that this ChannelDelta represents the // total number of commitment updates to this point. This can be viewed // as sort of a "commitment height" as this number is monotonically @@ -990,6 +1005,9 @@ func putOpenChannel(openChanBucket *bolt.Bucket, nodeChanBucket *bolt.Bucket, if err := putChanOpenHeight(openChanBucket, channel); err != nil { return err } + if err := putChanCommitFee(openChanBucket, channel); err != nil { + return err + } // Next, write out the fields of the channel update less frequently. if err := putChannelIDs(nodeChanBucket, channel); err != nil { @@ -1080,6 +1098,9 @@ func fetchOpenChannel(openChanBucket *bolt.Bucket, nodeChanBucket *bolt.Bucket, if err := fetchChanOpenHeight(openChanBucket, channel); err != nil { return nil, err } + if err = fetchChanCommitFee(openChanBucket, channel); err != nil { + return nil, err + } return channel, nil } @@ -1113,6 +1134,9 @@ func deleteOpenChannel(openChanBucket *bolt.Bucket, nodeChanBucket *bolt.Bucket, if err := deleteChanOpenHeight(openChanBucket, channelID); err != nil { return err } + if err := deleteChanCommitFee(openChanBucket, channelID); err != nil { + return err + } // Finally, delete all the fields directly within the node's channel // bucket. @@ -1591,6 +1615,46 @@ func deleteChanCommitKeys(nodeChanBucket *bolt.Bucket, chanID []byte) error { return nodeChanBucket.Delete(commitKey) } +func putChanCommitFee(openChanBucket *bolt.Bucket, channel *OpenChannel) error { + scratch := make([]byte, 8) + byteOrder.PutUint64(scratch, uint64(channel.CommitFee)) + + var b bytes.Buffer + if err := writeOutpoint(&b, channel.ChanID); err != nil { + return err + } + + keyPrefix := make([]byte, 3+b.Len()) + copy(keyPrefix, commitFeePrefix) + copy(keyPrefix[3:], b.Bytes()) + + return openChanBucket.Put(keyPrefix, scratch) +} + +func fetchChanCommitFee(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, commitFeePrefix) + copy(keyPrefix[3:], b.Bytes()) + + commitFeeBytes := openChanBucket.Get(keyPrefix) + channel.CommitFee = btcutil.Amount(byteOrder.Uint64(commitFeeBytes)) + + return nil +} + +func deleteChanCommitFee(openChanBucket *bolt.Bucket, chanID []byte) error { + commitFeeKey := make([]byte, 3+len(chanID)) + copy(commitFeeKey, commitFeePrefix) + copy(commitFeeKey[3:], chanID) + + return openChanBucket.Delete(commitFeeKey) +} + func fetchChanCommitKeys(nodeChanBucket *bolt.Bucket, channel *OpenChannel) error { // Construct the key which stores the commitment keys: ckk || channelID. @@ -2119,6 +2183,11 @@ func serializeChannelDelta(w io.Writer, delta *ChannelDelta) error { } } + byteOrder.PutUint64(scratch[:], uint64(delta.CommitFee)) + if _, err := w.Write(scratch[:]); err != nil { + return err + } + return nil } @@ -2157,6 +2226,10 @@ func deserializeChannelDelta(r io.Reader) (*ChannelDelta, error) { delta.Htlcs[i] = htlc } + if _, err := r.Read(scratch[:]); err != nil { + return nil, err + } + delta.CommitFee = btcutil.Amount(byteOrder.Uint64(scratch[:])) return delta, nil } diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 6b570211..b0a2e558 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -232,6 +232,11 @@ type commitment struct { ourBalance btcutil.Amount theirBalance btcutil.Amount + // fee is the amount that will be paid as fees for this commitment + // transaction. The fee is recorded here so that it can be added + // back and recalculated for each new update to the channel state. + fee btcutil.Amount + // htlcs is the set of HTLCs which remain unsettled within this // commitment. outgoingHTLCs []PaymentDescriptor @@ -251,6 +256,7 @@ func (c *commitment) toChannelDelta(ourCommit bool) (*channeldb.ChannelDelta, er LocalBalance: c.ourBalance, RemoteBalance: c.theirBalance, UpdateNum: c.height, + CommitFee: c.fee, Htlcs: make([]*channeldb.HTLC, 0, numHtlcs), } @@ -1181,6 +1187,8 @@ func (lc *LightningChannel) restoreStateLogs() error { lc.localCommitChain.tail().theirMessageIndex = theirCounter lc.remoteCommitChain.tail().ourMessageIndex = ourCounter lc.remoteCommitChain.tail().theirMessageIndex = theirCounter + lc.localCommitChain.tail().fee = lc.channelState.CommitFee + lc.remoteCommitChain.tail().fee = lc.channelState.CommitFee return nil } @@ -1245,12 +1253,13 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool, // TODO(roasbeef): don't assume view is always fetched from tip? var ourBalance, theirBalance btcutil.Amount - if commitChain.tip() == nil { - ourBalance = lc.channelState.OurBalance - theirBalance = lc.channelState.TheirBalance - } else { - ourBalance = commitChain.tip().ourBalance - theirBalance = commitChain.tip().theirBalance + ourBalance = commitChain.tip().ourBalance + theirBalance = commitChain.tip().theirBalance + + if lc.channelState.IsInitiator { + ourBalance = ourBalance + commitChain.tip().fee + } else if !lc.channelState.IsInitiator { + theirBalance = theirBalance + commitChain.tip().fee } nextHeight := commitChain.tip().height + 1 diff --git a/lnwallet/reservation.go b/lnwallet/reservation.go index 61d8ac6b..74c13c8e 100644 --- a/lnwallet/reservation.go +++ b/lnwallet/reservation.go @@ -215,6 +215,7 @@ func NewChannelReservation(capacity, fundingAmt btcutil.Amount, minFeeRate btcut TheirBalance: theirBalance, MinFeePerKb: minFeeRate, Db: wallet.ChannelDB, + CommitFee: commitFee, }, numConfsToOpen: numConfs, pushSat: pushSat,