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

[feature]: Include last state in close summary and exportchanbackup #7658

Open
kroese opened this issue May 1, 2023 · 4 comments
Open

[feature]: Include last state in close summary and exportchanbackup #7658

kroese opened this issue May 1, 2023 · 4 comments
Labels
backups enhancement Improvements to existing features / behaviour P2 should be fixed if one has time recovery Related to the backup/restoration of LND data (e.g. wallet seeds) safety General label for issues/PRs related to the safety of using the software SCB Related to static channel backup

Comments

@kroese
Copy link

kroese commented May 1, 2023

As described in issue #7426 (comment) there are situations where it would be very helpful if the last state was included in the data of the close summary and/or in exportchanbackup.

Ofcourse this extra data must be totally ignored by all existing commands like restorechanbackup. But in cases were you are 100 percent certain the channel state did not change, you would at least be able to manually extract the state from this data using an external tool like chantools.

I made a very expensive mistake with abandonchannel by assuming exportchanbackup would allow me to restore the state. If the state had been stored in the close summary or in that backup, it would have allowed me to recover my funds.

I cannot think of any downsides by implementing this feature, except for the close summary and the returnvalue of exportchanbackup growing a few bytes in size. Which seems a small tradeoff compared to the advantage it brings.

@kroese kroese added the enhancement Improvements to existing features / behaviour label May 1, 2023
@guggero guggero added safety General label for issues/PRs related to the safety of using the software recovery Related to the backup/restoration of LND data (e.g. wallet seeds) backups SCB Related to static channel backup labels May 1, 2023
@guggero guggero added this to the v0.17.0 milestone May 1, 2023
@saubyk saubyk added the P2 should be fixed if one has time label Aug 8, 2023
@saubyk saubyk removed this from the Medium Priority milestone Aug 8, 2023
@starius
Copy link
Collaborator

starius commented Oct 21, 2023

I researched this issue. It is work in progress and I want to share intermediate results.

We can put LocalForceCloseSummary or OpenChannel to such a backup. I explored both directions.

LocalForceCloseSummary

LocalForceCloseSummary is produced by lnwallet.LightningChannel.ForceClose method, which also force-closes the channel. I factored out the code to a separate public method GetLocalForceCloseSummary().

How to produce LocalForceCloseSummary in my dev branch.

// get one channel
channel, err := r.server.chanStateDB.FetchChannel(nil, chanPoint)
if err != nil {
	return nil, err
}

// get all channels
channels, err := r.server.chanStateDB.FetchAllOpenChannels()
if err != nil {
	return nil, err
}

chanMachine, err := lnwallet.NewLightningChannel(
	r.server.cc.Signer, channel, nil,
)
if err != nil {
	return nil, err
}

closeSummary, err := chanMachine.GetLocalForceCloseSummary()
if err != nil {
	return nil, err
}

Then, during recovery, the following code generates the commitment transaction:

var txBuf bytes.Buffer
if err := closeSummary.CloseTx.Serialize(&txBuf); err != nil {
	panic(err)
}
fmt.Println("CloseTx", hex.EncodeToString(txBuf.Bytes()))

Then, another transaction is needed to sweep the funds from the output of the commitment transaction. I found the code to generate inputs for that transaction in commit_sweep_resolver.go. There also 3 pieces of information missing in LocalForceCloseSummary needed to produce sweep tx: LeaseExpiry, IsInitiator and ChanType. They are passed to commit_sweep_resolver in method SupplementState. In my dev branch I added them to CommitOutputResolution as well as the code needed to generate the input of sweep transaction.

Using this method, the following code generated commit sweep transaction.

	inp, err := closeSummary.CommitResolution.MakeCommitSweepInput(uint32(bestHeight))
	if err != nil {
		panic(err)
	}

	sweepTx, err := r.server.sweeper.CreateSweepTx(
		[]input.Input{inp},
		sweep.FeePreference{
			ConfTarget: 6,
		},
		0,
	)
	if err != nil {
		return nil, err
	}
	var sweepTxBuf bytes.Buffer
	if err := sweepTx.Serialize(&sweepTxBuf); err != nil {
		panic(err)
	}
	fmt.Println("SweepTx", hex.EncodeToString(sweepTxBuf.Bytes()))

