Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Anchor-outputs (1/3): Refactoring chain backend to extract PackageTemplate #642

Merged

Conversation

ariard
Copy link

@ariard ariard commented Jun 19, 2020

This is 1/3 of anchor ouput implementation

I invite reviewers to only Concept ACK/Approach ACK until I open the complete serie of patchset to see if implementation makes sense.

Rational

Anchor ouptut proposal is a spec move towards introducing dynamic fee-setting for LN transactions. Actually, feerate is selected by channel opener through update_fee and must be adapted to follow network-wise mempool increases/decreases. This model is broken with regards to quick fee spikes, adversial counterparty exploiting mempool policy rules or update_fee in itself introducing funny attack vectors (Flood & Loot among others).

Anchor output modifies commitment transaction format by introducing a special per-party output for adjusting feerate through CPFP. Note, without base layer changes this is still insecure and can only be relied for favorable mempool congestion in case of intentional unilateral closure.

Implementing anchor output consists in both onchain changes to support CPFP logic and offchain ones to dynamically add this new anchor output. This CPFP logic and the further onchain backend will likely get more complex in the future both by security and fee efficiency (like "aggressive" aggregation, cross-commitment CPFP, don't-go-onchain-if-your-HTLC-is-non-economic, don't-bump-more-than-HTLC-in-danger, ...). So this PR envision this by removing code complexity from OnchainTxHandler to some new interface OnchainRequest/PackageTemplate mimicking "packets".

Motivations behind OnchainRequest/PackageTemplate is to provide a generic interface for OnchainTxHandler without this component being aware of actual transaction type processed. It's nice to introduce later pre-signed justice transactions for watchtowers or anchor output garbage-collecting transactions.

Interface holds high-level methods like package_weight or package_amounts. Concretely it kills the monster OnchainTxHandler::generate_claim_tx to get it ready for CPFP support. Current interface is an enum, we may want to switch to trait object but it comes with trade-offs in Rust (thoughts ?)

PR scope

This PR SHOULD NOT introduce any behavior change.

