-
Notifications
You must be signed in to change notification settings - Fork 2.2k
input+lnwallet: update taproot scripts to accept optional aux leaf #8556
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ import ( | |
| "github.com/btcsuite/btcd/btcutil" | ||
| "github.com/btcsuite/btcd/txscript" | ||
| "github.com/btcsuite/btcd/wire" | ||
| "github.com/lightningnetwork/lnd/fn" | ||
| "github.com/lightningnetwork/lnd/lnutils" | ||
| "golang.org/x/crypto/ripemd160" | ||
| ) | ||
|
|
@@ -638,6 +639,12 @@ type HtlcScriptTree struct { | |
| // TimeoutTapLeaf is the tapleaf for the timeout path. | ||
| TimeoutTapLeaf txscript.TapLeaf | ||
|
|
||
| // AuxLeaf is an axuillaruy leaf that can be used to extend the base | ||
| // HTLC script tree with new spend paths, or just as extra commitment | ||
| // space. When present, this leaf will always be in the left-most or | ||
| // right-most area of the tapscript tree. | ||
| AuxLeaf fn.Option[txscript.TapLeaf] | ||
|
|
||
| htlcType htlcType | ||
| } | ||
|
|
||
|
|
@@ -714,7 +721,8 @@ var _ TapscriptDescriptor = (*HtlcScriptTree)(nil) | |
| // the HTLC key for HTLCs on the sender's commitment. | ||
| func senderHtlcTapScriptTree(senderHtlcKey, receiverHtlcKey, | ||
| revokeKey *btcec.PublicKey, payHash []byte, | ||
| hType htlcType) (*HtlcScriptTree, error) { | ||
| hType htlcType, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. micro-nit: looks like |
||
| auxLeaf fn.Option[txscript.TapLeaf]) (*HtlcScriptTree, error) { | ||
|
|
||
| // First, we'll obtain the tap leaves for both the success and timeout | ||
| // path. | ||
|
|
@@ -731,10 +739,15 @@ func senderHtlcTapScriptTree(senderHtlcKey, receiverHtlcKey, | |
| return nil, err | ||
| } | ||
|
|
||
| tapLeaves := []txscript.TapLeaf{successTapLeaf, timeoutTapLeaf} | ||
| auxLeaf.WhenSome(func(l txscript.TapLeaf) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it worth noting here that if there is an odd number of leaves, then the last one will always end up being a level higher than the other leaves. Which in our case works out to exactly what we want, with just three leaves. But the assumptions here (with using
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah this is a somewhat specific cut out that should work for our use case until there's some drastic overhaul of taproot chans. |
||
| tapLeaves = append(tapLeaves, l) | ||
| }) | ||
|
|
||
| // With the two leaves obtained, we'll now make the tapscript tree, | ||
| // then obtain the root from that | ||
| tapscriptTree := txscript.AssembleTaprootScriptTree( | ||
| successTapLeaf, timeoutTapLeaf, | ||
| tapLeaves..., | ||
| ) | ||
|
|
||
| tapScriptRoot := tapscriptTree.RootNode.TapHash() | ||
|
|
@@ -754,6 +767,7 @@ func senderHtlcTapScriptTree(senderHtlcKey, receiverHtlcKey, | |
| }, | ||
| SuccessTapLeaf: successTapLeaf, | ||
| TimeoutTapLeaf: timeoutTapLeaf, | ||
| AuxLeaf: auxLeaf, | ||
| htlcType: hType, | ||
| }, nil | ||
| } | ||
|
|
@@ -788,7 +802,8 @@ func senderHtlcTapScriptTree(senderHtlcKey, receiverHtlcKey, | |
| // unilaterally spend the created output. | ||
| func SenderHTLCScriptTaproot(senderHtlcKey, receiverHtlcKey, | ||
| revokeKey *btcec.PublicKey, payHash []byte, | ||
| localCommit bool) (*HtlcScriptTree, error) { | ||
| localCommit bool, | ||
| auxLeaf fn.Option[txscript.TapLeaf]) (*HtlcScriptTree, error) { | ||
|
|
||
| var hType htlcType | ||
| if localCommit { | ||
|
|
@@ -802,7 +817,7 @@ func SenderHTLCScriptTaproot(senderHtlcKey, receiverHtlcKey, | |
| // tap leaf paths. | ||
| return senderHtlcTapScriptTree( | ||
| senderHtlcKey, receiverHtlcKey, revokeKey, payHash, | ||
| hType, | ||
| hType, auxLeaf, | ||
| ) | ||
| } | ||
|
|
||
|
|
@@ -1273,7 +1288,8 @@ 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, hType htlcType) (*HtlcScriptTree, error) { | ||
| cltvExpiry uint32, hType htlcType, | ||
| auxLeaf fn.Option[txscript.TapLeaf]) (*HtlcScriptTree, error) { | ||
|
|
||
| // First, we'll obtain the tap leaves for both the success and timeout | ||
| // path. | ||
|
|
@@ -1290,10 +1306,15 @@ func receiverHtlcTapScriptTree(senderHtlcKey, receiverHtlcKey, | |
| return nil, err | ||
| } | ||
|
|
||
| tapLeaves := []txscript.TapLeaf{timeoutTapLeaf, successTapLeaf} | ||
| auxLeaf.WhenSome(func(l txscript.TapLeaf) { | ||
| tapLeaves = append(tapLeaves, l) | ||
| }) | ||
|
|
||
| // With the two leaves obtained, we'll now make the tapscript tree, | ||
| // then obtain the root from that | ||
| tapscriptTree := txscript.AssembleTaprootScriptTree( | ||
| timeoutTapLeaf, successTapLeaf, | ||
| tapLeaves..., | ||
| ) | ||
|
|
||
| tapScriptRoot := tapscriptTree.RootNode.TapHash() | ||
|
|
@@ -1313,6 +1334,7 @@ func receiverHtlcTapScriptTree(senderHtlcKey, receiverHtlcKey, | |
| }, | ||
| SuccessTapLeaf: successTapLeaf, | ||
| TimeoutTapLeaf: timeoutTapLeaf, | ||
| AuxLeaf: auxLeaf, | ||
| htlcType: hType, | ||
| }, nil | ||
| } | ||
|
|
@@ -1347,7 +1369,8 @@ func receiverHtlcTapScriptTree(senderHtlcKey, receiverHtlcKey, | |
| // the tap leaf are returned. | ||
| func ReceiverHTLCScriptTaproot(cltvExpiry uint32, | ||
| senderHtlcKey, receiverHtlcKey, revocationKey *btcec.PublicKey, | ||
| payHash []byte, ourCommit bool) (*HtlcScriptTree, error) { | ||
| payHash []byte, ourCommit bool, | ||
| auxLeaf fn.Option[txscript.TapLeaf]) (*HtlcScriptTree, error) { | ||
|
|
||
| var hType htlcType | ||
| if ourCommit { | ||
|
|
@@ -1361,7 +1384,7 @@ func ReceiverHTLCScriptTaproot(cltvExpiry uint32, | |
| // tap leaf paths. | ||
| return receiverHtlcTapScriptTree( | ||
| senderHtlcKey, receiverHtlcKey, revocationKey, payHash, | ||
| cltvExpiry, hType, | ||
| cltvExpiry, hType, auxLeaf, | ||
| ) | ||
| } | ||
|
|
||
|
|
@@ -1592,7 +1615,8 @@ func TaprootSecondLevelTapLeaf(delayKey *btcec.PublicKey, | |
| // SecondLevelHtlcTapscriptTree construct the indexed tapscript tree needed to | ||
| // generate the taptweak to create the final output and also control block. | ||
| func SecondLevelHtlcTapscriptTree(delayKey *btcec.PublicKey, | ||
| csvDelay uint32) (*txscript.IndexedTapScriptTree, error) { | ||
| csvDelay uint32, | ||
| auxLeaf fn.Option[txscript.TapLeaf]) (*txscript.IndexedTapScriptTree, error) { | ||
|
|
||
| // First grab the second level leaf script we need to create the top | ||
| // level output. | ||
|
|
@@ -1601,9 +1625,14 @@ func SecondLevelHtlcTapscriptTree(delayKey *btcec.PublicKey, | |
| return nil, err | ||
| } | ||
|
|
||
| tapLeaves := []txscript.TapLeaf{secondLevelTapLeaf} | ||
| auxLeaf.WhenSome(func(l txscript.TapLeaf) { | ||
| tapLeaves = append(tapLeaves, l) | ||
| }) | ||
|
|
||
| // Now that we have the sole second level script, we can create the | ||
| // tapscript tree that commits to both the leaves. | ||
| return txscript.AssembleTaprootScriptTree(secondLevelTapLeaf), nil | ||
| return txscript.AssembleTaprootScriptTree(tapLeaves...), nil | ||
| } | ||
|
|
||
| // TaprootSecondLevelHtlcScript is the uniform script that's used as the output | ||
|
|
@@ -1623,12 +1652,13 @@ func SecondLevelHtlcTapscriptTree(delayKey *btcec.PublicKey, | |
| // | ||
| // The keyspend path require knowledge of the top level revocation private key. | ||
| func TaprootSecondLevelHtlcScript(revokeKey, delayKey *btcec.PublicKey, | ||
| csvDelay uint32) (*btcec.PublicKey, error) { | ||
| csvDelay uint32, | ||
| auxLeaf fn.Option[txscript.TapLeaf]) (*btcec.PublicKey, error) { | ||
|
|
||
| // First, we'll make the tapscript tree that commits to the redemption | ||
| // path. | ||
| tapScriptTree, err := SecondLevelHtlcTapscriptTree( | ||
| delayKey, csvDelay, | ||
| delayKey, csvDelay, auxLeaf, | ||
| ) | ||
| if err != nil { | ||
| return nil, err | ||
|
|
@@ -1653,17 +1683,22 @@ type SecondLevelScriptTree struct { | |
|
|
||
| // SuccessTapLeaf is the tapleaf for the redemption path. | ||
| SuccessTapLeaf txscript.TapLeaf | ||
|
|
||
| // AuxLeaf is an optional leaf that can be used to extend the script | ||
| // tree. | ||
| AuxLeaf fn.Option[txscript.TapLeaf] | ||
| } | ||
|
|
||
| // TaprootSecondLevelScriptTree constructs the tapscript tree used to spend the | ||
| // second level HTLC output. | ||
| func TaprootSecondLevelScriptTree(revokeKey, delayKey *btcec.PublicKey, | ||
| csvDelay uint32) (*SecondLevelScriptTree, error) { | ||
| csvDelay uint32, | ||
| auxLeaf fn.Option[txscript.TapLeaf]) (*SecondLevelScriptTree, error) { | ||
|
|
||
| // First, we'll make the tapscript tree that commits to the redemption | ||
| // path. | ||
| tapScriptTree, err := SecondLevelHtlcTapscriptTree( | ||
| delayKey, csvDelay, | ||
| delayKey, csvDelay, auxLeaf, | ||
| ) | ||
| if err != nil { | ||
| return nil, err | ||
|
|
@@ -1684,6 +1719,7 @@ func TaprootSecondLevelScriptTree(revokeKey, delayKey *btcec.PublicKey, | |
| InternalKey: revokeKey, | ||
| }, | ||
| SuccessTapLeaf: tapScriptTree.LeafMerkleProofs[0].TapLeaf, | ||
| AuxLeaf: auxLeaf, | ||
| }, nil | ||
| } | ||
|
|
||
|
|
@@ -2060,6 +2096,14 @@ type CommitScriptTree struct { | |
| // RevocationLeaf is the leaf used to spend the output with the | ||
| // revocation key signature. | ||
| RevocationLeaf txscript.TapLeaf | ||
|
|
||
| // AuxLeaf is an axuillaruy leaf that can be used to extend the base | ||
| // commitment script tree with new spend paths, or just as extra | ||
| // commitment space. When present, this leaf will always be in the | ||
| // left-most or right-most area of the tapscript tree. | ||
| // | ||
| // TODO(roasbeeF): need to ensure commitment position... | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this TODO related to my comment above? About relying on the logic in
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, but as it it works since here we have it be that odd element out, which'll give it the top right position in the tree. |
||
| AuxLeaf fn.Option[txscript.TapLeaf] | ||
| } | ||
|
|
||
| // A compile time check to ensure CommitScriptTree implements the | ||
|
|
@@ -2120,7 +2164,8 @@ func (c *CommitScriptTree) CtrlBlockForPath(path ScriptPath, | |
| // NewLocalCommitScriptTree returns a new CommitScript tree that can be used to | ||
| // create and spend the commitment output for the local party. | ||
| func NewLocalCommitScriptTree(csvTimeout uint32, | ||
| selfKey, revokeKey *btcec.PublicKey) (*CommitScriptTree, error) { | ||
| selfKey, revokeKey *btcec.PublicKey, | ||
| auxLeaf fn.Option[txscript.TapLeaf]) (*CommitScriptTree, error) { | ||
|
|
||
| // First, we'll need to construct the tapLeaf that'll be our delay CSV | ||
| // clause. | ||
|
|
@@ -2140,8 +2185,14 @@ func NewLocalCommitScriptTree(csvTimeout uint32, | |
| // the two leaves, and then obtain a root from that. | ||
| delayTapLeaf := txscript.NewBaseTapLeaf(delayScript) | ||
| revokeTapLeaf := txscript.NewBaseTapLeaf(revokeScript) | ||
|
|
||
| tapLeaves := []txscript.TapLeaf{delayTapLeaf, revokeTapLeaf} | ||
| auxLeaf.WhenSome(func(l txscript.TapLeaf) { | ||
| tapLeaves = append(tapLeaves, l) | ||
| }) | ||
|
|
||
| tapScriptTree := txscript.AssembleTaprootScriptTree( | ||
| delayTapLeaf, revokeTapLeaf, | ||
| tapLeaves..., | ||
| ) | ||
| tapScriptRoot := tapScriptTree.RootNode.TapHash() | ||
|
|
||
|
|
@@ -2160,6 +2211,7 @@ func NewLocalCommitScriptTree(csvTimeout uint32, | |
| }, | ||
| SettleLeaf: delayTapLeaf, | ||
| RevocationLeaf: revokeTapLeaf, | ||
| AuxLeaf: auxLeaf, | ||
| }, nil | ||
| } | ||
|
|
||
|
|
@@ -2229,7 +2281,7 @@ func TaprootCommitScriptToSelf(csvTimeout uint32, | |
| selfKey, revokeKey *btcec.PublicKey) (*btcec.PublicKey, error) { | ||
|
|
||
| commitScriptTree, err := NewLocalCommitScriptTree( | ||
| csvTimeout, selfKey, revokeKey, | ||
| csvTimeout, selfKey, revokeKey, fn.None[txscript.TapLeaf](), | ||
| ) | ||
| if err != nil { | ||
| return nil, err | ||
|
|
@@ -2558,7 +2610,7 @@ func CommitScriptToRemoteConfirmed(key *btcec.PublicKey) ([]byte, error) { | |
| // NewRemoteCommitScriptTree constructs a new script tree for the remote party | ||
| // to sweep their funds after a hard coded 1 block delay. | ||
| func NewRemoteCommitScriptTree(remoteKey *btcec.PublicKey, | ||
| ) (*CommitScriptTree, error) { | ||
| auxLeaf fn.Option[txscript.TapLeaf]) (*CommitScriptTree, error) { | ||
|
|
||
| // First, construct the remote party's tapscript they'll use to sweep | ||
| // their outputs. | ||
|
|
@@ -2574,10 +2626,16 @@ func NewRemoteCommitScriptTree(remoteKey *btcec.PublicKey, | |
| return nil, err | ||
| } | ||
|
|
||
| tapLeaf := txscript.NewBaseTapLeaf(remoteScript) | ||
|
|
||
| tapLeaves := []txscript.TapLeaf{tapLeaf} | ||
| auxLeaf.WhenSome(func(l txscript.TapLeaf) { | ||
| tapLeaves = append(tapLeaves, l) | ||
| }) | ||
|
|
||
| // With this script constructed, we'll map that into a tapLeaf, then | ||
| // make a new tapscript root from that. | ||
| tapLeaf := txscript.NewBaseTapLeaf(remoteScript) | ||
| tapScriptTree := txscript.AssembleTaprootScriptTree(tapLeaf) | ||
| tapScriptTree := txscript.AssembleTaprootScriptTree(tapLeaves...) | ||
| tapScriptRoot := tapScriptTree.RootNode.TapHash() | ||
|
|
||
| // Now that we have our root, we can arrive at the final output script | ||
|
|
@@ -2594,6 +2652,7 @@ func NewRemoteCommitScriptTree(remoteKey *btcec.PublicKey, | |
| InternalKey: &TaprootNUMSKey, | ||
| }, | ||
| SettleLeaf: tapLeaf, | ||
| AuxLeaf: auxLeaf, | ||
| }, nil | ||
| } | ||
|
|
||
|
|
@@ -2610,9 +2669,9 @@ func NewRemoteCommitScriptTree(remoteKey *btcec.PublicKey, | |
| // <remotepubkey> OP_CHECKSIG | ||
| // 1 OP_CHECKSEQUENCEVERIFY OP_DROP | ||
| func TaprootCommitScriptToRemote(remoteKey *btcec.PublicKey, | ||
| ) (*btcec.PublicKey, error) { | ||
| auxLeaf fn.Option[txscript.TapLeaf]) (*btcec.PublicKey, error) { | ||
|
|
||
| commitScriptTree, err := NewRemoteCommitScriptTree(remoteKey) | ||
| commitScriptTree, err := NewRemoteCommitScriptTree(remoteKey, auxLeaf) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in order to be open-ended with regards to flexibility, couldn't we have multiple aux leaves here? something like
fn.Slicewe could hide everything under a single
AuxLeafbut I think just having multiple leaves will keep it simpler overall on the script level itself