mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-02-21 14:04:06 +01:00
lnwallet: update channel state machine to use new ScriptDescriptor interface
In this commit, we update the channel state machine to use the new ScriptDescriptor interface. This fixes some subtle issues with the existing commits, as for p2wsh we always sign the same witness script, but for p2tr, the witness script differs depending on which branch is taken. With the new abstractions, we can treat p2wsh and p2tr as the same mostly, right up until we need to obtain a control block or a tap tweak. All tests have been updated accordingly.
This commit is contained in:
parent
a244a30f32
commit
4b0139c9ba
8 changed files with 639 additions and 396 deletions
|
@ -451,10 +451,10 @@ func (c *chainWatcher) handleUnknownLocalState(
|
|||
pkScript := output.PkScript
|
||||
|
||||
switch {
|
||||
case bytes.Equal(localScript.PkScript, pkScript):
|
||||
case bytes.Equal(localScript.PkScript(), pkScript):
|
||||
ourCommit = true
|
||||
|
||||
case bytes.Equal(remoteScript.PkScript, pkScript):
|
||||
case bytes.Equal(remoteScript.PkScript(), pkScript):
|
||||
ourCommit = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/lnutils"
|
||||
"golang.org/x/crypto/ripemd160"
|
||||
)
|
||||
|
||||
|
@ -605,12 +606,31 @@ func SenderHTLCTapLeafSuccess(receiverHtlcKey *btcec.PublicKey,
|
|||
return txscript.NewBaseTapLeaf(successLeafScript), nil
|
||||
}
|
||||
|
||||
// htlcType is an enum value that denotes what type of HTLC script this is.
|
||||
type htlcType uint8
|
||||
|
||||
const (
|
||||
// htlcLocalIncoming represents an incoming HTLC on the local
|
||||
// commitment transaction.
|
||||
htlcLocalIncoming htlcType = iota
|
||||
|
||||
// htlcLocalOutgoing represents an outgoing HTLC on the local
|
||||
// commitment transaction.
|
||||
htlcLocalOutgoing
|
||||
|
||||
// htlcRemoteIncoming represents an incoming HTLC on the remote
|
||||
// commitment transaction.
|
||||
htlcRemoteIncoming
|
||||
|
||||
// htlcRemoteOutgoing represents an outgoing HTLC on the remote
|
||||
// commitment transaction.
|
||||
htlcRemoteOutgoing
|
||||
)
|
||||
|
||||
// HtlcScriptTree holds the taproot output key, as well as the two script path
|
||||
// leaves that every taproot HTLC script depends on.
|
||||
type HtlcScriptTree struct {
|
||||
// TaprootKey is the key that will be used to generate the taproot
|
||||
// output.
|
||||
TaprootKey *btcec.PublicKey
|
||||
ScriptTree
|
||||
|
||||
// SuccessTapLeaf is the tapleaf for the redemption path.
|
||||
SuccessTapLeaf txscript.TapLeaf
|
||||
|
@ -618,18 +638,83 @@ type HtlcScriptTree struct {
|
|||
// TimeoutTapLeaf is the tapleaf for the timeout path.
|
||||
TimeoutTapLeaf txscript.TapLeaf
|
||||
|
||||
// TapscriptTree is the full tapscript tree that also includes the
|
||||
// control block needed to spend each of the leaves.
|
||||
TapscriptTree *txscript.IndexedTapScriptTree
|
||||
|
||||
// TapscriptTreeRoot is the root hash of the tapscript tree.
|
||||
TapscriptRoot []byte
|
||||
htlcType htlcType
|
||||
}
|
||||
|
||||
// WitnessScriptToSign returns the witness script that we'll use when signing
|
||||
// for the remote party, and also verifying signatures on our transactions. As
|
||||
// an example, when we create an outgoing HTLC for the remote party, we want to
|
||||
// sign the success path for them, so we'll return the success path leaf.
|
||||
func (h *HtlcScriptTree) WitnessScriptToSign() []byte {
|
||||
switch h.htlcType {
|
||||
// For incoming HLTCs on our local commitment, we care about verifying
|
||||
// the sucess path.
|
||||
case htlcLocalIncoming:
|
||||
return h.SuccessTapLeaf.Script
|
||||
|
||||
// For incoming HTLCs on the remote party's commitment, we want to sign
|
||||
// the the timeout path for them.
|
||||
case htlcRemoteIncoming:
|
||||
return h.TimeoutTapLeaf.Script
|
||||
|
||||
// For outgoing HTLCs on our local commitment, we want to verify the
|
||||
// timeout path.
|
||||
case htlcLocalOutgoing:
|
||||
return h.TimeoutTapLeaf.Script
|
||||
|
||||
// For outgoing HTLCs on the remote party's commitment, we want to sign
|
||||
// the success path for them.
|
||||
case htlcRemoteOutgoing:
|
||||
return h.SuccessTapLeaf.Script
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown htlc type: %v", h.htlcType))
|
||||
}
|
||||
}
|
||||
|
||||
// WitnessScriptForPath returns the witness script for the given spending path.
|
||||
// An error is returned if the path is unknown.
|
||||
func (h *HtlcScriptTree) WitnessScriptForPath(path ScriptPath) ([]byte, error) {
|
||||
switch path {
|
||||
case ScriptPathSuccess:
|
||||
return h.SuccessTapLeaf.Script, nil
|
||||
case ScriptPathTimeout:
|
||||
return h.TimeoutTapLeaf.Script, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown script path: %v", path)
|
||||
}
|
||||
}
|
||||
|
||||
// CtrlBlockForPath returns the control block for the given spending path. For
|
||||
// script types that don't have a control block, nil is returned.
|
||||
func (h *HtlcScriptTree) CtrlBlockForPath(path ScriptPath,
|
||||
) (*txscript.ControlBlock, error) {
|
||||
|
||||
switch path {
|
||||
case ScriptPathSuccess:
|
||||
return lnutils.Ptr(MakeTaprootCtrlBlock(
|
||||
h.SuccessTapLeaf.Script, h.InternalKey,
|
||||
h.TapscriptTree,
|
||||
)), nil
|
||||
case ScriptPathTimeout:
|
||||
return lnutils.Ptr(MakeTaprootCtrlBlock(
|
||||
h.TimeoutTapLeaf.Script, h.InternalKey,
|
||||
h.TapscriptTree,
|
||||
)), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown script path: %v", path)
|
||||
}
|
||||
}
|
||||
|
||||
// A compile time check to ensure HtlcScriptTree implements the
|
||||
// TapscriptMultiplexer interface.
|
||||
var _ TapscriptDescriptor = (*HtlcScriptTree)(nil)
|
||||
|
||||
// senderHtlcTapScriptTree builds the tapscript tree which is used to anchor
|
||||
// the HTLC key for HTLCs on the sender's commitment.
|
||||
func senderHtlcTapScriptTree(senderHtlcKey, receiverHtlcKey,
|
||||
revokeKey *btcec.PublicKey, payHash []byte) (*HtlcScriptTree, error) {
|
||||
revokeKey *btcec.PublicKey, payHash []byte,
|
||||
hType htlcType) (*HtlcScriptTree, error) {
|
||||
|
||||
// First, we'll obtain the tap leaves for both the success and timeout
|
||||
// path.
|
||||
|
@ -661,11 +746,15 @@ func senderHtlcTapScriptTree(senderHtlcKey, receiverHtlcKey,
|
|||
)
|
||||
|
||||
return &HtlcScriptTree{
|
||||
TaprootKey: htlcKey,
|
||||
ScriptTree: ScriptTree{
|
||||
TaprootKey: htlcKey,
|
||||
TapscriptTree: tapscriptTree,
|
||||
TapscriptRoot: tapScriptRoot[:],
|
||||
InternalKey: revokeKey,
|
||||
},
|
||||
SuccessTapLeaf: successTapLeaf,
|
||||
TimeoutTapLeaf: timeoutTapLeaf,
|
||||
TapscriptTree: tapscriptTree,
|
||||
TapscriptRoot: tapScriptRoot[:],
|
||||
htlcType: hType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -698,13 +787,22 @@ func senderHtlcTapScriptTree(senderHtlcKey, receiverHtlcKey,
|
|||
// The top level keyspend key is the revocation key, which allows a defender to
|
||||
// unilaterally spend the created output.
|
||||
func SenderHTLCScriptTaproot(senderHtlcKey, receiverHtlcKey,
|
||||
revokeKey *btcec.PublicKey, payHash []byte) (*HtlcScriptTree, error) {
|
||||
revokeKey *btcec.PublicKey, payHash []byte,
|
||||
localCommit bool) (*HtlcScriptTree, error) {
|
||||
|
||||
var hType htlcType
|
||||
if localCommit {
|
||||
hType = htlcLocalOutgoing
|
||||
} else {
|
||||
hType = htlcRemoteIncoming
|
||||
}
|
||||
|
||||
// Given all the necessary parameters, we'll return the HTLC script
|
||||
// tree that includes the top level output script, as well as the two
|
||||
// tap leaf paths.
|
||||
return senderHtlcTapScriptTree(
|
||||
senderHtlcKey, receiverHtlcKey, revokeKey, payHash,
|
||||
hType,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1175,7 +1273,7 @@ func ReceiverHtlcTapLeafSuccess(receiverHtlcKey *btcec.PublicKey,
|
|||
// the HTLC key for HTLCs on the receiver's commitment.
|
||||
func receiverHtlcTapScriptTree(senderHtlcKey, receiverHtlcKey,
|
||||
revokeKey *btcec.PublicKey, payHash []byte,
|
||||
cltvExpiry uint32) (*HtlcScriptTree, error) {
|
||||
cltvExpiry uint32, hType htlcType) (*HtlcScriptTree, error) {
|
||||
|
||||
// First, we'll obtain the tap leaves for both the success and timeout
|
||||
// path.
|
||||
|
@ -1207,11 +1305,15 @@ func receiverHtlcTapScriptTree(senderHtlcKey, receiverHtlcKey,
|
|||
)
|
||||
|
||||
return &HtlcScriptTree{
|
||||
TaprootKey: htlcKey,
|
||||
ScriptTree: ScriptTree{
|
||||
TaprootKey: htlcKey,
|
||||
TapscriptTree: tapscriptTree,
|
||||
TapscriptRoot: tapScriptRoot[:],
|
||||
InternalKey: revokeKey,
|
||||
},
|
||||
SuccessTapLeaf: successTapLeaf,
|
||||
TimeoutTapLeaf: timeoutTapLeaf,
|
||||
TapscriptTree: tapscriptTree,
|
||||
TapscriptRoot: tapScriptRoot[:],
|
||||
htlcType: hType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -1245,14 +1347,21 @@ func receiverHtlcTapScriptTree(senderHtlcKey, receiverHtlcKey,
|
|||
// the tap leaf are returned.
|
||||
func ReceiverHTLCScriptTaproot(cltvExpiry uint32,
|
||||
senderHtlcKey, receiverHtlcKey, revocationKey *btcec.PublicKey,
|
||||
payHash []byte) (*HtlcScriptTree, error) {
|
||||
payHash []byte, ourCommit bool) (*HtlcScriptTree, error) {
|
||||
|
||||
var hType htlcType
|
||||
if ourCommit {
|
||||
hType = htlcLocalIncoming
|
||||
} else {
|
||||
hType = htlcRemoteOutgoing
|
||||
}
|
||||
|
||||
// Given all the necessary parameters, we'll return the HTLC script
|
||||
// tree that includes the top level output script, as well as the two
|
||||
// tap leaf paths.
|
||||
return receiverHtlcTapScriptTree(
|
||||
senderHtlcKey, receiverHtlcKey, revocationKey, payHash,
|
||||
cltvExpiry,
|
||||
cltvExpiry, hType,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1540,21 +1649,10 @@ func TaprootSecondLevelHtlcScript(revokeKey, delayKey *btcec.PublicKey,
|
|||
// SecondLevelScriptTree is a tapscript tree used to spend the second level
|
||||
// HTLC output after the CSV delay has passed.
|
||||
type SecondLevelScriptTree struct {
|
||||
// TaprootKey is the key that will be used to generate the taproot output.
|
||||
TaprootKey *btcec.PublicKey
|
||||
|
||||
// PkScript is the pkScript of the second level output.
|
||||
PkScript []byte
|
||||
ScriptTree
|
||||
|
||||
// SuccessTapLeaf is the tapleaf for the redemption path.
|
||||
SuccessTapLeaf txscript.TapLeaf
|
||||
|
||||
// TapscriptTree is the full tapscript tree that also includes the
|
||||
// control block needed to spend each of the leaves.
|
||||
TapscriptTree *txscript.IndexedTapScriptTree
|
||||
|
||||
// TapscriptTreeRoot is the root hash of the tapscript tree.
|
||||
TapscriptRoot []byte
|
||||
}
|
||||
|
||||
// TaprootSecondLevelScriptTree constructs the tapscript tree used to spend the
|
||||
|
@ -1577,21 +1675,65 @@ func TaprootSecondLevelScriptTree(revokeKey, delayKey *btcec.PublicKey,
|
|||
outputKey := txscript.ComputeTaprootOutputKey(
|
||||
revokeKey, tapScriptRoot[:],
|
||||
)
|
||||
pkScript, err := PayToTaprootScript(outputKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to make taproot "+
|
||||
"pkscript: %w", err)
|
||||
}
|
||||
|
||||
return &SecondLevelScriptTree{
|
||||
TaprootKey: outputKey,
|
||||
PkScript: pkScript,
|
||||
ScriptTree: ScriptTree{
|
||||
TaprootKey: outputKey,
|
||||
TapscriptTree: tapScriptTree,
|
||||
TapscriptRoot: tapScriptRoot[:],
|
||||
InternalKey: revokeKey,
|
||||
},
|
||||
SuccessTapLeaf: tapScriptTree.LeafMerkleProofs[0].TapLeaf,
|
||||
TapscriptTree: tapScriptTree,
|
||||
TapscriptRoot: tapScriptRoot[:],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WitnessScript returns the witness script that we'll use when signing for the
|
||||
// remote party, and also verifying signatures on our transactions. As an
|
||||
// example, when we create an outgoing HTLC for the remote party, we want to
|
||||
// sign their success path.
|
||||
func (s *SecondLevelScriptTree) WitnessScriptToSign() []byte {
|
||||
return s.SuccessTapLeaf.Script
|
||||
}
|
||||
|
||||
// WitnessScriptForPath returns the witness script for the given spending path.
|
||||
// An error is returned if the path is unknown.
|
||||
func (s *SecondLevelScriptTree) WitnessScriptForPath(path ScriptPath,
|
||||
) ([]byte, error) {
|
||||
|
||||
switch path {
|
||||
case ScriptPathDelay:
|
||||
fallthrough
|
||||
case ScriptPathSuccess:
|
||||
return s.SuccessTapLeaf.Script, nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown script path: %v", path)
|
||||
}
|
||||
}
|
||||
|
||||
// CtrlBlockForPath returns the control block for the given spending path. For
|
||||
// script types that don't have a control block, nil is returned.
|
||||
func (s *SecondLevelScriptTree) CtrlBlockForPath(path ScriptPath,
|
||||
) (*txscript.ControlBlock, error) {
|
||||
|
||||
switch path {
|
||||
case ScriptPathDelay:
|
||||
fallthrough
|
||||
case ScriptPathSuccess:
|
||||
return lnutils.Ptr(MakeTaprootCtrlBlock(
|
||||
s.SuccessTapLeaf.Script, s.InternalKey,
|
||||
s.TapscriptTree,
|
||||
)), nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown script path: %v", path)
|
||||
}
|
||||
}
|
||||
|
||||
// A compile time check to ensure SecondLevelScriptTree implements the
|
||||
// TapscriptDescriptor interface.
|
||||
var _ TapscriptDescriptor = (*SecondLevelScriptTree)(nil)
|
||||
|
||||
// TaprootHtlcSpendRevoke spends a second-level HTLC output via the revocation
|
||||
// path. This uses the top level keyspend path to redeem the contested output.
|
||||
//
|
||||
|
@ -1910,9 +2052,7 @@ func CommitScriptToSelf(csvTimeout uint32, selfKey, revokeKey *btcec.PublicKey)
|
|||
// key, or a NUMs point for the remote output) along with the tapscript leaf
|
||||
// that can spend the output after a delay.
|
||||
type CommitScriptTree struct {
|
||||
// TaprootKey is the key that will be used to generate the taproot
|
||||
// output.
|
||||
TaprootKey *btcec.PublicKey
|
||||
ScriptTree
|
||||
|
||||
// SettleLeaf is the leaf used to settle the output after the delay.
|
||||
SettleLeaf txscript.TapLeaf
|
||||
|
@ -1920,13 +2060,61 @@ type CommitScriptTree struct {
|
|||
// RevocationLeaf is the leaf used to spend the output with the
|
||||
// revocation key signature.
|
||||
RevocationLeaf txscript.TapLeaf
|
||||
}
|
||||
|
||||
// TapscriptTree is the full tapscript tree that also includes the
|
||||
// control block needed to spend each of the leaves.
|
||||
TapscriptTree *txscript.IndexedTapScriptTree
|
||||
// A compile time check to ensure CommitScriptTree implements the
|
||||
// TapscriptDescriptor interface.
|
||||
var _ TapscriptDescriptor = (*CommitScriptTree)(nil)
|
||||
|
||||
// TapscriptTreeRoot is the root hash of the tapscript tree.
|
||||
TapscriptRoot []byte
|
||||
// WitnessScript returns the witness script that we'll use when signing for the
|
||||
// remote party, and also verifying signatures on our transactions. As an
|
||||
// example, when we create an outgoing HTLC for the remote party, we want to
|
||||
// sign their success path.
|
||||
func (s *CommitScriptTree) WitnessScriptToSign() []byte {
|
||||
// TODO(roasbeef): abstraction leak here? always dependent
|
||||
return nil
|
||||
}
|
||||
|
||||
// WitnessScriptForPath returns the witness script for the given spending path.
|
||||
// An error is returned if the path is unknown.
|
||||
func (c *CommitScriptTree) WitnessScriptForPath(path ScriptPath,
|
||||
) ([]byte, error) {
|
||||
|
||||
switch path {
|
||||
// For the commitment output, the delay and success path are the same,
|
||||
// so we'll fall through here to success.
|
||||
case ScriptPathDelay:
|
||||
fallthrough
|
||||
case ScriptPathSuccess:
|
||||
return c.SettleLeaf.Script, nil
|
||||
case ScriptPathRevocation:
|
||||
return c.RevocationLeaf.Script, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown script path: %v", path)
|
||||
}
|
||||
}
|
||||
|
||||
// CtrlBlockForPath returns the control block for the given spending path. For
|
||||
// script types that don't have a control block, nil is returned.
|
||||
func (c *CommitScriptTree) CtrlBlockForPath(path ScriptPath,
|
||||
) (*txscript.ControlBlock, error) {
|
||||
|
||||
switch path {
|
||||
case ScriptPathDelay:
|
||||
fallthrough
|
||||
case ScriptPathSuccess:
|
||||
return lnutils.Ptr(MakeTaprootCtrlBlock(
|
||||
c.SettleLeaf.Script, c.InternalKey,
|
||||
c.TapscriptTree,
|
||||
)), nil
|
||||
case ScriptPathRevocation:
|
||||
return lnutils.Ptr(MakeTaprootCtrlBlock(
|
||||
c.RevocationLeaf.Script, c.InternalKey,
|
||||
c.TapscriptTree,
|
||||
)), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown script path: %v", path)
|
||||
}
|
||||
}
|
||||
|
||||
// NewLocalCommitScriptTree returns a new CommitScript tree that can be used to
|
||||
|
@ -1977,11 +2165,14 @@ func NewLocalCommitScriptTree(csvTimeout uint32,
|
|||
)
|
||||
|
||||
return &CommitScriptTree{
|
||||
ScriptTree: ScriptTree{
|
||||
TaprootKey: toLocalOutputKey,
|
||||
TapscriptTree: tapScriptTree,
|
||||
TapscriptRoot: tapScriptRoot[:],
|
||||
InternalKey: &TaprootNUMSKey,
|
||||
},
|
||||
SettleLeaf: delayTapLeaf,
|
||||
RevocationLeaf: revokeTapLeaf,
|
||||
TaprootKey: toLocalOutputKey,
|
||||
TapscriptTree: tapScriptTree,
|
||||
TapscriptRoot: tapScriptRoot[:],
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -2380,10 +2571,13 @@ func NewRemoteCommitScriptTree(remoteKey *btcec.PublicKey,
|
|||
)
|
||||
|
||||
return &CommitScriptTree{
|
||||
TaprootKey: toRemoteOutputKey,
|
||||
SettleLeaf: tapLeaf,
|
||||
TapscriptTree: tapScriptTree,
|
||||
TapscriptRoot: tapScriptRoot[:],
|
||||
ScriptTree: ScriptTree{
|
||||
TaprootKey: toRemoteOutputKey,
|
||||
TapscriptTree: tapScriptTree,
|
||||
TapscriptRoot: tapScriptRoot[:],
|
||||
InternalKey: &TaprootNUMSKey,
|
||||
},
|
||||
SettleLeaf: tapLeaf,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -2552,19 +2746,10 @@ func CommitScriptAnchor(key *btcec.PublicKey) ([]byte, error) {
|
|||
// AnchorScriptTree holds all the contents needed to sweep a taproot anchor
|
||||
// output on chain.
|
||||
type AnchorScriptTree struct {
|
||||
// TaprootKey is the key that will be used to generate the taproot
|
||||
// output.
|
||||
TaprootKey *btcec.PublicKey
|
||||
ScriptTree
|
||||
|
||||
// SweepLeaf is the leaf used to settle the output after the delay.
|
||||
SweepLeaf txscript.TapLeaf
|
||||
|
||||
// TapscriptTree is the full tapscript tree that also includes the
|
||||
// control block needed to spend each of the leaves.
|
||||
TapscriptTree *txscript.IndexedTapScriptTree
|
||||
|
||||
// TapscriptTreeRoot is the root hash of the tapscript tree.
|
||||
TapscriptRoot []byte
|
||||
}
|
||||
|
||||
// NewAnchorScriptTree makes a new script tree for an anchor output with the
|
||||
|
@ -2596,13 +2781,63 @@ func NewAnchorScriptTree(anchorKey *btcec.PublicKey,
|
|||
)
|
||||
|
||||
return &AnchorScriptTree{
|
||||
TaprootKey: anchorOutputKey,
|
||||
SweepLeaf: tapLeaf,
|
||||
TapscriptTree: tapScriptTree,
|
||||
TapscriptRoot: tapScriptRoot[:],
|
||||
ScriptTree: ScriptTree{
|
||||
TaprootKey: anchorOutputKey,
|
||||
TapscriptTree: tapScriptTree,
|
||||
TapscriptRoot: tapScriptRoot[:],
|
||||
InternalKey: anchorKey,
|
||||
},
|
||||
SweepLeaf: tapLeaf,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WitnessScript returns the witness script that we'll use when signing for the
|
||||
// remote party, and also verifying signatures on our transactions. As an
|
||||
// example, when we create an outgoing HTLC for the remote party, we want to
|
||||
// sign their success path.
|
||||
func (s *AnchorScriptTree) WitnessScriptToSign() []byte {
|
||||
return s.SweepLeaf.Script
|
||||
}
|
||||
|
||||
// WitnessScriptForPath returns the witness script for the given spending path.
|
||||
// An error is returned if the path is unknown.
|
||||
func (s *AnchorScriptTree) WitnessScriptForPath(path ScriptPath,
|
||||
) ([]byte, error) {
|
||||
|
||||
switch path {
|
||||
case ScriptPathDelay:
|
||||
fallthrough
|
||||
case ScriptPathSuccess:
|
||||
return s.SweepLeaf.Script, nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown script path: %v", path)
|
||||
}
|
||||
}
|
||||
|
||||
// CtrlBlockForPath returns the control block for the given spending path. For
|
||||
// script types that don't have a control block, nil is returned.
|
||||
func (a *AnchorScriptTree) CtrlBlockForPath(path ScriptPath,
|
||||
) (*txscript.ControlBlock, error) {
|
||||
|
||||
switch path {
|
||||
case ScriptPathDelay:
|
||||
fallthrough
|
||||
case ScriptPathSuccess:
|
||||
return lnutils.Ptr(MakeTaprootCtrlBlock(
|
||||
a.SweepLeaf.Script, a.InternalKey,
|
||||
a.TapscriptTree,
|
||||
)), nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown script path: %v", path)
|
||||
}
|
||||
}
|
||||
|
||||
// A compile time check to ensure AnchorScriptTree implements the
|
||||
// TapscriptDescriptor interface.
|
||||
var _ TapscriptDescriptor = (*AnchorScriptTree)(nil)
|
||||
|
||||
// TaprootOutputKeyAnchor returns the segwit v1 (taproot) witness program that
|
||||
// encodes the anchor output spending conditions: the passed key can be used
|
||||
// for keyspend, with the OP_CSV 16 clause living within an internal tapscript
|
||||
|
|
|
@ -1060,7 +1060,7 @@ var witnessSizeTests = []witnessSizeTest{
|
|||
|
||||
htlcScriptTree, err := input.SenderHTLCScriptTaproot(
|
||||
senderKey.PubKey(), receiverKey.PubKey(),
|
||||
revokeKey.PubKey(), payHash[:],
|
||||
revokeKey.PubKey(), payHash[:], false,
|
||||
)
|
||||
|
||||
signDesc := &input.SignDescriptor{
|
||||
|
@ -1100,7 +1100,7 @@ var witnessSizeTests = []witnessSizeTest{
|
|||
htlcScriptTree, err := input.ReceiverHTLCScriptTaproot(
|
||||
testCLTVExpiry, senderKey.PubKey(),
|
||||
receiverKey.PubKey(), revokeKey.PubKey(),
|
||||
payHash[:],
|
||||
payHash[:], false,
|
||||
)
|
||||
|
||||
signDesc := &input.SignDescriptor{
|
||||
|
@ -1140,7 +1140,7 @@ var witnessSizeTests = []witnessSizeTest{
|
|||
htlcScriptTree, err := input.ReceiverHTLCScriptTaproot(
|
||||
testCLTVExpiry, senderKey.PubKey(),
|
||||
receiverKey.PubKey(), revokeKey.PubKey(),
|
||||
payHash[:],
|
||||
payHash[:], false,
|
||||
)
|
||||
|
||||
timeoutLeaf := htlcScriptTree.TimeoutTapLeaf
|
||||
|
@ -1183,7 +1183,7 @@ var witnessSizeTests = []witnessSizeTest{
|
|||
|
||||
htlcScriptTree, err := input.SenderHTLCScriptTaproot(
|
||||
senderKey.PubKey(), receiverKey.PubKey(),
|
||||
revokeKey.PubKey(), payHash[:],
|
||||
revokeKey.PubKey(), payHash[:], false,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -1242,8 +1242,7 @@ var witnessSizeTests = []witnessSizeTest{
|
|||
|
||||
htlcScriptTree, err := input.SenderHTLCScriptTaproot(
|
||||
senderKey.PubKey(), receiverKey.PubKey(),
|
||||
revokeKey.PubKey(),
|
||||
payHash[:],
|
||||
revokeKey.PubKey(), payHash[:], false,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -1289,7 +1288,7 @@ var witnessSizeTests = []witnessSizeTest{
|
|||
htlcScriptTree, err := input.ReceiverHTLCScriptTaproot(
|
||||
testCLTVExpiry, senderKey.PubKey(),
|
||||
receiverKey.PubKey(), revokeKey.PubKey(),
|
||||
payHash[:],
|
||||
payHash[:], false,
|
||||
)
|
||||
|
||||
successsLeaf := htlcScriptTree.SuccessTapLeaf
|
||||
|
@ -1373,7 +1372,7 @@ func genTimeoutTx(t *testing.T,
|
|||
)
|
||||
if chanType.IsTaproot() {
|
||||
tapscriptTree, err = input.SenderHTLCScriptTaproot(
|
||||
testPubkey, testPubkey, testPubkey, testHash160,
|
||||
testPubkey, testPubkey, testPubkey, testHash160, false,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -1442,7 +1441,7 @@ func genSuccessTx(t *testing.T, chanType channeldb.ChannelType) *wire.MsgTx {
|
|||
if chanType.IsTaproot() {
|
||||
tapscriptTree, err = input.ReceiverHTLCScriptTaproot(
|
||||
testCLTVExpiry, testPubkey, testPubkey, testPubkey,
|
||||
testHash160,
|
||||
testHash160, false,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ func newTestSenderHtlcScriptTree(t *testing.T) *testSenderHtlcScriptTree {
|
|||
payHash := preImage.Hash()
|
||||
htlcScriptTree, err := SenderHTLCScriptTaproot(
|
||||
senderKey.PubKey(), receiverKey.PubKey(), revokeKey.PubKey(),
|
||||
payHash[:],
|
||||
payHash[:], false,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -471,7 +471,7 @@ func newTestReceiverHtlcScriptTree(t *testing.T) *testReceiverHtlcScriptTree {
|
|||
payHash := preImage.Hash()
|
||||
htlcScriptTree, err := ReceiverHTLCScriptTaproot(
|
||||
cltvExpiry, senderKey.PubKey(), receiverKey.PubKey(),
|
||||
revokeKey.PubKey(), payHash[:],
|
||||
revokeKey.PubKey(), payHash[:], false,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
|
|
@ -821,8 +821,8 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight,
|
|||
if err != nil {
|
||||
return pd, err
|
||||
}
|
||||
ourP2WSH = scriptInfo.PkScript
|
||||
ourWitnessScript = scriptInfo.WitnessScript
|
||||
ourP2WSH = scriptInfo.PkScript()
|
||||
ourWitnessScript = scriptInfo.WitnessScriptToSign()
|
||||
}
|
||||
isDustRemote := HtlcIsDust(
|
||||
chanType, htlc.Incoming, false, feeRate,
|
||||
|
@ -836,8 +836,8 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight,
|
|||
if err != nil {
|
||||
return pd, err
|
||||
}
|
||||
theirP2WSH = scriptInfo.PkScript
|
||||
theirWitnessScript = scriptInfo.WitnessScript
|
||||
theirP2WSH = scriptInfo.PkScript()
|
||||
theirWitnessScript = scriptInfo.WitnessScriptToSign()
|
||||
}
|
||||
|
||||
// Reconstruct the proper local/remote output indexes from the HTLC's
|
||||
|
@ -1575,8 +1575,8 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
pd.theirPkScript = scriptInfo.PkScript
|
||||
pd.theirWitnessScript = scriptInfo.WitnessScript
|
||||
pd.theirPkScript = scriptInfo.PkScript()
|
||||
pd.theirWitnessScript = scriptInfo.WitnessScriptToSign()
|
||||
}
|
||||
|
||||
// For HTLC's we're offered we'll fetch the original offered HTLC
|
||||
|
@ -2480,7 +2480,6 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
|
|||
|
||||
// If the returned *RevocationLog is non-nil, use it to derive the info
|
||||
// we need.
|
||||
isTaproot := chanState.ChanType.IsTaproot()
|
||||
if revokedLog != nil {
|
||||
br, ourAmt, theirAmt, err = createBreachRetribution(
|
||||
revokedLog, spendTx, chanState, keyRing,
|
||||
|
@ -2513,12 +2512,21 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
|
|||
// If our balance exceeds the remote party's dust limit, instantiate
|
||||
// the sign descriptor for our output.
|
||||
if ourAmt >= int64(chanState.RemoteChanCfg.DustLimit) {
|
||||
// As we're about to sweep our own output w/o a delay, we'll obtain
|
||||
// the witness script for the success/delay path.
|
||||
witnessScript, err := ourScript.WitnessScriptForPath(
|
||||
input.ScriptPathDelay,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
br.LocalOutputSignDesc = &input.SignDescriptor{
|
||||
SingleTweak: keyRing.LocalCommitKeyTweak,
|
||||
KeyDesc: chanState.LocalChanCfg.PaymentBasePoint,
|
||||
WitnessScript: ourScript.WitnessScript,
|
||||
WitnessScript: witnessScript,
|
||||
Output: &wire.TxOut{
|
||||
PkScript: ourScript.PkScript,
|
||||
PkScript: ourScript.PkScript(),
|
||||
Value: ourAmt,
|
||||
},
|
||||
HashType: txscript.SigHashAll,
|
||||
|
@ -2527,13 +2535,16 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
|
|||
// For taproot channels, we'll make sure to set the script path
|
||||
// spend (as our output on their revoked tx still needs the
|
||||
// delay), and set the control block.
|
||||
if isTaproot {
|
||||
if scriptTree, ok := ourScript.(input.TapscriptDescriptor); ok {
|
||||
br.LocalOutputSignDesc.SignMethod = input.TaprootScriptSpendSignMethod
|
||||
|
||||
ctrlBlock := input.MakeTaprootCtrlBlock(
|
||||
br.LocalOutputSignDesc.WitnessScript,
|
||||
&input.TaprootNUMSKey, ourScript.ScriptTree,
|
||||
ctrlBlock, err := scriptTree.CtrlBlockForPath(
|
||||
input.ScriptPathDelay,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
br.LocalOutputSignDesc.ControlBlock, err = ctrlBlock.ToBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -2544,28 +2555,45 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
|
|||
// Similarly, if their balance exceeds the remote party's dust limit,
|
||||
// assemble the sign descriptor for their output, which we can sweep.
|
||||
if theirAmt >= int64(chanState.RemoteChanCfg.DustLimit) {
|
||||
// As we're trying to defend the channel against a breach
|
||||
// attempt from the remote party, we want to obain the
|
||||
// revocation witness script here.
|
||||
witnessScript, err := theirScript.WitnessScriptForPath(
|
||||
input.ScriptPathRevocation,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
br.RemoteOutputSignDesc = &input.SignDescriptor{
|
||||
KeyDesc: chanState.LocalChanCfg.
|
||||
RevocationBasePoint,
|
||||
DoubleTweak: commitmentSecret,
|
||||
WitnessScript: theirScript.WitnessScript,
|
||||
WitnessScript: witnessScript,
|
||||
Output: &wire.TxOut{
|
||||
PkScript: theirScript.PkScript,
|
||||
PkScript: theirScript.PkScript(),
|
||||
Value: theirAmt,
|
||||
},
|
||||
HashType: txscript.SigHashAll,
|
||||
}
|
||||
|
||||
// For taproot channels, the remote ouput (the revoked outuput)
|
||||
// can be spent using a single key spend, now that we know the
|
||||
// revocation key.
|
||||
if isTaproot {
|
||||
br.RemoteOutputSignDesc.SignMethod = input.TaprootKeySpendSignMethod
|
||||
// For taproot channels, the remote output (the revoked output)
|
||||
// is spent with a script path to ensure all information 3rd
|
||||
// parties need to sweep anchors is revealed on chain.
|
||||
if scriptTree, ok := theirScript.(input.TapscriptDescriptor); ok {
|
||||
//nolint:lll
|
||||
br.RemoteOutputSignDesc.SignMethod = input.TaprootScriptSpendSignMethod
|
||||
|
||||
// We'll also need to set the taptweak as we'll be
|
||||
// signing with the full output key.
|
||||
tapscriptRoot := theirScript.ScriptTree.RootNode.TapHash()
|
||||
br.RemoteOutputSignDesc.TapTweak = tapscriptRoot[:]
|
||||
ctrlBlock, err := scriptTree.CtrlBlockForPath(
|
||||
input.ScriptPathRevocation,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
br.RemoteOutputSignDesc.ControlBlock, err = ctrlBlock.ToBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2621,9 +2649,9 @@ func createHtlcRetribution(chanState *channeldb.OpenChannel,
|
|||
KeyDesc: chanState.LocalChanCfg.
|
||||
RevocationBasePoint,
|
||||
DoubleTweak: commitmentSecret,
|
||||
WitnessScript: scriptInfo.WitnessScript,
|
||||
WitnessScript: scriptInfo.WitnessScriptToSign(),
|
||||
Output: &wire.TxOut{
|
||||
PkScript: scriptInfo.PkScript,
|
||||
PkScript: scriptInfo.PkScript(),
|
||||
Value: int64(htlc.Amt),
|
||||
},
|
||||
HashType: txscript.SigHashAll,
|
||||
|
@ -2632,20 +2660,26 @@ func createHtlcRetribution(chanState *channeldb.OpenChannel,
|
|||
// For taproot HTLC outputs, we need to set the sign method to key
|
||||
// spend, and also set the tap tweak root needed to derive the proper
|
||||
// private key.
|
||||
if chanState.ChanType.IsTaproot() {
|
||||
if scriptTree, ok := scriptInfo.(input.TapscriptDescriptor); ok {
|
||||
signDesc.SignMethod = input.TaprootKeySpendSignMethod
|
||||
|
||||
tapscriptRoot := scriptInfo.ScriptTree.RootNode.TapHash()
|
||||
signDesc.TapTweak = tapscriptRoot[:]
|
||||
signDesc.TapTweak = scriptTree.TapTweak()
|
||||
}
|
||||
|
||||
// The second levle script we sign will always be the sucess path.
|
||||
secondLevelWitnessScript, err := secondLevelScript.WitnessScriptForPath(
|
||||
input.ScriptPathSuccess,
|
||||
)
|
||||
if err != nil {
|
||||
return emptyRetribution, err
|
||||
}
|
||||
|
||||
// If this is a taproot output, we'll also need to obtain the second
|
||||
// level tap tweak as well.
|
||||
var secondLevelTapTweak [32]byte
|
||||
if chanState.ChanType.IsTaproot() {
|
||||
tapscriptRoot := secondLevelScript.ScriptTree.RootNode.TapHash()
|
||||
if scriptTree, ok := secondLevelScript.(input.TapscriptDescriptor); ok {
|
||||
copy(
|
||||
secondLevelTapTweak[:], tapscriptRoot[:],
|
||||
secondLevelTapTweak[:], scriptTree.TapTweak(),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -2655,7 +2689,8 @@ func createHtlcRetribution(chanState *channeldb.OpenChannel,
|
|||
Hash: commitHash,
|
||||
Index: uint32(htlc.OutputIndex),
|
||||
},
|
||||
SecondLevelWitnessScript: secondLevelScript.WitnessScript,
|
||||
SecondLevelWitnessScript: secondLevelWitnessScript,
|
||||
SecondLevelTapTweak: secondLevelTapTweak,
|
||||
IsIncoming: htlc.Incoming,
|
||||
}, nil
|
||||
}
|
||||
|
@ -2778,7 +2813,7 @@ func createBreachRetribution(revokedLog *channeldb.RevocationLog,
|
|||
func createBreachRetributionLegacy(revokedLog *channeldb.ChannelCommitment,
|
||||
chanState *channeldb.OpenChannel, keyRing *CommitmentKeyRing,
|
||||
commitmentSecret *btcec.PrivateKey,
|
||||
ourScript, theirScript *ScriptInfo,
|
||||
ourScript, theirScript input.ScriptDescriptor,
|
||||
leaseExpiry uint32) (*BreachRetribution, int64, int64, error) {
|
||||
|
||||
commitHash := revokedLog.CommitTx.TxHash()
|
||||
|
@ -2793,9 +2828,9 @@ func createBreachRetributionLegacy(revokedLog *channeldb.ChannelCommitment,
|
|||
// to find the exact index of the commitment outputs.
|
||||
for i, txOut := range revokedLog.CommitTx.TxOut {
|
||||
switch {
|
||||
case bytes.Equal(txOut.PkScript, ourScript.PkScript):
|
||||
case bytes.Equal(txOut.PkScript, ourScript.PkScript()):
|
||||
ourOutpoint.Index = uint32(i)
|
||||
case bytes.Equal(txOut.PkScript, theirScript.PkScript):
|
||||
case bytes.Equal(txOut.PkScript, theirScript.PkScript()):
|
||||
theirOutpoint.Index = uint32(i)
|
||||
}
|
||||
}
|
||||
|
@ -6398,7 +6433,7 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
|
|||
)
|
||||
|
||||
for outputIndex, txOut := range commitTxBroadcast.TxOut {
|
||||
if bytes.Equal(txOut.PkScript, selfScript.PkScript) {
|
||||
if bytes.Equal(txOut.PkScript, selfScript.PkScript()) {
|
||||
selfPoint = &wire.OutPoint{
|
||||
Hash: *commitSpend.SpenderTxHash,
|
||||
Index: uint32(outputIndex),
|
||||
|
@ -6414,15 +6449,25 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
|
|||
var commitResolution *CommitOutputResolution
|
||||
if selfPoint != nil {
|
||||
localPayBase := chanState.LocalChanCfg.PaymentBasePoint
|
||||
|
||||
// As the remote party has force closed, we just need the
|
||||
// success witness script.
|
||||
witnessScript, err := selfScript.WitnessScriptForPath(
|
||||
input.ScriptPathSuccess,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
commitResolution = &CommitOutputResolution{
|
||||
SelfOutPoint: *selfPoint,
|
||||
SelfOutputSignDesc: input.SignDescriptor{
|
||||
KeyDesc: localPayBase,
|
||||
SingleTweak: keyRing.LocalCommitKeyTweak,
|
||||
WitnessScript: selfScript.WitnessScript,
|
||||
WitnessScript: witnessScript,
|
||||
Output: &wire.TxOut{
|
||||
Value: localBalance,
|
||||
PkScript: selfScript.PkScript,
|
||||
PkScript: selfScript.PkScript(),
|
||||
},
|
||||
HashType: txscript.SigHashAll,
|
||||
},
|
||||
|
@ -6431,16 +6476,16 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
|
|||
|
||||
// For taproot channels, we'll need to set some additional
|
||||
// fields to ensure the output can be swept.
|
||||
//
|
||||
// TODO(roasbef): abstract into new func
|
||||
if chanState.ChanType.IsTaproot() {
|
||||
if scriptTree, ok := selfScript.(input.TapscriptDescriptor); ok {
|
||||
commitResolution.SelfOutputSignDesc.SignMethod =
|
||||
input.TaprootScriptSpendSignMethod
|
||||
|
||||
ctrlBlock := input.MakeTaprootCtrlBlock(
|
||||
commitResolution.SelfOutputSignDesc.WitnessScript,
|
||||
&input.TaprootNUMSKey, selfScript.ScriptTree,
|
||||
ctrlBlock, err := scriptTree.CtrlBlockForPath(
|
||||
input.ScriptPathSuccess,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
commitResolution.SelfOutputSignDesc.ControlBlock, err = ctrlBlock.ToBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -6630,8 +6675,17 @@ func newOutgoingHtlcResolution(signer input.Signer,
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
htlcPkScript := htlcScriptInfo.PkScript
|
||||
htlcWitnessScript := htlcScriptInfo.WitnessScript
|
||||
htlcPkScript := htlcScriptInfo.PkScript()
|
||||
|
||||
// As this is an outgoing HTLC, we just care about the timeout path
|
||||
// here.
|
||||
scriptPath := input.ScriptPathTimeout
|
||||
htlcWitnessScript, err := htlcScriptInfo.WitnessScriptForPath(
|
||||
scriptPath,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If we're spending this HTLC output from the remote node's
|
||||
// commitment, then we won't need to go to the second level as our
|
||||
|
@ -6650,12 +6704,16 @@ func newOutgoingHtlcResolution(signer input.Signer,
|
|||
HashType: txscript.SigHashAll,
|
||||
}
|
||||
|
||||
if chanType.IsTaproot() {
|
||||
scriptTree, ok := htlcScriptInfo.(input.TapscriptDescriptor)
|
||||
if ok {
|
||||
signDesc.SignMethod = input.TaprootScriptSpendSignMethod
|
||||
ctrlBlock := input.MakeTaprootCtrlBlock(
|
||||
htlcWitnessScript, keyRing.RevocationKey,
|
||||
htlcScriptInfo.ScriptTree,
|
||||
|
||||
ctrlBlock, err := scriptTree.CtrlBlockForPath(
|
||||
scriptPath,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signDesc.ControlBlock, err = ctrlBlock.ToBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -6718,7 +6776,7 @@ func newOutgoingHtlcResolution(signer input.Signer,
|
|||
// for the timeout transaction, and populate it as well.
|
||||
sigHashType := HtlcSigHashType(chanType)
|
||||
var timeoutWitness wire.TxWitness
|
||||
if chanType.IsTaproot() {
|
||||
if scriptTree, ok := htlcScriptInfo.(input.TapscriptDescriptor); ok {
|
||||
// TODO(roasbeef): make sure default elsewhere
|
||||
timeoutSignDesc.SignMethod = input.TaprootScriptSpendSignMethod
|
||||
timeoutSignDesc.HashType = txscript.SigHashDefault
|
||||
|
@ -6726,7 +6784,7 @@ func newOutgoingHtlcResolution(signer input.Signer,
|
|||
timeoutWitness, err = input.SenderHTLCScriptTaprootTimeout(
|
||||
htlcSig, sigHashType, signer, &timeoutSignDesc,
|
||||
timeoutTx, keyRing.RevocationKey,
|
||||
htlcScriptInfo.ScriptTree,
|
||||
scriptTree.TapScriptTree(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -6761,7 +6819,7 @@ func newOutgoingHtlcResolution(signer input.Signer,
|
|||
// transaction creates so we can generate the signDesc required to
|
||||
// complete the claim process after a delay period.
|
||||
var (
|
||||
htlcSweepScript *ScriptInfo
|
||||
htlcSweepScript input.ScriptDescriptor
|
||||
signMethod input.SignMethod
|
||||
ctrlBlock []byte
|
||||
)
|
||||
|
@ -6781,21 +6839,29 @@ func newOutgoingHtlcResolution(signer input.Signer,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
htlcSweepScript = &ScriptInfo{
|
||||
PkScript: secondLevelScriptTree.PkScript,
|
||||
WitnessScript: secondLevelScriptTree.SuccessTapLeaf.Script,
|
||||
}
|
||||
|
||||
signMethod = input.TaprootScriptSpendSignMethod
|
||||
|
||||
controlBlock := input.MakeTaprootCtrlBlock(
|
||||
htlcSweepScript.WitnessScript, keyRing.RevocationKey,
|
||||
secondLevelScriptTree.TapscriptTree,
|
||||
controlBlock, err := secondLevelScriptTree.CtrlBlockForPath(
|
||||
input.ScriptPathSuccess,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctrlBlock, err = controlBlock.ToBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
htlcSweepScript = secondLevelScriptTree
|
||||
}
|
||||
|
||||
// In this case, the witness script that needs to be signed will always
|
||||
// be that of the success path.
|
||||
htlcSweepWitnessScript, err := htlcSweepScript.WitnessScriptForPath(
|
||||
input.ScriptPathSuccess,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
localDelayTweak := input.SingleTweakBytes(
|
||||
|
@ -6813,9 +6879,9 @@ func newOutgoingHtlcResolution(signer input.Signer,
|
|||
SweepSignDesc: input.SignDescriptor{
|
||||
KeyDesc: localChanCfg.DelayBasePoint,
|
||||
SingleTweak: localDelayTweak,
|
||||
WitnessScript: htlcSweepScript.WitnessScript,
|
||||
WitnessScript: htlcSweepWitnessScript,
|
||||
Output: &wire.TxOut{
|
||||
PkScript: htlcSweepScript.PkScript,
|
||||
PkScript: htlcSweepScript.PkScript(),
|
||||
Value: int64(secondLevelOutputAmt),
|
||||
},
|
||||
HashType: txscript.SigHashAll,
|
||||
|
@ -6854,8 +6920,17 @@ func newIncomingHtlcResolution(signer input.Signer,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
htlcPkScript := scriptInfo.PkScript
|
||||
htlcWitnessScript := scriptInfo.WitnessScript
|
||||
htlcPkScript := scriptInfo.PkScript()
|
||||
|
||||
// As this is an incoming HTLC, we're attempting to sweep with the
|
||||
// success path.
|
||||
scriptPath := input.ScriptPathSuccess
|
||||
htlcWitnessScript, err := scriptInfo.WitnessScriptForPath(
|
||||
scriptPath,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If we're spending this output from the remote node's commitment,
|
||||
// then we can skip the second layer and spend the output directly.
|
||||
|
@ -6873,12 +6948,12 @@ func newIncomingHtlcResolution(signer input.Signer,
|
|||
HashType: txscript.SigHashAll,
|
||||
}
|
||||
|
||||
if chanType.IsTaproot() {
|
||||
if scriptTree, ok := scriptInfo.(input.TapscriptDescriptor); ok {
|
||||
signDesc.SignMethod = input.TaprootScriptSpendSignMethod
|
||||
ctrlBlock := input.MakeTaprootCtrlBlock(
|
||||
htlcWitnessScript, keyRing.RevocationKey,
|
||||
scriptInfo.ScriptTree,
|
||||
)
|
||||
ctrlBlock, err := scriptTree.CtrlBlockForPath(scriptPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signDesc.ControlBlock, err = ctrlBlock.ToBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -6935,14 +7010,14 @@ func newIncomingHtlcResolution(signer input.Signer,
|
|||
// becomes known.
|
||||
var successWitness wire.TxWitness
|
||||
sigHashType := HtlcSigHashType(chanType)
|
||||
if chanType.IsTaproot() {
|
||||
// TODO(roasbeef): make sure default elsewhere
|
||||
if scriptTree, ok := scriptInfo.(input.TapscriptDescriptor); ok {
|
||||
successSignDesc.HashType = txscript.SigHashDefault
|
||||
successSignDesc.SignMethod = input.TaprootScriptSpendSignMethod
|
||||
|
||||
successWitness, err = input.ReceiverHTLCScriptTaprootRedeem(
|
||||
htlcSig, sigHashType, nil, signer, &successSignDesc,
|
||||
successTx, keyRing.RevocationKey, scriptInfo.ScriptTree,
|
||||
successTx, keyRing.RevocationKey,
|
||||
scriptTree.TapScriptTree(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -6976,7 +7051,7 @@ func newIncomingHtlcResolution(signer input.Signer,
|
|||
// creates so we can generate the proper signDesc to sweep it after the
|
||||
// CSV delay has passed.
|
||||
var (
|
||||
htlcSweepScript *ScriptInfo
|
||||
htlcSweepScript input.ScriptDescriptor
|
||||
signMethod input.SignMethod
|
||||
ctrlBlock []byte
|
||||
)
|
||||
|
@ -6996,23 +7071,31 @@ func newIncomingHtlcResolution(signer input.Signer,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
htlcSweepScript = &ScriptInfo{
|
||||
PkScript: secondLevelScriptTree.PkScript,
|
||||
WitnessScript: secondLevelScriptTree.SuccessTapLeaf.Script,
|
||||
}
|
||||
|
||||
signMethod = input.TaprootScriptSpendSignMethod
|
||||
|
||||
controlBlock := input.MakeTaprootCtrlBlock(
|
||||
htlcSweepScript.WitnessScript, keyRing.RevocationKey,
|
||||
secondLevelScriptTree.TapscriptTree,
|
||||
controlBlock, err := secondLevelScriptTree.CtrlBlockForPath(
|
||||
input.ScriptPathSuccess,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctrlBlock, err = controlBlock.ToBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO(roasbeef): conslidate logic to reduce vertical noise
|
||||
|
||||
htlcSweepScript = secondLevelScriptTree
|
||||
}
|
||||
|
||||
// In this case, the witness script that needs to be signed will always
|
||||
// be that of the success path.
|
||||
htlcSweepWitnessScript, err := htlcSweepScript.WitnessScriptForPath(
|
||||
input.ScriptPathSuccess,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
localDelayTweak := input.SingleTweakBytes(
|
||||
|
@ -7029,9 +7112,9 @@ func newIncomingHtlcResolution(signer input.Signer,
|
|||
SweepSignDesc: input.SignDescriptor{
|
||||
KeyDesc: localChanCfg.DelayBasePoint,
|
||||
SingleTweak: localDelayTweak,
|
||||
WitnessScript: htlcSweepScript.WitnessScript,
|
||||
WitnessScript: htlcSweepWitnessScript,
|
||||
Output: &wire.TxOut{
|
||||
PkScript: htlcSweepScript.PkScript,
|
||||
PkScript: htlcSweepScript.PkScript(),
|
||||
Value: int64(secondLevelOutputAmt),
|
||||
},
|
||||
HashType: txscript.SigHashAll,
|
||||
|
@ -7281,7 +7364,7 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel,
|
|||
delayOut *wire.TxOut
|
||||
)
|
||||
for i, txOut := range commitTx.TxOut {
|
||||
if !bytes.Equal(toLocalScript.PkScript, txOut.PkScript) {
|
||||
if !bytes.Equal(toLocalScript.PkScript(), txOut.PkScript) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -7298,6 +7381,16 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel,
|
|||
// nil.
|
||||
var commitResolution *CommitOutputResolution
|
||||
if delayOut != nil {
|
||||
// When attempting to sweep our own output, we only need the
|
||||
// witness script for the delay path
|
||||
scriptPath := input.ScriptPathDelay
|
||||
witnessScript, err := toLocalScript.WitnessScriptForPath(
|
||||
scriptPath,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
localBalance := delayOut.Value
|
||||
commitResolution = &CommitOutputResolution{
|
||||
SelfOutPoint: wire.OutPoint{
|
||||
|
@ -7307,7 +7400,7 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel,
|
|||
SelfOutputSignDesc: input.SignDescriptor{
|
||||
KeyDesc: chanState.LocalChanCfg.DelayBasePoint,
|
||||
SingleTweak: keyRing.LocalCommitKeyTweak,
|
||||
WitnessScript: toLocalScript.WitnessScript,
|
||||
WitnessScript: witnessScript,
|
||||
Output: &wire.TxOut{
|
||||
PkScript: delayOut.PkScript,
|
||||
Value: localBalance,
|
||||
|
@ -7321,14 +7414,17 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel,
|
|||
// fields to ensure the output can be swept.
|
||||
//
|
||||
// TODO(roasbef): abstract into new func
|
||||
if chanState.ChanType.IsTaproot() {
|
||||
scriptTree, ok := toLocalScript.(input.TapscriptDescriptor)
|
||||
if ok {
|
||||
commitResolution.SelfOutputSignDesc.SignMethod =
|
||||
input.TaprootScriptSpendSignMethod
|
||||
|
||||
ctrlBlock := input.MakeTaprootCtrlBlock(
|
||||
commitResolution.SelfOutputSignDesc.WitnessScript,
|
||||
keyRing.RevocationKey, toLocalScript.ScriptTree,
|
||||
ctrlBlock, err := scriptTree.CtrlBlockForPath(
|
||||
scriptPath,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
commitResolution.SelfOutputSignDesc.ControlBlock, err = ctrlBlock.ToBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -7712,15 +7808,26 @@ func NewAnchorResolution(chanState *channeldb.OpenChannel,
|
|||
localAnchor, remoteAnchor = remoteAnchor, localAnchor
|
||||
}
|
||||
|
||||
// TODO(roasbeef): remote anchor not needed above
|
||||
|
||||
// Look up the script on the commitment transaction. It may not be
|
||||
// present if there is no output paying to us.
|
||||
found, index := input.FindScriptOutputIndex(
|
||||
commitTx, localAnchor.PkScript,
|
||||
commitTx, localAnchor.PkScript(),
|
||||
)
|
||||
if !found {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// For anchor outputs, we'll only ever care about the success path.
|
||||
// script (sweep after 1 block csv delay).
|
||||
anchorWitnessScript, err := localAnchor.WitnessScriptForPath(
|
||||
input.ScriptPathSuccess,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outPoint := &wire.OutPoint{
|
||||
Hash: commitTx.TxHash(),
|
||||
Index: index,
|
||||
|
@ -7729,9 +7836,9 @@ func NewAnchorResolution(chanState *channeldb.OpenChannel,
|
|||
// Instantiate the sign descriptor that allows sweeping of the anchor.
|
||||
signDesc := &input.SignDescriptor{
|
||||
KeyDesc: chanState.LocalChanCfg.MultiSigKey,
|
||||
WitnessScript: localAnchor.WitnessScript,
|
||||
WitnessScript: anchorWitnessScript,
|
||||
Output: &wire.TxOut{
|
||||
PkScript: localAnchor.PkScript,
|
||||
PkScript: localAnchor.PkScript(),
|
||||
Value: int64(anchorSize),
|
||||
},
|
||||
HashType: txscript.SigHashAll,
|
||||
|
@ -7739,12 +7846,12 @@ func NewAnchorResolution(chanState *channeldb.OpenChannel,
|
|||
|
||||
// For taproot outputs, we'll need to ensure that the proper sign
|
||||
// method is used, and the tweak as well.
|
||||
if chanState.ChanType.IsTaproot() {
|
||||
if scriptTree, ok := localAnchor.(input.TapscriptDescriptor); ok {
|
||||
signDesc.SignMethod = input.TaprootKeySpendSignMethod
|
||||
signDesc.HashType = txscript.SigHashDefault
|
||||
|
||||
signDesc.PrevOutputFetcher = txscript.NewCannedPrevOutputFetcher(
|
||||
localAnchor.PkScript, int64(anchorSize),
|
||||
localAnchor.PkScript(), int64(anchorSize),
|
||||
)
|
||||
|
||||
// For anchor outputs with taproot channels, the key desc is
|
||||
|
@ -7766,13 +7873,9 @@ func NewAnchorResolution(chanState *channeldb.OpenChannel,
|
|||
signDesc.KeyDesc = chanState.LocalChanCfg.PaymentBasePoint
|
||||
}
|
||||
|
||||
// TODO(roasbeef): need to be payment point if remote, delay
|
||||
// point otherwise?
|
||||
|
||||
// Finally, as this is a keyspend method, we'll need to also
|
||||
// include the taptweak as well.
|
||||
tapscriptRoot := localAnchor.ScriptTree.RootNode.TapHash()
|
||||
signDesc.TapTweak = tapscriptRoot[:]
|
||||
signDesc.TapTweak = scriptTree.TapTweak()
|
||||
}
|
||||
|
||||
var witnessWeight int64
|
||||
|
|
|
@ -9915,11 +9915,11 @@ func TestCreateBreachRetributionLegacy(t *testing.T) {
|
|||
theirOp := revokedLog.CommitTx.TxOut[1]
|
||||
|
||||
// Create the dummy scripts.
|
||||
ourScript := &ScriptInfo{
|
||||
PkScript: ourOp.PkScript,
|
||||
ourScript := &WitnessScriptDesc{
|
||||
OutputScript: ourOp.PkScript,
|
||||
}
|
||||
theirScript := &ScriptInfo{
|
||||
PkScript: theirOp.PkScript,
|
||||
theirScript := &WitnessScriptDesc{
|
||||
OutputScript: theirOp.PkScript,
|
||||
}
|
||||
|
||||
// Create the breach retribution using the legacy format.
|
||||
|
|
|
@ -180,20 +180,41 @@ func DeriveCommitmentKeys(commitPoint *btcec.PublicKey,
|
|||
return keyRing
|
||||
}
|
||||
|
||||
// ScriptInfo holds a redeem script and hash.
|
||||
type ScriptInfo struct {
|
||||
// PkScript is the output's PkScript.
|
||||
PkScript []byte
|
||||
// WitnessScriptDesc holds the output script and the witness script for p2wsh
|
||||
// outputs.
|
||||
type WitnessScriptDesc struct {
|
||||
// OutputScript is the output's PkScript.
|
||||
OutputScript []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
|
||||
}
|
||||
|
||||
// ScriptTree is the script tree that stores all the scripts that are
|
||||
// committed to by the above PkScript, if it's a P2TR script template.
|
||||
ScriptTree *txscript.IndexedTapScriptTree
|
||||
// PkScript is the public key script that commits to the final
|
||||
// contract.
|
||||
func (w *WitnessScriptDesc) PkScript() []byte {
|
||||
return w.OutputScript
|
||||
}
|
||||
|
||||
// WitnessScript returns the witness script that we'll use when signing for the
|
||||
// remote party, and also verifying signatures on our transactions. As an
|
||||
// example, when we create an outgoing HTLC for the remote party, we want to
|
||||
// sign their success path.
|
||||
func (w *WitnessScriptDesc) WitnessScriptToSign() []byte {
|
||||
return w.WitnessScript
|
||||
}
|
||||
|
||||
// WitnessScriptForPath returns the witness script for the given spending path.
|
||||
// An error is returned if the path is unknown. This is useful as when
|
||||
// constructing a contrl block for a given path, one also needs witness script
|
||||
// being signed.
|
||||
func (w *WitnessScriptDesc) WitnessScriptForPath(path input.ScriptPath,
|
||||
) ([]byte, error) {
|
||||
|
||||
return w.WitnessScript, nil
|
||||
}
|
||||
|
||||
// CommitScriptToSelf constructs the public key script for the output on the
|
||||
|
@ -203,8 +224,9 @@ type ScriptInfo struct {
|
|||
// 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) {
|
||||
selfKey, revokeKey *btcec.PublicKey, csvDelay, leaseExpiry uint32,
|
||||
) (
|
||||
input.ScriptDescriptor, error) {
|
||||
|
||||
switch {
|
||||
// For taproot scripts, we'll need to make a slightly modified script
|
||||
|
@ -213,28 +235,9 @@ func CommitScriptToSelf(chanType channeldb.ChannelType, initiator bool,
|
|||
//
|
||||
// Our "redeem" script here is just the taproot witness program.
|
||||
case chanType.IsTaproot():
|
||||
toLocalScriptTree, err := input.NewLocalCommitScriptTree(
|
||||
return input.NewLocalCommitScriptTree(
|
||||
csvDelay, selfKey, revokeKey,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to generate taproot "+
|
||||
"key: %w", err)
|
||||
}
|
||||
|
||||
toLocalPkScript, err := input.PayToTaprootScript(
|
||||
toLocalScriptTree.TaprootKey,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to gen taproot "+
|
||||
"pkscript: %w", err)
|
||||
}
|
||||
|
||||
// TODO(rosabeef): recator to be able to get script key
|
||||
return &ScriptInfo{
|
||||
WitnessScript: toLocalScriptTree.SettleLeaf.Script,
|
||||
PkScript: toLocalPkScript,
|
||||
ScriptTree: toLocalScriptTree.TapscriptTree,
|
||||
}, nil
|
||||
|
||||
// If we are the initiator of a leased channel, then we have an
|
||||
// additional CLTV requirement in addition to the usual CSV
|
||||
|
@ -254,8 +257,8 @@ func CommitScriptToSelf(chanType channeldb.ChannelType, initiator bool,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return &ScriptInfo{
|
||||
PkScript: toLocalScriptHash,
|
||||
return &WitnessScriptDesc{
|
||||
OutputScript: toLocalScriptHash,
|
||||
WitnessScript: toLocalRedeemScript,
|
||||
}, nil
|
||||
|
||||
|
@ -274,8 +277,8 @@ func CommitScriptToSelf(chanType channeldb.ChannelType, initiator bool,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return &ScriptInfo{
|
||||
PkScript: toLocalScriptHash,
|
||||
return &WitnessScriptDesc{
|
||||
OutputScript: toLocalScriptHash,
|
||||
WitnessScript: toLocalRedeemScript,
|
||||
}, nil
|
||||
}
|
||||
|
@ -288,7 +291,7 @@ func CommitScriptToSelf(chanType channeldb.ChannelType, initiator bool,
|
|||
// what must be satisfied in order to spend the output.
|
||||
func CommitScriptToRemote(chanType channeldb.ChannelType, initiator bool,
|
||||
remoteKey *btcec.PublicKey,
|
||||
leaseExpiry uint32) (*ScriptInfo, uint32, error) {
|
||||
leaseExpiry uint32) (input.ScriptDescriptor, uint32, error) {
|
||||
|
||||
switch {
|
||||
// If we are not the initiator of a leased channel, then the remote
|
||||
|
@ -307,8 +310,8 @@ func CommitScriptToRemote(chanType channeldb.ChannelType, initiator bool,
|
|||
return nil, 0, err
|
||||
}
|
||||
|
||||
return &ScriptInfo{
|
||||
PkScript: p2wsh,
|
||||
return &WitnessScriptDesc{
|
||||
OutputScript: p2wsh,
|
||||
WitnessScript: script,
|
||||
}, 1, nil
|
||||
|
||||
|
@ -323,18 +326,7 @@ func CommitScriptToRemote(chanType channeldb.ChannelType, initiator bool,
|
|||
return nil, 0, err
|
||||
}
|
||||
|
||||
toRemotePkScript, err := input.PayToTaprootScript(
|
||||
toRemoteScriptTree.TaprootKey,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return &ScriptInfo{
|
||||
WitnessScript: toRemoteScriptTree.SettleLeaf.Script,
|
||||
PkScript: toRemotePkScript,
|
||||
ScriptTree: toRemoteScriptTree.TapscriptTree,
|
||||
}, 1, nil
|
||||
return toRemoteScriptTree, 1, nil
|
||||
|
||||
// If this channel type has anchors, we derive the delayed to_remote
|
||||
// script.
|
||||
|
@ -349,8 +341,8 @@ func CommitScriptToRemote(chanType channeldb.ChannelType, initiator bool,
|
|||
return nil, 0, err
|
||||
}
|
||||
|
||||
return &ScriptInfo{
|
||||
PkScript: p2wsh,
|
||||
return &WitnessScriptDesc{
|
||||
OutputScript: p2wsh,
|
||||
WitnessScript: script,
|
||||
}, 1, nil
|
||||
|
||||
|
@ -363,9 +355,9 @@ func CommitScriptToRemote(chanType channeldb.ChannelType, initiator bool,
|
|||
|
||||
// Since this is a regular P2WKH, the WitnessScipt and PkScript
|
||||
// should both be set to the script hash.
|
||||
return &ScriptInfo{
|
||||
return &WitnessScriptDesc{
|
||||
OutputScript: p2wkh,
|
||||
WitnessScript: p2wkh,
|
||||
PkScript: p2wkh,
|
||||
}, 0, nil
|
||||
}
|
||||
}
|
||||
|
@ -417,59 +409,48 @@ func HtlcSecondLevelInputSequence(chanType channeldb.ChannelType) uint32 {
|
|||
// we are generating the to_local script for.
|
||||
func SecondLevelHtlcScript(chanType channeldb.ChannelType, initiator bool,
|
||||
revocationKey, delayKey *btcec.PublicKey,
|
||||
csvDelay, leaseExpiry uint32) (*ScriptInfo, error) {
|
||||
csvDelay, leaseExpiry uint32) (input.ScriptDescriptor, error) {
|
||||
|
||||
var (
|
||||
witnessScript []byte
|
||||
pkScript []byte
|
||||
err error
|
||||
)
|
||||
switch {
|
||||
// For taproot channels, the pkScript is a segwit v1 p2tr output.
|
||||
case chanType.IsTaproot():
|
||||
taprootOutputKey, err := input.TaprootSecondLevelHtlcScript(
|
||||
return input.TaprootSecondLevelScriptTree(
|
||||
revocationKey, delayKey, csvDelay,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pkScript, err = input.PayToTaprootScript(taprootOutputKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 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(
|
||||
witnessScript, err := input.LeaseSecondLevelHtlcScript(
|
||||
revocationKey, delayKey, csvDelay, leaseExpiry,
|
||||
)
|
||||
|
||||
pkScript, err = input.WitnessScriptHash(witnessScript)
|
||||
pkScript, err := input.WitnessScriptHash(witnessScript)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &WitnessScriptDesc{
|
||||
OutputScript: pkScript,
|
||||
WitnessScript: witnessScript,
|
||||
}, nil
|
||||
|
||||
default:
|
||||
witnessScript, err = input.SecondLevelHtlcScript(
|
||||
witnessScript, err := input.SecondLevelHtlcScript(
|
||||
revocationKey, delayKey, csvDelay,
|
||||
)
|
||||
|
||||
pkScript, err = input.WitnessScriptHash(witnessScript)
|
||||
pkScript, err := input.WitnessScriptHash(witnessScript)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ScriptInfo{
|
||||
PkScript: pkScript,
|
||||
WitnessScript: witnessScript,
|
||||
}, nil
|
||||
return &WitnessScriptDesc{
|
||||
OutputScript: pkScript,
|
||||
WitnessScript: witnessScript,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// CommitWeight returns the base commitment weight before adding HTLCs.
|
||||
|
@ -529,11 +510,14 @@ func HtlcSuccessFee(chanType channeldb.ChannelType,
|
|||
// anchor.
|
||||
func CommitScriptAnchors(chanType channeldb.ChannelType,
|
||||
localChanCfg, remoteChanCfg *channeldb.ChannelConfig,
|
||||
keyRing *CommitmentKeyRing) (*ScriptInfo, *ScriptInfo, error) {
|
||||
keyRing *CommitmentKeyRing) (
|
||||
input.ScriptDescriptor, input.ScriptDescriptor, error) {
|
||||
|
||||
var (
|
||||
anchorScript func(key *btcec.PublicKey) (*ScriptInfo, error)
|
||||
keySelector func(*channeldb.ChannelConfig,
|
||||
anchorScript func(key *btcec.PublicKey) (
|
||||
input.ScriptDescriptor, error)
|
||||
|
||||
keySelector func(*channeldb.ChannelConfig,
|
||||
bool) *btcec.PublicKey
|
||||
)
|
||||
|
||||
|
@ -542,25 +526,11 @@ func CommitScriptAnchors(chanType channeldb.ChannelType,
|
|||
// level key is now the (relative) local delay and remote public key,
|
||||
// since these are fully revealed once the commitment hits the chain.
|
||||
case chanType.IsTaproot():
|
||||
anchorScript = func(key *btcec.PublicKey) (*ScriptInfo, error) {
|
||||
anchorScriptTree, err := input.NewAnchorScriptTree(
|
||||
anchorScript = func(key *btcec.PublicKey,
|
||||
) (input.ScriptDescriptor, error) {
|
||||
return input.NewAnchorScriptTree(
|
||||
key,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
anchorPkScript, err := input.PayToTaprootScript(
|
||||
anchorScriptTree.TaprootKey,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ScriptInfo{
|
||||
PkScript: anchorPkScript,
|
||||
ScriptTree: anchorScriptTree.TapscriptTree,
|
||||
}, nil
|
||||
}
|
||||
|
||||
keySelector = func(cfg *channeldb.ChannelConfig,
|
||||
|
@ -578,7 +548,9 @@ func CommitScriptAnchors(chanType channeldb.ChannelType,
|
|||
default:
|
||||
// For normal channels, we'll create a p2wsh script based on
|
||||
// the target key.
|
||||
anchorScript = func(key *btcec.PublicKey) (*ScriptInfo, error) {
|
||||
anchorScript = func(key *btcec.PublicKey,
|
||||
) (input.ScriptDescriptor, error) {
|
||||
|
||||
script, err := input.CommitScriptAnchor(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -589,8 +561,8 @@ func CommitScriptAnchors(chanType channeldb.ChannelType,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return &ScriptInfo{
|
||||
PkScript: scriptHash,
|
||||
return &WitnessScriptDesc{
|
||||
OutputScript: scriptHash,
|
||||
WitnessScript: script,
|
||||
}, nil
|
||||
}
|
||||
|
@ -924,7 +896,7 @@ func CreateCommitTx(chanType channeldb.ChannelType,
|
|||
localOutput := amountToLocal >= localChanCfg.DustLimit
|
||||
if localOutput {
|
||||
commitTx.AddTxOut(&wire.TxOut{
|
||||
PkScript: toLocalScript.PkScript,
|
||||
PkScript: toLocalScript.PkScript(),
|
||||
Value: int64(amountToLocal),
|
||||
})
|
||||
}
|
||||
|
@ -932,7 +904,7 @@ func CreateCommitTx(chanType channeldb.ChannelType,
|
|||
remoteOutput := amountToRemote >= localChanCfg.DustLimit
|
||||
if remoteOutput {
|
||||
commitTx.AddTxOut(&wire.TxOut{
|
||||
PkScript: toRemoteScript.PkScript,
|
||||
PkScript: toRemoteScript.PkScript(),
|
||||
Value: int64(amountToRemote),
|
||||
})
|
||||
}
|
||||
|
@ -950,7 +922,7 @@ func CreateCommitTx(chanType channeldb.ChannelType,
|
|||
// or there are HTLCs.
|
||||
if localOutput || numHTLCs > 0 {
|
||||
commitTx.AddTxOut(&wire.TxOut{
|
||||
PkScript: localAnchor.PkScript,
|
||||
PkScript: localAnchor.PkScript(),
|
||||
Value: int64(anchorSize),
|
||||
})
|
||||
}
|
||||
|
@ -959,7 +931,7 @@ func CreateCommitTx(chanType channeldb.ChannelType,
|
|||
// output or there are HTLCs.
|
||||
if remoteOutput || numHTLCs > 0 {
|
||||
commitTx.AddTxOut(&wire.TxOut{
|
||||
PkScript: remoteAnchor.PkScript,
|
||||
PkScript: remoteAnchor.PkScript(),
|
||||
Value: int64(anchorSize),
|
||||
})
|
||||
}
|
||||
|
@ -1013,7 +985,7 @@ func CoopCloseBalance(chanType channeldb.ChannelType, isInitiator bool,
|
|||
// channel.
|
||||
func genSegwitV0HtlcScript(chanType channeldb.ChannelType,
|
||||
isIncoming, ourCommit bool, timeout uint32, rHash [32]byte,
|
||||
keyRing *CommitmentKeyRing) (*ScriptInfo, error) {
|
||||
keyRing *CommitmentKeyRing) (*WitnessScriptDesc, error) {
|
||||
|
||||
var (
|
||||
witnessScript []byte
|
||||
|
@ -1077,8 +1049,8 @@ func genSegwitV0HtlcScript(chanType channeldb.ChannelType,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return &ScriptInfo{
|
||||
PkScript: htlcP2WSH,
|
||||
return &WitnessScriptDesc{
|
||||
OutputScript: htlcP2WSH,
|
||||
WitnessScript: witnessScript,
|
||||
}, nil
|
||||
}
|
||||
|
@ -1086,12 +1058,12 @@ func genSegwitV0HtlcScript(chanType channeldb.ChannelType,
|
|||
// genTaprootHtlcScript generates the HTLC scripts for a taproot+musig2
|
||||
// channel.
|
||||
func genTaprootHtlcScript(isIncoming, ourCommit bool, timeout uint32,
|
||||
rHash [32]byte, keyRing *CommitmentKeyRing) (*ScriptInfo, error) {
|
||||
rHash [32]byte,
|
||||
keyRing *CommitmentKeyRing) (*input.HtlcScriptTree, error) {
|
||||
|
||||
var (
|
||||
taprootKey *btcec.PublicKey
|
||||
secondLevelScript []byte
|
||||
tapScriptTree *txscript.IndexedTapScriptTree
|
||||
htlcScriptTree *input.HtlcScriptTree
|
||||
err error
|
||||
)
|
||||
|
||||
// Generate the proper redeem scripts for the HTLC output modified by
|
||||
|
@ -1102,127 +1074,68 @@ func genTaprootHtlcScript(isIncoming, ourCommit bool, timeout uint32,
|
|||
// transaction. So we need to use the receiver's version of HTLC the
|
||||
// script.
|
||||
case isIncoming && ourCommit:
|
||||
scriptTree, err := input.ReceiverHTLCScriptTaproot(
|
||||
htlcScriptTree, err = input.ReceiverHTLCScriptTaproot(
|
||||
timeout, keyRing.RemoteHtlcKey, keyRing.LocalHtlcKey,
|
||||
keyRing.RevocationKey, rHash[:],
|
||||
keyRing.RevocationKey, rHash[:], ourCommit,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tapScriptTree = scriptTree.TapscriptTree
|
||||
taprootKey = scriptTree.TaprootKey
|
||||
|
||||
// As this is an HTLC on our commitment transaction, the second
|
||||
// level path we care about here is the success path.
|
||||
// Therefore, we'll grab the tapLeaf corresponding to the
|
||||
// success path.
|
||||
secondLevelScript = scriptTree.SuccessTapLeaf.Script
|
||||
|
||||
// We're being paid via an HTLC by the remote party, and the HTLC is
|
||||
// being added to their commitment transaction, so we use the sender's
|
||||
// version of the HTLC script.
|
||||
case isIncoming && !ourCommit:
|
||||
scriptTree, err := input.SenderHTLCScriptTaproot(
|
||||
htlcScriptTree, err = input.SenderHTLCScriptTaproot(
|
||||
keyRing.RemoteHtlcKey, keyRing.LocalHtlcKey,
|
||||
keyRing.RevocationKey, rHash[:],
|
||||
keyRing.RevocationKey, rHash[:], ourCommit,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tapScriptTree = scriptTree.TapscriptTree
|
||||
taprootKey = scriptTree.TaprootKey
|
||||
|
||||
// In this case, this is an incoming HTLC on the commitment
|
||||
// transaction of the remote party, so we'll return the timeout
|
||||
// tapleaf since that's the second level spend they need in the
|
||||
// case of a broadcast.
|
||||
secondLevelScript = scriptTree.TimeoutTapLeaf.Script
|
||||
|
||||
// We're sending an HTLC which is being added to our commitment
|
||||
// transaction. Therefore, we need to use the sender's version of the
|
||||
// HTLC script.
|
||||
case !isIncoming && ourCommit:
|
||||
scriptTree, err := input.SenderHTLCScriptTaproot(
|
||||
htlcScriptTree, err = input.SenderHTLCScriptTaproot(
|
||||
keyRing.LocalHtlcKey, keyRing.RemoteHtlcKey,
|
||||
keyRing.RevocationKey, rHash[:],
|
||||
keyRing.RevocationKey, rHash[:], ourCommit,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tapScriptTree = scriptTree.TapscriptTree
|
||||
taprootKey = scriptTree.TaprootKey
|
||||
|
||||
// This is an outgoing HTLC on our commitment transaction, so
|
||||
// we need to be able to generate/verify signatures for the
|
||||
// timeout path.
|
||||
secondLevelScript = scriptTree.TimeoutTapLeaf.Script
|
||||
|
||||
// Finally, we're paying the remote party via an HTLC, which is being
|
||||
// added to their commitment transaction. Therefore, we use the
|
||||
// receiver's version of the HTLC script.
|
||||
case !isIncoming && !ourCommit:
|
||||
scriptTree, err := input.ReceiverHTLCScriptTaproot(
|
||||
htlcScriptTree, err = input.ReceiverHTLCScriptTaproot(
|
||||
timeout, keyRing.LocalHtlcKey, keyRing.RemoteHtlcKey,
|
||||
keyRing.RevocationKey, rHash[:],
|
||||
keyRing.RevocationKey, rHash[:], ourCommit,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tapScriptTree = scriptTree.TapscriptTree
|
||||
taprootKey = scriptTree.TaprootKey
|
||||
|
||||
// This is an outgoing HTLC on the remote party's commitment
|
||||
// transaction. In this case if they go on chain, they'll need
|
||||
// the second level success spend, so we grab that tapscript
|
||||
// path.
|
||||
secondLevelScript = scriptTree.SuccessTapLeaf.Script
|
||||
}
|
||||
|
||||
// Now that we have the redeem scripts, create the P2TR public key
|
||||
// script for the output itself.
|
||||
p2trOutput, err := input.PayToTaprootScript(taprootKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ScriptInfo{
|
||||
PkScript: p2trOutput,
|
||||
WitnessScript: secondLevelScript,
|
||||
ScriptTree: tapScriptTree,
|
||||
}, nil
|
||||
return htlcScriptTree, nil
|
||||
}
|
||||
|
||||
// genHtlcScript generates the proper P2WSH public key scripts for the HTLC
|
||||
// output modified by two-bits denoting if this is an incoming HTLC, and if the
|
||||
// HTLC is being applied to their commitment transaction or ours.
|
||||
// HTLC is being applied to their commitment transaction or ours. A script
|
||||
// multiplexer for the various spending paths is returned. The script path that
|
||||
// we need to sign for the remote party (2nd level HTLCs) is also returned
|
||||
// along side the multiplexer.
|
||||
func genHtlcScript(chanType channeldb.ChannelType, isIncoming, ourCommit bool,
|
||||
timeout uint32, rHash [32]byte,
|
||||
keyRing *CommitmentKeyRing) (*ScriptInfo, error) {
|
||||
|
||||
var (
|
||||
scriptInfo *ScriptInfo
|
||||
err error
|
||||
)
|
||||
timeout uint32, rHash [32]byte, keyRing *CommitmentKeyRing,
|
||||
) (input.ScriptDescriptor, error) {
|
||||
|
||||
if !chanType.IsTaproot() {
|
||||
scriptInfo, err = genSegwitV0HtlcScript(
|
||||
return genSegwitV0HtlcScript(
|
||||
chanType, isIncoming, ourCommit, timeout, rHash,
|
||||
keyRing,
|
||||
)
|
||||
} else {
|
||||
scriptInfo, err = genTaprootHtlcScript(
|
||||
return genTaprootHtlcScript(
|
||||
isIncoming, ourCommit, timeout, rHash, keyRing,
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return scriptInfo, nil
|
||||
}
|
||||
|
||||
// addHTLC adds a new HTLC to the passed commitment transaction. One of four
|
||||
|
@ -1246,21 +1159,22 @@ func addHTLC(commitTx *wire.MsgTx, ourCommit bool,
|
|||
return err
|
||||
}
|
||||
|
||||
witnessProgram := scriptInfo.PkScript
|
||||
witnessScript := scriptInfo.WitnessScript
|
||||
pkScript := scriptInfo.PkScript()
|
||||
|
||||
// Add the new HTLC outputs to the respective commitment transactions.
|
||||
amountPending := int64(paymentDesc.Amount.ToSatoshis())
|
||||
commitTx.AddTxOut(wire.NewTxOut(amountPending, witnessProgram))
|
||||
commitTx.AddTxOut(wire.NewTxOut(amountPending, pkScript))
|
||||
|
||||
// Store the pkScript of this particular PaymentDescriptor so we can
|
||||
// quickly locate it within the commitment transaction later.
|
||||
if ourCommit {
|
||||
paymentDesc.ourPkScript = witnessProgram
|
||||
paymentDesc.ourWitnessScript = witnessScript
|
||||
paymentDesc.ourPkScript = pkScript
|
||||
|
||||
paymentDesc.ourWitnessScript = scriptInfo.WitnessScriptToSign()
|
||||
} else {
|
||||
paymentDesc.theirPkScript = witnessProgram
|
||||
paymentDesc.theirWitnessScript = witnessScript
|
||||
paymentDesc.theirPkScript = pkScript
|
||||
|
||||
paymentDesc.theirWitnessScript = scriptInfo.WitnessScriptToSign()
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -1329,9 +1243,9 @@ func findOutputIndexesFromRemote(revocationPreimage *chainhash.Hash,
|
|||
// 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):
|
||||
case bytes.Equal(txOut.PkScript, ourScript.PkScript()):
|
||||
ourIndex = uint32(i)
|
||||
case bytes.Equal(txOut.PkScript, theirScript.PkScript):
|
||||
case bytes.Equal(txOut.PkScript, theirScript.PkScript()):
|
||||
theirIndex = uint32(i)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,8 +66,6 @@ func CreateHtlcSuccessTx(chanType channeldb.ChannelType, initiator bool,
|
|||
}
|
||||
successTx.AddTxIn(txin)
|
||||
|
||||
var pkScript []byte
|
||||
|
||||
// Next, we'll generate the script used as the output for all second
|
||||
// level HTLC which forces a covenant w.r.t what can be done with all
|
||||
// HTLC outputs.
|
||||
|
@ -79,13 +77,11 @@ func CreateHtlcSuccessTx(chanType channeldb.ChannelType, initiator bool,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
pkScript = scriptInfo.PkScript
|
||||
|
||||
// Finally, the output is simply the amount of the HTLC (minus the
|
||||
// required fees), paying to the timeout script.
|
||||
successTx.AddTxOut(&wire.TxOut{
|
||||
Value: int64(htlcAmt),
|
||||
PkScript: pkScript,
|
||||
PkScript: scriptInfo.PkScript(),
|
||||
})
|
||||
|
||||
return successTx, nil
|
||||
|
@ -133,8 +129,6 @@ func CreateHtlcTimeoutTx(chanType channeldb.ChannelType, initiator bool,
|
|||
}
|
||||
timeoutTx.AddTxIn(txin)
|
||||
|
||||
var pkScript []byte
|
||||
|
||||
// Next, we'll generate the script used as the output for all second
|
||||
// level HTLC which forces a covenant w.r.t what can be done with all
|
||||
// HTLC outputs.
|
||||
|
@ -146,13 +140,11 @@ func CreateHtlcTimeoutTx(chanType channeldb.ChannelType, initiator bool,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
pkScript = scriptInfo.PkScript
|
||||
|
||||
// Finally, the output is simply the amount of the HTLC (minus the
|
||||
// required fees), paying to the regular second level HTLC script.
|
||||
timeoutTx.AddTxOut(&wire.TxOut{
|
||||
Value: int64(htlcAmt),
|
||||
PkScript: pkScript,
|
||||
PkScript: scriptInfo.PkScript(),
|
||||
})
|
||||
|
||||
return timeoutTx, nil
|
||||
|
|
Loading…
Add table
Reference in a new issue