Overview of PR:

  • move InputDescriptors in new onchain_utils
  • replace InputDescriptors by PackageTemplate, switching from per-input claim to holistic package make it easier to compute weight, which is less footgunish
  • replace ClaimTxBumpMaterial/ClaimRequest by a unique structure, easing aggregatiion
  • extract fee computing utilitties from `OnchainTxHandler

lightning/src/ln/channel.rs Outdated Show resolved Hide resolved
lightning/src/ln/onchain_utils.rs Outdated Show resolved Hide resolved
lightning/src/ln/onchain_utils.rs Outdated Show resolved Hide resolved
},
}
}
pub(crate) fn package_split(&mut self, outp: &BitcoinOutPoint) -> Option<PackageTemplate> {
Copy link
Contributor

Choose a reason for hiding this comment

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

This method needs comments, because package_split is not descriptive.

lightning/src/ln/onchain_utils.rs Outdated Show resolved Hide resolved
}
}
}
pub(crate) fn package_merge(&mut self, mut template: PackageTemplate) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as package_split.

// Don't claim a outpoint twice that would be bad for privacy and may uselessly lock a CPFP input for a while
if let Some(_) = self.claimable_outpoints.get(&req.outpoint) { log_trace!(logger, "Bouncing off outpoint {}:{}, already registered its claiming request", req.outpoint.txid, req.outpoint.vout); } else {
if let Some(_) = self.claimable_outpoints.get(req.content.outpoints()[0]) { log_trace!(logger, "Bouncing off outpoint {}:{}, already registered its claiming request", req.content.outpoints()[0].txid, req.content.outpoints()[0].vout); } else {
Copy link
Contributor

Choose a reason for hiding this comment

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

you keep accessing the first outpoint, so I'd just put it in a variable for easier legibility

@ariard ariard changed the title [anchor-output] Refactoring chain backend to extract OnchainRequest/PackageTemplate Anchor-output (1/3): Refactoring chain backend to extract OnchainRequest/PackageTemplate Sep 30, 2020
@ariard ariard force-pushed the 2020-06-anchor-backend-refacto branch from 37d2f79 to e4c8b0b Compare September 30, 2020 00:54
@ariard ariard changed the title Anchor-output (1/3): Refactoring chain backend to extract OnchainRequest/PackageTemplate Anchor-outputs (1/3): Refactoring chain backend to extract OnchainRequest/PackageTemplate Sep 30, 2020
Copy link
Member

@devrandom devrandom left a comment

Choose a reason for hiding this comment

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

Here are my initial thoughts on this PR

lightning/src/ln/onchain_utils.rs Outdated Show resolved Hide resolved
/// of the claim tx has its feerate increased. For the latter case, access to the whole package
/// sizea and pre-committed fee is required to compute an efficient bump.
#[derive(Clone, PartialEq)]
pub(crate) enum PackageTemplate {
Copy link
Member

Choose a reason for hiding this comment

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

There's a lot of matching going on in the implementation code below. Maybe it will be cleaner to have this be a hierarchy of types rather than an enum?

Copy link
Author

Choose a reason for hiding this comment

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

Good point, I think we can aggregate MalleableJusticeTx/CounterpartyHTLCTx on a lot of cases. Though I'm holding this on the discussion to switch to dynamic trait objects instead of an enum pseudo-interface.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Dynamic traits kinda seems like cheating here, can we not convert the trait variants to enum variants and place structs that each implement some PackageTemplateVariant trait for the inner structs instead? That would keep runtime cost the same but allow similar code cleanup.

lightning/src/ln/onchain_utils.rs Outdated Show resolved Hide resolved
};
bumped_tx.get_weight() + witnesses_weight
}
pub(crate) fn package_finalize<L: Deref, ChanSigner: ChannelKeys>(&self, onchain_handler: &mut OnchainTxHandler<ChanSigner>, value: u64, destination_script: Script, logger: &L) -> Option<Transaction>
Copy link
Member

@devrandom devrandom Nov 8, 2020

Choose a reason for hiding this comment

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

this creates a circular dependency between this class and OnChainTxHandler. Perhaps extract an interface from OnChainTxHandler to break this cycle? actually this seems to be a pre-existing issue.

Copy link
Author

Choose a reason for hiding this comment

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

Yes in the future I think we can just pass key_signer as we're just interested with signer for construction and signing. Maybe we can remove entirely signer from OnchainTxHandler and just give it to PackageTemplate constructor ?

Note, you still have get_fully_signed_htlc_tx/get_fully_signed_holder_tx blocking to remove the passing of OnchainTxHandler, it's a leftover from the past which is my bad. OnchainTxHandler shouldn't have any commitment transaction state directly exposed and it should be wrapped inside package. I started to do this move while writing this PR but it was already a big refactor...

Copy link
Member

Choose a reason for hiding this comment

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

yeah, sounds like a good topic for a separate PR

lightning/src/ln/onchain_utils.rs Outdated Show resolved Hide resolved
}
// ...else just increase the previous feerate by 25% (because that's a nice number)
} else {
let fee = previous_feerate * (predicted_weight as u64) / 750;
Copy link
Member

Choose a reason for hiding this comment

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

isn't this 1000/750 = 33% or did I miss something?

Copy link
Author

Choose a reason for hiding this comment

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

I think you're right :

  • (feerate=1000) * (weight=100) / (weight_denominator=1000) = 100
  • (feerate=1000) * (weight=100) / (weight_denominator=750) = 133

So good catch! I need to update tests so not marking at resolved yet. If you have a better idea on the % heuristic lmk it's quite arbitrary rn :)

Copy link
Member

Choose a reason for hiding this comment

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

well, to be obvious, this has to be high enough so it tracks feerate spikes within the expiration time, especially when there's an attack on the mempool. if I understand the code correctly, a bump happens on every block, right?

so, the desired bump % is dependent on the expiration time in blocks and the ratio of the spike to original feerate. one issue is if the original feerate was very low, let's say because the network was quiet. then perhaps we can get a very high spike ratio.

for example, with a 100x spike height and 144 block expiration, the bump should be around 3.2%. we could handle much higher spikes with a 10% increase - up to a (1.1 ^ 144) = 900000 feerate increase over 144 blocks.

we should have this in a const with some documentation on the const justifying the value based on assumptions on maximum spike height and minimum expiration time.

Copy link
Author

Choose a reason for hiding this comment

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

Bump happens accordingly to OnchainTxHandler::get_height_timer, if expiration is coming in next 3 blocks, bumps at every block.

What do you qualify as spike height here ? The original height at which the transaction is broadcast for first time ? Ideally you want the fee-bump to be exponential, save on fees if timelock is far away, be ready to burn as funds at stake when the expiration is really near.

Agree on documenting our assumptions.

Copy link
Member

Choose a reason for hiding this comment

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

apologies, missed this reply. I should have said "spike factor" - which I meant as the ratio of the feerate when we start to try to publish and the feerate at the peak of the spike.

I agree that the bumps should be exponential (and they are in your code, right?).

But my assumption about the bump frequency was incorrect. Looking at get_height_timer, if we have 144 blocks, we bump 9 times every 15 blocks, then twice every 3 blocks then another 3 times, for a total of 14 or so. I might be slightly off. so 1.25 ^ 14 = 22.

I'm not sure that's actually enough to be safe, since we do stand to lose the funds in the worst case. I think a factor of 100 would be better.

@ariard
Copy link
Author

ariard commented Nov 10, 2020

Thanks for review @devrandom, updated with at least the minor fixed.

I think before to go further with this PR review there is a discussion between using enum as a pseudo-interface or moving to trait objects. The downside with trait objects (experimented a bit for offchain generic output in #619) is you need a Box and a memory over-head. It might be okay for onchain stuff which isn't a hot path.

Note, I see this PR as a real blocker to work further on third-party watchtower support, future PackageTemplate should be DelegatedJusticeTx/HolderDLCTx. Once we resolve the code design point above, I would be glad to land this PR as soon as we can.

@devrandom
Copy link
Member

Regarding efficiency - I usually don't try to optimize this early in a project's lifetime. This especially applies to crypto code, where sign/encrypt/verify operations are usually the bulk of the CPU load.

@ariard ariard force-pushed the 2020-06-anchor-backend-refacto branch from 077c314 to de93efc Compare March 9, 2021 00:03
@ariard
Copy link
Author

ariard commented Mar 9, 2021

Updated at de93efc.

Currently, this is the main area where I'm curious of feedback. If we prefer trait objects, I'll need to re-work this PR, so first I would like to settle on trait objecs or enum as a abstract package interface.

I think before to go further with this PR review there is a discussion between using enum as a pseudo-interface or moving to trait objects. The downside with trait objects (experimented a bit for offchain generic output in #619) is you need a Box and a memory over-head. It might be okay for onchain stuff which isn't a hot path.

Copy link
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

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

Overall looks like a good refactor. Have a couple of API questions.

lightning/src/ln/onchain_utils.rs Outdated Show resolved Hide resolved
}
}

/// An enum to describe a claim content which is generated by ChannelMonitor and
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you expand the doc comments and add docs on each variant describing these in t a bit more detail? Does this datastructure restrict the possible things we can claim in one transaction (ie can we claim CounterpartyHTLCTx outputs in the same transaction as MalleableJusticeTx outputs, or do we always punish revoked counterpaty HTLC transactions via a MalleableJusticeTx PackageTemplate)? More docs would help here.

/// of the claim tx has its feerate increased. For the latter case, access to the whole package
/// sizea and pre-committed fee is required to compute an efficient bump.
#[derive(Clone, PartialEq)]
pub(crate) enum PackageTemplate {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Dynamic traits kinda seems like cheating here, can we not convert the trait variants to enum variants and place structs that each implement some PackageTemplateVariant trait for the inner structs instead? That would keep runtime cost the same but allow similar code cleanup.

lightning/src/ln/onchain_utils.rs Outdated Show resolved Hide resolved
lightning/src/ln/onchain_utils.rs Outdated Show resolved Hide resolved
pub(crate) height_timer: Option<u32>,
// Block height before which claiming is exclusive to one party,
// after reaching it, claiming may be contentious.
pub(crate) absolute_timelock: u32,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same here, shouldn't this be a function on PackageTemplate?

Copy link
Author

@ariard ariard Apr 18, 2021

Choose a reason for hiding this comment

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

Yeah, I'll should study if I can merge OnchainRequest with PackageTemplate, now that I've isolated outpoints and their solving materials in PackageSolvingData. Maybe, the original reason is to facilitate aggregation/fragmentation at block connect/disconnect.

@TheBlueMatt
Copy link
Collaborator

Regarding efficiency - I usually don't try to optimize this early in a project's lifetime. This especially applies to crypto code, where sign/encrypt/verify operations are usually the bulk of the CPU load.

This I agree with for CPU operations, but memory allocation operations are more tricky. My personal experience on Core made me very, very scared of too much allocation, its very easy to have a program that ends up with 3x RSS as is required from memory fragmentation and other overhead, and not really have an easy way to fix it - trying to reduce memory allocations after the fact can be a very challenging rebase, doubly so because there aren't any good tools for debugging or profiling these issues.

/// An enum to describe a claim content which is generated by ChannelMonitor and
/// used by OnchainTxHandler to regenerate feerate-bump transactions to settle claims.
///
/// Template may be either malleable (a justice tx, a counterparty HTLC tx) or lockdown (a holder htlc
Copy link
Member

Choose a reason for hiding this comment

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

Why is "counterparty HTLC tx" malleable, but "holder HTLC tx" not? is the term "HTLC tx" used differently in the two cases?

Copy link
Author

Choose a reason for hiding this comment

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

Holder HTLC tx only become malleable with anchor changes introduced in other branches of this patchset :)
Though they're only malleable under SIGHASH_SINGLE. Claiming transactions on counterparty HTLC outputs have always been malleable beyond witness satisfaction (i.e settup nLocktime correctly) ?

@ariard ariard force-pushed the 2020-06-anchor-backend-refacto branch from de93efc to 8b27973 Compare April 19, 2021 00:04
@ariard
Copy link
Author

ariard commented Apr 19, 2021

Okay pushed a refactored version of the refactor at 8b27973. It gets rides of code duplication by encapsulating outputs special solving data in a new PackageSolvingData. Still needs heavily documentation but will do once we agree on onchain_utils's structs.

Also, another point I did like with trait objects compare to enum was the ability to easily support other package/outputs types without this new logic being in-tree. Though, we can do it with enum to, e.g add CounterpartyPTLCOutput in PackageSolvingData that we can gate on #[cfg(feature = "ptlc")]

@ariard ariard changed the title Anchor-outputs (1/3): Refactoring chain backend to extract OnchainRequest/PackageTemplate Anchor-outputs (1/3): Refactoring chain backend to extract PackageTemplate Apr 20, 2021
@ariard
Copy link
Author

ariard commented Apr 22, 2021

Rebased at 238dd96. Working on API documentation and test coverage.

@ariard ariard force-pushed the 2020-06-anchor-backend-refacto branch from 7aae256 to 850460d Compare April 26, 2021 22:23
@TheBlueMatt TheBlueMatt added this to the 0.0.15 milestone May 4, 2021
Copy link
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

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

Note that a number of tests fail, and a few appear to hang.

@@ -1565,10 +1420,10 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
// *we* sign a holder commitment transaction, not when e.g. a watchtower broadcasts one of our
// holder commitment transactions.
if self.broadcasted_holder_revokable_script.is_some() {
let (claim_reqs, _) = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx);
let (claim_reqs, _) = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx, /*XXX(ariard) Option<height> */ 0);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I assume these weren't meant to slip in :)

Copy link
Author

Choose a reason for hiding this comment

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

Removed, they're markers for the remaining patchset. For height values passed they don't matter as we can't yet bump them.

@@ -286,9 +150,9 @@ pub struct OnchainTxHandler<ChannelSigner: Sign> {
// us and is immutable until all outpoint of the claimable set are post-anti-reorg-delay solved.
// Entry is cache of elements need to generate a bumped claiming transaction (see ClaimTxBumpMaterial)
#[cfg(test)] // Used in functional_test to verify sanitization
pub pending_claim_requests: HashMap<Txid, ClaimTxBumpMaterial>,
pub pending_claim_requests: HashMap<Txid, PackageTemplate>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

rustc doesn't like this - even though the whole module is only pub(crate) the fact that this includes a pub(crate) thing makes it sad. We can just make this pub(crate) instead.

counterparty_delayed_payment_base_key: PublicKey,
counterparty_htlc_base_key: PublicKey,
per_commitment_key: SecretKey,
input_descriptor: InputDescriptors,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Its a little confusing that a RevokedOutput could have type OfferedHTLC/ReceivedHTLC. Can we split out the type here or assert that the type is a Revoked... one in build (it looks like the non-revoked types are really just used for get_witnesses_weight).

Copy link
Author

Choose a reason for hiding this comment

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

So I dried-up InputDescriptors and instead I'm using new named constants. It turns out InputDescriptors and the associated helper get_witnesses_weight don't serve a real purpose. At detection, spend weights can be marked as static, we won't touch them anymore until transaction generation.

let weight = match self {
PackageSolvingData::RevokedOutput(ref outp) => { get_witnesses_weight(&[outp.input_descriptor]) },
PackageSolvingData::CounterpartyHTLCOutput(ref outp) => { get_witnesses_weight(if outp.preimage.is_some() { &[InputDescriptors::OfferedHTLC] } else { &[InputDescriptors::ReceivedHTLC] }) },
PackageSolvingData::HolderHTLCOutput(..) => { 0 },
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you add a comment describing why its ok that these are 0 here, or what the calling preconditions are for this function?

Copy link
Author

Choose a reason for hiding this comment

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

Added a comment, values should change with the remaining anchor patchset, introducing fee-bumping of holder outputs spending packages.

@jkczyz jkczyz assigned jkczyz and unassigned jkczyz May 4, 2021
@jkczyz jkczyz self-requested a review May 4, 2021 23:41
@ariard ariard force-pushed the 2020-06-anchor-backend-refacto branch 2 times, most recently from 0f0c0cf to f73d7ed Compare May 11, 2021 16:02
@codecov
Copy link

codecov bot commented May 11, 2021

Codecov Report

Merging #642 (499d84c) into master (f8450a7) will decrease coverage by 0.02%.
The diff coverage is 85.87%.

❗ Current head 499d84c differs from pull request most recent head 0065308. Consider uploading reports for the commit 0065308 to get more accurate results
Impacted file tree graph

@@            Coverage Diff             @@
##           master     #642      +/-   ##
==========================================
- Coverage   90.42%   90.40%   -0.03%     
==========================================
  Files          59       59              
  Lines       30173    30299     +126     
==========================================
+ Hits        27285    27392     +107     
- Misses       2888     2907      +19     
Impacted Files Coverage Δ
lightning/src/util/enforcing_trait_impls.rs 90.38% <ø> (ø)
lightning/src/routing/router.rs 96.05% <50.00%> (-0.12%) ⬇️
lightning/src/util/ser_macros.rs 91.11% <62.06%> (-5.63%) ⬇️
lightning/src/ln/channel.rs 88.24% <81.81%> (-0.06%) ⬇️
lightning/src/ln/functional_test_utils.rs 94.90% <88.88%> (-0.17%) ⬇️
lightning/src/ln/channelmanager.rs 83.19% <95.83%> (+0.13%) ⬆️
lightning/src/ln/functional_tests.rs 97.02% <97.43%> (-0.05%) ⬇️
lightning/src/chain/channelmonitor.rs 91.21% <100.00%> (+0.05%) ⬆️
lightning/src/chain/keysinterface.rs 94.96% <100.00%> (+0.04%) ⬆️
lightning/src/ln/msgs.rs 88.20% <100.00%> (-0.02%) ⬇️
... and 6 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f8450a7...0065308. Read the comment docs.

Copy link
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

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

This is great, but I think we can simplify the logic a bunch more and pull more out of channelmonitor. It may be that some of it makes more sense in a followup, but probably it could go in here.

#[derive(PartialEq, Clone, Copy)]
pub(crate) enum InputDescriptors {
RevokedOfferedHTLC,
RevokedReceivedHTLC,
OfferedHTLC,
ReceivedHTLC,
RevokedOutput, // either a revoked to_local output on commitment tx, a revoked HTLC-Timeout output or a revoked HTLC-Success output
RevokedOutput, // either a revoked to_holder output on commitment tx, a revoked HTLC-Timeout output or a revoked HTLC-Success output
Copy link
Collaborator

Choose a reason for hiding this comment

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

In the previous commit (while moving this block) you change it from to_holder to to_local, and then change it back here.

Copy link
Author

Choose a reason for hiding this comment

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

Right, corrected in "Add onchain_utils"

}

impl Writeable for RevokedOutput {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit here and below: can we implement this with impl_writeable!()?

Copy link
Author

Choose a reason for hiding this comment

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

Yep good idea, done.


impl Readable for RevokedOutput {
fn read<R: ::std::io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
let per_commitment_point = Readable::read(reader)?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

style nit: If we're reading this in-order we can also put the Readable::read calls inside the struct creation below.

let weight = match self {
PackageSolvingData::RevokedOutput(ref outp) => { get_witnesses_weight(&[outp.input_descriptor]) },
PackageSolvingData::CounterpartyHTLCOutput(ref outp) => { get_witnesses_weight(if outp.preimage.is_some() { &[InputDescriptors::OfferedHTLC] } else { &[InputDescriptors::ReceivedHTLC]}) },
// Note: Currently, weights of holder outputs spending witnesses aren't used
Copy link
Collaborator

Choose a reason for hiding this comment

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

If this is unused can we at least have a debug_assert!(false) instead of returning a bogus value?

Copy link
Author

Choose a reason for hiding this comment

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

Amount is exercised in generate_claim_tx so added a debug_assert(amt == 0) otherwise added for weight.

@@ -1571,7 +1571,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
// First, process non-htlc outputs (to_holder & to_counterparty)
for (idx, outp) in tx.output.iter().enumerate() {
if outp.script_pubkey == revokeable_p2wsh {
let revk_outp = RevokedOutput::build(per_commitment_point, per_commitment_key, self.counterparty_tx_cache.counterparty_delayed_payment_base_key, self.counterparty_tx_cache.counterparty_htlc_base_key, InputDescriptors::RevokedOutput, outp.value, None, self.counterparty_tx_cache.on_counterparty_tx_csv);
let revk_outp = RevokedOutput::build(per_commitment_point, per_commitment_key, self.counterparty_tx_cache.counterparty_delayed_payment_base_key, self.counterparty_tx_cache.counterparty_htlc_base_key, WEIGHT_REVOKED_OUTPUT, outp.value, None, self.counterparty_tx_cache.on_counterparty_tx_csv);
Copy link
Collaborator

Choose a reason for hiding this comment

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

This feels like even more of a layer violation than the original InputDescriptors enum. I think my original complaint about it was more about how its confusing to have two separate enums (and that you can have a non-Revoked InputDescriptor inside of a RevokedOutput which is obviously nonsensical). Instead, can we make RevokedOutput at least contain an enum so that the htlc field isn't an option and we don't need to store a weight passed in from another layer and can calculate it locally?

Copy link
Author

Choose a reason for hiding this comment

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

Addressed with new RevokedHTLCOutput and corresponding constructor

let witness_data = InputMaterial::Revoked { per_commitment_point, counterparty_delayed_payment_base_key: self.counterparty_tx_cache.counterparty_delayed_payment_base_key, counterparty_htlc_base_key: self.counterparty_tx_cache.counterparty_htlc_base_key, per_commitment_key, input_descriptor: InputDescriptors::RevokedOutput, amount: outp.value, htlc: None, on_counterparty_tx_csv: self.counterparty_tx_cache.on_counterparty_tx_csv};
claimable_outpoints.push(ClaimRequest { absolute_timelock: height + self.counterparty_tx_cache.on_counterparty_tx_csv as u32, aggregable: true, outpoint: BitcoinOutPoint { txid: commitment_txid, vout: idx as u32 }, witness_data});
let revk_outp = RevokedOutput::build(per_commitment_point, per_commitment_key, self.counterparty_tx_cache.counterparty_delayed_payment_base_key, self.counterparty_tx_cache.counterparty_htlc_base_key, WEIGHT_REVOKED_OUTPUT, outp.value, None, self.counterparty_tx_cache.on_counterparty_tx_csv);
let justice_package = PackageTemplate::build_package(commitment_txid, idx as u32, PackageSolvingData::RevokedOutput(revk_outp), PackageMalleability::Malleable, height + self.counterparty_tx_cache.on_counterparty_tx_csv as u32, true, 0, None, height);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Similarly - can we simplify this further and push more logic down into onchain_utils? eg even having channelmonitor aware of what types of outputs are and are not Malleable seems like split logic - ideally ChannelMonitor can only know about the transactions its looking at and then tell onchain_utils or onchaintx which of a given enum an output is and let it handle the rest (maybe with different constructors depending on the types out outputs being spent instead of one big constructor?).

Copy link
Author

Choose a reason for hiding this comment

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

Good call, this one is super straightforward to address. Instead of splitting constructor, I did embed malleability assignment based on output type. channelmonitor is now blind w.r.t to package malleability

@ariard ariard force-pushed the 2020-06-anchor-backend-refacto branch 2 times, most recently from 2d52570 to 59a27af Compare May 15, 2021 23:12
@ariard ariard force-pushed the 2020-06-anchor-backend-refacto branch from 59a27af to 9e6c186 Compare May 18, 2021 20:50
Copy link
Contributor

@jkczyz jkczyz left a comment

Choose a reason for hiding this comment

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

Mostly nits on chain_utils. Can take a closer look at later commits tonight.

lightning/src/ln/onchain_utils.rs Outdated Show resolved Hide resolved
lightning/src/ln/onchain_utils.rs Outdated Show resolved Hide resolved
lightning/src/ln/onchain_utils.rs Outdated Show resolved Hide resolved
lightning/src/ln/onchain_utils.rs Outdated Show resolved Hide resolved
lightning/src/ln/onchain_utils.rs Outdated Show resolved Hide resolved
lightning/src/ln/onchain_utils.rs Outdated Show resolved Hide resolved
lightning/src/ln/onchain_utils.rs Outdated Show resolved Hide resolved
lightning/src/ln/onchain_utils.rs Outdated Show resolved Hide resolved
lightning/src/chain/onchain_utils.rs Outdated Show resolved Hide resolved
lightning/src/chain/onchain_utils.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@jkczyz jkczyz left a comment

Choose a reason for hiding this comment

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

Code mostly looks good. Comments are primarily around doc confusion and code organization.

lightning/src/ln/onchain_utils.rs Outdated Show resolved Hide resolved
lightning/src/ln/onchain_utils.rs Outdated Show resolved Hide resolved
@@ -821,3 +822,104 @@ pub(crate) fn get_witnesses_weight(inputs: &[InputDescriptors]) -> usize {
}
tx_weight
}

/// Attempt to propose a fee based on consumed outpoints's values and predicted weight of the claiming
Copy link
Contributor

Choose a reason for hiding this comment

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

It's unclear what outpoints are being consumed from this context. Seems to be talking about a call site. Likewise throughout.

Copy link
Author

Choose a reason for hiding this comment

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

Yes, reword a bit, let me know if it's clearer.

lightning/src/ln/onchain_utils.rs Outdated Show resolved Hide resolved
lightning/src/ln/onchain_utils.rs Outdated Show resolved Hide resolved
lightning/src/ln/onchain_utils.rs Outdated Show resolved Hide resolved
lightning/src/chain/mod.rs Outdated Show resolved Hide resolved
@@ -347,7 +333,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {

// Compute new height timer to decide when we need to regenerate a new bumped version of the claim tx (if we
// didn't receive confirmation of it before, or not enough reorg-safe depth on top of it).
let new_timer = Some(Self::get_height_timer(height, cached_request.timelock()));
let new_timer = Some(onchain_utils::get_height_timer(height, cached_request.timelock()));
Copy link
Contributor

Choose a reason for hiding this comment

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

Why move this to onchain_utils if it is only used here? I think we should avoid "utils" modules as they tend to collect unrelated code.

Copy link
Author

@ariard ariard May 20, 2021

Choose a reason for hiding this comment

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

Right, made it part of the package interface as it's belonging to it. We don't have an other data struct with a height timer through the rest of chain/

@ariard ariard force-pushed the 2020-06-anchor-backend-refacto branch 4 times, most recently from 4f76fd3 to 3f47291 Compare May 21, 2021 03:01
lightning/src/chain/package.rs Outdated Show resolved Hide resolved
lightning/src/chain/package.rs Outdated Show resolved Hide resolved
lightning/src/chain/package.rs Outdated Show resolved Hide resolved
lightning/src/chain/package.rs Outdated Show resolved Hide resolved
lightning/src/ln/package.rs Outdated Show resolved Hide resolved
lightning/src/ln/package.rs Outdated Show resolved Hide resolved
lightning/src/ln/package.rs Outdated Show resolved Hide resolved
lightning/src/ln/package.rs Outdated Show resolved Hide resolved
Comment on lines 249 to 256
#[derive(PartialEq)]
enum OutputCategory {
Revoked,
CounterpartyOfferedHTLC,
CounterpartyReceivedHTLC,
HolderHTLC,
HolderFunding,
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Looking at how this is used, it doesn't really seem necessary. You can instead have a method on PackageSolvingData that takes another PackageSolvingData and returns whether they are compatible. No need to introduce the term "category" which isn't defined. This would also eliminate the redundancy between OutputCategory and PackageSolvingData.

Copy link
Contributor

Choose a reason for hiding this comment

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

BTW, such comparison can be accomplished for complex enum types like PackageSolvingData using std::mem::discriminant.

https://doc.rust-lang.org/std/mem/fn.discriminant.html

Copy link
Author

Choose a reason for hiding this comment

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

Okay finally dropped OutputCategory/OutputType! And leverage usage of std::mem::discriminant. There is still a bunch of special logic in output_type_eq to handle the forced-equality for revoked outputs.

@ariard ariard force-pushed the 2020-06-anchor-backend-refacto branch 2 times, most recently from ccae4e4 to b410e75 Compare May 24, 2021 05:33
lightning/src/chain/package.rs Outdated Show resolved Hide resolved
lightning/src/chain/package.rs Show resolved Hide resolved
lightning/src/chain/package.rs Outdated Show resolved Hide resolved
Comment on lines 299 to 300
PackageSolvingData::RevokedHTLCOutput(..) => { return true },
PackageSolvingData::RevokedOutput(..) => { return true },
Copy link
Contributor

Choose a reason for hiding this comment

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

If these variants only differ by one field but are considered "equal", would it be simpler to make that particular field an enum and collapse these two variants into one variant rather than repeat the six other fields across each?

Copy link
Author

Choose a reason for hiding this comment

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

I was anticipating future implementation of disaggregation-after-timelock to solve this transaction pinning : lightning/bolts#803. If this mitigation is implemented they won't be considered "equal" anymore.

@ariard ariard force-pushed the 2020-06-anchor-backend-refacto branch from b410e75 to c5f4c78 Compare May 25, 2021 23:53
Antoine Riard added 7 commits May 25, 2021 19:54
Package.rs aims to gather interfaces to communicate between
onchain channel transactions parser (ChannelMonitor) and outputs
claiming logic (OnchainTxHandler). These interfaces are data
structures, generated per-case by ChannelMonitor and consumed
blindly by OnchainTxHandler.
PackageTemplate aims to replace InputMaterial, introducing a clean
interface to manipulate a wide range of claim types without
OnchainTxHandler aware of special content of each.

This is used in next commits.
Duplicated code in onchain.rs is removed in next commits.
This commit replaces InputMaterial in both ChannelMonitor/
OnchainTxHandler.

This doesn't change behavior.
@ariard ariard force-pushed the 2020-06-anchor-backend-refacto branch from 0065308 to c40ebf1 Compare May 26, 2021 03:19
Copy link
Contributor

@jkczyz jkczyz left a comment

Choose a reason for hiding this comment

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

Looks good! Thanks for addressing everything.

Copy link
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

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

Looks good. Anything else can come in a followup.

@TheBlueMatt TheBlueMatt merged commit f074343 into lightningdevkit:master May 26, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants