Skip to content

tapchannel: support HTLC revocation sweeps #1994

Open
GeorgeTsagk wants to merge 10 commits intolightninglabs:mainfrom
GeorgeTsagk:htlc-revocation
Open

tapchannel: support HTLC revocation sweeps #1994
GeorgeTsagk wants to merge 10 commits intolightninglabs:mainfrom
GeorgeTsagk:htlc-revocation

Conversation

@GeorgeTsagk
Copy link
Copy Markdown
Member

Description

This PR adds support for sweeping revoked HTLC outputs in Taproot Asset channels.

The aux sweeper is extended with resolution logic for revoked offered/accepted HTLCs and second-level HTLC revocations. The signing path is updated to handle breach scenarios, where inputs are spent via key spend rather than script spend, with the HTLC index applied as a tweak to match the commitment's taproot internal key.

Additionally, the NotifyBroadcast interface gains a skipBroadcast flag. During breach resolution, LND crafts multiple competing justice transactions as a pinning mitigation. Rather than notifying tapd at time, LND now notifies after confirmation — the flag signals that the tx is already on-chain and proof finalization can proceed without re-broadcasting.

Depends on https://github.com/lightningnetwork/lnd/pull/10583/commits

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @GeorgeTsagk, 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 significantly enhances the 'tapchannel' functionality by introducing robust support for sweeping revoked HTLC outputs in Taproot Asset channels. It refines the auxiliary sweeper's resolution capabilities to manage various types of revoked HTLCs and adapts the signing path to correctly handle key spend operations required for breach recovery. Furthermore, it optimizes transaction broadcasting during breach events by allowing the system to skip broadcasting for already confirmed transactions, which is crucial for pinning mitigation.

Highlights

  • HTLC Revocation Sweeps: Implemented support for sweeping revoked HTLC outputs within Taproot Asset channels, enabling recovery in breach scenarios.
  • Auxiliary Sweeper Enhancements: Extended the auxiliary sweeper with specific resolution logic for offered, accepted, and second-level revoked HTLCs.
  • Breach Scenario Signing Path: Updated the signing process to correctly handle key spend for breach scenarios, applying the HTLC index as a tweak to the commitment's taproot internal key.
  • Conditional Transaction Broadcasting: Introduced a 'skipBroadcast' flag to the NotifyBroadcast interface, allowing tapd to avoid re-broadcasting transactions already on-chain during breach resolution.

🧠 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.

Changelog
  • go.mod
    • Updated Go language version to 1.25.5.
    • Added temporary replace directives for 'github.com/lightningnetwork/lnd' and 'github.com/lightningnetwork/lnd/sqldb' to a specific commit by GeorgeTsagk.
  • go.sum
    • Updated checksums to reflect the Go version change and the new LND replace directives.
  • server.go
    • Modified the 'NotifyBroadcast' method signature to include a 'skipBroadcast' boolean parameter.
    • Updated the logging for 'NotifyBroadcast' to include the 'skipBroadcast' status.
    • Passed the new 'skipBroadcast' parameter to the underlying auxiliary sweeper's 'NotifyBroadcast' method.
  • tapchannel/aux_closer.go
    • Modified the 'shipChannelTxn' function signature to accept a 'skipBroadcast' boolean parameter.
    • Updated the creation of 'PreAnchoredParcel' to use the new 'skipBroadcast' parameter.
    • Passed 'false' for 'skipBroadcast' when calling 'shipChannelTxn' from 'FinalizeClose'.
  • tapchannel/aux_leaf_signer.go
    • Refactored 'applySignDescToVIn' to distinguish between key spend (breach scenarios) and script spend.
    • For key spend, it now applies 'DoubleTweak' (revocation key) first, then 'SingleTweak' (HTLC index) to the signing key.
    • Adjusted the return value for 'leafToSign' to be empty for key spend scenarios.
    • Updated comments to clarify the tweak application order for breach scenarios.
  • tapchannel/aux_sweeper.go
    • Added a 'skipBroadcast' field to the 'broadcastReq' struct.
    • Imported the 'encoding/binary' package.
    • Modified 'signSweepVpackets' to handle key spend scenarios for HTLC revocations, applying the HTLC index as a 'SingleTweak' and setting the correct 'signMethod' and 'signingKey' for verification.
    • Introduced 'htlcOfferedRevokeSweepDesc', 'htlcAcceptedRevokeSweepDesc', and 'htlcSecondLevelRevokeSweepDesc' functions to generate sweep descriptors for various revoked HTLC types, ensuring correct key tweaking.
    • Updated 'importCommitTx' to pass 'true' for 'skipBroadcast' when shipping commitment transactions, indicating they are already on-chain.
    • Added 'errNoHtlcID' error constant.
    • Extended the 'resolveContract' function to include new resolution types for 'TaprootHtlcOfferedRevoke', 'TaprootHtlcAcceptedRevoke', and 'TaprootHtlcSecondLevelRevoke', filtering assets by HTLC ID and generating appropriate sweep descriptors.
    • Modified 'registerAndBroadcastSweep' and 'NotifyBroadcast' to accept and pass through the 'skipBroadcast' parameter.
