lnwallet: Split tx generation code out of fetchCommitmentView.
This moves the commitment transaction generation code out of fetchCommitmentView into createCommitmentTx. Aside from being a pretty clean logical split, this allows the transaction generation code to be unit tested more effectively.
This commit is contained in:
parent
bd497438af
commit
7c1ae8bda3
@ -237,6 +237,10 @@ type commitment struct {
|
||||
// update number of this commitment.
|
||||
height uint64
|
||||
|
||||
// isOurs indicates whether this is the local or remote node's version of
|
||||
// the commitment.
|
||||
isOurs bool
|
||||
|
||||
// [our|their]MessageIndex are indexes into the HTLC log, up to which
|
||||
// this commitment transaction includes. These indexes allow both sides
|
||||
// to independently, and concurrent send create new commitments. Each
|
||||
@ -272,6 +276,10 @@ type commitment struct {
|
||||
// transaction's fee.
|
||||
feePerKw btcutil.Amount
|
||||
|
||||
// dustLimit is the limit on the commitment transaction such that no output
|
||||
// values should be below this amount.
|
||||
dustLimit btcutil.Amount
|
||||
|
||||
// outgoingHTLCs is a slice of all the outgoing HTLC's (from our PoV)
|
||||
// on this commitment transaction.
|
||||
outgoingHTLCs []PaymentDescriptor
|
||||
@ -1810,122 +1818,21 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool,
|
||||
} else {
|
||||
dustLimit = lc.localChanCfg.DustLimit
|
||||
}
|
||||
numHTLCs := int64(0)
|
||||
for _, htlc := range filteredHTLCView.ourUpdates {
|
||||
if htlcIsDust(false, ourCommitTx, feePerKw,
|
||||
htlc.Amount.ToSatoshis(), dustLimit) {
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
numHTLCs++
|
||||
}
|
||||
for _, htlc := range filteredHTLCView.theirUpdates {
|
||||
if htlcIsDust(true, ourCommitTx, feePerKw,
|
||||
htlc.Amount.ToSatoshis(), dustLimit) {
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
numHTLCs++
|
||||
}
|
||||
|
||||
// Next, we'll calculate the fee for the commitment transaction based
|
||||
// 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
|
||||
// fee.
|
||||
totalCommitWeight := commitWeight + (htlcWeight * numHTLCs)
|
||||
|
||||
// With the weight known, we can now calculate the commitment fee,
|
||||
// ensuring that we account for any dust outputs trimmed above.
|
||||
commitFee := btcutil.Amount((int64(feePerKw) * totalCommitWeight) / 1000)
|
||||
|
||||
// Currently, within the protocol, the initiator always pays the fees.
|
||||
// So we'll subtract the fee amount from the balance of the current
|
||||
// initiator.
|
||||
if lc.channelState.IsInitiator {
|
||||
ourBalance -= lnwire.NewMSatFromSatoshis(commitFee)
|
||||
} else if !lc.channelState.IsInitiator {
|
||||
theirBalance -= lnwire.NewMSatFromSatoshis(commitFee)
|
||||
}
|
||||
|
||||
var (
|
||||
delay uint32
|
||||
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 {
|
||||
delay = uint32(lc.remoteChanCfg.CsvDelay)
|
||||
delayBalance = theirBalance.ToSatoshis()
|
||||
p2wkhBalance = ourBalance.ToSatoshis()
|
||||
} else {
|
||||
delay = uint32(lc.localChanCfg.CsvDelay)
|
||||
delayBalance = ourBalance.ToSatoshis()
|
||||
p2wkhBalance = theirBalance.ToSatoshis()
|
||||
}
|
||||
|
||||
// Generate a new commitment transaction with all the latest
|
||||
// unsettled/un-timed out HTLCs.
|
||||
commitTx, err := CreateCommitTx(lc.fundingTxIn, keyRing, delay,
|
||||
delayBalance, p2wkhBalance, dustLimit)
|
||||
if err != nil {
|
||||
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.
|
||||
for _, htlc := range filteredHTLCView.ourUpdates {
|
||||
if htlcIsDust(false, !remoteChain, feePerKw,
|
||||
htlc.Amount.ToSatoshis(), dustLimit) {
|
||||
continue
|
||||
}
|
||||
|
||||
err := lc.addHTLC(commitTx, ourCommitTx, false, htlc, keyRing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for _, htlc := range filteredHTLCView.theirUpdates {
|
||||
if htlcIsDust(true, !remoteChain, feePerKw,
|
||||
htlc.Amount.ToSatoshis(), dustLimit) {
|
||||
continue
|
||||
}
|
||||
|
||||
err := lc.addHTLC(commitTx, ourCommitTx, true, htlc, keyRing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Set the state hint of the commitment transaction to facilitate
|
||||
// quickly recovering the necessary penalty state in the case of an
|
||||
// uncooperative broadcast.
|
||||
obsfucator := lc.stateHintObsfucator
|
||||
stateNum := nextHeight
|
||||
if err := SetStateNumHint(commitTx, stateNum, obsfucator); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Sort the transactions according to the agreed upon canonical
|
||||
// ordering. This lets us skip sending the entire transaction over,
|
||||
// instead we'll just send signatures.
|
||||
txsort.InPlaceSort(commitTx)
|
||||
c := &commitment{
|
||||
txn: commitTx,
|
||||
height: nextHeight,
|
||||
ourBalance: ourBalance,
|
||||
ourMessageIndex: ourLogIndex,
|
||||
theirMessageIndex: theirLogIndex,
|
||||
theirBalance: theirBalance,
|
||||
fee: commitFee,
|
||||
feePerKw: feePerKw,
|
||||
dustLimit: dustLimit,
|
||||
isOurs: !remoteChain,
|
||||
}
|
||||
|
||||
// Actually generate unsigned commitment transaction for this view.
|
||||
if err := lc.createCommitmentTx(c, filteredHTLCView, keyRing); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// In order to ensure _none_ of the HTLC's associated with this new
|
||||
@ -1949,6 +1856,122 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool,
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// createCommitmentTx generates the unsigned commitment transaction for a
|
||||
// commitment view and assigns to txn field.
|
||||
func (lc *LightningChannel) createCommitmentTx(c *commitment,
|
||||
filteredHTLCView *htlcView, keyRing *commitmentKeyRing) error {
|
||||
|
||||
ourBalance := c.ourBalance
|
||||
theirBalance := c.theirBalance
|
||||
|
||||
numHTLCs := int64(0)
|
||||
for _, htlc := range filteredHTLCView.ourUpdates {
|
||||
if htlcIsDust(false, c.isOurs, c.feePerKw,
|
||||
htlc.Amount.ToSatoshis(), c.dustLimit) {
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
numHTLCs++
|
||||
}
|
||||
for _, htlc := range filteredHTLCView.theirUpdates {
|
||||
if htlcIsDust(true, c.isOurs, c.feePerKw,
|
||||
htlc.Amount.ToSatoshis(), c.dustLimit) {
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
numHTLCs++
|
||||
}
|
||||
|
||||
// Next, we'll calculate the fee for the commitment transaction based
|
||||
// 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
|
||||
// fee.
|
||||
totalCommitWeight := commitWeight + (htlcWeight * numHTLCs)
|
||||
|
||||
// With the weight known, we can now calculate the commitment fee,
|
||||
// ensuring that we account for any dust outputs trimmed above.
|
||||
commitFee := btcutil.Amount((int64(c.feePerKw) * totalCommitWeight) / 1000)
|
||||
|
||||
// Currently, within the protocol, the initiator always pays the fees.
|
||||
// So we'll subtract the fee amount from the balance of the current
|
||||
// initiator.
|
||||
if lc.channelState.IsInitiator {
|
||||
ourBalance -= lnwire.NewMSatFromSatoshis(commitFee)
|
||||
} else if !lc.channelState.IsInitiator {
|
||||
theirBalance -= lnwire.NewMSatFromSatoshis(commitFee)
|
||||
}
|
||||
|
||||
var (
|
||||
delay uint32
|
||||
delayBalance, p2wkhBalance btcutil.Amount
|
||||
)
|
||||
if c.isOurs {
|
||||
delay = uint32(lc.localChanCfg.CsvDelay)
|
||||
delayBalance = ourBalance.ToSatoshis()
|
||||
p2wkhBalance = theirBalance.ToSatoshis()
|
||||
} else {
|
||||
delay = uint32(lc.remoteChanCfg.CsvDelay)
|
||||
delayBalance = theirBalance.ToSatoshis()
|
||||
p2wkhBalance = ourBalance.ToSatoshis()
|
||||
}
|
||||
|
||||
// Generate a new commitment transaction with all the latest
|
||||
// unsettled/un-timed out HTLCs.
|
||||
commitTx, err := CreateCommitTx(lc.fundingTxIn, keyRing, delay,
|
||||
delayBalance, p2wkhBalance, c.dustLimit)
|
||||
if err != nil {
|
||||
return 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.
|
||||
for _, htlc := range filteredHTLCView.ourUpdates {
|
||||
if htlcIsDust(false, c.isOurs, c.feePerKw,
|
||||
htlc.Amount.ToSatoshis(), c.dustLimit) {
|
||||
continue
|
||||
}
|
||||
|
||||
err := lc.addHTLC(commitTx, c.isOurs, false, htlc, keyRing)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, htlc := range filteredHTLCView.theirUpdates {
|
||||
if htlcIsDust(true, c.isOurs, c.feePerKw,
|
||||
htlc.Amount.ToSatoshis(), c.dustLimit) {
|
||||
continue
|
||||
}
|
||||
|
||||
err := lc.addHTLC(commitTx, c.isOurs, true, htlc, keyRing)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Set the state hint of the commitment transaction to facilitate
|
||||
// quickly recovering the necessary penalty state in the case of an
|
||||
// uncooperative broadcast.
|
||||
err = SetStateNumHint(commitTx, c.height, lc.stateHintObsfucator)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Sort the transactions according to the agreed upon canonical
|
||||
// ordering. This lets us skip sending the entire transaction over,
|
||||
// instead we'll just send signatures.
|
||||
txsort.InPlaceSort(commitTx)
|
||||
|
||||
c.txn = commitTx
|
||||
c.fee = commitFee
|
||||
c.ourBalance = ourBalance
|
||||
c.theirBalance = theirBalance
|
||||
return nil
|
||||
}
|
||||
|
||||
// evaluateHTLCView processes all update entries in both HTLC update logs,
|
||||
// producing a final view which is the result of properly applying all adds,
|
||||
// settles, and timeouts found in both logs. The resulting view returned
|
||||
|
@ -1046,30 +1046,21 @@ func SingleTweakBytes(commitPoint, basePoint *btcec.PublicKey) []byte {
|
||||
// TODO(roasbeef): should be using double-scalar mult here
|
||||
func TweakPubKey(basePoint, commitPoint *btcec.PublicKey) *btcec.PublicKey {
|
||||
tweakBytes := SingleTweakBytes(commitPoint, basePoint)
|
||||
tweakX, tweakY := btcec.S256().ScalarBaseMult(tweakBytes)
|
||||
|
||||
// TODO(roasbeef): check that both passed on curve?
|
||||
|
||||
x, y := btcec.S256().Add(basePoint.X, basePoint.Y, tweakX, tweakY)
|
||||
|
||||
return &btcec.PublicKey{
|
||||
X: x,
|
||||
Y: y,
|
||||
Curve: btcec.S256(),
|
||||
}
|
||||
return TweakPubKeyWithTweak(basePoint, tweakBytes)
|
||||
}
|
||||
|
||||
// TweakPubKeyWithTweak is the exact same as the TweakPubKey function, however
|
||||
// it accepts the raw tweak bytes directly rather than the commitment point.
|
||||
func TweakPubKeyWithTweak(pubKey *btcec.PublicKey, tweakBytes []byte) *btcec.PublicKey {
|
||||
tweakX, tweakY := btcec.S256().ScalarBaseMult(tweakBytes)
|
||||
|
||||
x, y := btcec.S256().Add(pubKey.X, pubKey.Y, tweakX, tweakY)
|
||||
curve := btcec.S256()
|
||||
tweakX, tweakY := curve.ScalarBaseMult(tweakBytes)
|
||||
|
||||
// TODO(roasbeef): check that both passed on curve?
|
||||
x, y := curve.Add(pubKey.X, pubKey.Y, tweakX, tweakY)
|
||||
return &btcec.PublicKey{
|
||||
X: x,
|
||||
Y: y,
|
||||
Curve: btcec.S256(),
|
||||
Curve: curve,
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user