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:
Johan T. Halseth
2020-03-06 16:11:46 +01:00
parent 50199aeaf3
commit af68ff1640
6 changed files with 136 additions and 33 deletions

View File

@@ -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
}