I tested it on testnet - both transactions work!

The remaining part is recovery of HTLC funds. I haven't looked into that yet.

OpenChannel

Then I discovered that chantools is working with OpenChannel structure. OpenChannel is a source of information for LocalForceCloseSummary (see the code above).

To get OpenChannel's, FetchChannel or FetchAllOpenChannels can be used. The structure was not originally JSON-friendly for encoding and decoding, but I managed to encode it in LND in my another branch. I used the value of OpenChannel in chantools forceclose (patched in my branch to use the file with OpenChannel's instead of channel.db).

Unfortunately it produced an invalid transaction. When I tried to broadcast it, I got the following error: "non-mandatory-script-verify-flag (Witness program hash mismatch)". I'm debugging this.

RevocationStore

OpenChannel also includes RevocationStore which can be used to punish another node if it broadcasts an old commitment transaction during channel recovery process. LND does not have a protection against that at the moment, so it makes sense for the remote node to try to play the system, if its balance in the latest channel state is small (e.g. 1%).

Size of RevocationStore is below 2 kilobytes thanks to O(log N) storage solution.

I think, this is an argument in favor of including whole OpenChannel into the backup.

Open questions

  1. How to serialize OpenChannel or LocalForceCloseSummary properly? I used JSON, but LND seems to use custom encoding formats across the code space.
  2. Which structure is the best structure to put into such a backup? OpenChannel or LocalForceCloseSummary?
  3. How to pass RevocationStore to lncli restorechanbackup to punish another node in case of cheating?

@guggero
Copy link
Collaborator

guggero commented Oct 23, 2023

Thanks for taking a closer look at this.

Just a few general points before I get to your questions:

  1. The idea here is to support exactly two situations: When a user calls abandonchannel by accident and the funding transaction gets published but the channel never becomes active. And when the user only has the channel.backup available but the remote peer is no longer reachable for responding to the DLP protocol request.
  2. In both situations you wouldn't have the information required to pull any HTLCs (same as when using DLP in general), so those would be lost.
  3. In both situations, it wouldn't be lnd reading the additional information and publishing the force-close transaction, as that would just be too dangerous in most situations (lnd can't find out if it's really safe to do so). Therefore we would encode the information in the channel.backup file but it would only be read by chantools upon a manual user interaction (so we can provide the user with a checklist to find out if it's safe to publish this potentially out-of-date force close transaction).
  4. The only information that should be added to the channel.backup file is the last state of the local commitment transaction (the local force-close transaction). Once that is published, lnd will know how to sweep that on its own, all the info required to do that is already in the channel.backup file.

To your questions:

How to serialize OpenChannel or LocalForceCloseSummary properly? I used JSON, but LND seems to use custom encoding formats across the code space.

Again, just the commitment transaction should be added. This can be done with a new version based encoding here:

func (s *Single) Serialize(w io.Writer) error {

How to pass RevocationStore to lncli restorechanbackup to punish another node in case of cheating?

If you've only got the channel.backup file, you won't be able to punish another node if they attempt to cheat. That's what you need watchtowers for. Or you make sure to not lose your database state in the first place.

@starius
Copy link
Collaborator

starius commented Nov 16, 2023

@guggero Thank you for feedback!

I have a working implementation storing close tx inside SCB: #8183
lightninglabs/chantools#95 to extract close tx

I tested it on testnet. The channel is in lncli pendingchannels! I guess the funds will be unlocked after timeout.

Could you check that I'm moving the right direction, please?

@guggero
Copy link
Collaborator

guggero commented Nov 27, 2023

Hey. I was away for a bit but will take a look at your PRs soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backups enhancement Improvements to existing features / behaviour P2 should be fixed if one has time recovery Related to the backup/restoration of LND data (e.g. wallet seeds) safety General label for issues/PRs related to the safety of using the software SCB Related to static channel backup
Projects
None yet
Development

No branches or pull requests

4 participants