Skip to content

Refactor output redeem type from TransactionWithInputInfo#3056

Closed
t-bast wants to merge 1 commit intomasterfrom
refactor-input-info
Closed

Refactor output redeem type from TransactionWithInputInfo#3056
t-bast wants to merge 1 commit intomasterfrom
refactor-input-info

Conversation

@t-bast
Copy link
Member

@t-bast t-bast commented Apr 4, 2025

We refactor TransactionWithInputInfo to extract the redeem information from the InputInfo class. This is necessary for taproot channels (and other future segwit versions) to easily integrate into our transaction class hierarchy: this way we can explicitly specify which kind of redeem paths are possible for each transaction type. For example, we can now express that a CommitTx uses either a segwit v0 input or a taproot input that we can spend via the key path.

Note that when we'll later introduce v3 commitments, we may have txs that can be spent:

  • via segwit v0 for non-taproot channels
  • via a taproot script path for taproot channels
  • AND via a taproot key path for v3 taproot channels

This is why we cannot simply pick a single one of the taproot redeem paths, we may need to support multiple of them in the future.

The core of this PR is just the following small type changes, that should be reviewed first:

  • we introduce the RedeemInfo trait
  • we remove the redeem information from InputInfo, which now becomes a trivial container for OutPoint + TxOut
  • we update CommitmentOutputLink to use an abstract RedeemInfo in the CommitmentOutput, which will make it easy to add taproot outputs without modifying the CommitmentOutputLink type
  • we take the commitmentFormat parameter when creating transactions, which makes it easy to pattern match on it to produce the right redeem information

Note that this highlighted that we unnecessarily recomputed HTLC scripts when creating claim-htlc transactions: we can directly leverage the existing CommitmentOutputs. I wondered if we could do this for every transaction creation, but we can't because we need to handle spending revoked commitments, for which we don't have a CommitmentSpec object and thus cannot create the set of CommitmentOutputs.

@t-bast t-bast requested a review from sstone April 4, 2025 16:48
@t-bast t-bast requested a review from pm47 April 4, 2025 16:54
@t-bast t-bast force-pushed the refactor-input-info branch from bb5db04 to 1cd554a Compare April 4, 2025 17:18
We refactor `TransactionWithInputInfo` to extract the redeem information
from the `InputInfo` class. This is necessary for taproot channels (and
other future segwit versions) to easily integrate into our transaction
class hierarchy: this way we can explicitly specify which kind of redeem
paths are possible for each transaction type. For example, we can now
express that a `CommitTx` uses either a segwit v0 input or a taproot
input that we can spend via the key path.

Note that when we'll later introduce v3 commitments, we may have txs
that can be spent:

- via segwit v0 for non-taproot channels
- via a taproot script path for taproot channels
- AND via a taproot key path for v3 taproot channels

This is why we cannot simply pick a single one of the taproot redeem
paths, we may need to support multiple of them in the future.

The core of this PR is just the following small type changes, that
should be reviewed first:

- we introduce the `RedeemInfo` trait
- we remove the redeem information from `InputInfo`, which now becomes
  a trivial container for `OutPoint` + `TxOut`
- we update `CommitmentOutputLink` to use an abstract `RedeemInfo` in
  the `CommitmentOutput`, which will make it easy to add taproot
  outputs without modifying the `CommitmentOutputLink` type
- we take the `commitmentFormat` parameter when creating transactions,
  which makes it easy to pattern match on it to produce the right redeem
  information

Note that this highlighted that we unnecessarily recomputed HTLC scripts
when creating claim-htlc transactions: we can directly leverage the
existing `CommitmentOutput`s. I wondered if we could do this for every
transaction creation, but we can't because we need to handle spending
revoked commitments, for which we don't have a `CommitmentSpec` object
and thus cannot create the set of `CommitmentOutput`s.
@t-bast t-bast force-pushed the refactor-input-info branch from 1cd554a to fe0252c Compare April 4, 2025 19:11
case class ToRemote(redeemInfo: RedeemInfo.TaprootScriptPathOrSegwitV0) extends CommitmentOutput
case class ToLocalAnchor(redeemInfo: RedeemInfo.TaprootKeyPathOrSegwitV0) extends CommitmentOutput
case class ToRemoteAnchor(redeemInfo: RedeemInfo.TaprootKeyPathOrSegwitV0) extends CommitmentOutput
case class InHtlc(redeemInfo: RedeemInfo.TaprootScriptPathOrSegwitV0, incomingHtlc: IncomingHtlc) extends CommitmentOutput
Copy link
Member

Choose a reason for hiding this comment

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

This does not look right: redeem information is logically attached to inputs, not outputs because (we don't know how they will be spent and for the same taproot outputs is could be through different script paths or through the key path.

Copy link
Member Author

Choose a reason for hiding this comment

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

redeem information is logically attached to inputs, not outputs

Those are outputs of the commitment transaction, which means they are outputs that we will spend if this commitment is confirmed. An output of a commitment transaction is simply an input of a 2nd-stage transaction, so I'm not sure your comment makes sense? We want to store this information in the output of a commitment, so that we can easily construct the corresponding input of the 2nd-stage transaction.

Also, this isn't introduced by this refactoring: we were already storing the redeemScript along with the CommitmentOutput in CommitmentOutputLink, I only made that clearer by bundling them together because:

  • they were always used in combination
  • and it made pattern matching and typing function arguments simpler

for the same taproot outputs is could be through different script paths or through the key path.

Do you have an example of that? On the contrary, this relies on the fact that we always only use a single spending path per output (otherwise our whole addSigs architecture wouldn't work either):

  • ToLocal: we can only spend it with our local key after the delay
  • ToRemote: we cannot spend it, we could in theory not fill this information
  • ToLocalAnchor: the only way we're spending that is with our key
  • ToRemoteAnchor: the only way we could spend that is after the 16-blocks delay (but we never really implemented claiming them)
  • InHtlc: the only way we can spend is by revealing the preimage (success path)
  • OutHtlc: the only way we can spend is by using the timeout path

Note that revoked commitments are handled differently: they are the only "other" way we can spend a commitment output.

Or am I misunderstanding what you're suggesting?

sealed trait TransactionWithInputInfo {
def input: InputInfo
/** Redeem information for the [[input]] this transaction spends. */
def redeemInfo: RedeemInfo
Copy link
Member

Choose a reason for hiding this comment

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

Why not make it a member of InputInfo ?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is actually linked to the previous comment: the RedeemInfo is first created as part of a CommitmentOutput, and later used to create a 2nd-stage transaction that contains the RedeemInfo and the outpoint details. But for the first step, when you create the commit tx itself, you're creating an output, so including input details is impossible (chicken-and-egg).

Put differently, we know the redeem path of each output when we create commitment transactions. We thus create redeem information to enrich outputs with details about how to spend them. We later can use this information to actually spend those outputs. I think that it makes a lot of sense when you put it that way?

@t-bast
Copy link
Member Author

t-bast commented May 7, 2025

Closing in favor of #3074

@t-bast t-bast closed this May 7, 2025
@t-bast t-bast deleted the refactor-input-info branch May 7, 2025 13:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants