lnwallet: support transactions and scripts for new commitment type
This commit modifies the channel state machine to be able to derive the proper commitment and second-level HTLC output scripts required by the new script-enforced leased channel commitment type.
This commit is contained in:
1 parent
01e9bb2bff
commit
8cfb53f64a
11 files changed
+230
-98
No files matched your search
+99
-34
@@ -192,14 +192,31 @@ type ScriptInfo struct {
|
||||
|
||||
// CommitScriptToSelf constructs the public key script for the output on the
|
||||
// commitment transaction paying to the "owner" of said commitment transaction.
|
||||
// If the other party learns of the preimage to the revocation hash, then they
|
||||
// can claim all the settled funds in the channel, plus the unsettled funds.
|
||||
func CommitScriptToSelf(selfKey, revokeKey *btcec.PublicKey, csvDelay uint32) (
|
||||
// The `initiator` argument should correspond to the owner of the commitment
|
||||
// tranasction which we are generating the to_local script for. If the other
|
||||
// party learns of the preimage to the revocation hash, then they can claim all
|
||||
// the settled funds in the channel, plus the unsettled funds.
|
||||
func CommitScriptToSelf(chanType channeldb.ChannelType, initiator bool,
|
||||
selfKey, revokeKey *btcec.PublicKey, csvDelay, leaseExpiry uint32) (
|
||||
*ScriptInfo, error) {
|
||||
|
||||
toLocalRedeemScript, err := input.CommitScriptToSelf(
|
||||
csvDelay, selfKey, revokeKey,
|
||||
var (
|
||||
toLocalRedeemScript []byte
|
||||
err error
|
||||
)
|
||||
switch {
|
||||
// If we are the initiator of a leased channel, then we have an
|
||||
// additional CLTV requirement in addition to the usual CSV requirement.
|
||||
case initiator && chanType.HasLeaseExpiration():
|
||||
toLocalRedeemScript, err = input.LeaseCommitScriptToSelf(
|
||||
selfKey, revokeKey, csvDelay, leaseExpiry,
|
||||
)
|
||||
|
||||
default:
|
||||
toLocalRedeemScript, err = input.CommitScriptToSelf(
|
||||
csvDelay, selfKey, revokeKey,
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -216,14 +233,38 @@ func CommitScriptToSelf(selfKey, revokeKey *btcec.PublicKey, csvDelay uint32) (
|
||||
}
|
||||
|
||||
// CommitScriptToRemote derives the appropriate to_remote script based on the
|
||||
// channel's commitment type. The second return value is the CSV delay 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) {
|
||||
// channel's commitment type. The `initiator` argument should correspond to the
|
||||
// owner of the commitment tranasction which we are generating the to_remote
|
||||
// script for. The second return value is the CSV delay of the output script,
|
||||
// what must be satisfied in order to spend the output.
|
||||
func CommitScriptToRemote(chanType channeldb.ChannelType, initiator bool,
|
||||
key *btcec.PublicKey, leaseExpiry uint32) (*ScriptInfo, uint32, error) {
|
||||
|
||||
switch {
|
||||
// If we are not the initiator of a leased channel, then the remote
|
||||
// party has an additional CLTV requirement in addition to the 1 block
|
||||
// CSV requirement.
|
||||
case chanType.HasLeaseExpiration() && !initiator:
|
||||
script, err := input.LeaseCommitScriptToRemoteConfirmed(
|
||||
key, leaseExpiry,
|
||||
)
|
||||
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
|
||||
|
||||
// If this channel type has anchors, we derive the delayed to_remote
|
||||
// script.
|
||||
if chanType.HasAnchors() {
|
||||
case chanType.HasAnchors():
|
||||
script, err := input.CommitScriptToRemoteConfirmed(key)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
@@ -238,20 +279,22 @@ func CommitScriptToRemote(chanType channeldb.ChannelType,
|
||||
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, 0, err
|
||||
}
|
||||
default:
|
||||
// Otherwise the to_remote will be a simple p2wkh.
|
||||
p2wkh, err := input.CommitScriptUnencumbered(key)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// Since this is a regular P2WKH, the WitnessScipt and PkScript should
|
||||
// both be set to the script hash.
|
||||
return &ScriptInfo{
|
||||
WitnessScript: p2wkh,
|
||||
PkScript: p2wkh,
|
||||
}, 0, nil
|
||||
// Since this is a regular P2WKH, the WitnessScipt and PkScript
|
||||
// should both be set to the script hash.
|
||||
return &ScriptInfo{
|
||||
WitnessScript: p2wkh,
|
||||
PkScript: p2wkh,
|
||||
}, 0, nil
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// HtlcSigHashType returns the sighash type to use for HTLC success and timeout
|
||||
@@ -296,13 +339,30 @@ func HtlcSecondLevelInputSequence(chanType channeldb.ChannelType) uint32 {
|
||||
// on the channel's commitment type. It is the uniform script that's used as the
|
||||
// output for the second-level HTLC transactions. The second level transaction
|
||||
// act as a sort of covenant, ensuring that a 2-of-2 multi-sig output can only
|
||||
// be spent in a particular way, and to a particular output.
|
||||
func SecondLevelHtlcScript(revocationKey, delayKey *btcec.PublicKey,
|
||||
csvDelay uint32) (*ScriptInfo, error) {
|
||||
// be spent in a particular way, and to a particular output. The `initiator`
|
||||
// argument should correspond to the owner of the commitment tranasction which
|
||||
// we are generating the to_local script for.
|
||||
func SecondLevelHtlcScript(chanType channeldb.ChannelType, initiator bool,
|
||||
revocationKey, delayKey *btcec.PublicKey,
|
||||
csvDelay, leaseExpiry uint32) (*ScriptInfo, error) {
|
||||
|
||||
witnessScript, err := input.SecondLevelHtlcScript(
|
||||
revocationKey, delayKey, csvDelay,
|
||||
var (
|
||||
witnessScript []byte
|
||||
err error
|
||||
)
|
||||
switch {
|
||||
// If we are the initiator of a leased channel, then we have an
|
||||
// additional CLTV requirement in addition to the usual CSV requirement.
|
||||
case initiator && chanType.HasLeaseExpiration():
|
||||
witnessScript, err = input.LeaseSecondLevelHtlcScript(
|
||||
revocationKey, delayKey, csvDelay, leaseExpiry,
|
||||
)
|
||||
|
||||
default:
|
||||
witnessScript, err = input.SecondLevelHtlcScript(
|
||||
revocationKey, delayKey, csvDelay,
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -549,19 +609,23 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance,
|
||||
// CreateCommitTx with parameters matching the perspective, to generate
|
||||
// a new commitment transaction with all the latest unsettled/un-timed
|
||||
// out HTLCs.
|
||||
var leaseExpiry uint32
|
||||
if cb.chanState.ChanType.HasLeaseExpiration() {
|
||||
leaseExpiry = cb.chanState.ThawHeight
|
||||
}
|
||||
if isOurs {
|
||||
commitTx, err = CreateCommitTx(
|
||||
cb.chanState.ChanType, fundingTxIn(cb.chanState), keyRing,
|
||||
&cb.chanState.LocalChanCfg, &cb.chanState.RemoteChanCfg,
|
||||
ourBalance.ToSatoshis(), theirBalance.ToSatoshis(),
|
||||
numHTLCs,
|
||||
numHTLCs, cb.chanState.IsInitiator, leaseExpiry,
|
||||
)
|
||||
} else {
|
||||
commitTx, err = CreateCommitTx(
|
||||
cb.chanState.ChanType, fundingTxIn(cb.chanState), keyRing,
|
||||
&cb.chanState.RemoteChanCfg, &cb.chanState.LocalChanCfg,
|
||||
theirBalance.ToSatoshis(), ourBalance.ToSatoshis(),
|
||||
numHTLCs,
|
||||
numHTLCs, !cb.chanState.IsInitiator, leaseExpiry,
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
@@ -660,12 +724,13 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance,
|
||||
// output paying to the "owner" of the commitment transaction which can be
|
||||
// spent after a relative block delay or revocation event, and a remote output
|
||||
// paying the counterparty within the channel, which can be spent immediately
|
||||
// or after a delay depending on the commitment type..
|
||||
// or after a delay depending on the commitment type. The `initiator` argument
|
||||
// should correspond to the owner of the commitment tranasction we are creating.
|
||||
func CreateCommitTx(chanType channeldb.ChannelType,
|
||||
fundingOutput wire.TxIn, keyRing *CommitmentKeyRing,
|
||||
localChanCfg, remoteChanCfg *channeldb.ChannelConfig,
|
||||
amountToLocal, amountToRemote btcutil.Amount,
|
||||
numHTLCs int64) (*wire.MsgTx, error) {
|
||||
numHTLCs int64, initiator bool, leaseExpiry uint32) (*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
|
||||
@@ -673,8 +738,8 @@ func CreateCommitTx(chanType channeldb.ChannelType,
|
||||
// the funds with the revocation key if we broadcast a revoked
|
||||
// commitment transaction.
|
||||
toLocalScript, err := CommitScriptToSelf(
|
||||
keyRing.ToLocalKey, keyRing.RevocationKey,
|
||||
uint32(localChanCfg.CsvDelay),
|
||||
chanType, initiator, keyRing.ToLocalKey, keyRing.RevocationKey,
|
||||
uint32(localChanCfg.CsvDelay), leaseExpiry,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -682,7 +747,7 @@ func CreateCommitTx(chanType channeldb.ChannelType,
|
||||
|
||||
// Next, we create the script paying to the remote.
|
||||
toRemoteScript, _, err := CommitScriptToRemote(
|
||||
chanType, keyRing.ToRemoteKey,
|
||||
chanType, initiator, keyRing.ToRemoteKey, leaseExpiry,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
Reference in new issue
Block a user