lnwallet: add anchor commitmenttype
With this commitment type, we'll add extra anchor outputs to the commitment transaction if the anchor channel type is active.
This commit is contained in:
parent
50199aeaf3
commit
af68ff1640
@ -338,13 +338,15 @@ type ChannelCommitment struct {
|
||||
// LocalBalance is the current available settled balance within the
|
||||
// channel directly spendable by us.
|
||||
//
|
||||
// NOTE: This is the balance *after* subtracting any commitment fee.
|
||||
// NOTE: This is the balance *after* subtracting any commitment fee,
|
||||
// AND anchor output values.
|
||||
LocalBalance lnwire.MilliSatoshi
|
||||
|
||||
// RemoteBalance is the current available settled balance within the
|
||||
// channel directly spendable by the remote node.
|
||||
//
|
||||
// NOTE: This is the balance *after* subtracting any commitment fee.
|
||||
// NOTE: This is the balance *after* subtracting any commitment fee,
|
||||
// AND anchor output values.
|
||||
RemoteBalance lnwire.MilliSatoshi
|
||||
|
||||
// CommitFee is the amount calculated to be paid in fees for the
|
||||
|
@ -351,9 +351,8 @@ func isOurCommitment(localChanCfg, remoteChanCfg channeldb.ChannelConfig,
|
||||
|
||||
// With the keys derived, we'll construct the remote script that'll be
|
||||
// present if they have a non-dust balance on the commitment.
|
||||
remoteDelay := uint32(remoteChanCfg.CsvDelay)
|
||||
remoteScript, err := lnwallet.CommitScriptToRemote(
|
||||
chanType, remoteDelay, commitKeyRing.ToRemoteKey,
|
||||
remoteScript, _, err := lnwallet.CommitScriptToRemote(
|
||||
chanType, commitKeyRing.ToRemoteKey,
|
||||
)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
@ -503,7 +503,8 @@ type commitment struct {
|
||||
// evaluating all the add/remove/settle log entries before the listed
|
||||
// indexes.
|
||||
//
|
||||
// NOTE: This is the balance *after* subtracting any commitment fee.
|
||||
// NOTE: This is the balance *after* subtracting any commitment fee,
|
||||
// AND anchor output values.
|
||||
ourBalance lnwire.MilliSatoshi
|
||||
theirBalance lnwire.MilliSatoshi
|
||||
|
||||
@ -2089,9 +2090,8 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
|
||||
|
||||
// Since it is the remote breach we are reconstructing, the output going
|
||||
// to us will be a to-remote script with our local params.
|
||||
ourDelay := uint32(chanState.LocalChanCfg.CsvDelay)
|
||||
ourScript, err := CommitScriptToRemote(
|
||||
chanState.ChanType, ourDelay, keyRing.ToRemoteKey,
|
||||
ourScript, _, err := CommitScriptToRemote(
|
||||
chanState.ChanType, keyRing.ToRemoteKey,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -5027,8 +5027,8 @@ func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) {
|
||||
}
|
||||
|
||||
// CommitOutputResolution carries the necessary information required to allow
|
||||
// us to sweep our direct commitment output in the case that either party goes
|
||||
// to chain.
|
||||
// us to sweep our commitment output in the case that either party goes to
|
||||
// chain.
|
||||
type CommitOutputResolution struct {
|
||||
// SelfOutPoint is the full outpoint that points to out pay-to-self
|
||||
// output within the closing commitment transaction.
|
||||
@ -5040,8 +5040,7 @@ type CommitOutputResolution struct {
|
||||
|
||||
// MaturityDelay is the relative time-lock, in blocks for all outputs
|
||||
// that pay to the local party within the broadcast commitment
|
||||
// transaction. This value will be non-zero iff, this output was on our
|
||||
// commitment transaction.
|
||||
// transaction.
|
||||
MaturityDelay uint32
|
||||
}
|
||||
|
||||
@ -5122,9 +5121,8 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
|
||||
// Before we can generate the proper sign descriptor, we'll need to
|
||||
// locate the output index of our non-delayed output on the commitment
|
||||
// transaction.
|
||||
localDelay := uint32(chanState.LocalChanCfg.CsvDelay)
|
||||
selfScript, err := CommitScriptToRemote(
|
||||
chanState.ChanType, localDelay, keyRing.ToRemoteKey,
|
||||
selfScript, maturityDelay, err := CommitScriptToRemote(
|
||||
chanState.ChanType, keyRing.ToRemoteKey,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create self commit "+
|
||||
@ -5165,7 +5163,7 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
|
||||
},
|
||||
HashType: txscript.SigHashAll,
|
||||
},
|
||||
MaturityDelay: 0,
|
||||
MaturityDelay: maturityDelay,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,9 @@ import (
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
)
|
||||
|
||||
// anchorSize is the constant anchor output size.
|
||||
const anchorSize = btcutil.Amount(330)
|
||||
|
||||
// CommitmentKeyRing holds all derived keys needed to construct commitment and
|
||||
// HTLC transactions. The keys are derived differently depending whether the
|
||||
// commitment transaction is ours or the remote peer's. Private keys associated
|
||||
@ -183,13 +186,34 @@ type ScriptInfo struct {
|
||||
|
||||
// CommitScriptToRemote creates the script that will pay to the non-owner of
|
||||
// the commitment transaction, adding a delay to the script based on the
|
||||
// channel type.
|
||||
func CommitScriptToRemote(_ channeldb.ChannelType, csvTimeout uint32,
|
||||
key *btcec.PublicKey) (*ScriptInfo, error) {
|
||||
// channel type. The second return value is the CSV deleay of the output
|
||||
// script, what must be satisfied in order to spend the output.
|
||||
func CommitScriptToRemote(chanType channeldb.ChannelType,
|
||||
key *btcec.PublicKey) (*ScriptInfo, uint32, error) {
|
||||
|
||||
// If this channel type has anchors, we derive the delayed to_remote
|
||||
// script.
|
||||
if chanType.HasAnchors() {
|
||||
script, err := input.CommitScriptToRemoteConfirmed(key)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
p2wsh, err := input.WitnessScriptHash(script)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return &ScriptInfo{
|
||||
PkScript: p2wsh,
|
||||
WitnessScript: script,
|
||||
}, 1, nil
|
||||
}
|
||||
|
||||
// Otherwise the to_remote will be a simple p2wkh.
|
||||
p2wkh, err := input.CommitScriptUnencumbered(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// Since this is a regular P2WKH, the WitnessScipt and PkScript should
|
||||
@ -197,7 +221,47 @@ func CommitScriptToRemote(_ channeldb.ChannelType, csvTimeout uint32,
|
||||
return &ScriptInfo{
|
||||
WitnessScript: p2wkh,
|
||||
PkScript: p2wkh,
|
||||
}, nil
|
||||
}, 0, nil
|
||||
}
|
||||
|
||||
// CommitScriptAnchors return the scripts to use for the local and remote
|
||||
// anchor.
|
||||
func CommitScriptAnchors(localChanCfg,
|
||||
remoteChanCfg *channeldb.ChannelConfig) (*ScriptInfo,
|
||||
*ScriptInfo, error) {
|
||||
|
||||
// Helper to create anchor ScriptInfo from key.
|
||||
anchorScript := func(key *btcec.PublicKey) (*ScriptInfo, error) {
|
||||
script, err := input.CommitScriptAnchor(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
scriptHash, err := input.WitnessScriptHash(script)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ScriptInfo{
|
||||
PkScript: scriptHash,
|
||||
WitnessScript: script,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Get the script used for the anchor output spendable by the local
|
||||
// node.
|
||||
localAnchor, err := anchorScript(localChanCfg.MultiSigKey.PubKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// And the anchor spemdable by the remote node.
|
||||
remoteAnchor, err := anchorScript(remoteChanCfg.MultiSigKey.PubKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return localAnchor, remoteAnchor, nil
|
||||
}
|
||||
|
||||
// CommitmentBuilder is a type that wraps the type of channel we are dealing
|
||||
@ -216,6 +280,11 @@ type CommitmentBuilder struct {
|
||||
|
||||
// NewCommitmentBuilder creates a new CommitmentBuilder from chanState.
|
||||
func NewCommitmentBuilder(chanState *channeldb.OpenChannel) *CommitmentBuilder {
|
||||
// The anchor channel type MUST be tweakless.
|
||||
if chanState.ChanType.HasAnchors() && !chanState.ChanType.IsTweakless() {
|
||||
panic("invalid channel type combination")
|
||||
}
|
||||
|
||||
return &CommitmentBuilder{
|
||||
chanState: chanState,
|
||||
obfuscator: createStateHintObfuscator(chanState),
|
||||
@ -248,8 +317,9 @@ type unsignedCommitmentTx struct {
|
||||
// fee is the total fee of the commitment transaction.
|
||||
fee btcutil.Amount
|
||||
|
||||
// ourBalance|theirBalance is the balances of this commitment. This can
|
||||
// be different than the balances before creating the commitment
|
||||
// ourBalance|theirBalance are the balances of this commitment *after*
|
||||
// subtracting commitment fees and anchor outputs. This can be
|
||||
// different than the balances before creating the commitment
|
||||
// transaction as one party must pay the commitment fee.
|
||||
ourBalance lnwire.MilliSatoshi
|
||||
theirBalance lnwire.MilliSatoshi
|
||||
@ -258,7 +328,7 @@ type unsignedCommitmentTx struct {
|
||||
// createUnsignedCommitmentTx generates the unsigned commitment transaction for
|
||||
// a commitment view and returns it as part of the unsignedCommitmentTx. The
|
||||
// passed in balances should be balances *before* subtracting any commitment
|
||||
// fees.
|
||||
// fees, but after anchor outputs.
|
||||
func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance,
|
||||
theirBalance lnwire.MilliSatoshi, isOurs bool,
|
||||
feePerKw chainfee.SatPerKWeight, height uint64,
|
||||
@ -333,12 +403,14 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance,
|
||||
cb.chanState.ChanType, fundingTxIn(cb.chanState), keyRing,
|
||||
&cb.chanState.LocalChanCfg, &cb.chanState.RemoteChanCfg,
|
||||
ourBalance.ToSatoshis(), theirBalance.ToSatoshis(),
|
||||
numHTLCs,
|
||||
)
|
||||
} else {
|
||||
commitTx, err = CreateCommitTx(
|
||||
cb.chanState.ChanType, fundingTxIn(cb.chanState), keyRing,
|
||||
&cb.chanState.RemoteChanCfg, &cb.chanState.LocalChanCfg,
|
||||
theirBalance.ToSatoshis(), ourBalance.ToSatoshis(),
|
||||
numHTLCs,
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
@ -436,7 +508,8 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance,
|
||||
func CreateCommitTx(chanType channeldb.ChannelType,
|
||||
fundingOutput wire.TxIn, keyRing *CommitmentKeyRing,
|
||||
localChanCfg, remoteChanCfg *channeldb.ChannelConfig,
|
||||
amountToLocal, amountToRemote btcutil.Amount) (*wire.MsgTx, error) {
|
||||
amountToLocal, amountToRemote btcutil.Amount,
|
||||
numHTLCs int64) (*wire.MsgTx, error) {
|
||||
|
||||
// First, we create the script for the delayed "pay-to-self" output.
|
||||
// This output has 2 main redemption clauses: either we can redeem the
|
||||
@ -458,8 +531,8 @@ func CreateCommitTx(chanType channeldb.ChannelType,
|
||||
}
|
||||
|
||||
// Next, we create the script paying to the remote.
|
||||
toRemoteScript, err := CommitScriptToRemote(
|
||||
chanType, uint32(remoteChanCfg.CsvDelay), keyRing.ToRemoteKey,
|
||||
toRemoteScript, _, err := CommitScriptToRemote(
|
||||
chanType, keyRing.ToRemoteKey,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -472,19 +545,50 @@ func CreateCommitTx(chanType channeldb.ChannelType,
|
||||
commitTx.AddTxIn(&fundingOutput)
|
||||
|
||||
// Avoid creating dust outputs within the commitment transaction.
|
||||
if amountToLocal >= localChanCfg.DustLimit {
|
||||
localOutput := amountToLocal >= localChanCfg.DustLimit
|
||||
if localOutput {
|
||||
commitTx.AddTxOut(&wire.TxOut{
|
||||
PkScript: toLocalScriptHash,
|
||||
Value: int64(amountToLocal),
|
||||
})
|
||||
}
|
||||
if amountToRemote >= localChanCfg.DustLimit {
|
||||
|
||||
remoteOutput := amountToRemote >= localChanCfg.DustLimit
|
||||
if remoteOutput {
|
||||
commitTx.AddTxOut(&wire.TxOut{
|
||||
PkScript: toRemoteScript.PkScript,
|
||||
Value: int64(amountToRemote),
|
||||
})
|
||||
}
|
||||
|
||||
// If this channel type has anchors, we'll also add those.
|
||||
if chanType.HasAnchors() {
|
||||
localAnchor, remoteAnchor, err := CommitScriptAnchors(
|
||||
localChanCfg, remoteChanCfg,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Add local anchor output only if we have a commitment output
|
||||
// or there are HTLCs.
|
||||
if localOutput || numHTLCs > 0 {
|
||||
commitTx.AddTxOut(&wire.TxOut{
|
||||
PkScript: localAnchor.PkScript,
|
||||
Value: int64(anchorSize),
|
||||
})
|
||||
}
|
||||
|
||||
// Add anchor output to remote only if they have a commitment
|
||||
// output or there are HTLCs.
|
||||
if remoteOutput || numHTLCs > 0 {
|
||||
commitTx.AddTxOut(&wire.TxOut{
|
||||
PkScript: remoteAnchor.PkScript,
|
||||
Value: int64(anchorSize),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return commitTx, nil
|
||||
}
|
||||
|
||||
|
@ -1090,7 +1090,7 @@ func testSpendValidation(t *testing.T, tweakless bool) {
|
||||
}
|
||||
commitmentTx, err := CreateCommitTx(
|
||||
channelType, *fakeFundingTxIn, keyRing, aliceChanCfg,
|
||||
bobChanCfg, channelBalance, channelBalance,
|
||||
bobChanCfg, channelBalance, channelBalance, 0,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create commitment transaction: %v", nil)
|
||||
|
@ -784,7 +784,7 @@ func CreateCommitmentTxns(localBalance, remoteBalance btcutil.Amount,
|
||||
|
||||
ourCommitTx, err := CreateCommitTx(
|
||||
chanType, fundingTxIn, localCommitmentKeys, ourChanCfg,
|
||||
theirChanCfg, localBalance, remoteBalance,
|
||||
theirChanCfg, localBalance, remoteBalance, 0,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@ -797,7 +797,7 @@ func CreateCommitmentTxns(localBalance, remoteBalance btcutil.Amount,
|
||||
|
||||
theirCommitTx, err := CreateCommitTx(
|
||||
chanType, fundingTxIn, remoteCommitmentKeys, theirChanCfg,
|
||||
ourChanCfg, remoteBalance, localBalance,
|
||||
ourChanCfg, remoteBalance, localBalance, 0,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
Loading…
Reference in New Issue
Block a user