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.
This commit is contained in:
bryanvu 2017-04-30 22:53:54 -07:00 committed by Olaoluwa Osuntokun
parent 7768415adc
commit 320bed7e6b
3 changed files with 90 additions and 7 deletions

@ -68,7 +68,7 @@ var (
updatePrefix = []byte("uup") updatePrefix = []byte("uup")
satSentPrefix = []byte("ssp") satSentPrefix = []byte("ssp")
satReceivedPrefix = []byte("srp") satReceivedPrefix = []byte("srp")
netFeesPrefix = []byte("ntp") commitFeePrefix = []byte("cfp")
isPendingPrefix = []byte("pdg") isPendingPrefix = []byte("pdg")
openHeightPrefix = []byte("open-height-prefix") openHeightPrefix = []byte("open-height-prefix")
@ -181,6 +181,13 @@ type OpenChannel struct {
// channel directly spendable by the remote node. // channel directly spendable by the remote node.
TheirBalance btcutil.Amount 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, // OurCommitKey is the latest version of the commitment state,
// broadcast able by us. // broadcast able by us.
OurCommitTx *wire.MsgTx OurCommitTx *wire.MsgTx
@ -410,6 +417,7 @@ func (c *OpenChannel) UpdateCommitment(newCommitment *wire.MsgTx,
c.TheirBalance = delta.RemoteBalance c.TheirBalance = delta.RemoteBalance
c.NumUpdates = delta.UpdateNum c.NumUpdates = delta.UpdateNum
c.Htlcs = delta.Htlcs c.Htlcs = delta.Htlcs
c.CommitFee = delta.CommitFee
// First we'll write out the current latest dynamic channel // First we'll write out the current latest dynamic channel
// state: the current channel balance, the number of updates, // 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 { if err := putChanNumUpdates(chanBucket, c); err != nil {
return err return err
} }
if err := putChanCommitFee(chanBucket, c); err != nil {
return err
}
if err := putChanCommitTxns(nodeChanBucket, c); err != nil { if err := putChanCommitTxns(nodeChanBucket, c); err != nil {
return err return err
} }
@ -492,6 +503,10 @@ type ChannelDelta struct {
// update number. // update number.
RemoteBalance btcutil.Amount 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 // UpdateNum is the update number that this ChannelDelta represents the
// total number of commitment updates to this point. This can be viewed // total number of commitment updates to this point. This can be viewed
// as sort of a "commitment height" as this number is monotonically // 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 { if err := putChanOpenHeight(openChanBucket, channel); err != nil {
return err return err
} }
if err := putChanCommitFee(openChanBucket, channel); err != nil {
return err
}
// Next, write out the fields of the channel update less frequently. // Next, write out the fields of the channel update less frequently.
if err := putChannelIDs(nodeChanBucket, channel); err != nil { 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 { if err := fetchChanOpenHeight(openChanBucket, channel); err != nil {
return nil, err return nil, err
} }
if err = fetchChanCommitFee(openChanBucket, channel); err != nil {
return nil, err
}
return channel, nil return channel, nil
} }
@ -1113,6 +1134,9 @@ func deleteOpenChannel(openChanBucket *bolt.Bucket, nodeChanBucket *bolt.Bucket,
if err := deleteChanOpenHeight(openChanBucket, channelID); err != nil { if err := deleteChanOpenHeight(openChanBucket, channelID); err != nil {
return err return err
} }
if err := deleteChanCommitFee(openChanBucket, channelID); err != nil {
return err
}
// Finally, delete all the fields directly within the node's channel // Finally, delete all the fields directly within the node's channel
// bucket. // bucket.
@ -1591,6 +1615,46 @@ func deleteChanCommitKeys(nodeChanBucket *bolt.Bucket, chanID []byte) error {
return nodeChanBucket.Delete(commitKey) 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 { func fetchChanCommitKeys(nodeChanBucket *bolt.Bucket, channel *OpenChannel) error {
// Construct the key which stores the commitment keys: ckk || channelID. // 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 return nil
} }
@ -2157,6 +2226,10 @@ func deserializeChannelDelta(r io.Reader) (*ChannelDelta, error) {
delta.Htlcs[i] = htlc delta.Htlcs[i] = htlc
} }
if _, err := r.Read(scratch[:]); err != nil {
return nil, err
}
delta.CommitFee = btcutil.Amount(byteOrder.Uint64(scratch[:]))
return delta, nil return delta, nil
} }

@ -232,6 +232,11 @@ type commitment struct {
ourBalance btcutil.Amount ourBalance btcutil.Amount
theirBalance 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 // htlcs is the set of HTLCs which remain unsettled within this
// commitment. // commitment.
outgoingHTLCs []PaymentDescriptor outgoingHTLCs []PaymentDescriptor
@ -251,6 +256,7 @@ func (c *commitment) toChannelDelta(ourCommit bool) (*channeldb.ChannelDelta, er
LocalBalance: c.ourBalance, LocalBalance: c.ourBalance,
RemoteBalance: c.theirBalance, RemoteBalance: c.theirBalance,
UpdateNum: c.height, UpdateNum: c.height,
CommitFee: c.fee,
Htlcs: make([]*channeldb.HTLC, 0, numHtlcs), Htlcs: make([]*channeldb.HTLC, 0, numHtlcs),
} }
@ -1181,6 +1187,8 @@ func (lc *LightningChannel) restoreStateLogs() error {
lc.localCommitChain.tail().theirMessageIndex = theirCounter lc.localCommitChain.tail().theirMessageIndex = theirCounter
lc.remoteCommitChain.tail().ourMessageIndex = ourCounter lc.remoteCommitChain.tail().ourMessageIndex = ourCounter
lc.remoteCommitChain.tail().theirMessageIndex = theirCounter lc.remoteCommitChain.tail().theirMessageIndex = theirCounter
lc.localCommitChain.tail().fee = lc.channelState.CommitFee
lc.remoteCommitChain.tail().fee = lc.channelState.CommitFee
return nil return nil
} }
@ -1245,12 +1253,13 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool,
// TODO(roasbeef): don't assume view is always fetched from tip? // TODO(roasbeef): don't assume view is always fetched from tip?
var ourBalance, theirBalance btcutil.Amount var ourBalance, theirBalance btcutil.Amount
if commitChain.tip() == nil { ourBalance = commitChain.tip().ourBalance
ourBalance = lc.channelState.OurBalance theirBalance = commitChain.tip().theirBalance
theirBalance = lc.channelState.TheirBalance
} else { if lc.channelState.IsInitiator {
ourBalance = commitChain.tip().ourBalance ourBalance = ourBalance + commitChain.tip().fee
theirBalance = commitChain.tip().theirBalance } else if !lc.channelState.IsInitiator {
theirBalance = theirBalance + commitChain.tip().fee
} }
nextHeight := commitChain.tip().height + 1 nextHeight := commitChain.tip().height + 1

@ -215,6 +215,7 @@ func NewChannelReservation(capacity, fundingAmt btcutil.Amount, minFeeRate btcut
TheirBalance: theirBalance, TheirBalance: theirBalance,
MinFeePerKb: minFeeRate, MinFeePerKb: minFeeRate,
Db: wallet.ChannelDB, Db: wallet.ChannelDB,
CommitFee: commitFee,
}, },
numConfsToOpen: numConfs, numConfsToOpen: numConfs,
pushSat: pushSat, pushSat: pushSat,