Skip to content

Commit 0a29b37

Browse files
authored
Merge pull request #7733 from ellemouton/taprootTowers
watchtower: support taproot channel commitments
2 parents 24a79c7 + 2a61c91 commit 0a29b37

27 files changed

+1451
-319
lines changed

cmd/lncli/wtclient.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,8 +284,14 @@ var policyCommand = cli.Command{
284284
"policy. (default)",
285285
},
286286
cli.BoolFlag{
287-
Name: "anchor",
288-
Usage: "Retrieve the anchor tower client's current policy.",
287+
Name: "anchor",
288+
Usage: "Retrieve the anchor tower client's current " +
289+
"policy.",
290+
},
291+
cli.BoolFlag{
292+
Name: "taproot",
293+
Usage: "Retrieve the taproot tower client's current " +
294+
"policy.",
289295
},
290296
},
291297
}
@@ -305,6 +311,8 @@ func policy(ctx *cli.Context) error {
305311
policyType = wtclientrpc.PolicyType_ANCHOR
306312
case ctx.Bool("legacy"):
307313
policyType = wtclientrpc.PolicyType_LEGACY
314+
case ctx.Bool("taproot"):
315+
policyType = wtclientrpc.PolicyType_TAPROOT
308316

309317
// For backwards compatibility with original rpc behavior.
310318
default:

docs/release-notes/release-notes-0.18.0.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,13 @@
8585
control to [handle pathfinding errors](https://github.com/lightningnetwork/lnd/pull/8095)
8686
for blinded paths are also included.
8787
* A new config value,
88-
[http-header-timeout](https://github.com/lightningnetwork/lnd/pull/7715), is added so users can specify the amount of time the http server will wait for a request to complete before closing the connection. The default value is 5 seconds.
88+
[http-header-timeout](https://github.com/lightningnetwork/lnd/pull/7715), is
89+
added so users can specify the amount of time the http server will wait for a
90+
request to complete before closing the connection. The default value is 5
91+
seconds.
92+
* Update [watchtowers to be Taproot
93+
ready](https://github.com/lightningnetwork/lnd/pull/7733)
94+
8995

9096
* [`routerrpc.usestatusinitiated` is
9197
introduced](https://github.com/lightningnetwork/lnd/pull/8177) to signal that
@@ -190,7 +196,7 @@
190196
* [Add a watchtower tower client
191197
multiplexer](https://github.com/lightningnetwork/lnd/pull/7702) to manage
192198
tower clients of different types.
193-
199+
194200
* [Introduce CommitmentType and JusticeKit
195201
interface](https://github.com/lightningnetwork/lnd/pull/7736) to simplify the
196202
code.

input/script_utils.go

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2124,27 +2124,14 @@ func NewLocalCommitScriptTree(csvTimeout uint32,
21242124

21252125
// First, we'll need to construct the tapLeaf that'll be our delay CSV
21262126
// clause.
2127-
builder := txscript.NewScriptBuilder()
2128-
builder.AddData(schnorr.SerializePubKey(selfKey))
2129-
builder.AddOp(txscript.OP_CHECKSIG)
2130-
builder.AddInt64(int64(csvTimeout))
2131-
builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY)
2132-
builder.AddOp(txscript.OP_DROP)
2133-
2134-
delayScript, err := builder.Script()
2127+
delayScript, err := TaprootLocalCommitDelayScript(csvTimeout, selfKey)
21352128
if err != nil {
21362129
return nil, err
21372130
}
21382131

21392132
// Next, we'll need to construct the revocation path, which is just a
21402133
// simple checksig script.
2141-
builder = txscript.NewScriptBuilder()
2142-
builder.AddData(schnorr.SerializePubKey(selfKey))
2143-
builder.AddOp(txscript.OP_DROP)
2144-
builder.AddData(schnorr.SerializePubKey(revokeKey))
2145-
builder.AddOp(txscript.OP_CHECKSIG)
2146-
2147-
revokeScript, err := builder.Script()
2134+
revokeScript, err := TaprootLocalCommitRevokeScript(selfKey, revokeKey)
21482135
if err != nil {
21492136
return nil, err
21502137
}
@@ -2176,6 +2163,35 @@ func NewLocalCommitScriptTree(csvTimeout uint32,
21762163
}, nil
21772164
}
21782165

2166+
// TaprootLocalCommitDelayScript builds the tap leaf with the CSV delay script
2167+
// for the to-local output.
2168+
func TaprootLocalCommitDelayScript(csvTimeout uint32,
2169+
selfKey *btcec.PublicKey) ([]byte, error) {
2170+
2171+
builder := txscript.NewScriptBuilder()
2172+
builder.AddData(schnorr.SerializePubKey(selfKey))
2173+
builder.AddOp(txscript.OP_CHECKSIG)
2174+
builder.AddInt64(int64(csvTimeout))
2175+
builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY)
2176+
builder.AddOp(txscript.OP_DROP)
2177+
2178+
return builder.Script()
2179+
}
2180+
2181+
// TaprootLocalCommitRevokeScript builds the tap leaf with the revocation path
2182+
// for the to-local output.
2183+
func TaprootLocalCommitRevokeScript(selfKey, revokeKey *btcec.PublicKey) (
2184+
[]byte, error) {
2185+
2186+
builder := txscript.NewScriptBuilder()
2187+
builder.AddData(schnorr.SerializePubKey(selfKey))
2188+
builder.AddOp(txscript.OP_DROP)
2189+
builder.AddData(schnorr.SerializePubKey(revokeKey))
2190+
builder.AddOp(txscript.OP_CHECKSIG)
2191+
2192+
return builder.Script()
2193+
}
2194+
21792195
// TaprootCommitScriptToSelf creates the taproot witness program that commits
21802196
// to the revocation (script path) and delay path (script path) in a single
21812197
// taproot output key. Both the delay script and the revocation script are part

itest/lnd_revocation_test.go

Lines changed: 26 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -715,30 +715,24 @@ func testRevokedCloseRetributionRemoteHodl(ht *lntest.HarnessTest) {
715715
// asserts that Willy responds by broadcasting the justice transaction on
716716
// Carol's behalf sweeping her funds without a reward.
717717
func testRevokedCloseRetributionAltruistWatchtower(ht *lntest.HarnessTest) {
718-
testCases := []struct {
719-
name string
720-
anchors bool
721-
}{{
722-
name: "anchors",
723-
anchors: true,
724-
}, {
725-
name: "legacy",
726-
anchors: false,
727-
}}
728-
729-
for _, tc := range testCases {
730-
tc := tc
718+
for _, commitType := range []lnrpc.CommitmentType{
719+
lnrpc.CommitmentType_LEGACY,
720+
lnrpc.CommitmentType_ANCHORS,
721+
lnrpc.CommitmentType_SIMPLE_TAPROOT,
722+
} {
723+
testName := fmt.Sprintf("%v", commitType.String())
724+
ct := commitType
731725
testFunc := func(ht *lntest.HarnessTest) {
732726
testRevokedCloseRetributionAltruistWatchtowerCase(
733-
ht, tc.anchors,
727+
ht, ct,
734728
)
735729
}
736730

737-
success := ht.Run(tc.name, func(tt *testing.T) {
731+
success := ht.Run(testName, func(tt *testing.T) {
738732
st := ht.Subtest(tt)
739733

740734
st.RunTestCase(&lntest.TestCase{
741-
Name: tc.name,
735+
Name: testName,
742736
TestFunc: testFunc,
743737
})
744738
})
@@ -756,7 +750,7 @@ func testRevokedCloseRetributionAltruistWatchtower(ht *lntest.HarnessTest) {
756750
}
757751

758752
func testRevokedCloseRetributionAltruistWatchtowerCase(ht *lntest.HarnessTest,
759-
anchors bool) {
753+
commitType lnrpc.CommitmentType) {
760754

761755
const (
762756
chanAmt = funding.MaxBtcFundingAmount
@@ -767,18 +761,19 @@ func testRevokedCloseRetributionAltruistWatchtowerCase(ht *lntest.HarnessTest,
767761

768762
// Since we'd like to test some multi-hop failure scenarios, we'll
769763
// introduce another node into our test network: Carol.
770-
carolArgs := []string{"--hodl.exit-settle"}
771-
if anchors {
772-
carolArgs = append(carolArgs, "--protocol.anchors")
773-
}
764+
carolArgs := lntest.NodeArgsForCommitType(commitType)
765+
carolArgs = append(carolArgs, "--hodl.exit-settle")
766+
774767
carol := ht.NewNode("Carol", carolArgs)
775768

776769
// Willy the watchtower will protect Dave from Carol's breach. He will
777770
// remain online in order to punish Carol on Dave's behalf, since the
778771
// breach will happen while Dave is offline.
779772
willy := ht.NewNode(
780-
"Willy", []string{"--watchtower.active",
781-
"--watchtower.externalip=" + externalIP},
773+
"Willy", []string{
774+
"--watchtower.active",
775+
"--watchtower.externalip=" + externalIP,
776+
},
782777
)
783778

784779
willyInfo := willy.RPC.GetInfoWatchtower()
@@ -801,13 +796,8 @@ func testRevokedCloseRetributionAltruistWatchtowerCase(ht *lntest.HarnessTest,
801796
// Dave will be the breached party. We set --nolisten to ensure Carol
802797
// won't be able to connect to him and trigger the channel data
803798
// protection logic automatically.
804-
daveArgs := []string{
805-
"--nolisten",
806-
"--wtclient.active",
807-
}
808-
if anchors {
809-
daveArgs = append(daveArgs, "--protocol.anchors")
810-
}
799+
daveArgs := lntest.NodeArgsForCommitType(commitType)
800+
daveArgs = append(daveArgs, "--nolisten", "--wtclient.active")
811801
dave := ht.NewNode("Dave", daveArgs)
812802

813803
addTowerReq := &wtclientrpc.AddTowerRequest{
@@ -833,8 +823,10 @@ func testRevokedCloseRetributionAltruistWatchtowerCase(ht *lntest.HarnessTest,
833823
// closure by Carol, we'll first open up a channel between them with a
834824
// 0.5 BTC value.
835825
params := lntest.OpenChannelParams{
836-
Amt: 3 * (chanAmt / 4),
837-
PushAmt: chanAmt / 4,
826+
Amt: 3 * (chanAmt / 4),
827+
PushAmt: chanAmt / 4,
828+
CommitmentType: commitType,
829+
Private: true,
838830
}
839831
chanPoint := ht.OpenChannel(dave, carol, params)
840832

@@ -956,7 +948,7 @@ func testRevokedCloseRetributionAltruistWatchtowerCase(ht *lntest.HarnessTest,
956948
willyBalResp := willy.RPC.WalletBalance()
957949

958950
if willyBalResp.ConfirmedBalance != 0 {
959-
return fmt.Errorf("Expected Willy to have no funds "+
951+
return fmt.Errorf("expected Willy to have no funds "+
960952
"after justice transaction was mined, found %v",
961953
willyBalResp)
962954
}
@@ -994,7 +986,7 @@ func testRevokedCloseRetributionAltruistWatchtowerCase(ht *lntest.HarnessTest,
994986
ht.AssertNumPendingForceClose(dave, 0)
995987

996988
// If this is an anchor channel, Dave would sweep the anchor.
997-
if anchors {
989+
if lntest.CommitTypeHasAnchors(commitType) {
998990
ht.MineBlocksAndAssertNumTxes(1, 1)
999991
}
1000992

lnrpc/wtclientrpc/wtclient.go

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -273,9 +273,9 @@ func (c *WatchtowerClient) ListTowers(ctx context.Context,
273273
// for the legacy client to the existing tower.
274274
rpcTowers := make(map[wtdb.TowerID]*Tower)
275275
for blobType, towers := range towersPerBlobType {
276-
policyType := PolicyType_LEGACY
277-
if blobType.IsAnchorChannel() {
278-
policyType = PolicyType_ANCHOR
276+
policyType, err := blobTypeToPolicyType(blobType)
277+
if err != nil {
278+
return nil, err
279279
}
280280

281281
for _, tower := range towers {
@@ -331,9 +331,9 @@ func (c *WatchtowerClient) GetTowerInfo(ctx context.Context,
331331

332332
var resTower *Tower
333333
for blobType, tower := range towersPerBlobType {
334-
policyType := PolicyType_LEGACY
335-
if blobType.IsAnchorChannel() {
336-
policyType = PolicyType_ANCHOR
334+
policyType, err := blobTypeToPolicyType(blobType)
335+
if err != nil {
336+
return nil, err
337337
}
338338

339339
rpcTower := marshallTower(
@@ -437,15 +437,9 @@ func (c *WatchtowerClient) Policy(ctx context.Context,
437437
return nil, err
438438
}
439439

440-
var blobType blob.Type
441-
switch req.PolicyType {
442-
case PolicyType_LEGACY:
443-
blobType = blob.TypeAltruistCommit
444-
case PolicyType_ANCHOR:
445-
blobType = blob.TypeAltruistAnchorCommit
446-
default:
447-
return nil, fmt.Errorf("unknown policy type: %v",
448-
req.PolicyType)
440+
blobType, err := policyTypeToBlobType(req.PolicyType)
441+
if err != nil {
442+
return nil, err
449443
}
450444

451445
policy, err := c.cfg.ClientMgr.Policy(blobType)
@@ -525,3 +519,35 @@ func marshallTower(tower *wtclient.RegisteredTower, policyType PolicyType,
525519

526520
return rpcTower
527521
}
522+
523+
func blobTypeToPolicyType(t blob.Type) (PolicyType, error) {
524+
switch t {
525+
case blob.TypeAltruistTaprootCommit:
526+
return PolicyType_TAPROOT, nil
527+
528+
case blob.TypeAltruistAnchorCommit:
529+
return PolicyType_ANCHOR, nil
530+
531+
case blob.TypeAltruistCommit:
532+
return PolicyType_LEGACY, nil
533+
534+
default:
535+
return 0, fmt.Errorf("unknown blob type: %s", t)
536+
}
537+
}
538+
539+
func policyTypeToBlobType(t PolicyType) (blob.Type, error) {
540+
switch t {
541+
case PolicyType_TAPROOT:
542+
return blob.TypeAltruistTaprootCommit, nil
543+
544+
case PolicyType_ANCHOR:
545+
return blob.TypeAltruistAnchorCommit, nil
546+
547+
case PolicyType_LEGACY:
548+
return blob.TypeAltruistCommit, nil
549+
550+
default:
551+
return 0, fmt.Errorf("unknown policy type: %s", t)
552+
}
553+
}

0 commit comments

Comments
 (0)