Activity
  • No specific activity (comments, reviews, progress updates) was provided in the context for this pull request.
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.

Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

Code Review

This pull request introduces support for sweeping revoked HTLC outputs in Taproot Asset channels, a crucial feature for channel security. The changes are extensive, primarily affecting the tapchannel package. Key modifications include extending the auxiliary sweeper with resolution logic for various HTLC revocation scenarios and updating the signing path to handle breach scenarios via key-path spends. A skipBroadcast flag has also been added to the NotifyBroadcast interface to handle transactions that are already on-chain, such as justice transactions. The code is generally well-structured and includes detailed comments explaining the complex logic, especially around cryptographic tweaks. I've identified a couple of areas for improvement: one for enhancing code maintainability by reducing duplication, and another to make the HTLC lookup logic more robust. Overall, this is a solid contribution that adds significant functionality.

Comment on lines +2205 to +2209
// If not found in outgoing, try incoming (accepted HTLCs).
if len(assetOutputs) == 0 {
htlcOutputs = commitState.IncomingHtlcAssets.Val
assetOutputs = htlcOutputs.FilterByHtlcIndex(htlcID)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

If the HTLC is not found in OutgoingHtlcAssets, the code proceeds to check IncomingHtlcAssets. However, if it's not found there either, assetOutputs will be empty, and the function will proceed without error, likely resulting in an empty resolution blob later on. It would be more robust to return an error if the HTLC cannot be found in either list, to make debugging easier in case of an unexpected state.

		if len(assetOutputs) == 0 {
			htlcOutputs = commitState.IncomingHtlcAssets.Val
			assetOutputs = htlcOutputs.FilterByHtlcIndex(htlcID)
			if len(assetOutputs) == 0 {
				return lfn.Errf[returnType](
					"htlc with id %d not found", htlcID,
				)
			}
		}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

At the very least should log here. Invariants further up the stack should prevent this though.

@coveralls
Copy link
Copy Markdown

Pull Request Test Coverage Report for Build 22064145876

Details

  • 0 of 344 (0.0%) changed or added relevant lines in 4 files are covered.
  • 8349 unchanged lines in 129 files lost coverage.
  • Overall coverage decreased (-6.9%) to 49.529%

Changes Missing Coverage Covered Lines Changed/Added Lines %
tapchannel/aux_closer.go 0 3 0.0%
server.go 0 6 0.0%
tapchannel/aux_leaf_signer.go 0 75 0.0%
tapchannel/aux_sweeper.go 0 260 0.0%
Files with Coverage Reduction New Missed Lines %
tapchannel/aux_closer.go 1 1.21%
authmailbox/client.go 2 69.84%
commitment/proof.go 2 87.29%
fn/retry.go 2 92.5%
fn/errors.go 3 90.32%
itest/multisig.go 3 97.46%
universe/archive.go 3 81.05%
universe/interface.go 3 74.21%
commitment/encoding.go 4 68.75%
mssmt/encoding.go 4 76.67%
Totals Coverage Status
Change from base Build 22061897493: -6.9%
Covered Lines: 58972
Relevant Lines: 119066

💛 - Coveralls

@Roasbeef Roasbeef self-requested a review February 24, 2026 19:50
@jtobin jtobin requested a review from gijswijs February 24, 2026 19:52
}
if signDesc.DoubleTweak != nil {
if isBreach {
// Breach scenario: Apply DoubleTweak first, then SingleTweak.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why does the ordering matter here? The operation should be commutative.

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.

Ah you're right. The append order of PSBT unknowns is indeed irrelevant since the signer identifies them by key type, not position.


// Revoked second-level HTLC transaction. We sweep this using the
// revocation path.
case input.TaprootHtlcSecondLevelRevoke:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There aren't two diff enum types for accepted vs offered here?

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.

Right, LND's TaprootHtlcSecondLevelRevoke doesn't distinguish offered vs accepted (unlike first-level which has separate TaprootHtlcOfferedRevoke/TaprootHtlcAcceptedRevoke). We handle this by trying outgoing assets first, then falling back to incoming. HTLC IDs are unique within a commitment so at most one lookup succeeds. If neither has assets for that HTLC ID, it's a non-asset HTLC and doesn't need asset-level resolution.

Comment on lines +2205 to +2209
// If not found in outgoing, try incoming (accepted HTLCs).
if len(assetOutputs) == 0 {
htlcOutputs = commitState.IncomingHtlcAssets.Val
assetOutputs = htlcOutputs.FilterByHtlcIndex(htlcID)
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

At the very least should log here. Invariants further up the stack should prevent this though.


// Now that we have the script tree, we'll make the control block
// needed to spend it using the revocation path.
ctrlBlock, err := tweakedScriptTree.CtrlBlockForPath(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should add tests here.

AFIACT, this would fail in prod, as it's using an enum, but that value isn't handled by this func: https://github.com/lightningnetwork/lnd/blob/0b00c662318c4960a0f8f814e16e43155029ca35/input/script_utils.go#L1754-L1771

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yeah we should add unit level tests here to ensure that this func is able to sign+verify using the same routines used to create the txns+scripts in the first place.

@github-project-automation github-project-automation bot moved this from 🆕 New to 👀 In review in Taproot-Assets Project Board Feb 25, 2026
@GeorgeTsagk
Copy link
Copy Markdown
Member Author

GeorgeTsagk commented Mar 4, 2026

Added a gist explaining the 2nd level situation, focused around the sighash of the pre-signed 2nd level HTLC transaction: https://gist.github.com/GeorgeTsagk/9775947b6b9d8cc07cd23e038a246719

Will soon push a version using the sighash_all approach (HTLCs pay their own fee)

@GeorgeTsagk
Copy link
Copy Markdown
Member Author

Breach itest has now been moved here, no need for a Lit PR

We add the missing cases for resolving contracts that correspond to
revoked offered/accepted HTLCs, and revoked HTLCs that have been taken
to the second level.

For signing, we distinguish between normal and breach scenarios: normal
force close sweeps use scriptspend (control block present), while breach
sweeps use keyspend (no control block). The breach path is auto-detected
by checking if both SingleTweak and DoubleTweak are present in the sign
descriptor.

We also supply the HTLC index as a single tweak, to be applied by the
signer later. This is crucial for matching the taproot internal key that
was placed in the commitment during creation.
A short intro: For sweeping revoked states the breach arbitrator of LND
crafts 3 different sweep transactions:
a) spendAll: spends commitment outputs (local, remote) and all HTLCs
b) spendCommitOuts: spends only the commitment outputs (local, remote)
c) spendHTLCs: spends only the HTLCs

Initially LND broadcasts version a) of the sweep. If that is not
confirmed within a few blocks(4) then it will broadcast b) and c). This
serves as a pinning attack mitigation.

