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

Breach Arbiter Sweep 2nd layer HTLCs #264

Merged
merged 15 commits into from Sep 21, 2017

Conversation

cfromknecht
Copy link
Contributor

@cfromknecht cfromknecht commented Aug 24, 2017

This PR builds upon the initial work of #241 by enabling the breach arbiter to sweep 2nd layer HTLCs. In it's current state, this PR:

  • sweeps 2nd layer HTLCs from revoked commitment states.
  • uses proper fee estimation when generating justice and commit sweep transactions.
  • uses updated BOLT 5 penalty txn fee calculation for justice txn fee.
  • adds a SpendableOutput interface that helps decouple transaction creation from the types of outputs being spent.
  • refactors txn generation logic to be more flexible w/ regard to spending these various types of outputs.
  • delays and memoizes the computation of the witness generation function needed to spend an output of interest.
  • add config for breach arbiter to enable more granular test cases and interleaving of different subsystems in the future.

Looking into a fix for issue #256 as this seems like the appropriate time to resolve this. A fix has been implemented, though it will be added in a separate PR to keep this one from spreading further.

This PR builds upon #308 which adds HodlHTLC mode. The modified integration test (testRevokedCloseRetributionRemoteHodl) requires the remote node to be started with the --htlchodl flag in order to properly test that we are sweeping extended-but-unsettled HTLCs.

@mention-bot
Copy link

@cfromknecht, thanks for your PR! By analyzing the history of the files in this pull request, we identified @Roasbeef, @bryanvu and @cjamthagen to be potential reviewers.

@cfromknecht cfromknecht force-pushed the sweep-2nd-layer-htlcs branch 4 times, most recently from f06fa4d to d80a41f Compare August 28, 2017 19:17
Copy link
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

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

Nice work! Most of my comments are pretty minor: mostly style related, constant usage, etc.

I'll wait until you've finished up the remainder of the TODO items (tests, the slight decoupling) to start the next review cycle.

breacharbiter.go Outdated
return nil
}

func (b *breachArbiter) waitForPendingCloseConfs(currentHeight int32) error {
Copy link
Member

Choose a reason for hiding this comment

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

Missing a godoc comment on this new method. I dig the code move though, makes the Start method much more digestible.

}

// TODO(conner): move responsibility of channel closure into
Copy link
Member

Choose a reason for hiding this comment

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

As is now, it seems the this is unnecessary as the goroutine within the LightningChannel struct should block until it's received a signal that the breachArbiter has written the retribution info to disk.

Copy link
Member

Choose a reason for hiding this comment

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

Actually, scratch that. I remember now that we started to draft up this approach, but abandoned it at the might for the sake of minimizing that prior PR.

Can you create an issue describing this case and what needs to be done to address it? Thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Finally got around to this 🐢🐢🐢, peep le issue #327

breacharbiter.go Outdated
witnessFunc lnwallet.WitnessGenerator
}

// newBreachedOutput assembles new breachedOutput that can be used by the breach
Copy link
Member

Choose a reason for hiding this comment

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

assembles new -> assembles a new

breacharbiter.go Outdated
// deterministically generate a valid witness for each output. This will
// allow the breach arbiter to recover from failures, in the event that
// it must sign and broadcast the justice transaction.
var htlcOutputs = make([]*breachedOutput, nHtlcs)
Copy link
Member

Choose a reason for hiding this comment

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

Any particular reason for this initialization style? Could instead just be:

htlcOutputs := make([]*breachedOutput, nHtlcs)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Typically I use var to alert the reader to the fact that the variable is mutable, which helps to logically categorize them (especially in debugging+review) from static references, e.g. :=. Most often, the scope in which a variable is modified is limited to the following stanza, which permits the reader to assume that following stanza modifies or constructs the mutable variable being introduced. Even if the variable is modified much later in the function, the reader is at least alerted to which variables are intended to be altered dynamically. Counterintuitively, this in fact gives greater confidence as to which variables can be assumed to be static, which reduces working complexity and cognitive load on behalf of the reader. Otherwise, one more or less has to assume that any variable assigned with := could be mutable. There are certainly exceptions, but in general I've found it to be a very helpful form of self-documentation. Happy to change if we prefer the brevity of define+assign.

