lnrpc+rpcserver: add production taproot commitment type to RPC interface
What changed, and why it matters
This commit adds a new 'production' taproot channel option to LND's remote-control API. It is a feature addition, not a fix for an active security bug. The change lets external apps request a finalized taproot channel type and ensures the server sets the correct protocol flags. There is no direct evidence in the commit of a vulnerability being patched.
Treat as a routine feature commit. Reviewers should verify that the new feature-bit mapping matches the production taproot specification and that the private-channel restriction is consistently enforced elsewhere (e.g., in the funding manager or peer handshake). No urgent security action is indicated by this diff alone.
Security signals we found
New RPC enum value maps to a new wire feature bit (SimpleTaprootChannelsRequiredFinal)
Added private-channel enforcement for the new taproot commitment type
Reverse mapping added so existing taproot-final channels are reported correctly via RPC
No input validation, memory-safety, or authorization changes observed
No vendor security disclosure or CVE references present in commit
Evidence from the diff
The patch extends the RPC commitment-type enum with SIMPLE_TAPROOT_FINAL (value 7), updates generated protobuf/swagger code, and teaches rpcServer to map that enum to lnwire.SimpleTaprootChannelsRequiredFinal plus optional ZeroConfRequired/ScidAliasRequired feature bits. It also adds a private-channel requirement and a reverse mapping from channeldb.ChannelType to the new RPC enum. The existing SIMPLE_TAPROOT enum is re-documented as the staging/development variant.
Changed components
lnrpc/lightning.protolnrpc/lightning.pb.golnrpc/lightning.swagger.jsonrpcserver.go (OpenChannel parsing and commitment-type reporting)Inspect captured patch +48 / −5
diff --git a/lnrpc/lightning.pb.go b/lnrpc/lightning.pb.go
index b1b2145..59d0191 100644
--- a/lnrpc/lightning.pb.go
+++ b/lnrpc/lightning.pb.go
@@ -231,8 +231,13 @@ const (
// channel before its maturity date.
CommitmentType_SCRIPT_ENFORCED_LEASE CommitmentType = 4
// A channel that uses musig2 for the funding output, and the new tapscript
- // features where relevant.
+ // features where relevant. This is the staging version using development
+ // scripts.
CommitmentType_SIMPLE_TAPROOT CommitmentType = 5
+ // A channel that uses musig2 for the funding output, and the new tapscript
+ // features where relevant. This is the production version using final scripts
+ // and feature bits 80/81.
+ CommitmentType_SIMPLE_TAPROOT_FINAL CommitmentType = 7
// Identical to the SIMPLE_TAPROOT channel type, but with extra functionality.
// This channel type also commits to additional meta data in the tapscript
// leaves for the scripts in a channel.
@@ -248,6 +253,7 @@ var (
3: "ANCHORS",
4: "SCRIPT_ENFORCED_LEASE",
5: "SIMPLE_TAPROOT",
+ 7: "SIMPLE_TAPROOT_FINAL",
6: "SIMPLE_TAPROOT_OVERLAY",
}
CommitmentType_value = map[string]int32{
@@ -257,6 +263,7 @@ var (
"ANCHORS": 3,
"SCRIPT_ENFORCED_LEASE": 4,
"SIMPLE_TAPROOT": 5,
+ "SIMPLE_TAPROOT_FINAL": 7,
"SIMPLE_TAPROOT_OVERLAY": 6,
}
)
@@ -20356,7 +20363,7 @@ const file_lightning_proto_rawDesc = "" +
"\x1aUNUSED_WITNESS_PUBKEY_HASH\x10\x02\x12\x1d\n" +
"\x19UNUSED_NESTED_PUBKEY_HASH\x10\x03\x12\x12\n" +
"\x0eTAPROOT_PUBKEY\x10\x04\x12\x19\n" +
- "\x15UNUSED_TAPROOT_PUBKEY\x10\x05*\xa8\x01\n" +
+ "\x15UNUSED_TAPROOT_PUBKEY\x10\x05*\xc2\x01\n" +
"\x0eCommitmentType\x12\x1b\n" +
"\x17UNKNOWN_COMMITMENT_TYPE\x10\x00\x12\n" +
"\n" +
@@ -20364,7 +20371,8 @@ const file_lightning_proto_rawDesc = "" +
"\x11STATIC_REMOTE_KEY\x10\x02\x12\v\n" +
"\aANCHORS\x10\x03\x12\x19\n" +
"\x15SCRIPT_ENFORCED_LEASE\x10\x04\x12\x12\n" +
- "\x0eSIMPLE_TAPROOT\x10\x05\x12\x1a\n" +
+ "\x0eSIMPLE_TAPROOT\x10\x05\x12\x18\n" +
+ "\x14SIMPLE_TAPROOT_FINAL\x10\a\x12\x1a\n" +
"\x16SIMPLE_TAPROOT_OVERLAY\x10\x06*a\n" +
"\tInitiator\x12\x15\n" +
"\x11INITIATOR_UNKNOWN\x10\x00\x12\x13\n" +
diff --git a/lnrpc/lightning.proto b/lnrpc/lightning.proto
index 7fe99ac..b60f45e 100644
--- a/lnrpc/lightning.proto
+++ b/lnrpc/lightning.proto
@@ -1516,10 +1516,18 @@ enum CommitmentType {
/*
A channel that uses musig2 for the funding output, and the new tapscript
- features where relevant.
+ features where relevant. This is the staging version using development
+ scripts.
*/
SIMPLE_TAPROOT = 5;
+ /*
+ A channel that uses musig2 for the funding output, and the new tapscript
+ features where relevant. This is the production version using final scripts
+ and feature bits 80/81.
+ */
+ SIMPLE_TAPROOT_FINAL = 7;
+
/*
Identical to the SIMPLE_TAPROOT channel type, but with extra functionality.
This channel type also commits to additional meta data in the tapscript
diff --git a/lnrpc/lightning.swagger.json b/lnrpc/lightning.swagger.json
index 8c30966..0818132 100644
--- a/lnrpc/lightning.swagger.json
+++ b/lnrpc/lightning.swagger.json
@@ -4929,10 +4929,11 @@
"ANCHORS",
"SCRIPT_ENFORCED_LEASE",
"SIMPLE_TAPROOT",
+ "SIMPLE_TAPROOT_FINAL",
"SIMPLE_TAPROOT_OVERLAY"
],
"default": "UNKNOWN_COMMITMENT_TYPE",
- "description": " - UNKNOWN_COMMITMENT_TYPE: Returned when the commitment type isn't known or unavailable.\n - LEGACY: A channel using the legacy commitment format having tweaked to_remote\nkeys.\n - STATIC_REMOTE_KEY: A channel that uses the modern commitment format where the key in the\noutput of the remote party does not change each state. This makes back\nup and recovery easier as when the channel is closed, the funds go\ndirectly to that key.\n - ANCHORS: A channel that uses a commitment format that has anchor outputs on the\ncommitments, allowing fee bumping after a force close transaction has\nbeen broadcast.\n - SCRIPT_ENFORCED_LEASE: A channel that uses a commitment type that builds upon the anchors\ncommitment format, but in addition requires a CLTV clause to spend outputs\npaying to the channel initiator. This is intended for use on leased channels\nto guarantee that the channel initiator has no incentives to close a leased\nchannel before its maturity date.\n - SIMPLE_TAPROOT: A channel that uses musig2 for the funding output, and the new tapscript\nfeatures where relevant.\n - SIMPLE_TAPROOT_OVERLAY: Identical to the SIMPLE_TAPROOT channel type, but with extra functionality.\nThis channel type also commits to additional meta data in the tapscript\nleaves for the scripts in a channel."
+ "description": " - UNKNOWN_COMMITMENT_TYPE: Returned when the commitment type isn't known or unavailable.\n - LEGACY: A channel using the legacy commitment format having tweaked to_remote\nkeys.\n - STATIC_REMOTE_KEY: A channel that uses the modern commitment format where the key in the\noutput of the remote party does not change each state. This makes back\nup and recovery easier as when the channel is closed, the funds go\ndirectly to that key.\n - ANCHORS: A channel that uses a commitment format that has anchor outputs on the\ncommitments, allowing fee bumping after a force close transaction has\nbeen broadcast.\n - SCRIPT_ENFORCED_LEASE: A channel that uses a commitment type that builds upon the anchors\ncommitment format, but in addition requires a CLTV clause to spend outputs\npaying to the channel initiator. This is intended for use on leased channels\nto guarantee that the channel initiator has no incentives to close a leased\nchannel before its maturity date.\n - SIMPLE_TAPROOT: A channel that uses musig2 for the funding output, and the new tapscript\nfeatures where relevant. This is the staging version using development\nscripts.\n - SIMPLE_TAPROOT_FINAL: A channel that uses musig2 for the funding output, and the new tapscript\nfeatures where relevant. This is the production version using final scripts\nand feature bits 80/81.\n - SIMPLE_TAPROOT_OVERLAY: Identical to the SIMPLE_TAPROOT channel type, but with extra functionality.\nThis channel type also commits to additional meta data in the tapscript\nleaves for the scripts in a channel."
},
"lnrpcConnectPeerRequest": {
"type": "object",
diff --git a/rpcserver.go b/rpcserver.go
index ded2bba..a069aa7 100644
--- a/rpcserver.go
+++ b/rpcserver.go
@@ -2401,6 +2401,29 @@ func (r *rpcServer) parseOpenChannelReq(in *lnrpc.OpenChannelRequest,
*channelType = lnwire.ChannelType(*fv)
+ case lnrpc.CommitmentType_SIMPLE_TAPROOT_FINAL:
+ // If the final taproot channel type is being set, then the channel
+ // MUST be private (unadvertised) for now.
+ if !in.Private {
+ return nil, fmt.Errorf("taproot channels must be " +
+ "private")
+ }
+
+ channelType = new(lnwire.ChannelType)
+ fv := lnwire.NewRawFeatureVector(
+ lnwire.SimpleTaprootChannelsRequiredFinal,
+ )
+
+ if in.ZeroConf {
+ fv.Set(lnwire.ZeroConfRequired)
+ }
+
+ if in.ScidAlias {
+ fv.Set(lnwire.ScidAliasRequired)
+ }
+
+ *channelType = lnwire.ChannelType(*fv)
+
case lnrpc.CommitmentType_SIMPLE_TAPROOT_OVERLAY:
// If the taproot overlay channel type is being set, then the
// channel MUST be private.
@@ -4898,6 +4921,9 @@ func rpcCommitmentType(chanType channeldb.ChannelType) lnrpc.CommitmentType {
case chanType.HasTapscriptRoot():
return lnrpc.CommitmentType_SIMPLE_TAPROOT_OVERLAY
+ case chanType.IsTaprootFinal():
+ return lnrpc.CommitmentType_SIMPLE_TAPROOT_FINAL
+
case chanType.IsTaproot():
return lnrpc.CommitmentType_SIMPLE_TAPROOT
Why this scored 20/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.