input+lnwallet: update taproot scripts to accept optional aux leaf

In this commit, we update all the taproot scripts to also accept an
optional aux leaf. This aux leaf can be used to add more redemption
paths for advanced channels, or just as an extra commitment space.
This commit is contained in:
Olaoluwa Osuntokun
2024-03-15 11:43:21 -04:00
committed by Oliver Gugger
parent 8588c9bfd7
commit 1aae47fd71
11 changed files with 273 additions and 108 deletions

View File

@@ -1,13 +1,16 @@
package input
import (
"bytes"
"crypto/rand"
"fmt"
"testing"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/stretchr/testify/require"
@@ -31,7 +34,9 @@ type testSenderHtlcScriptTree struct {
htlcAmt int64
}
func newTestSenderHtlcScriptTree(t *testing.T) *testSenderHtlcScriptTree {
func newTestSenderHtlcScriptTree(t *testing.T,
auxLeaf AuxTapLeaf) *testSenderHtlcScriptTree {
var preImage lntypes.Preimage
_, err := rand.Read(preImage[:])
require.NoError(t, err)
@@ -48,7 +53,7 @@ func newTestSenderHtlcScriptTree(t *testing.T) *testSenderHtlcScriptTree {
payHash := preImage.Hash()
htlcScriptTree, err := SenderHTLCScriptTaproot(
senderKey.PubKey(), receiverKey.PubKey(), revokeKey.PubKey(),
payHash[:], lntypes.Remote,
payHash[:], lntypes.Remote, auxLeaf,
)
require.NoError(t, err)
@@ -207,13 +212,9 @@ func htlcSenderTimeoutWitnessGen(sigHash txscript.SigHashType,
}
}
// TestTaprootSenderHtlcSpend tests that all the positive and negative paths
// for the sender HTLC tapscript tree work as expected.
func TestTaprootSenderHtlcSpend(t *testing.T) {
t.Parallel()
func testTaprootSenderHtlcSpend(t *testing.T, auxLeaf AuxTapLeaf) {
// First, create a new test script tree.
htlcScriptTree := newTestSenderHtlcScriptTree(t)
htlcScriptTree := newTestSenderHtlcScriptTree(t, auxLeaf)
spendTx := wire.NewMsgTx(2)
spendTx.AddTxIn(&wire.TxIn{})
@@ -432,6 +433,26 @@ func TestTaprootSenderHtlcSpend(t *testing.T) {
}
}
// TestTaprootSenderHtlcSpend tests that all the positive and negative paths
// for the sender HTLC tapscript tree work as expected.
func TestTaprootSenderHtlcSpend(t *testing.T) {
t.Parallel()
for _, hasAuxLeaf := range []bool{true, false} {
name := fmt.Sprintf("aux_leaf=%v", hasAuxLeaf)
t.Run(name, func(t *testing.T) {
var auxLeaf AuxTapLeaf
if hasAuxLeaf {
auxLeaf = fn.Some(txscript.NewBaseTapLeaf(
bytes.Repeat([]byte{0x01}, 32),
))
}
testTaprootSenderHtlcSpend(t, auxLeaf)
})
}
}
type testReceiverHtlcScriptTree struct {
preImage lntypes.Preimage
@@ -452,7 +473,9 @@ type testReceiverHtlcScriptTree struct {
lockTime int32
}
func newTestReceiverHtlcScriptTree(t *testing.T) *testReceiverHtlcScriptTree {
func newTestReceiverHtlcScriptTree(t *testing.T,
auxLeaf AuxTapLeaf) *testReceiverHtlcScriptTree {
var preImage lntypes.Preimage
_, err := rand.Read(preImage[:])
require.NoError(t, err)
@@ -471,7 +494,7 @@ func newTestReceiverHtlcScriptTree(t *testing.T) *testReceiverHtlcScriptTree {
payHash := preImage.Hash()
htlcScriptTree, err := ReceiverHTLCScriptTaproot(
cltvExpiry, senderKey.PubKey(), receiverKey.PubKey(),
revokeKey.PubKey(), payHash[:], lntypes.Remote,
revokeKey.PubKey(), payHash[:], lntypes.Remote, auxLeaf,
)
require.NoError(t, err)
@@ -629,15 +652,11 @@ func htlcReceiverSuccessWitnessGen(sigHash txscript.SigHashType,
}
}
// TestTaprootReceiverHtlcSpend tests that all possible paths for redeeming an
// accepted HTLC (on the commitment transaction) of the receiver work properly.
func TestTaprootReceiverHtlcSpend(t *testing.T) {
t.Parallel()
func testTaprootReceiverHtlcSpend(t *testing.T, auxLeaf AuxTapLeaf) {
// We'll start by creating the HTLC script tree (contains all 3 valid
// spend paths), and also a mock spend transaction that we'll be
// signing below.
htlcScriptTree := newTestReceiverHtlcScriptTree(t)
htlcScriptTree := newTestReceiverHtlcScriptTree(t, auxLeaf)
// TODO(roasbeef): issue with revoke key??? ctrl block even/odd
@@ -891,6 +910,28 @@ func TestTaprootReceiverHtlcSpend(t *testing.T) {
}
}
// TestTaprootReceiverHtlcSpend tests that all possible paths for redeeming an
// accepted HTLC (on the commitment transaction) of the receiver work properly.
func TestTaprootReceiverHtlcSpend(t *testing.T) {
t.Parallel()
for _, hasAuxLeaf := range []bool{true, false} {
name := fmt.Sprintf("aux_leaf=%v", hasAuxLeaf)
t.Run(name, func(t *testing.T) {
var auxLeaf AuxTapLeaf
if hasAuxLeaf {
auxLeaf = fn.Some(
txscript.NewBaseTapLeaf(
bytes.Repeat([]byte{0x01}, 32),
),
)
}
testTaprootReceiverHtlcSpend(t, auxLeaf)
})
}
}
type testCommitScriptTree struct {
csvDelay uint32
@@ -905,7 +946,9 @@ type testCommitScriptTree struct {
*CommitScriptTree
}
func newTestCommitScriptTree(local bool) (*testCommitScriptTree, error) {
func newTestCommitScriptTree(local bool,
auxLeaf AuxTapLeaf) (*testCommitScriptTree, error) {
selfKey, err := btcec.NewPrivateKey()
if err != nil {
return nil, err
@@ -925,10 +968,11 @@ func newTestCommitScriptTree(local bool) (*testCommitScriptTree, error) {
if local {
commitScriptTree, err = NewLocalCommitScriptTree(
csvDelay, selfKey.PubKey(), revokeKey.PubKey(),
auxLeaf,
)
} else {
commitScriptTree, err = NewRemoteCommitScriptTree(
selfKey.PubKey(),
selfKey.PubKey(), auxLeaf,
)
}
if err != nil {
@@ -1020,12 +1064,8 @@ func localCommitRevokeWitGen(sigHash txscript.SigHashType,
}
}
// TestTaprootCommitScriptToSelf tests that the taproot script for redeeming
// one's output after a force close behaves as expected.
func TestTaprootCommitScriptToSelf(t *testing.T) {
t.Parallel()
commitScriptTree, err := newTestCommitScriptTree(true)
func testTaprootCommitScriptToSelf(t *testing.T, auxLeaf AuxTapLeaf) {
commitScriptTree, err := newTestCommitScriptTree(true, auxLeaf)
require.NoError(t, err)
spendTx := wire.NewMsgTx(2)
@@ -1187,6 +1227,26 @@ func TestTaprootCommitScriptToSelf(t *testing.T) {
}
}
// TestTaprootCommitScriptToSelf tests that the taproot script for redeeming
// one's output after a force close behaves as expected.
func TestTaprootCommitScriptToSelf(t *testing.T) {
t.Parallel()
for _, hasAuxLeaf := range []bool{true, false} {
name := fmt.Sprintf("aux_leaf=%v", hasAuxLeaf)
t.Run(name, func(t *testing.T) {
var auxLeaf AuxTapLeaf
if hasAuxLeaf {
auxLeaf = fn.Some(txscript.NewBaseTapLeaf(
bytes.Repeat([]byte{0x01}, 32),
))
}
testTaprootCommitScriptToSelf(t, auxLeaf)
})
}
}
func remoteCommitSweepWitGen(sigHash txscript.SigHashType,
commitScriptTree *testCommitScriptTree) witnessGen {
@@ -1220,12 +1280,8 @@ func remoteCommitSweepWitGen(sigHash txscript.SigHashType,
}
}
// TestTaprootCommitScriptRemote tests that the remote party can properly sweep
// their output after force close.
func TestTaprootCommitScriptRemote(t *testing.T) {
t.Parallel()
commitScriptTree, err := newTestCommitScriptTree(false)
func testTaprootCommitScriptRemote(t *testing.T, auxLeaf AuxTapLeaf) {
commitScriptTree, err := newTestCommitScriptTree(false, auxLeaf)
require.NoError(t, err)
spendTx := wire.NewMsgTx(2)
@@ -1364,6 +1420,26 @@ func TestTaprootCommitScriptRemote(t *testing.T) {
}
}
// TestTaprootCommitScriptRemote tests that the remote party can properly sweep
// their output after force close.
func TestTaprootCommitScriptRemote(t *testing.T) {
t.Parallel()
for _, hasAuxLeaf := range []bool{true, false} {
name := fmt.Sprintf("aux_leaf=%v", hasAuxLeaf)
t.Run(name, func(t *testing.T) {
var auxLeaf AuxTapLeaf
if hasAuxLeaf {
auxLeaf = fn.Some(txscript.NewBaseTapLeaf(
bytes.Repeat([]byte{0x01}, 32),
))
}
testTaprootCommitScriptRemote(t, auxLeaf)
})
}
}
type testAnchorScriptTree struct {
sweepKey *btcec.PrivateKey
@@ -1599,25 +1675,21 @@ type testSecondLevelHtlcTree struct {
tapScriptRoot []byte
}
func newTestSecondLevelHtlcTree() (*testSecondLevelHtlcTree, error) {
func newTestSecondLevelHtlcTree(t *testing.T,
auxLeaf AuxTapLeaf) *testSecondLevelHtlcTree {
delayKey, err := btcec.NewPrivateKey()
if err != nil {
return nil, err
}
require.NoError(t, err)
revokeKey, err := btcec.NewPrivateKey()
if err != nil {
return nil, err
}
require.NoError(t, err)
const csvDelay = 6
scriptTree, err := SecondLevelHtlcTapscriptTree(
delayKey.PubKey(), csvDelay,
delayKey.PubKey(), csvDelay, auxLeaf,
)
if err != nil {
return nil, err
}
require.NoError(t, err)
tapScriptRoot := scriptTree.RootNode.TapHash()
@@ -1626,9 +1698,7 @@ func newTestSecondLevelHtlcTree() (*testSecondLevelHtlcTree, error) {
)
pkScript, err := PayToTaprootScript(htlcKey)
if err != nil {
return nil, err
}
require.NoError(t, err)
const amt = 100
@@ -1643,7 +1713,7 @@ func newTestSecondLevelHtlcTree() (*testSecondLevelHtlcTree, error) {
amt: amt,
scriptTree: scriptTree,
tapScriptRoot: tapScriptRoot[:],
}, nil
}
}
func secondLevelHtlcSuccessWitGen(sigHash txscript.SigHashType,
@@ -1713,13 +1783,8 @@ func secondLevelHtlcRevokeWitnessgen(sigHash txscript.SigHashType,
}
}
// TestTaprootSecondLevelHtlcScript tests that a channel peer can properly
// spend the second level HTLC script to resolve HTLCs.
func TestTaprootSecondLevelHtlcScript(t *testing.T) {
t.Parallel()
htlcScriptTree, err := newTestSecondLevelHtlcTree()
require.NoError(t, err)
func testTaprootSecondLevelHtlcScript(t *testing.T, auxLeaf AuxTapLeaf) {
htlcScriptTree := newTestSecondLevelHtlcTree(t, auxLeaf)
spendTx := wire.NewMsgTx(2)
spendTx.AddTxIn(&wire.TxIn{})
@@ -1879,3 +1944,23 @@ func TestTaprootSecondLevelHtlcScript(t *testing.T) {
})
}
}
// TestTaprootSecondLevelHtlcScript tests that a channel peer can properly
// spend the second level HTLC script to resolve HTLCs.
func TestTaprootSecondLevelHtlcScript(t *testing.T) {
t.Parallel()
for _, hasAuxLeaf := range []bool{true, false} {
name := fmt.Sprintf("aux_leaf=%v", hasAuxLeaf)
t.Run(name, func(t *testing.T) {
var auxLeaf AuxTapLeaf
if hasAuxLeaf {
auxLeaf = fn.Some(txscript.NewBaseTapLeaf(
bytes.Repeat([]byte{0x01}, 32),
))
}
testTaprootSecondLevelHtlcScript(t, auxLeaf)
})
}
}