-
Notifications
You must be signed in to change notification settings - Fork 407
Anchor-outputs (1/3): Refactoring chain backend to extract PackageTemplate #642
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
Conversation
lightning/src/ln/onchain_utils.rs
Outdated
}, | ||
} | ||
} | ||
pub(crate) fn package_split(&mut self, outp: &BitcoinOutPoint) -> Option<PackageTemplate> { |
There was a problem hiding this comment.
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
} | ||
} | ||
} | ||
pub(crate) fn package_merge(&mut self, mut template: PackageTemplate) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as package_split
.
lightning/src/ln/onchaintx.rs
Outdated
// 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 { |
There was a problem hiding this comment.
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
37d2f79
to
e4c8b0b
Compare
There was a problem hiding this 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
/// 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 { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
}; | ||
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> |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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
} | ||
// ...else just increase the previous feerate by 25% (because that's a nice number) | ||
} else { | ||
let fee = previous_feerate * (predicted_weight as u64) / 750; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 :)
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
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. |
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. |
077c314
to
de93efc
Compare
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.
|
There was a problem hiding this 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
} | ||
} | ||
|
||
/// An enum to describe a claim content which is generated by ChannelMonitor and |
There was a problem hiding this comment.
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.
lightning/src/ln/onchain_utils.rs
Outdated
/// 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 { |
There was a problem hiding this comment.
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
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, |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
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. |
lightning/src/ln/onchain_utils.rs
Outdated
/// 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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) ?
de93efc
to
8b27973
Compare
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 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 |
Rebased at 238dd96. Working on API documentation and test coverage. |
7aae256
to
850460d
Compare
There was a problem hiding this 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); |
There was a problem hiding this comment.
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 :)
There was a problem hiding this comment.
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.
lightning/src/chain/onchaintx.rs
Outdated
@@ -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>, |
There was a problem hiding this comment.
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.
lightning/src/chain/onchain_utils.rs
Outdated
counterparty_delayed_payment_base_key: PublicKey, | ||
counterparty_htlc_base_key: PublicKey, | ||
per_commitment_key: SecretKey, | ||
input_descriptor: InputDescriptors, |
There was a problem hiding this comment.
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
).
There was a problem hiding this comment.
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.
lightning/src/chain/onchain_utils.rs
Outdated
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 }, |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
0f0c0cf
to
f73d7ed
Compare
Codecov Report
@@ 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
Continue to review full report at Codecov.
|
There was a problem hiding this 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.
lightning/src/ln/onchain_utils.rs
Outdated
#[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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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"
lightning/src/ln/onchain_utils.rs
Outdated
} | ||
|
||
impl Writeable for RevokedOutput { | ||
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> { |
There was a problem hiding this comment.
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!()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep good idea, done.
lightning/src/ln/onchain_utils.rs
Outdated
|
||
impl Readable for RevokedOutput { | ||
fn read<R: ::std::io::Read>(reader: &mut R) -> Result<Self, DecodeError> { | ||
let per_commitment_point = Readable::read(reader)?; |
There was a problem hiding this comment.
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.
lightning/src/ln/onchain_utils.rs
Outdated
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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?).
There was a problem hiding this comment.
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
2d52570
to
59a27af
Compare
59a27af
to
9e6c186
Compare
There was a problem hiding this 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.
There was a problem hiding this 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
@@ -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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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/chain/onchaintx.rs
Outdated
@@ -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())); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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/
4f76fd3
to
3f47291
Compare
lightning/src/ln/package.rs
Outdated
#[derive(PartialEq)] | ||
enum OutputCategory { | ||
Revoked, | ||
CounterpartyOfferedHTLC, | ||
CounterpartyReceivedHTLC, | ||
HolderHTLC, | ||
HolderFunding, | ||
} |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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.
ccae4e4
to
b410e75
Compare
lightning/src/chain/package.rs
Outdated
PackageSolvingData::RevokedHTLCOutput(..) => { return true }, | ||
PackageSolvingData::RevokedOutput(..) => { return true }, |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
b410e75
to
c5f4c78
Compare
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.
c5f4c78
to
0065308
Compare
0065308
to
c40ebf1
Compare
There was a problem hiding this 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.
There was a problem hiding this 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.
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 orupdate_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 interfaceOnchainRequest
/PackageTemplate
mimicking "packets".Motivations behind
OnchainRequest
/PackageTemplate
is to provide a generic interface forOnchainTxHandler
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
orpackage_amounts
. Concretely it kills the monsterOnchainTxHandler::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:
InputDescriptors
in newonchain_utils
InputDescriptors
byPackageTemplate
, switching from per-input claim to holistic package make it easier to compute weight, which is less footgunishClaimTxBumpMaterial
/ClaimRequest
by a unique structure, easing aggregatiion