lnwallet: update fetchCommitmentView due to new commitment design

This commit updates the central fetchCommitmentView method to manage
and derive the necessary easy required to create new commitments due to
the new state machine design within the specification. Each state now
requires us to derive a number of keys for each commitment state:
localDelay, remoteDelay, localKey, remoteKey, the commitment point, and
finally the revocation key itself.
This commit is contained in:
Olaoluwa Osuntokun 2017-07-30 13:07:38 -07:00
parent 3a052d2874
commit b9da43a2b8
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2

@ -194,8 +194,10 @@ type PaymentDescriptor struct {
// NOTE: These values may change within the logs themselves, however, // NOTE: These values may change within the logs themselves, however,
// they'll stay consistent within the commitment chain entries // they'll stay consistent within the commitment chain entries
// themselves. // themselves.
ourPkScript []byte ourPkScript []byte
theirPkScript []byte ourWitnessScript []byte
theirPkScript []byte
theirWitnessScript []byte
// EntryType denotes the exact type of the PaymentDescriptor. In the // EntryType denotes the exact type of the PaymentDescriptor. In the
// case of a Timeout, or Settle type, then the Parent field will point // case of a Timeout, or Settle type, then the Parent field will point
@ -256,8 +258,6 @@ type commitment struct {
// outgoingHTLCs is a slice of all the outgoing HTLC's (from our PoV) // outgoingHTLCs is a slice of all the outgoing HTLC's (from our PoV)
// on this commitment transaction. // on this commitment transaction.
outgoingHTLCs []PaymentDescriptor outgoingHTLCs []PaymentDescriptor
incomingHTLCs []PaymentDescriptor
}
// incomingHTLCs is a slice of all the incoming HTLC's (from our PoV) // incomingHTLCs is a slice of all the incoming HTLC's (from our PoV)
// on this commitment transaction. // on this commitment transaction.
@ -1288,11 +1288,6 @@ func (lc *LightningChannel) closeObserver(channelCloseNtfn *chainntnfs.SpendEven
} }
} }
// Stop gracefully shuts down any active goroutines spawned by the
// LightningChannel during regular duties.
func (lc *LightningChannel) Stop() {
if !atomic.CompareAndSwapInt32(&lc.shutdown, 0, 1) {
return
// htlcTimeoutFee returns the fee in satoshis required for an HTLC timeout // htlcTimeoutFee returns the fee in satoshis required for an HTLC timeout
// transaction based on the current fee rate. // transaction based on the current fee rate.
func htlcTimeoutFee(feePerKw btcutil.Amount) btcutil.Amount { func htlcTimeoutFee(feePerKw btcutil.Amount) btcutil.Amount {
@ -1344,7 +1339,6 @@ func htlcIsDust(incoming, ourCommit bool,
htlcFee = htlcTimeoutFee(feePerKw) htlcFee = htlcTimeoutFee(feePerKw)
} }
close(lc.quit)
return (htlcAmt - htlcFee) < dustLimit return (htlcAmt - htlcFee) < dustLimit
} }
@ -1497,17 +1491,18 @@ func (lc *LightningChannel) fetchHTLCView(theirLogIndex, ourLogIndex uint64) *ht
// both local and remote commitment transactions in order to sign or verify new // both local and remote commitment transactions in order to sign or verify new
// commitment updates. A fully populated commitment is returned which reflects // commitment updates. A fully populated commitment is returned which reflects
// the proper balances for both sides at this point in the commitment chain. // the proper balances for both sides at this point in the commitment chain.
//
// TODO(roasbeef): update commit to to have all keys?
func (lc *LightningChannel) fetchCommitmentView(remoteChain bool, func (lc *LightningChannel) fetchCommitmentView(remoteChain bool,
ourLogIndex, theirLogIndex uint64, revocationKey *btcec.PublicKey, ourLogIndex, theirLogIndex uint64,
revocationHash [32]byte) (*commitment, error) { commitPoint *btcec.PublicKey) (*commitment, error) {
var commitChain *commitmentChain commitChain := lc.localCommitChain
if remoteChain { if remoteChain {
commitChain = lc.remoteCommitChain commitChain = lc.remoteCommitChain
} else {
commitChain = lc.localCommitChain
} }
ourCommitTx := !remoteChain
ourBalance := commitChain.tip().ourBalance ourBalance := commitChain.tip().ourBalance
theirBalance := commitChain.tip().theirBalance theirBalance := commitChain.tip().theirBalance
@ -1528,38 +1523,12 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool,
// the balances on the commitment transaction accordingly. // the balances on the commitment transaction accordingly.
// TODO(roasbeef): error if log empty? // TODO(roasbeef): error if log empty?
htlcView := lc.fetchHTLCView(theirLogIndex, ourLogIndex) htlcView := lc.fetchHTLCView(theirLogIndex, ourLogIndex)
filteredHTLCView := lc.evaluateHTLCView(htlcView, &ourBalance, &theirBalance, filteredHTLCView := lc.evaluateHTLCView(htlcView, &ourBalance,
nextHeight, remoteChain) &theirBalance, nextHeight, remoteChain)
// Determine how many current HTLCs are over the dust limit, and should // Initiate feePerKw to the last committed fee for this chain as we'll
// be counted for the purpose of fee calculation. // need this to determine which HTLC's are dust, and also the final fee
// TODO(roasbeef): dust outputs need to be counted towards fees paid // rate.
// * tally in separate accumulator, subtract from fee amount
// * when dumping fees back into initiator output, only dumb explicit
// fee
var dustLimit, dustFees btcutil.Amount
if remoteChain {
dustLimit = lc.channelState.TheirDustLimit
} else {
dustLimit = lc.channelState.OurDustLimit
}
numHTLCs := 0
for _, htlc := range filteredHTLCView.ourUpdates {
if htlc.Amount < dustLimit {
dustFees += htlc.Amount
continue
}
numHTLCs++
}
for _, htlc := range filteredHTLCView.theirUpdates {
if htlc.Amount < dustLimit {
dustFees += htlc.Amount
continue
}
numHTLCs++
}
// Initiate feePerKw to the last committed fee for this chain.
feePerKw := commitChain.tail().feePerKw feePerKw := commitChain.tail().feePerKw
// Check if any fee updates have taken place since that last // Check if any fee updates have taken place since that last
@ -1592,12 +1561,44 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool,
} }
} }
// Determine how many current HTLCs are over the dust limit, and should
// be counted for the purpose of fee calculation.
var dustLimit, dustFees btcutil.Amount
if remoteChain {
dustLimit = lc.remoteChanCfg.DustLimit
} else {
dustLimit = lc.localChanCfg.DustLimit
}
numHTLCs := 0
for _, htlc := range filteredHTLCView.ourUpdates {
if htlcIsDust(false, ourCommitTx, feePerKw, htlc.Amount,
dustLimit) {
dustFees += htlc.Amount
continue
}
numHTLCs++
}
for _, htlc := range filteredHTLCView.theirUpdates {
if htlcIsDust(true, ourCommitTx, feePerKw, htlc.Amount,
dustLimit) {
dustFees += htlc.Amount
continue
}
numHTLCs++
}
// Next, we'll calculate the fee for the commitment transaction based // Next, we'll calculate the fee for the commitment transaction based
// on its total weight. Once we have the total weight, we'll multiply // on its total weight. Once we have the total weight, we'll multiply
// by the current fee-per-kw, then divide by 1000 to get the proper // by the current fee-per-kw, then divide by 1000 to get the proper
// fee. // fee.
totalCommitWeight := commitWeight + btcutil.Amount(htlcWeight*numHTLCs) totalCommitWeight := commitWeight + btcutil.Amount(htlcWeight*numHTLCs)
// With the weight known, we can now calculate the commitment fee,
// ensuring that we account for any dust outputs trimmed above.
commitFee := (feePerKw * totalCommitWeight) / 1000 commitFee := (feePerKw * totalCommitWeight) / 1000
commitFee -= dustFees commitFee -= dustFees
@ -1611,42 +1612,70 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool,
} }
var ( var (
selfKey *btcec.PublicKey delayKey, paymentKey, revocationKey *btcec.PublicKey
remoteKey *btcec.PublicKey delay uint32
delay uint32 delayBalance, p2wkhBalance btcutil.Amount
delayBalance, p2wkhBalance btcutil.Amount
) )
// We'll now compute the delay, payment and revocation key based on the
// current commitment point. All keys are tweaked each state in order
// to ensure the keys from each state are unlinkable. TO create the
// revocation key, we take the opposite party's revocation base point
// and combine that with the current commitment point.
if remoteChain { if remoteChain {
selfKey = lc.channelState.TheirCommitKey delayKey = TweakPubKey(lc.remoteChanCfg.DelayBasePoint,
remoteKey = lc.channelState.OurCommitKey commitPoint)
delay = lc.channelState.RemoteCsvDelay paymentKey = TweakPubKey(lc.localChanCfg.PaymentBasePoint,
commitPoint)
revocationKey = DeriveRevocationPubkey(
lc.localChanCfg.RevocationBasePoint,
commitPoint,
)
delay = uint32(lc.remoteChanCfg.CsvDelay)
delayBalance = theirBalance delayBalance = theirBalance
p2wkhBalance = ourBalance p2wkhBalance = ourBalance
} else { } else {
selfKey = lc.channelState.OurCommitKey delayKey = TweakPubKey(lc.localChanCfg.DelayBasePoint,
remoteKey = lc.channelState.TheirCommitKey commitPoint)
delay = lc.channelState.LocalCsvDelay paymentKey = TweakPubKey(lc.remoteChanCfg.PaymentBasePoint,
commitPoint)
revocationKey = DeriveRevocationPubkey(
lc.remoteChanCfg.RevocationBasePoint,
commitPoint,
)
delay = uint32(lc.localChanCfg.CsvDelay)
delayBalance = ourBalance delayBalance = ourBalance
p2wkhBalance = theirBalance p2wkhBalance = theirBalance
} }
// TODO(roasbeef); create all keys unconditionally within commitment
// store in commitment, will need all when doing HTLC's
// Generate a new commitment transaction with all the latest // Generate a new commitment transaction with all the latest
// unsettled/un-timed out HTLCs. // unsettled/un-timed out HTLCs.
ourCommitTx := !remoteChain commitTx, err := CreateCommitTx(lc.fundingTxIn, delayKey, paymentKey,
commitTx, err := CreateCommitTx(lc.fundingTxIn, selfKey, remoteKey,
revocationKey, delay, delayBalance, p2wkhBalance, dustLimit) revocationKey, delay, delayBalance, p2wkhBalance, dustLimit)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// We'll now add all the HTLC outputs to the commitment transaction.
// Each output includes an off-chain 2-of-2 covenant clause, so we'll
// need the objective local/remote keys for this particular commitment
// as well.
// TODO(roasbeef): could avoid computing them both here
localKey := TweakPubKey(lc.localChanCfg.PaymentBasePoint, commitPoint)
remoteKey := TweakPubKey(lc.remoteChanCfg.PaymentBasePoint, commitPoint)
for _, htlc := range filteredHTLCView.ourUpdates { for _, htlc := range filteredHTLCView.ourUpdates {
if htlcIsDust(false, !remoteChain, feePerKw, htlc.Amount, if htlcIsDust(false, !remoteChain, feePerKw, htlc.Amount,
dustLimit) { dustLimit) {
continue continue
} }
err := lc.addHTLC(commitTx, ourCommitTx, htlc, err := lc.addHTLC(commitTx, ourCommitTx, false, htlc, localKey,
revocationHash, delay, false) remoteKey, revocationKey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1657,8 +1686,8 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool,
continue continue
} }
err := lc.addHTLC(commitTx, ourCommitTx, htlc, err := lc.addHTLC(commitTx, ourCommitTx, true, htlc, localKey,
revocationHash, delay, true) remoteKey, revocationKey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1700,6 +1729,12 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool,
c.incomingHTLCs[i] = *htlc c.incomingHTLCs[i] = *htlc
} }
// Finally, we'll populate all the HTLC indexes so we can track the
// locations of each HTLC in the commitment state.
if err := c.populateHtlcIndexes(ourCommitTx, dustLimit); err != nil {
return nil, err
}
return c, nil return c, nil
} }
@ -2063,8 +2098,9 @@ func (lc *LightningChannel) SignNextCommitment() (*btcec.Signature, []*btcec.Sig
// HTLC log entries. When we creating a new remote view, we include // HTLC log entries. When we creating a new remote view, we include
// _all_ of our changes (pending or committed) but only the remote // _all_ of our changes (pending or committed) but only the remote
// node's changes up to the last change we've ACK'd. // node's changes up to the last change we've ACK'd.
newCommitView, err := lc.fetchCommitmentView(true, lc.localUpdateLog.logIndex, newCommitView, err := lc.fetchCommitmentView(true,
lc.remoteUpdateLog.ackedIndex, remoteRevocationKey, remoteRevocationHash) lc.localUpdateLog.logIndex, lc.remoteUpdateLog.ackedIndex,
commitPoint)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }