Skip to content

lnwallet/btcwallet: support taproot script path fee estimation in FundPSBT - #10504

Open
Roasbeef wants to merge 3 commits into
lightningnetwork:masterfrom
Roasbeef:fund-psbt-script-path
Open

Roasbeef wants to merge 3 commits into
lightningnetwork:masterfrom
Roasbeef:fund-psbt-script-path

Conversation

@Roasbeef

Copy link
Copy Markdown
Member

This PR adds support for fee estimation in FundPsbt when the PSBT contains inputs that spend via a taproot script path. Previously, EstimateInputWeight would return an error for TaprootScriptSpendSignMethod inputs, making FundPsbt unusable for any workflow involving script path spends.

The fix reads the TaprootLeafScript field from the PSBT input to obtain the control block and revealed script. The leaf witness size is derived directly from the serialized script length, and the control block size is calculated from its inclusion proof. This allows AddTapscriptInput to compute an accurate weight estimate for the script path witness.

Script path witnesses are approximately twice the size of key path witnesses (~136 WU vs 67 WU for a simple OP_CHECKSIG script), so accurate estimation is important for proper fee calculation. The TaprootLeafScript field must be populated in the PSBT input for estimation to work; if not present, a descriptive error is returned.

An integration test verifies the fix by creating a tapscript address, funding it, constructing a PSBT with the script path input properly populated, and calling FundPsbt to verify the fee is calculated correctly.

@Roasbeef Roasbeef added enhancement Improvements to existing features / behaviour wallet The wallet (lnwallet) which LND uses labels Jan 15, 2026
@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @Roasbeef, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the FundPsbt functionality to correctly estimate transaction fees for Taproot script path spends. By implementing logic to parse Taproot leaf script information from PSBT inputs, the system can now accurately calculate witness sizes, which was previously a blocker for such transactions. This ensures proper fee calculation and expands the utility of FundPsbt for advanced Bitcoin transaction types.

Highlights

  • Taproot Script Path Fee Estimation: The FundPsbt function now supports accurate fee estimation for inputs that spend via a Taproot script path.
  • Error Resolution for Script Spends: Previously, EstimateInputWeight would return an error for TaprootScriptSpendSignMethod inputs, which has now been resolved.
  • Witness Size Calculation Logic: The fix involves reading the TaprootLeafScript field from the PSBT input to obtain the control block and revealed script, allowing for precise derivation of the leaf witness size and control block size.
  • Requirement for TaprootLeafScript: For fee estimation to work correctly, the TaprootLeafScript field must be populated in the PSBT input; if not present, a descriptive error is returned.
  • Integration Test Coverage: A new integration test, testFundPsbtTaprootScriptPath, has been added to verify the correct fee calculation for Taproot script path spends.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This PR adds support for fee estimation for Taproot script path spends in FundPsbt. The changes are mostly correct and include a good integration test. However, there is a significant issue in the fee estimation logic within lnwallet/btcwallet/psbt.go. The weight of the script witness is incorrectly estimated as the length of the script itself, which can lead to under-paying for transaction fees. I've provided a suggestion to use a more accurate heuristic for this estimation. The rest of the changes look good.

Comment thread lnwallet/btcwallet/psbt.go Resolved
@yyforyongyu
yyforyongyu self-requested a review January 15, 2026 08:48
@Roasbeef Roasbeef added reclassify Manually trigger PR severity reclassification and removed reclassify Manually trigger PR severity reclassification labels Jan 28, 2026
…dPsbt

In this commit, we add support for estimating fees when FundPsbt
encounters inputs that spend via a taproot script path. Previously,
EstimateInputWeight would return an error for TaprootScriptSpendSignMethod
inputs, making FundPsbt unusable for script path spends.

The fix reads the TaprootLeafScript field from the PSBT input to obtain
the control block and revealed script. The leaf witness size is derived
directly from the serialized script length, and the control block size
is calculated from its inclusion proof. This allows AddTapscriptInput
to compute an accurate weight estimate for the script path witness.

Note that the TaprootLeafScript field must be populated in the PSBT
input for fee estimation to work. If not present, a descriptive error
is returned indicating the requirement.
In this commit, we add an integration test that verifies FundPsbt can
correctly estimate fees for PSBTs containing taproot script path inputs.

The test creates a simple OP_CHECKSIG tapscript, sends coins to the
resulting taproot address, then constructs a PSBT with the script path
input properly populated (TaprootLeafScript, TaprootBip32Derivation).
It calls FundPsbt and verifies that the fee is calculated correctly
for a script path witness, which is approximately twice the size of
a key path witness (~136 WU vs 67 WU).
… estimation

In this commit, we address a fee estimation accuracy issue raised in
PR review for taproot script path inputs in FundPsbt.

The previous implementation passed `len(leafScript.Script)` as the
`leafWitnessSize` argument to `AddTapscriptInput`. This is incorrect:
`AddTapscriptInput` already derives the script and control block sizes
internally from the `tapscript` argument, so `leafWitnessSize` is only
meant to cover the witness stack elements consumed by the revealed
script (e.g. signatures). Using the script length as a proxy for the
signature size both double-counts the script and uses the wrong unit.

We introduce a per-input `witness_size_hint` field on `FundPsbtRequest`
keyed by outpoint. Callers that know the exact expected witness size
for a tapscript input (e.g. multi-signature leaves) can supply it for a
precise fee estimate. When no hint is provided, we fall back to the
common case of a single Schnorr signature
(`input.TaprootSignatureWitnessSize`), matching the convention used by
every existing call site of `AddTapscriptInput` in the codebase.

The `EstimateInputWeight` signature is updated to accept the optional
hint, and the wallet RPC `FundPsbt` handler builds the hint map from
the new RPC field before threading it through `fundPsbtCoinSelect`.

Unit tests in `lnwallet/btcwallet` cover both the default-heuristic
path and the explicit-hint override path. The integration test
`testFundPsbtTaprootScriptPath` is extended to exercise the new RPC
field end-to-end and verify that a larger hint produces a
correspondingly larger fee.
@Roasbeef
Roasbeef force-pushed the fund-psbt-script-path branch from 8d74a45 to 1c4d67b Compare April 30, 2026 22:24
@github-actions github-actions Bot added the severity-critical Requires expert review - security/consensus critical label Apr 30, 2026
@github-actions

Copy link
Copy Markdown

🔴 PR Severity: CRITICAL

Highest-severity file determines PR level | 4 files (non-test, non-generated) | 163 lines changed

