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.
|
// update number of this commitment.
|
||||||
height uint64
|
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
|
// [our|their]MessageIndex are indexes into the HTLC log, up to which
|
||||||
// this commitment transaction includes. These indexes allow both sides
|
// this commitment transaction includes. These indexes allow both sides
|
||||||
// to independently, and concurrent send create new commitments. Each
|
// to independently, and concurrent send create new commitments. Each
|
||||||
@ -272,6 +276,10 @@ type commitment struct {
|
|||||||
// transaction's fee.
|
// transaction's fee.
|
||||||
feePerKw btcutil.Amount
|
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)
|
// 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
|
||||||
@ -1810,122 +1818,21 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool,
|
|||||||
} else {
|
} else {
|
||||||
dustLimit = lc.localChanCfg.DustLimit
|
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{
|
c := &commitment{
|
||||||
txn: commitTx,
|
|
||||||
height: nextHeight,
|
height: nextHeight,
|
||||||
ourBalance: ourBalance,
|
ourBalance: ourBalance,
|
||||||
ourMessageIndex: ourLogIndex,
|
ourMessageIndex: ourLogIndex,
|
||||||
theirMessageIndex: theirLogIndex,
|
theirMessageIndex: theirLogIndex,
|
||||||
theirBalance: theirBalance,
|
theirBalance: theirBalance,
|
||||||
fee: commitFee,
|
|
||||||
feePerKw: feePerKw,
|
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
|
// 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
|
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,
|
// evaluateHTLCView processes all update entries in both HTLC update logs,
|
||||||
// producing a final view which is the result of properly applying all adds,
|
// producing a final view which is the result of properly applying all adds,
|
||||||
// settles, and timeouts found in both logs. The resulting view returned
|
// 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
|
// TODO(roasbeef): should be using double-scalar mult here
|
||||||
func TweakPubKey(basePoint, commitPoint *btcec.PublicKey) *btcec.PublicKey {
|
func TweakPubKey(basePoint, commitPoint *btcec.PublicKey) *btcec.PublicKey {
|
||||||
tweakBytes := SingleTweakBytes(commitPoint, basePoint)
|
tweakBytes := SingleTweakBytes(commitPoint, basePoint)
|
||||||
tweakX, tweakY := btcec.S256().ScalarBaseMult(tweakBytes)
|
return TweakPubKeyWithTweak(basePoint, 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(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TweakPubKeyWithTweak is the exact same as the TweakPubKey function, however
|
// TweakPubKeyWithTweak is the exact same as the TweakPubKey function, however
|
||||||
// it accepts the raw tweak bytes directly rather than the commitment point.
|
// it accepts the raw tweak bytes directly rather than the commitment point.
|
||||||
func TweakPubKeyWithTweak(pubKey *btcec.PublicKey, tweakBytes []byte) *btcec.PublicKey {
|
func TweakPubKeyWithTweak(pubKey *btcec.PublicKey, tweakBytes []byte) *btcec.PublicKey {
|
||||||
tweakX, tweakY := btcec.S256().ScalarBaseMult(tweakBytes)
|
curve := btcec.S256()
|
||||||
|
tweakX, tweakY := curve.ScalarBaseMult(tweakBytes)
|
||||||
x, y := btcec.S256().Add(pubKey.X, pubKey.Y, tweakX, tweakY)
|
|
||||||
|
|
||||||
|
// TODO(roasbeef): check that both passed on curve?
|
||||||
|
x, y := curve.Add(pubKey.X, pubKey.Y, tweakX, tweakY)
|
||||||
return &btcec.PublicKey{
|
return &btcec.PublicKey{
|
||||||
X: x,
|
X: x,
|
||||||
Y: y,
|
Y: y,
|
||||||
Curve: btcec.S256(),
|
Curve: curve,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user