The tricky part for taproot assets related to the call sequence of
"NotifyBroadcast". That call finalizes the proofs for the transfer given
the txid of the transaction that contains it. Now, we have multiple
competing sweeps fighting to get in a block, so we are uncertain about
which one is going to make it on-chain.

By changing LND into calling NotifyBroadcast after confirming any sweep
transaction, we have a way of certainly telling which proofs need to be
crafted. Given that the transaction is already confirmed, we need to
signal to the NotifyBroadcast call that we do not wish to broadcast, as
that is prone to fail.

The new skipBroadcast flag serves as that signal, and is set by the
caller (LND).
Add a unit test that performs a full sign+verify round-trip for all
three revocation sweep descriptor types (offered, accepted, and
second-level HTLC). The test generates real key material, derives the
revocation signing key using the same routines used in production
(DeriveRevocationPrivKey + TweakPrivKey), and verifies the resulting
schnorr signature against the taproot output key from the sweep
descriptor.

This validates that the sweep descriptor functions produce script trees
whose taproot output keys are consistent with the private key
derivation path, ensuring that breach sweeps will produce valid
signatures at runtime.
…ent caching

Add SigHashDefaultHTLCsRequired (4) and SigHashDefaultHTLCsOptional (5)
feature bits for negotiating SigHashDefault on HTLC second-level
transactions. The optional bit is advertised locally.

