mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 18:10:34 +01:00
361 lines
12 KiB
Go
361 lines
12 KiB
Go
package migration30
|
|
|
|
import (
|
|
"bytes"
|
|
|
|
"github.com/btcsuite/btcd/btcec/v2"
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
mig25 "github.com/lightningnetwork/lnd/channeldb/migration25"
|
|
mig26 "github.com/lightningnetwork/lnd/channeldb/migration26"
|
|
mig "github.com/lightningnetwork/lnd/channeldb/migration_01_to_11"
|
|
"github.com/lightningnetwork/lnd/input"
|
|
)
|
|
|
|
// 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
|
|
// with each key may belong to the commitment owner or the "other party" which
|
|
// is referred to in the field comments, regardless of which is local and which
|
|
// is remote.
|
|
type CommitmentKeyRing struct {
|
|
// CommitPoint is the "per commitment point" used to derive the tweak
|
|
// for each base point.
|
|
CommitPoint *btcec.PublicKey
|
|
|
|
// LocalCommitKeyTweak is the tweak used to derive the local public key
|
|
// from the local payment base point or the local private key from the
|
|
// base point secret. This may be included in a SignDescriptor to
|
|
// generate signatures for the local payment key.
|
|
//
|
|
// NOTE: This will always refer to "our" local key, regardless of
|
|
// whether this is our commit or not.
|
|
LocalCommitKeyTweak []byte
|
|
|
|
// TODO(roasbeef): need delay tweak as well?
|
|
|
|
// LocalHtlcKeyTweak is the tweak used to derive the local HTLC key
|
|
// from the local HTLC base point. This value is needed in order to
|
|
// derive the final key used within the HTLC scripts in the commitment
|
|
// transaction.
|
|
//
|
|
// NOTE: This will always refer to "our" local HTLC key, regardless of
|
|
// whether this is our commit or not.
|
|
LocalHtlcKeyTweak []byte
|
|
|
|
// LocalHtlcKey is the key that will be used in any clause paying to
|
|
// our node of any HTLC scripts within the commitment transaction for
|
|
// this key ring set.
|
|
//
|
|
// NOTE: This will always refer to "our" local HTLC key, regardless of
|
|
// whether this is our commit or not.
|
|
LocalHtlcKey *btcec.PublicKey
|
|
|
|
// RemoteHtlcKey is the key that will be used in clauses within the
|
|
// HTLC script that send money to the remote party.
|
|
//
|
|
// NOTE: This will always refer to "their" remote HTLC key, regardless
|
|
// of whether this is our commit or not.
|
|
RemoteHtlcKey *btcec.PublicKey
|
|
|
|
// ToLocalKey is the commitment transaction owner's key which is
|
|
// included in HTLC success and timeout transaction scripts. This is
|
|
// the public key used for the to_local output of the commitment
|
|
// transaction.
|
|
//
|
|
// NOTE: Who's key this is depends on the current perspective. If this
|
|
// is our commitment this will be our key.
|
|
ToLocalKey *btcec.PublicKey
|
|
|
|
// ToRemoteKey is the non-owner's payment key in the commitment tx.
|
|
// This is the key used to generate the to_remote output within the
|
|
// commitment transaction.
|
|
//
|
|
// NOTE: Who's key this is depends on the current perspective. If this
|
|
// is our commitment this will be their key.
|
|
ToRemoteKey *btcec.PublicKey
|
|
|
|
// RevocationKey is the key that can be used by the other party to
|
|
// redeem outputs from a revoked commitment transaction if it were to
|
|
// be published.
|
|
//
|
|
// NOTE: Who can sign for this key depends on the current perspective.
|
|
// If this is our commitment, it means the remote node can sign for
|
|
// this key in case of a breach.
|
|
RevocationKey *btcec.PublicKey
|
|
}
|
|
|
|
// ScriptInfo holds a redeem script and hash.
|
|
type ScriptInfo struct {
|
|
// PkScript is the output's PkScript.
|
|
PkScript []byte
|
|
|
|
// WitnessScript is the full script required to properly redeem the
|
|
// output. This field should be set to the full script if a p2wsh
|
|
// output is being signed. For p2wkh it should be set equal to the
|
|
// PkScript.
|
|
WitnessScript []byte
|
|
}
|
|
|
|
// findOutputIndexesFromRemote finds the index of our and their outputs from
|
|
// the remote commitment transaction. It derives the key ring to compute the
|
|
// output scripts and compares them against the outputs inside the commitment
|
|
// to find the match.
|
|
func findOutputIndexesFromRemote(revocationPreimage *chainhash.Hash,
|
|
chanState *mig26.OpenChannel,
|
|
oldLog *mig.ChannelCommitment) (uint32, uint32, error) {
|
|
|
|
// Init the output indexes as empty.
|
|
ourIndex := uint32(OutputIndexEmpty)
|
|
theirIndex := uint32(OutputIndexEmpty)
|
|
|
|
chanCommit := oldLog
|
|
_, commitmentPoint := btcec.PrivKeyFromBytes(revocationPreimage[:])
|
|
|
|
// With the commitment point generated, we can now derive the king ring
|
|
// which will be used to generate the output scripts.
|
|
keyRing := DeriveCommitmentKeys(
|
|
commitmentPoint, false, chanState.ChanType,
|
|
&chanState.LocalChanCfg, &chanState.RemoteChanCfg,
|
|
)
|
|
|
|
// Since it's remote commitment chain, we'd used the mirrored values.
|
|
//
|
|
// We use the remote's channel config for the csv delay.
|
|
theirDelay := uint32(chanState.RemoteChanCfg.CsvDelay)
|
|
|
|
// If we are the initiator of this channel, then it's be false from the
|
|
// remote's PoV.
|
|
isRemoteInitiator := !chanState.IsInitiator
|
|
|
|
var leaseExpiry uint32
|
|
if chanState.ChanType.HasLeaseExpiration() {
|
|
leaseExpiry = chanState.ThawHeight
|
|
}
|
|
|
|
// Map the scripts from our PoV. When facing a local commitment, the to
|
|
// local output belongs to us and the to remote output belongs to them.
|
|
// When facing a remote commitment, the to local output belongs to them
|
|
// and the to remote output belongs to us.
|
|
|
|
// Compute the to local script. From our PoV, when facing a remote
|
|
// commitment, the to local output belongs to them.
|
|
theirScript, err := CommitScriptToSelf(
|
|
chanState.ChanType, isRemoteInitiator, keyRing.ToLocalKey,
|
|
keyRing.RevocationKey, theirDelay, leaseExpiry,
|
|
)
|
|
if err != nil {
|
|
return ourIndex, theirIndex, err
|
|
}
|
|
|
|
// Compute the to remote script. From our PoV, when facing a remote
|
|
// commitment, the to remote output belongs to us.
|
|
ourScript, _, err := CommitScriptToRemote(
|
|
chanState.ChanType, isRemoteInitiator, keyRing.ToRemoteKey,
|
|
leaseExpiry,
|
|
)
|
|
if err != nil {
|
|
return ourIndex, theirIndex, err
|
|
}
|
|
|
|
// Now compare the scripts to find our/their output index.
|
|
for i, txOut := range chanCommit.CommitTx.TxOut {
|
|
switch {
|
|
case bytes.Equal(txOut.PkScript, ourScript.PkScript):
|
|
ourIndex = uint32(i)
|
|
case bytes.Equal(txOut.PkScript, theirScript.PkScript):
|
|
theirIndex = uint32(i)
|
|
}
|
|
}
|
|
|
|
return ourIndex, theirIndex, nil
|
|
}
|
|
|
|
// DeriveCommitmentKeys generates a new commitment key set using the base points
|
|
// and commitment point. The keys are derived differently depending on the type
|
|
// of channel, and whether the commitment transaction is ours or the remote
|
|
// peer's.
|
|
func DeriveCommitmentKeys(commitPoint *btcec.PublicKey,
|
|
isOurCommit bool, chanType mig25.ChannelType,
|
|
localChanCfg, remoteChanCfg *mig.ChannelConfig) *CommitmentKeyRing {
|
|
|
|
tweaklessCommit := chanType.IsTweakless()
|
|
|
|
// Depending on if this is our commit or not, we'll choose the correct
|
|
// base point.
|
|
localBasePoint := localChanCfg.PaymentBasePoint
|
|
if isOurCommit {
|
|
localBasePoint = localChanCfg.DelayBasePoint
|
|
}
|
|
|
|
// First, we'll derive all the keys that don't depend on the context of
|
|
// whose commitment transaction this is.
|
|
keyRing := &CommitmentKeyRing{
|
|
CommitPoint: commitPoint,
|
|
|
|
LocalCommitKeyTweak: input.SingleTweakBytes(
|
|
commitPoint, localBasePoint.PubKey,
|
|
),
|
|
LocalHtlcKeyTweak: input.SingleTweakBytes(
|
|
commitPoint, localChanCfg.HtlcBasePoint.PubKey,
|
|
),
|
|
LocalHtlcKey: input.TweakPubKey(
|
|
localChanCfg.HtlcBasePoint.PubKey, commitPoint,
|
|
),
|
|
RemoteHtlcKey: input.TweakPubKey(
|
|
remoteChanCfg.HtlcBasePoint.PubKey, commitPoint,
|
|
),
|
|
}
|
|
|
|
// We'll now compute the to_local, to_remote, 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.
|
|
var (
|
|
toLocalBasePoint *btcec.PublicKey
|
|
toRemoteBasePoint *btcec.PublicKey
|
|
revocationBasePoint *btcec.PublicKey
|
|
)
|
|
if isOurCommit {
|
|
toLocalBasePoint = localChanCfg.DelayBasePoint.PubKey
|
|
toRemoteBasePoint = remoteChanCfg.PaymentBasePoint.PubKey
|
|
revocationBasePoint = remoteChanCfg.RevocationBasePoint.PubKey
|
|
} else {
|
|
toLocalBasePoint = remoteChanCfg.DelayBasePoint.PubKey
|
|
toRemoteBasePoint = localChanCfg.PaymentBasePoint.PubKey
|
|
revocationBasePoint = localChanCfg.RevocationBasePoint.PubKey
|
|
}
|
|
|
|
// With the base points assigned, we can now derive the actual keys
|
|
// using the base point, and the current commitment tweak.
|
|
keyRing.ToLocalKey = input.TweakPubKey(toLocalBasePoint, commitPoint)
|
|
keyRing.RevocationKey = input.DeriveRevocationPubkey(
|
|
revocationBasePoint, commitPoint,
|
|
)
|
|
|
|
// If this commitment should omit the tweak for the remote point, then
|
|
// we'll use that directly, and ignore the commitPoint tweak.
|
|
if tweaklessCommit {
|
|
keyRing.ToRemoteKey = toRemoteBasePoint
|
|
|
|
// If this is not our commitment, the above ToRemoteKey will be
|
|
// ours, and we blank out the local commitment tweak to
|
|
// indicate that the key should not be tweaked when signing.
|
|
if !isOurCommit {
|
|
keyRing.LocalCommitKeyTweak = nil
|
|
}
|
|
} else {
|
|
keyRing.ToRemoteKey = input.TweakPubKey(
|
|
toRemoteBasePoint, commitPoint,
|
|
)
|
|
}
|
|
|
|
return keyRing
|
|
}
|
|
|
|
// CommitScriptToRemote derives the appropriate to_remote script based on the
|
|
// channel's commitment type. The `initiator` argument should correspond to the
|
|
// owner of the commitment transaction 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 mig25.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.
|
|
case 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
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
// CommitScriptToSelf constructs the public key script for the output on the
|
|
// commitment transaction paying to the "owner" of said commitment transaction.
|
|
// The `initiator` argument should correspond to the owner of the commitment
|
|
// transaction 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 mig25.ChannelType, initiator bool,
|
|
selfKey, revokeKey *btcec.PublicKey, csvDelay, leaseExpiry uint32) (
|
|
*ScriptInfo, error) {
|
|
|
|
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
|
|
}
|
|
|
|
toLocalScriptHash, err := input.WitnessScriptHash(toLocalRedeemScript)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &ScriptInfo{
|
|
PkScript: toLocalScriptHash,
|
|
WitnessScript: toLocalRedeemScript,
|
|
}, nil
|
|
}
|