🔴 Critical (1 file)
  • lnwallet/btcwallet/psbt.go - lnwallet/* package: wallet operations, PSBT signing/finalization logic
🟠 High (2 files)
  • lnrpc/walletrpc/walletkit_server.go - lnrpc/* package: RPC/API server implementation
  • lnrpc/walletrpc/walletkit.swagger.json - lnrpc/* package: API surface definitions
🟡 Medium (1 file)
  • lnrpc/walletrpc/walletkit.proto - .proto file: API protocol changes
⬜ Excluded from classification (5 files)
  • itest/list_on_test.go - integration test file
  • itest/lnd_psbt_test.go - integration test file
  • lnrpc/walletrpc/walletkit.pb.go - auto-generated protobuf file
  • lnrpc/walletrpc/walletkit_server_test.go - test file
  • lnwallet/btcwallet/psbt_test.go - test file

Analysis

This PR modifies lnwallet/btcwallet/psbt.go, which falls under the lnwallet/* critical package covering wallet operations, channel funding, signing, and commitment transactions. The changes appear to extend PSBT (Partially Signed Bitcoin Transaction) functionality — specifically adding new fields or options to the PSBT workflow as evidenced by the accompanying .proto and RPC server changes.

The lnwallet package is at the core of fund management in lnd, and any changes to PSBT signing/finalization paths require expert review to ensure correctness of transaction construction, signature validation, and key handling. The RPC layer changes (walletkit proto + server) expand the API surface to expose this new PSBT capability.

Severity bump check: 4 non-test/non-generated files (threshold: 20) and ~163 lines (threshold: 500) — no bump triggered. Only one critical package touched.


To override, add a severity-override-{critical,high,medium,low} label.
<!-- pr-severity-bot -->

@saubyk saubyk added this to lnd v0.22 May 5, 2026
@github-project-automation github-project-automation Bot moved this to Backlog in lnd v0.22 May 5, 2026
@saubyk saubyk moved this from Backlog to In review in lnd v0.22 May 5, 2026
@ziggie1984
ziggie1984 self-requested a review May 5, 2026 19:49

@ziggie1984 ziggie1984 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good, pending some minor commits regarding commits and testing

// For script path spends, we require the
// TaprootLeafScript to be populated so we can
// calculate the control block size.
if len(in.TaprootLeafScript) == 0 {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's maybe force a length of 1 exactly:

Suggested change
if len(in.TaprootLeafScript) == 0 {
if len(in.TaprootLeafScript) != 1 {
return ErrUnexpectedNumberOfTaprootLeafScripts
}
leafScript := in.TaprootLeafScript[0]

Comment thread lnwallet/btcwallet/psbt_test.go Outdated
// Script path witness: element count (1) + control block base
// (33) + control block len prefix (1) + script (19) + script
// len prefix (1) + leaf witness size (19, the script length).
expectedWitnessWeight: 1 + 33 + 1 + len(dummyLeaf.Script) + 1 +

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't this the old version still and it should be:

  expectedWitnessWeight: 1 + 33 + 1 + 1 + len(dummyLeaf.Script) +
      input.TaprootSignatureWitnessSize,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok here it is fixed, should be squashed in the the first commit tho.

Comment thread itest/lnd_psbt_test.go
}}
in.SighashType = txscript.SigHashDefault

_ = keyDesc // Suppress unused warning

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we do not use keyDesc, we should use _ above where we create this variable.

Comment thread itest/lnd_psbt_test.go
// The fee should be at least higher than what a key path would cost.
// Key path: ~67 WU witness, ~80 vbytes total = 800 sats at 10 sat/vb.
// Script path should be at least 900+ sats.
require.Greater(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's just compare the expected size straight away ?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we use the script with only the taproot signature it should match perfectly

Comment thread itest/lnd_psbt_test.go
expectedFee := int64(10 * expectedVSize)

// Verify the fee is close to our expected calculation (within 20%).
require.InDelta(ht, expectedFee, int64(fee), float64(expectedFee)*0.2,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thats quite a big error range 20%, can we not be more detailed, that should match quite accuratly with taproot signatures, I would expect no errors at all ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah well constrain it further, as mentioned this should be a tight binding.

Comment thread itest/lnd_psbt_test.go
@@ -23,6 +23,7 @@ import (
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: quite noisy commit message and not really necessary to explain where the motivation come from imo

// The size only covers the witness elements consumed by the revealed
// script; the revealed script itself and the control block size are
// derived automatically from the PSBT's TaprootLeafScript field.
repeated TxWitnessSizeHint witness_size_hints = 15;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the size needs to also include the length encoding for the single witness elements to be accurate ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The call to AddTapscriptInput when we actually use it takes care of that.

@Roasbeef
Roasbeef requested a review from ziggie1984 May 12, 2026 01:46

@vctt94 vctt94 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR looks correct to me overall.

I checked whether this could pick the wrong leaf if multiple TaprootLeafScript entries are provided, but that case is already checked earlier. I left a more detailed comment about it.

Other than that, the approach looks correct. LGTM.

return ErrMissingTaprootLeafScript
}

leafScript := in.TaprootLeafScript[0]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked whether this could pick the wrong leaf if multiple TaprootLeafScript entries are provided.

I added a small unit test here to verify that case, and it turns out this is already rejected earlier by validateSigningMethod, so I don’t think this is an issue.

The only small nit is that the resulting error is a bit misleading: "specified leaf hash in taproot BIP0032 derivation but missing taproot leaf script"

even though multiple leaf scripts were provided.

@litbot-9000

Copy link
Copy Markdown
Collaborator

@yyforyongyu: review reminder
@ziggie1984: review reminder
@Roasbeef, remember to re-request review from reviewers when ready

6 similar comments
@litbot-9000

Copy link
Copy Markdown
Collaborator

@yyforyongyu: review reminder
@ziggie1984: review reminder
@Roasbeef, remember to re-request review from reviewers when ready

@litbot-9000

Copy link
Copy Markdown
Collaborator

@yyforyongyu: review reminder
@ziggie1984: review reminder
@Roasbeef, remember to re-request review from reviewers when ready

@litbot-9000

Copy link
Copy Markdown
Collaborator

@yyforyongyu: review reminder
@ziggie1984: review reminder
@Roasbeef, remember to re-request review from reviewers when ready

@litbot-9000

Copy link
Copy Markdown
Collaborator

@yyforyongyu: review reminder
@ziggie1984: review reminder
@Roasbeef, remember to re-request review from reviewers when ready

@litbot-9000

Copy link
Copy Markdown
Collaborator

@yyforyongyu: review reminder
@ziggie1984: review reminder
@Roasbeef, remember to re-request review from reviewers when ready

@litbot-9000

Copy link
Copy Markdown
Collaborator

@yyforyongyu: review reminder
@ziggie1984: review reminder
@Roasbeef, remember to re-request review from reviewers when ready

@litbot-9000

Copy link
Copy Markdown
Collaborator

@yyforyongyu: review reminder
@ziggie1984: review reminder
@Roasbeef, remember to re-request review from reviewers when ready

7 similar comments
@litbot-9000

Copy link
Copy Markdown
Collaborator

@yyforyongyu: review reminder
@ziggie1984: review reminder
@Roasbeef, remember to re-request review from reviewers when ready

@litbot-9000

Copy link
Copy Markdown
Collaborator

@yyforyongyu: review reminder
@ziggie1984: review reminder
@Roasbeef, remember to re-request review from reviewers when ready

@litbot-9000

Copy link
Copy Markdown
Collaborator

@yyforyongyu: review reminder
@ziggie1984: review reminder
@Roasbeef, remember to re-request review from reviewers when ready

@litbot-9000

Copy link
Copy Markdown
Collaborator

@yyforyongyu: review reminder
@ziggie1984: review reminder
@Roasbeef, remember to re-request review from reviewers when ready

@litbot-9000

Copy link
Copy Markdown
Collaborator

@yyforyongyu: review reminder
@ziggie1984: review reminder
@Roasbeef, remember to re-request review from reviewers when ready

@litbot-9000

Copy link
Copy Markdown
Collaborator

@yyforyongyu: review reminder
@ziggie1984: review reminder
@Roasbeef, remember to re-request review from reviewers when ready

@litbot-9000

Copy link
Copy Markdown
Collaborator

@yyforyongyu: review reminder
@ziggie1984: review reminder
@Roasbeef, remember to re-request review from reviewers when ready

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Improvements to existing features / behaviour reclassify Manually trigger PR severity reclassification severity-critical Requires expert review - security/consensus critical wallet The wallet (lnwallet) which LND uses

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

5 participants