Add a SigHashDefault field (TlvType6) to the Commitment record so the
negotiated flag is cached in the commitment blob and survives restarts.
This allows the HtlcSigHashType method to return the correct sighash
type during startup before the peer reconnects and channel_reestablish
is exchanged.
Implement Server.HtlcSigHashType (lnwallet.AuxSigner interface) which
checks live feature negotiation state first, then falls back to decoding
the commitment blob's cached SigHashDefault flag. Includes a nil guard
for the startup race when tapd config is not yet initialized.

Thread sigHashDefault through the commitment and leaf creation paths:

- aux_funding_controller: store sigHashDefault from negotiated features
  in pendingAssetFunding and pass through to initial commitment blobs.
- commitment.go: thread through GenerateCommitmentAllocations and
  SanityCheckAmounts so HtlcIsDust uses the correct fee threshold.
- aux_leaf_creator.go: extend FetchLeavesFromRevocation to accept
  AuxChanState, CommitmentKeyRing, and *wire.MsgTx so second-level
  HTLC aux leaves can be computed at runtime via populateSecondLevelLeaves.
  Thread sigHashDefault through FetchLeavesFromView and ApplyHtlcView.

Bump DefaultOnChainHtlcSat from 1x to 6x dust limit to provide headroom
for baked-in second-level HTLC fees under SigHashDefault.
When importing already-confirmed channel transactions (commitment and
second-level HTLC txs) during breach handling, the transactions have
placeholder witnesses that fail standard proof verification. Add
infrastructure to bypass verification in these cases:

- proof/verified.go: add AssumeVerifiedAnnotatedProofs which wraps
  proofs as verified without running the proof verifier.
- proof/verifier.go: add SkipExclusionProofVerification option and
  refactor VerifyProofIntegrity to call inclusion, split root, and
  exclusion verification separately so exclusion can be skipped.
- tapfreighter/parcel.go: add WithSkipProofVerify option for
  PreAnchoredParcel. When set, the parcel starts at
  SendStateStorePreBroadcast and propagates the flag.
- tapfreighter/chain_porter.go: use AssumeVerifiedAnnotatedProofs
  instead of VerifyAnnotatedProofs when SkipProofVerify is set.
- tapchannel/aux_closer.go: thread variadic PreAnchoredParcelOpt
  through shipChannelTxn so callers can pass WithSkipProofVerify.