breacharbiter.go Outdated
)
}

// TODO(conner) remove dependency on channel snapshot after decoupling
Copy link
Member

Choose a reason for hiding this comment

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

Minor nit on the TODO's added in this commit/PR: customarily in the project the format we use is TODO(nick): (notice the colon there). If we all use the same format, then it'll be easy for us to grep through later as we only have a single pattern to look for.

Plus, my current vimconfig detects this pattern and high-lights it distinctly ^_^

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Noted. 😂

breacharbiter.go Outdated
// witness_header_size.
var txWeight uint64 = 4*53 + 2
// Add receiver script witness and tx input
txWeight += 325 + 4*41
Copy link
Member

Choose a reason for hiding this comment

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

Similar comment here to that above: line spacing and magic numbers.

breacharbiter.go Outdated
// sweepSpendableOutputsTxn creates a signed transaction from a sequence of
// spendable outputs by sweeping the funds into a single p2wkh output.
func (b *breachArbiter) sweepSpendableOutputsTxn(
txWeight uint64,
Copy link
Member

Choose a reason for hiding this comment

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

Minor style nit: I think the first param can safely lie on the first line w/o going over the character limit.

breacharbiter.go Outdated
// sweepSpendableOutputsTxn creates a signed transaction from a sequence of
// spendable outputs by sweeping the funds into a single p2wkh output.
func (b *breachArbiter) sweepSpendableOutputsTxn(
txWeight uint64,
Copy link
Member

Choose a reason for hiding this comment

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

It seems that this function accounts for the weight of the inputs added (with their witnesses), but not the weight of the added outputs.

Copy link
Member

Choose a reason for hiding this comment

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

To remedy this, we can instead modify the param to be the totalWitnessWeight alone. Then within this function, we can craft the sweep transaction and compute the total weight of that. The final weight we'll use for fee estimation is the sum of both of these computed weights.


return sweepTx, nil
return txn, nil
Copy link
Member

Choose a reason for hiding this comment

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

Before returning transaction for singing, we should ensure that the transaction itself is sane. We can use blockchain.CheckTransactionSanity for this purpose. This'll catch things like a negative output due to a very high estimated fee.

In general whenever crafting transactions like this, we'll need to ensure that we don't try to create negative valued outputs, the CheckTransactionSanity function can do this job for us.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Didn't realize this was available, makes a lot of sense! :)

breacharbiter.go Outdated

// With the fee calculated, we can now create the transaction using the
// information gathered above and the provided retribution information.
var txn = wire.NewMsgTx(2)
Copy link
Member

Choose a reason for hiding this comment

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

tnx := wire.NewMsgTx(2)

@cfromknecht cfromknecht force-pushed the sweep-2nd-layer-htlcs branch 8 times, most recently from b4a163e to 2351449 Compare August 30, 2017 23:42
breacharbiter.go Outdated

// waitForPendingCloseConfs dispatches confirmation notification subscribers
// that mark any pending channels as fully closed when signaled.
func (b *breachArbiter) waitForPendingCloseConfs(currentHeight int32) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

The name wiatFor.. indicates that the method is synchronous, maybe call it watchForPendingCloseConfs

breacharbiter.go Outdated
// First, record the breach information and witness type for the local
// channel point. This will allow us to completely generate a valid
// witness in the event of failures, as it will be persisted in the
// retribution store. Here we use CommitmentNoDelay since
Copy link
Contributor

Choose a reason for hiding this comment

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

since..?

breacharbiter.go Outdated
@@ -1207,8 +1338,7 @@ func (bo *breachedOutput) Encode(w io.Writer) error {
return err
}

if err := lnwallet.WriteSignDescriptor(
w, &bo.signDescriptor); err != nil {
if err := lnwallet.WriteSignDescriptor(w, bo.signDesc); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

if line is too long, then maybe

err := lnwallet.WriteSignDescriptor(w, bo.signDesc)
if err != nil {

is preferred?

@@ -1,3 +1,5 @@
// +build !rpctest
Copy link
Contributor

Choose a reason for hiding this comment

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

I thought this flag was for integration tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct! This one is saying to not include the unit tests in this file when compiling the integration tests, note the exclaimation point :)

Copy link
Contributor

Choose a reason for hiding this comment

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

ah, right XD

@cfromknecht cfromknecht force-pushed the sweep-2nd-layer-htlcs branch 5 times, most recently from 061f5b7 to 026ddd6 Compare September 13, 2017 11:11
breacharbiter.go Outdated
Store RetributionStore

// GenSweepScript generates the receiving scripts for swept outputs.
GenSweepScript func(lnwallet.WalletController) ([]byte, error)
Copy link
Member

Choose a reason for hiding this comment

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

Don't think this actually needs to take a WalletController as an argument. The function itself can simply close over the WalletController and take no arguments.

breacharbiter.go Outdated
// HtlcSwitch gives the breach arbiter access to shutdown any channel
// links for which it detects a breach, ensuring no further activity
// will continue across the link.
HtlcSwitch *htlcswitch.Switch
Copy link
Member

Choose a reason for hiding this comment

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

We don't need the entire switch. Instead, we can simply take something with identical arguments to CloseLink.

breacharbiter.go Outdated

// Wallet facilitates sweep transaction publication and further
// facilitates the generation of receiver scripts when sweeping funds.
Wallet *lnwallet.LightningWallet
Copy link
Member

Choose a reason for hiding this comment

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

The Wallet itself can similarly be omitted. From the PoV of the breach arb, the only functionality it needs from the wallet is `PublishTransaction.

@cfromknecht cfromknecht force-pushed the sweep-2nd-layer-htlcs branch 3 times, most recently from 5fad2d9 to 4449a0e Compare September 20, 2017 00:32
@cfromknecht cfromknecht changed the title WIP: Breach Arbiter Sweep 2nd layer HTLCs Breach Arbiter Sweep 2nd layer HTLCs Sep 20, 2017
@cfromknecht cfromknecht force-pushed the sweep-2nd-layer-htlcs branch 2 times, most recently from a8f7ad5 to dd56302 Compare September 20, 2017 02:08
  This commit alters the existing retribution
  post breach conf test case with the intention
  of testing the correct response in event that
  the remote party broadcasts a prior state
  while HTLCs have been extended. This serves
  as a preliminary integration for an expansion
  of the breach arbiter integration tests.

  The primary change involves using the new
  htlchodl mode for debugging, which causes the
  remote peer to ignore any intent to settle
  incoming HTLCs. The result is that any
  payments sent to the remote party are held in
  limbo, allowing us to test for these
  conditions more accurately.

  Currently the test case only tests that the
  justice transaction is mined. After we have
  fully integrated the breach arbiter to sweep
  2nd layer HTLCs, this test will be altered
  to check for spends from the appropriate
  inputs.
  This commit also adds a BreachConfig to abstract
  the instantiation of the breach arbiter, as well
  as various formatting improvements.
        This commit also adds an incoming flag to
        HtlcRetribution struct to allow the breach arbiter to
        generate the appropriate witness based on the htlc's
        directionality.

        It also ensures that the size of the htlc retribution
        slice is now determined by the size of the number of
        htlcs present in the revoked snapshot, which fixes a
        minor bug that could lead to nil pointer deferences.
Copy link
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

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

LGTM 🎉

@Roasbeef Roasbeef merged commit 94b54f0 into lightningnetwork:master Sep 21, 2017
@cfromknecht cfromknecht deleted the sweep-2nd-layer-htlcs branch December 19, 2019 11:58
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.

None yet

4 participants