Handle the TaprootHtlcSecondLevelRevoke resolution type in the aux
sweeper to complete the breach justice flow for asset channels where
HTLCs have been taken to the second level.

Key changes in aux_sweeper.go:

- ResolveContract(TaprootHtlcSecondLevelRevoke): create second-level
  virtual packets, compute the aux leaf from the second-level tx, and
  replace commitment-level asset outputs with second-level outputs so
  the revocation sweep signs against the correct script keys.

- importSecondLevelHtlcTx: import the second-level HTLC transaction
  into the proof archive, building a proof that chains from the
  commitment proof to the second-level output.

- registerAndBroadcastSweep: fix PrevID.OutPoint on first-level sweep
  vPackets to match the actual BTC input outpoint. For second-level
  revoke inputs, the vPacket's PrevID still references the commitment
  HTLC output, but the justice tx actually spends the second-level tx
  output. Without this fixup the Porter cannot find the input proof.

- Pass WithSkipProofVerify when skipBroadcast is true (the justice tx
  is already confirmed, placeholder witnesses fail verification).
The default 30-second RPC timeout in lndclient is too short when the
integrated daemon restarts after many blocks have been mined while it
was offline. The chain sync during GetInfo can easily exceed 30 seconds
when catching up on 100+ blocks. Increase to 2 minutes to match
production configurations and prevent spurious restart failures during
integration tests.
Extend the custom_channels_breach integration test to cover revoked
HTLC outputs that have been advanced to second level. The test now:

- Creates hodl invoices on both sides to place HTLCs on the commitment
  before the DB backup (breach state).
- Settles the hodl invoices and sends an additional keysend to revoke
  the HTLC state.
- Suspends Charlie before the breach so Dave can advance his HTLCs to
  second level without Charlie's justice tx interfering.
- Mines ~100 blocks in batches to let Dave's HTLC timeout resolvers
  broadcast pre-signed second-level transactions.
- Resumes Charlie who detects the breach and sweeps commitment outputs
  plus second-level HTLC outputs.

Also adds a dedicated Zane node as proof courier (stays online when
Charlie is suspended) and SuspendNode to IntegratedNetworkHarness.
@GeorgeTsagk
Copy link
Copy Markdown
Member Author

Had to update some of the test assertions

Now that we bump DefaultOnChainHtlcSat amount some of the test expectations had to reflect the change.

@GeorgeTsagk
Copy link
Copy Markdown
Member Author

Realized that some of the custom channels itests failures on CI aren't flakes: Some actual test assertions need to change since the HTLC anchor amount was bumped.

Will address them asap, shouldn't be a blocker for reviewers.

Copy link
Copy Markdown
Contributor

@gijswijs gijswijs left a comment

Choose a reason for hiding this comment

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

So I did a thorough review of this PR.

The core cryptographic operations are sound but the surrounding infrastructure needs rework.

I've pointed out all issues with inline comments. I also have some nits wrt the commits (commitnit), which you'll find below:

Typos in commit message for commit d728fae:

The tricky part for taproot assets related to the call sequence of "NotifyBroadcast".

Shouldn't that read "is related"?

we need to signal to the NotifyBroadcast call that we do not wish to broadcast, as that is prone to fail.

prone to fail is an understatement. It will fail and makes no sense whatsoever.

ctx := context.Background()
secondLevelTxHash := secondLevelTx.TxHash()

// Check if already imported.
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.

Nice dedup guard here. The same pattern is needed in registerAndBroadcastSweep — it also goes through shipChannelTxn → LogPendingParcel → InsertAssetTransfer (plain INSERT), so duplicate NotifyBroadcast calls after restart will insert duplicate transfer rows. See the existing TODO at line 3048.

signDesc, vIn, &a.cfg.ChainParams, tapTweak,
)

// In this case, the witness isn't special, so we'll set
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.

nit:
I don't like this comment. "special" does a lot of heavy lifting without clarifying what ismeant by it.

// This is a normal scriptspend (not a breach keyspend), so the witness follows the standard structure.
// Set the control block on the leaf script that applySignDescToVIn prepared

},
SuccessTapLeaf: tree.SuccessTapLeaf,
TimeoutTapLeaf: tree.TimeoutTapLeaf,
AuxLeaf: tree.AuxLeaf,
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.

nit:
Why are we carrying the AuxLeaf value here. It has no actual usage here, it's only there because we are reusing the lnd stuct.

Comment on lines +1173 to +1186
// IMPORTANT: We must match the creation flow exactly:
// 1. Create script tree with UNTWEAKED keyring
// 2. Then apply HTLC index tweak to the tree's internal key
//
// During creation, GenTaprootHtlcScript is called with the untweaked
// keyring, then TweakHtlcTree applies the index tweak. We must do
// the same here.
//
// For TaprootHtlcAcceptedRevoke (htlc.Incoming=true in remote's log),
// this means incoming to us (they're sending to us).
// On remote's commitment with them sending, GenTaprootHtlcScript uses:
// isIncoming && whoseCommit.IsRemote() → SenderHTLCScriptTaproot
// with parameters: RemoteHtlcKey, LocalHtlcKey (in that order!)
// where RemoteHtlcKey = sender (them), LocalHtlcKey = receiver (us)
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.

ultranit: This comment is way longer than the comment at the same step in htlcOfferedRevokeSweepDesc. Shouldn't both comments be similar?

// WITHOUT the aux leaf (createSecondLevelHtlcAllocations passes
// None). The aux leaf only affects the BTC-level on-chain output,
// not the asset-level script key derivation.
_ = auxLeaf // Used at BTC level, not needed for asset signing.
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.

Why not remove the argument altogether? This does validate my earlier point about carrying the auxLeaf value. At the ASSET-level auxLeaf has no meaning.

// This is used for importing confirmed second-level HTLC transactions in
// breach scenarios where exclusion proofs for counterparty wallet outputs
// cannot be constructed.
func WithSkipExclusionProofVerification() ProofVerificationOption {
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.

Similar comment as with AssumeVerifiedAnnotatedProofs. This is a public function with no enforcement that it's only used for confirmed channel transactions. Could allow importing proofs where the same asset appears in multiple outputs.

Consider requiring a confirmation check parameter, or (in this case possible, not with AssumeVerifiedAnnotatedProofs) consider making this private.

// the HTLC script keys needed for a valid asset witness. The
// BTC-level transaction is already confirmed on-chain, which
// serves as proof of validity.
return shipChannelTxn(
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.

Nothing prevents these proofs from being later re-verified, exported, or served to peers where they would fail verification, right?

// This mirrors what RawTxInTaprootSignature does
// internally.
tapTweak := desc.scriptTree.TapTweak()
taprootPriv := txscript.TweakTaprootPrivKey(
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.

Instead of manually deriving the private key, construct a full virtual packet with both tweak unknowns set via applySignDescToVIn, then call SignVirtualPacket (the actual production signer), and verify the resulting signature against the expected public key. That closes the loop — you'd be testing that the PSBT signer interprets the unknowns the same way applySignDescToVIn intends.


log.Debugf("signing vPacket for input=%v",
limitSpewer.Sdump(vIn.PrevID))
log.Infof("signing vPacket[%d]: isBreach=%v, "+
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.

Consider logging singleTweak and signingKey at Trace level instead of Info.

}
}
}
t.Logf("Found %d second-level txns total", len(secondLevelTxns))
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.

secondLevelTxns populated and logged but never asserted. Add require.NotEmpty.

@lightninglabs-deploy
Copy link
Copy Markdown

@Roasbeef: review reminder
@GeorgeTsagk, 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

Projects

Status: 👀 In review

Development

Successfully merging this pull request may close these issues.

5 participants