Repository: lightningnetwork/lnd
Pull request #9687 (2025-04-08 18:06:36)

`AuxTrafficShaper.PaymentBandwidth` uses HTLC view
## Description

This PR adds an extra argument to the `PaymentBandwidth` hook, which helps with computing a more precise aux htlc view, ultimately leading to more precise bandwidth results.

We expose the HtlcView from the lighting channel for the link to use when checking whether an HTLC can be added to the channel.

coderabbitai[bot]: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: skip review by coderabbit.ai -->

> [!IMPORTANT]
> ## Review skipped
> 
> Auto reviews are limited to specific labels.
> 
> <details>
> <summary>:label: Labels to auto review (1)</summary>
> 
> * llm-review
> 
> </details>
> 
> Please check the settings in the CodeRabbit UI or the `.coderabbit.yaml` file in this repository. To trigger a single review, invoke the `@coderabbitai review` command.
> 
> You can disable this status message by setting the `reviews.review_status` to `false` in the CodeRabbit configuration file.

<!-- end of auto-generated comment: skip review by coderabbit.ai -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=lightningnetwork/lnd&utm_content=9687):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai plan` to trigger planning for file edits and PR creation.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

Patch:
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 22b0f993b..cde390823 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -12,8 +12,8 @@ Steps for reviewers to follow to test the change.
 
 ### Code Style and Documentation
 - [ ] The change is not [insubstantial](https://github.com/lightningnetwork/lnd/blob/master/docs/code_contribution_guidelines.md#substantial-contributions-only). Typo fixes are not accepted to fight bot spam.
-- [ ] The change obeys the [Code Documentation and Commenting](https://github.com/lightningnetwork/lnd/blob/master/docs/development_guidelines.md#code-documentation-and-commenting) guidelines, and lines wrap at 80.
-- [ ] Commits follow the [Ideal Git Commit Structure](https://github.com/lightningnetwork/lnd/blob/master/docs/development_guidelines.md#ideal-git-commit-structure).
+- [ ] The change obeys the [Code Documentation and Commenting](https://github.com/lightningnetwork/lnd/blob/master/docs/code_contribution_guidelines.md#code-documentation-and-commenting) guidelines, and lines wrap at 80.
+- [ ] Commits follow the [Ideal Git Commit Structure](https://github.com/lightningnetwork/lnd/blob/master/docs/code_contribution_guidelines.md#ideal-git-commit-structure).
 - [ ] Any new logging statements use an appropriate subsystem and logging level.
 - [ ] Any new lncli commands have appropriate tags in the comments for the rpc in the proto file.
 - [ ] [There is a change description in the release notes](https://github.com/lightningnetwork/lnd/tree/master/docs/release-notes), or `[skip ci]` in the commit message for small changes.
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 9362cd9b8..b39a358d7 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -28,10 +28,6 @@ jobs:
         uses: actions/setup-go@v5
         with:
           go-version: '${{ env.GO_VERSION }}'
-          cache: 'false'
-
-      - name: cleanup space
-        run: rm -rf /opt/hostedtoolcache
 
       - name: Set env
         run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
diff --git a/docs/code_contribution_guidelines.md b/docs/code_contribution_guidelines.md
index e17ca66af..61f17deaa 100644
--- a/docs/code_contribution_guidelines.md
+++ b/docs/code_contribution_guidelines.md
@@ -5,8 +5,16 @@
 4. [Substantial contributions only](#substantial-contributions-only)
 5. [Development Practices](#development-practices)
    1. [Share Early, Share Often](#share-early-share-often)
-   1. [Development Guidelines](#development-guidelines)
-5. [Code Approval Process](#code-approval-process)
+   1. [Testing](#testing)
+   1. [Code Documentation and Commenting](#code-documentation-and-commenting)
+   1. [Model Git Commit Messages](#model-git-commit-messages)
+   1. [Ideal Git Commit Structure](#ideal-git-commit-structure)
+   1. [Sign Your Git Commits](#sign-your-git-commits)
+   1. [Code Spacing and formatting](#code-spacing-and-formatting)
+   1. [Pointing to Remote Dependent Branches in Go Modules](#pointing-to-remote-dependent-branches-in-go-modules)
+   1. [Use of Log Levels](#use-of-log-levels)
+   1. [Use of Golang submodules](#use-of-golang-submodules)
+6. [Code Approval Process](#code-approval-process)
    1. [Code Review](#code-review)
    1. [Rework Code (if needed)](#rework-code-if-needed)
    1. [Acceptance](#acceptance)
@@ -138,22 +146,340 @@ This approach has several benefits:
 - The quicker your changes are merged to master, the less time you will need to
   spend rebasing and otherwise trying to keep up with the main code base
 
-## Development Guidelines
+## Testing
+
+One of the major design goals of all of `lnd`'s packages and the daemon itself is
+to aim for a high degree of test coverage.  This is financial software so bugs
+and regressions in the core logic can cost people real money.  For this reason
+every effort must be taken to ensure the code is as accurate and bug-free as
+possible.  Thorough testing is a good way to help achieve that goal.
+
+Unless a new feature you submit is completely trivial, it will probably be
+rejected unless it is also accompanied by adequate test coverage for both
+positive and negative conditions.  That is to say, the tests must ensure your
+code works correctly when it is fed correct data as well as incorrect data
+(error paths).
+
+
+Go provides an excellent test framework that makes writing test code and
+checking coverage statistics straightforward.  For more information about the
+test coverage tools, see the [golang cover blog post](https://blog.golang.org/cover).
+
+A quick summary of test practices follows:
+- All new code should be accompanied by tests that ensure the code behaves
+  correctly when given expected values, and, perhaps even more importantly, that
+  it handles errors gracefully
+- When you fix a bug, it should be accompanied by tests which exercise the bug
+  to both prove it has been resolved and to prevent future regressions
+- Changes to publicly exported packages such as
+  [brontide](https://github.com/lightningnetwork/lnd/tree/master/brontide) should
+  be accompanied by unit tests exercising the new or changed behavior.
+- Changes to behavior within the daemon's interaction with the P2P protocol,
+  or RPC's will need to be accompanied by integration tests which use the
+  [`networkHarness`framework](https://github.com/lightningnetwork/lnd/blob/master/lntest/harness.go)
+  contained within `lnd`. For example integration tests, see
+  [`lnd_test.go`](https://github.com/lightningnetwork/lnd/blob/master/itest/lnd_test.go).
+- The itest log files are automatically scanned for `[ERR]` lines. There
+  shouldn't be any of those in the logs, see [Use of Log Levels](#use-of-log-levels).
+
+Throughout the process of contributing to `lnd`, you'll likely also be
+extensively using the commands within our `Makefile`. As a result, we recommend
+[perusing the make file documentation](https://github.com/lightningnetwork/lnd/blob/master/docs/MAKEFILE.md).
+
+## Code Documentation and Commenting
+
+- At a minimum every function must be commented with its intended purpose and
+  any assumptions that it makes
+  - Function comments must always begin with the name of the function per
+    [Effective Go](https://golang.org/doc/effective_go.html)
+  - Function comments should be complete sentences since they allow a wide
+    variety of automated presentations such as [godoc.org](https://godoc.org)
+  - The general rule of thumb is to look at it as if you were completely
+    unfamiliar with the code and ask yourself, would this give me enough
+	information to understand what this function does and how I'd probably want
+	to use it?
+- Exported functions should also include detailed information the caller of the
+  function will likely need to know and/or understand:<br /><br />
+
+**WRONG**
+```go
+// generates a revocation key
+func DeriveRevocationPubkey(commitPubKey *btcec.PublicKey,
+	revokePreimage []byte) *btcec.PublicKey {
+```
+**RIGHT**
+```go
+// DeriveRevocationPubkey derives the revocation public key given the
+// counterparty's commitment key, and revocation preimage derived via a
+// pseudo-random-function. In the event that we (for some reason) broadcast a
+// revoked commitment transaction, then if the other party knows the revocation
+// preimage, then they'll be able to derive the corresponding private key to
+// this private key by exploiting the homomorphism in the elliptic curve group:
+//    * https://en.wikipedia.org/wiki/Group_homomorphism#Homomorphisms_of_abelian_groups
+//
+// The derivation is performed as follows:
+//
+//   revokeKey := commitKey + revokePoint
+//             := G*k + G*h
+//             := G * (k+h)
+//
+// Therefore, once we divulge the revocation preimage, the remote peer is able to
+// compute the proper private key for the revokeKey by computing:
+//   revokePriv := commitPriv + revokePreimge mod N
+//
+// Where N is the order of the sub-group.
+func DeriveRevocationPubkey(commitPubKey *btcec.PublicKey,
+	revokePreimage []byte) *btcec.PublicKey {
+```
+- Comments in the body of the code are highly encouraged, but they should
+  explain the intention of the code as opposed to just calling out the
+  obvious<br /><br />
+
+**WRONG**
+```go
+// return err if amt is less than 546
+if amt < 546 {
+	return err
+}
+```
+**RIGHT**
+```go
+// Treat transactions with amounts less than the amount which is considered dust
+// as non-standard.
+if amt < 546 {
+	return err
+}
+```
+**NOTE:** The above should really use a constant as opposed to a magic number,
+but it was left as a magic number to show how much of a difference a good
+comment can make.
+
+## Code Spacing and formatting
+
+Code in general (and Open Source code specifically) is _read_ by developers many
+more times during its lifecycle than it is modified. With this fact in mind, the
+Golang language was designed for readability (among other goals).
+While the enforced formatting of `go fmt` and some best practices already
+eliminate many discussions, the resulting code can still look and feel very
+differently among different developers.
+
+We aim to enforce a few additional rules to unify the look and feel of all code
+in `lnd` to help improve the overall readability.
+
+**Please refer to the [code formatting rules
+document](./code_formatting_rules.md)** to see the list of additional style
+rules we enforce.
+
+## Model Git Commit Messages
+
+This project prefers to keep a clean commit history with well-formed commit
+messages.  This section illustrates a model commit message and provides a bit
+of background for it.  This content was originally created by Tim Pope and made
+available on his website, however that website is no longer active, so it is
+being provided here.
+
+Here’s a model Git commit message:
+
+```text
+Short (50 chars or less) summary of changes
+
+More detailed explanatory text, if necessary.  Wrap it to about 72
+characters or so.  In some contexts, the first line is treated as the
+subject of an email and the rest of the text as the body.  The blank
+line separating the summary from the body is critical (unless you omit
+the body entirely); tools like rebase can get confused if you run the
+two together.
+
+Write your commit message in the present tense: "Fix bug" and not "Fixed
+bug."  This convention matches up with commit messages generated by
+commands like git merge and git revert.
+
+Further paragraphs come after blank lines.
+
+- Bullet points are okay, too
+- Typically a hyphen or asterisk is used for the bullet, preceded by a
+  single space, with blank lines in between, but conventions vary here
+- Use a hanging indent
+```
+
+Here are some of the reasons why wrapping your commit messages to 72 columns is
+a good thing.
+
+- git log doesn't do any special wrapping of the commit messages. With
+  the default pager of less -S, this means your paragraphs flow far off the edge
+  of the screen, making them difficult to read. On an 80 column terminal, if we
+  subtract 4 columns for the indent on the left and 4 more for symmetry on the
+  right, we’re left with 72 columns.
+- git format-patch --stdout converts a series of commits to a series of emails,
+  using the messages for the message body.  Good email netiquette dictates we
+  wrap our plain text emails such that there’s room for a few levels of nested
+  reply indicators without overflow in an 80 column terminal.
+  
+In addition to the Git commit message structure adhered to within the daemon
+all short-[commit messages are to be prefixed according to the convention
+outlined in the Go project](https://golang.org/doc/contribute.html#change). All
+commits should begin with the subsystem or package primarily affected by the
+change. In the case of a widespread change, the packages are to be delimited by
+either a '+' or a ','. This prefix seems minor but can be extremely helpful in
+determining the scope of a commit at a glance, or when bug hunting to find a
+commit which introduced a bug or regression. 
+
+## Ideal Git Commit Structure
+
+Within the project we prefer small, contained commits for a pull request over a
+single giant commit that touches several files/packages. Ideal commits build on
+their own, in order to facilitate easy usage of tools like `git bisect` to `git
+cherry-pick`. It's preferred that commits contain an isolated change in a
+single package. In this case, the commit header message should begin with the
+prefix of the modified package. For example, if a commit was made to modify the
+`lnwallet` package, it should start with `lnwallet: `. 
+
+In the case of changes that only build in tandem with changes made in other
+packages, it is permitted for a single commit to be made which contains several
+prefixes such as: `lnwallet+htlcswitch`. This prefix structure along with the
+requirement for atomic contained commits (when possible) make things like
+scanning the set of commits and debugging easier. In the case of changes that
+touch several packages, and can only compile with the change across several
+packages, a `multi: ` prefix should be used.
+
+Examples of common patterns w.r.t commit structures within the project:
+
+  * It is common that during the work on a PR, existing bugs are found and
+    fixed. If they can be fixed in isolation, they should have their own
+    commit. 
+  * File restructuring like moving a function to another file or changing order
+    of functions: with a separate commit because it is much easier to review
+    the real changes that go on top of the restructuring.
+  * Preparatory refactorings that are functionally equivalent: own commit.
+  * Project or package wide file renamings should be in their own commit.
+  * Ideally if a new package/struct/sub-system is added in a PR, there should
+    be a single commit which adds the new functionality, with follow up
+    individual commits that begin to integrate the functionality within the
+    codebase.
+  * If a PR only fixes a trivial issue, such as updating documentation on a
+    small scale, fix typos, or any changes that do not modify the code, the
+    commit message of the HEAD commit of the PR should end with `[skip ci]` to
+    skip the CI checks. When pushing to such an existing PR, the latest commit
+    being pushed should end with `[skip ci]` as to not inadvertently trigger the
+    CI checks.
+    
+## Sign your git commits
+
+When contributing to `lnd` it is recommended to sign your git commits. This is
+easy to do and will help in assuring the integrity of the tree. See [mailing
+list entry](https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2014-May/005877.html)
+for more information.
+
+### How to sign your commits?
+
+Provide the `-S` flag (or `--gpg-sign`) to git commit when you commit
+your changes, for example
+
+```shell
+$  git commit -m "Commit message" -S
+```
+
+Optionally you can provide a key id after the `-S` option to sign with a
+specific key.
+
+To instruct `git` to auto-sign every commit, add the following lines to your
+`~/.gitconfig` file:
+
+```text
+[commit]
+        gpgsign = true
+```
+
+### What if I forgot?
+
+You can retroactively sign your previous commit using `--amend`, for example
+
+```shell
+$  git commit -S --amend
+```
+
+If you need to go further back, you can use the interactive rebase
+command with 'edit'. Replace `HEAD~3` with the base commit from which
+you want to start.
+
+```shell
+$  git rebase -i HEAD~3
+```
+
+Replace 'pick' by 'edit' for the commit that you want to sign and the
+rebasing will stop after that commit. Then you can amend the commit as
+above. Afterwards, do
+
+```shell
+$  git rebase --continue
+```
+
+As this will rewrite history, you cannot do this when your commit is
+already merged. In that case, too bad, better luck next time.
+
+If you rewrite history for another reason - for example when squashing
+commits - make sure that you re-sign as the signatures will be lost.
+
+Multiple commits can also be re-signed with `git rebase`. For example, signing
+the last three commits can be done with:
+
+```shell
+$  git rebase --exec 'git commit --amend --no-edit -n -S' -i HEAD~3
+```
+
+### How to check if commits are signed?
+
+Use `git log` with `--show-signature`,
+
+```shell
+$  git log --show-signature
+```
+
+You can also pass the `--show-signature` option to `git show` to check a single
+commit.
+
+## Pointing to Remote Dependent Branches in Go Modules
+
+It's common that a developer may need to make a change in a dependent project
+of `lnd` such as `btcd`, `neutrino`, `btcwallet`, etc. In order to test changes
+without testing infrastructure, or simply make a PR into `lnd` that will build
+without any further work, the `go.mod` and `go.sum` files will need to be
+updated. Luckily, the `go mod` command has a handy tool to do this
+automatically so developers don't need to manually edit the `go.mod` file:
+```shell
+$  go mod edit -replace=IMPORT-PATH-IN-LND@LND-VERSION=DEV-FORK-IMPORT-PATH@DEV-FORK-VERSION
+```
 
-The `lnd` project emphasizes code readability and maintainability through
-specific development guidelines. Key aspects include: thorough code
-documentation with clear function comments and meaningful inline comments;
-consistent code spacing to separate logical blocks; adherence to an 80-character
-line limit with specific rules for wrapping function calls, definitions, and log
-messages (including structured logging); comprehensive unit and integration
-testing for all changes; well-structured Git commit messages with package
-prefixes and atomic commits; signing Git commits; proper handling of Go module
-dependencies and submodules; and appropriate use of log levels. Developers are
-encouraged to configure their editors to align with these standards.
+Here's an example replacing the `lightning-onion` version checked into `lnd` with a version in roasbeef's fork:
+```shell
+$  go mod edit -replace=github.com/lightningnetwork/lightning-onion@v0.0.0-20180605012408-ac4d9da8f1d6=github.com/roasbeef/lightning-onion@2e5ae87696046298365ab43bcd1cf3a7a1d69695
+```
 
-For the complete set of rules and examples, please refer to the detailed
-[Development Guidelines](development_guidelines.md).
+## Use of Log Levels
 
+There are six log levels available: `trace`, `debug`, `info`, `warn`, `error` and `critical`.
+
+Only use `error` for internal errors that are never expected to happen during
+normal operation. No event triggered by external sources (rpc, chain backend,
+etc) should lead to an `error` log.
+
+## Use of Golang submodules
+
+Changes to packages that are their own submodules (e.g. they contain a `go.mod`
+and `go.sum` file, for example `tor/go.mod`) require a specific process.
+We want to avoid the use of local replace directives in the root `go.mod`,
+therefore changes to a submodule are a bit involved.
+
+The main process for updating and then using code in a submodule is as follows:
+ - Create a PR for the changes to the submodule itself (e.g. edit something in
+   the `tor` package)
+ - Wait for the PR to be merged and a new tag (for example `tor/v1.0.x`) to be
+   pushed.
+ - Create a second PR that bumps the updated submodule in the root `go.mod` and
+   uses the new functionality in the main module.
+
+Of course the two PRs can be opened at the same time and be built on top of each
+other. But the merge and tag push order should always be maintained.
 
 # Code Approval Process
 
diff --git a/docs/code_formatting_rules.md b/docs/code_formatting_rules.md
new file mode 100644
index 000000000..ad58745d7
--- /dev/null
+++ b/docs/code_formatting_rules.md
@@ -0,0 +1,336 @@
+# Code formatting rules
+
+## Why this emphasis on formatting?
+
+Code in general (and Open Source code specifically) is _read_ by developers many
+more times during its lifecycle than it is modified. With this fact in mind, the
+Golang language was designed for readability (among other goals).
+While the enforced formatting of `go fmt` and some best practices already
+eliminate many discussions, the resulting code can still look and feel very
+differently among different developers.
+
+We aim to enforce a few additional rules to unify the look and feel of all code
+in `lnd` to help improve the overall readability.
+
+## Spacing
+
+Blocks of code within `lnd` should be segmented into logical stanzas of
+operation. Such spacing makes the code easier to follow at a skim, and reduces
+unnecessary line noise. Coupled with the commenting scheme specified in the
+[contribution guide](./code_contribution_guidelines.md#code-documentation-and-commenting),
+proper spacing allows readers to quickly scan code, extracting semantics quickly.
+Functions should _not_ just be laid out as a bare contiguous block of code.
+
+**WRONG**
+```go
+	witness := make([][]byte, 4)
+	witness[0] = nil
+	if bytes.Compare(pubA, pubB) == -1 {
+		witness[1] = sigB
+		witness[2] = sigA
+	} else {
+		witness[1] = sigA
+		witness[2] = sigB
+	}
+	witness[3] = witnessScript
+	return witness
+```
+**RIGHT**
+```go
+	witness := make([][]byte, 4)
+
+	// When spending a p2wsh multi-sig script, rather than an OP_0, we add
+	// a nil stack element to eat the extra pop.
+	witness[0] = nil
+
+	// When initially generating the witnessScript, we sorted the serialized
+	// public keys in descending order. So we do a quick comparison in order
+	// to ensure the signatures appear on the Script Virtual Machine stack in
+	// the correct order.
+	if bytes.Compare(pubA, pubB) == -1 {
+		witness[1] = sigB
+		witness[2] = sigA
+	} else {
+		witness[1] = sigA
+		witness[2] = sigB
+	}
+
+	// Finally, add the preimage as the last witness element.
+	witness[3] = witnessScript
+
+	return witness
+```
+
+Additionally, we favor spacing between stanzas within syntax like: switch case
+statements and select statements.
+
+**WRONG**
+```go
+	switch {
+		case a:
+			<code block>
+		case b:
+			<code block>
+		case c:
+			<code block>
+		case d:
+			<code block>
+		default:
+			<code block>
+	}
+```
+**RIGHT**
+```go
+	switch {
+		// Brief comment detailing instances of this case (repeat below).
+		case a:
+			<code block>
+
+		case b:
+			<code block>
+
+		case c:
+			<code block>
+
+		case d:
+			<code block>
+
+		default:
+			<code block>
+	}
+```
+
+## Additional Style Constraints
+
+Before a PR is submitted, the proposer should ensure that the file passes the
+set of linting scripts run by `make lint`. These include `gofmt`. In addition
+to `gofmt` we've opted to enforce the following style guidelines.
+
+### 80 character line length
+
+ALL columns (on a best effort basis) should be wrapped to 80 line columns.
+Editors should be set to treat a **tab as 8 spaces**.
+
+**WRONG**
+```go
+myKey := "0214cd678a565041d00e6cf8d62ef8add33b4af4786fb2beb87b366a2e151fcee7"
+```
+
+**RIGHT**
+```go
+myKey := "0214cd678a565041d00e6cf8d62ef8add33b4af4786fb2beb87b366a2e1" +
+	"51fcee7"
+```
+
+### Wrapping long function calls
+
+When wrapping a line that contains a function call as the unwrapped line exceeds
+the column limit, the close parenthesis should be placed on its own line.
+Additionally, all arguments should begin in a new line after the open parenthesis.
+
+**WRONG**
+```go
+value, err := bar(a,
+	a, b, c)
+```
+
+**RIGHT**
+```go
+value, err := bar(
+	a, a, b, c,
+)
+```
+
+#### Exception for log and error message formatting
+
+**Note that the above guidelines don't apply to log or error messages.** For
+log and error messages, committers should attempt to minimize the number of
+lines utilized, while still adhering to the 80-character column limit. For
+example:
+
+**WRONG**
+```go
+return fmt.Errorf(
+	"this is a long error message with a couple (%d) place holders",
+	len(things),
+)
+
+log.Debugf(
+	"Something happened here that we need to log: %v",
+	longVariableNameHere,
+)
+```
+
+**RIGHT**
+```go
+return fmt.Errorf("this is a long error message with a couple (%d) place "+
+	"holders", len(things))
+
+log.Debugf("Something happened here that we need to log: %v",
+	longVariableNameHere)
+```
+
+This helps to visually distinguish those formatting statements (where nothing
+of consequence happens except for formatting an error message or writing
+to a log) from actual method or function calls. This compact formatting should
+be used for calls to formatting functions like `fmt.Errorf`,
+`log.(Trace|Debug|Info|Warn|Error)f` and `fmt.Printf`.
+But not for statements that are important for the flow or logic of the code,
+like `require.NoErrorf()`.
+
+#### Exceptions and additional styling for structured logging
+
+When making use of structured logging calls (there are any `btclog.Logger` 
+methods ending in `S`), a few different rules and exceptions apply.
+
+1) **Static messages:** Structured log calls take a `context.Context` as a first
+parameter and a _static_ string as the second parameter (the `msg` parameter). 
+Formatted strings should ideally not be used for the construction of the `msg` 
+parameter. Instead, key-value pairs (or `slog` attributes) should be used to 
+provide additional variables to the log line.
+
+**WRONG**
+```go
+log.DebugS(ctx, fmt.Sprintf("User %d just spent %.8f to open a channel", userID, 0.0154))
+```
+
+**RIGHT**
+```go
+log.InfoS(ctx, "Channel open performed",
+        slog.Int("user_id", userID),
+        btclog.Fmt("amount", "%.8f", 0.00154))
+```
+
+2) **Key-value attributes**: The third parameter in any structured log method is 
+a variadic list of the `any` type but it is required that these are provided in 
+key-value pairs such that an associated `slog.Attr` variable can be created for 
+each key-value pair. The simplest way to specify this is to directly pass in the 
+key-value pairs as raw literals as follows:
+
+```go
+log.InfoS(ctx, "Channel open performed", "user_id", userID, "amount", 0.00154)
+```
+This does work, but it becomes easy to make a mistake and accidentally leave out
+a value for each key provided leading to a nonsensical log line. To avoid this, 
+it is suggested to make use of the various `slog.Attr` helper functions as 
+follows:
+
+```go
+log.InfoS(ctx, "Channel open performed",
+        slog.Int("user_id", userID),
+        btclog.Fmt("amount", "%.8f", 0.00154))
+```
+
+3) **Line wrapping**: Structured log lines are an exception to the 80-character
+line wrapping rule. This is so that the key-value pairs can be easily read and
+reasoned about. If it is the case that there is only a single key-value pair
+and the entire log line is still less than 80 characters, it is acceptable to
+have the key-value pair on the same line as the log message. However, if there
+are multiple key-value pairs, it is suggested to use the one line per key-value
+pair format. Due to this suggestion, it is acceptable for any single key-value
+pair line to exceed 80 characters for the sake of readability.
+
+**WRONG**
+```go
+// Example 1.
+log.InfoS(ctx, "User connected", 
+        "user_id", userID)
+
+// Example 2.
+log.InfoS(ctx, "Channel open performed", "user_id", userID,
+        btclog.Fmt("amount", "%.8f", 0.00154), "channel_id", channelID)
+
+// Example 3.
+log.InfoS(ctx, "Bytes received", 
+        "user_id", userID, 
+        btclog.Hex("peer_id", peerID.SerializeCompressed()),
+        btclog.Hex("message", []bytes{
+                0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+        })))
+```
+
+**RIGHT**
+```go
+// Example 1.
+log.InfoS(ctx, "User connected", "user_id", userID)
+
+// Example 2.
+log.InfoS(ctx, "Channel open performed",
+        slog.Int("user_id", userID),
+        btclog.Fmt("amount", "%.8f", 0.00154),
+        slog.String("channel_id", channelID))
+
+// Example 3.
+log.InfoS(ctx, "Bytes received", 
+        "user_id", userID,
+        btclog.Hex("peer_id", peerID.SerializeCompressed()),
+        btclog.Hex("message", []bytes{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08})))
+```
+
+### Wrapping long function definitions
+
+If one is forced to wrap lines of function arguments that exceed the 
+80-character limit, then indentation must be kept on the following lines. Also,
+lines should not end with an open parenthesis if the function definition isn't
+finished yet.
+
+**WRONG**
+```go
+func foo(a, b, c,
+) (d, error) {
+
+func bar(a, b, c) (
+	d, error,
+) {
+
+func baz(a, b, c) (
+	d, error) {
+ ```
+**RIGHT**
+```go
+func foo(a, b,
+	c) (d, error) {
+
+func baz(a, b, c) (d,
+	error) {
+
+func longFunctionName(
+	a, b, c) (d, error) {
+```
+
+If a function declaration spans multiple lines the body should start with an
+empty line to help visually distinguishing the two elements.
+
+**WRONG**
+```go
+func foo(a, b, c,
+	d, e) error {
+	var a int
+}
+```
+**RIGHT**
+```go
+func foo(a, b, c,
+	d, e) error {
+
+	var a int
+}
+```
+
+## Recommended settings for your editor
+
+To make it easier to follow the rules outlined above, we recommend setting up
+your editor with at least the following two settings:
+
+1. Set your tabulator width (also called "tab size") to **8 spaces**.
+2. Set a ruler or visual guide at 80 character.
+
+Note that the two above settings are automatically applied in editors that
+support the `EditorConfig` scheme (for example GoLand, GitHub, GitLab,
+VisualStudio). In addition, specific settings for Visual Studio Code are checked
+into the code base as well.
+
+Other editors (for example Atom, Notepad++, Vim, Emacs and so on) might install
+a plugin to understand the rules in the `.editorconfig` file.
+
+In Vim, you might want to use `set colorcolumn=80`.
diff --git a/docs/development_guidelines.md b/docs/development_guidelines.md
deleted file mode 100644
index 1ae7d92c2..000000000
--- a/docs/development_guidelines.md
+++ /dev/null
@@ -1,702 +0,0 @@
-# Development Guidelines
-1. [Code Documentation and Commenting](#code-documentation-and-commenting)
-1. [Code Spacing and Formatting](#code-spacing-and-formatting)
-1. [Additional Style Constraints](#additional-style-constraints)
-1. [Recommended settings for your editor](#recommended-settings-for-your-editor)
-1. [Testing](#testing)
-1. [Model Git Commit Messages](#model-git-commit-messages)
-1. [Ideal Git Commit Structure](#ideal-git-commit-structure)
-1. [Sign Your Git Commits](#sign-your-git-commits)
-1. [Pointing to Remote Dependent Branches in Go Modules](#pointing-to-remote-dependent-branches-in-go-modules)
-1. [Use of Log Levels](#use-of-log-levels)
-1. [Use of Golang submodules](#use-of-golang-submodules)
-
-## Why this emphasis on formatting?
-
-Code in general (and Open Source code specifically) is _read_ by developers many
-more times during its lifecycle than it is modified. With this fact in mind, the
-Golang language was designed for readability (among other goals).
-While the enforced formatting of `go fmt` and some best practices already
-eliminate many discussions, the resulting code can still look and feel very
-differently among different developers.
-
-We aim to enforce a few additional rules to unify the look and feel of all code
-in `lnd` to help improve the overall readability.
-
-## Code Documentation and Commenting
-
-- At a minimum every function must be commented with its intended purpose and
-  any assumptions that it makes
-- Function comments must always begin with the name of the function per
-    [Effective Go](https://golang.org/doc/effective_go.html)
-- Function comments should be complete sentences since they allow a wide
-    variety of automated presentations such as [godoc.org](https://godoc.org)
-- The general rule of thumb is to look at it as if you were completely
-  unfamiliar with the code and ask yourself, would this give me enough
-  information to understand what this function does and how I'd probably want to
-  use it?
-- Exported functions should also include detailed information the caller of the
-  function will likely need to know and/or understand:<br /><br />
-
-**WRONG**
-```go
-// generates a revocation key
-func DeriveRevocationPubkey(commitPubKey *btcec.PublicKey,
-	revokePreimage []byte) *btcec.PublicKey {
-```
-**RIGHT**
-```go
-// DeriveRevocationPubkey derives the revocation public key given the
-// counterparty's commitment key, and revocation preimage derived via a
-// pseudo-random-function. In the event that we (for some reason) broadcast a
-// revoked commitment transaction, then if the other party knows the revocation
-// preimage, then they'll be able to derive the corresponding private key to
-// this private key by exploiting the homomorphism in the elliptic curve group:
-//    * https://en.wikipedia.org/wiki/Group_homomorphism#Homomorphisms_of_abelian_groups
-//
-// The derivation is performed as follows:
-//
-//   revokeKey := commitKey + revokePoint
-//             := G*k + G*h
-//             := G * (k+h)
-//
-// Therefore, once we divulge the revocation preimage, the remote peer is able to
-// compute the proper private key for the revokeKey by computing:
-//   revokePriv := commitPriv + revokePreimge mod N
-//
-// Where N is the order of the sub-group.
-func DeriveRevocationPubkey(commitPubKey *btcec.PublicKey,
-	revokePreimage []byte) *btcec.PublicKey {
-```
-- Comments in the body of the code are highly encouraged, but they should
-  explain the intention of the code as opposed to just calling out the
-  obvious<br /><br />
-
-**WRONG**
-```go
-// return err if amt is less than 546
-if amt < 546 {
-	return err
-}
-```
-**RIGHT**
-```go
-// Treat transactions with amounts less than the amount which is considered dust
-// as non-standard.
-if amt < 546 {
-	return err
-}
-```
-**NOTE:** The above should really use a constant as opposed to a magic number,
-but it was left as a magic number to show how much of a difference a good
-comment can make.
-
-## Code Spacing and formatting
-
-Code in general (and Open Source code specifically) is _read_ by developers many
-more times during its lifecycle than it is modified. With this fact in mind, the
-Golang language was designed for readability (among other goals).
-While the enforced formatting of `go fmt` and some best practices already
-eliminate many discussions, the resulting code can still look and feel very
-differently among different developers.
-
-We aim to enforce a few additional rules to unify the look and feel of all code
-in `lnd` to help improve the overall readability.
-
-Blocks of code within `lnd` should be segmented into logical stanzas of
-operation. Such spacing makes the code easier to follow at a skim, and reduces
-unnecessary line noise. Coupled with the commenting scheme specified in the
-[contribution guide](#code-documentation-and-commenting),
-proper spacing allows readers to quickly scan code, extracting semantics quickly.
-Functions should _not_ just be laid out as a bare contiguous block of code.
-
-**WRONG**
-```go
-	witness := make([][]byte, 4)
-	witness[0] = nil
-	if bytes.Compare(pubA, pubB) == -1 {
-		witness[1] = sigB
-		witness[2] = sigA
-	} else {
-		witness[1] = sigA
-		witness[2] = sigB
-	}
-	witness[3] = witnessScript
-	return witness
-```
-**RIGHT**
-```go
-	witness := make([][]byte, 4)
-
-	// When spending a p2wsh multi-sig script, rather than an OP_0, we add
-	// a nil stack element to eat the extra pop.
-	witness[0] = nil
-
-	// When initially generating the witnessScript, we sorted the serialized
-	// public keys in descending order. So we do a quick comparison in order
-	// to ensure the signatures appear on the Script Virtual Machine stack in
-	// the correct order.
-	if bytes.Compare(pubA, pubB) == -1 {
-		witness[1] = sigB
-		witness[2] = sigA
-	} else {
-		witness[1] = sigA
-		witness[2] = sigB
-	}
-
-	// Finally, add the preimage as the last witness element.
-	witness[3] = witnessScript
-
-	return witness
-```
-
-Additionally, we favor spacing between stanzas within syntax like: switch case
-statements and select statements.
-
-**WRONG**
-```go
-	switch {
-		case a:
-			<code block>
-		case b:
-			<code block>
-		case c:
-			<code block>
-		case d:
-			<code block>
-		default:
-			<code block>
-	}
-```
-**RIGHT**
-```go
-	switch {
-		// Brief comment detailing instances of this case (repeat below).
-		case a:
-			<code block>
-
-		case b:
-			<code block>
-
-		case c:
-			<code block>
-
-		case d:
-			<code block>
-
-		default:
-			<code block>
-	}
-```
-
-## Additional Style Constraints
-
-Before a PR is submitted, the proposer should ensure that the file passes the
-set of linting scripts run by `make lint`. These include `gofmt`. In addition
-to `gofmt` we've opted to enforce the following style guidelines.
-
-### 80 character line length
-
-ALL columns (on a best effort basis) should be wrapped to 80 line columns.
-Editors should be set to treat a **tab as 8 spaces**.
-
-**WRONG**
-```go
-myKey := "0214cd678a565041d00e6cf8d62ef8add33b4af4786fb2beb87b366a2e151fcee7"
-```
-
-**RIGHT**
-```go
-myKey := "0214cd678a565041d00e6cf8d62ef8add33b4af4786fb2beb87b366a2e1" +
-	"51fcee7"
-```
-
-### Wrapping long function calls
-
-When wrapping a line that contains a function call as the unwrapped line exceeds
-the column limit, the close parenthesis should be placed on its own line.
-Additionally, all arguments should begin in a new line after the open parenthesis.
-
-**WRONG**
-```go
-value, err := bar(a,
-	a, b, c)
-```
-
-**RIGHT**
-```go
-value, err := bar(
-	a, a, b, c,
-)
-```
-
-#### Exception for log and error message formatting
-
-**Note that the above guidelines don't apply to log or error messages.** For
-log and error messages, committers should attempt to minimize the number of
-lines utilized, while still adhering to the 80-character column limit. For
-example:
-
-**WRONG**
-```go
-return fmt.Errorf(
-	"this is a long error message with a couple (%d) place holders",
-	len(things),
-)
-
-log.Debugf(
-	"Something happened here that we need to log: %v",
-	longVariableNameHere,
-)
-```
-
-**RIGHT**
-```go
-return fmt.Errorf("this is a long error message with a couple (%d) place "+
-	"holders", len(things))
-
-log.Debugf("Something happened here that we need to log: %v",
-	longVariableNameHere)
-```
-
-This helps to visually distinguish those formatting statements (where nothing
-of consequence happens except for formatting an error message or writing
-to a log) from actual method or function calls. This compact formatting should
-be used for calls to formatting functions like `fmt.Errorf`,
-`log.(Trace|Debug|Info|Warn|Error)f` and `fmt.Printf`.
-But not for statements that are important for the flow or logic of the code,
-like `require.NoErrorf()`.
-
-#### Exceptions and additional styling for structured logging
-
-When making use of structured logging calls (there are any `btclog.Logger` 
-methods ending in `S`), a few different rules and exceptions apply.
-
-1) **Static messages:** Structured log calls take a `context.Context` as a first
-parameter and a _static_ string as the second parameter (the `msg` parameter). 
-Formatted strings should ideally not be used for the construction of the `msg` 
-parameter. Instead, key-value pairs (or `slog` attributes) should be used to 
-provide additional variables to the log line.
-
-**WRONG**
-```go
-log.DebugS(ctx, fmt.Sprintf("User %d just spent %.8f to open a channel", userID, 0.0154))
-```
-
-**RIGHT**
-```go
-log.InfoS(ctx, "Channel open performed",
-        slog.Int("user_id", userID),
-        btclog.Fmt("amount", "%.8f", 0.00154))
-```
-
-2) **Key-value attributes**: The third parameter in any structured log method is 
-a variadic list of the `any` type but it is required that these are provided in 
-key-value pairs such that an associated `slog.Attr` variable can be created for 
-each key-value pair. The simplest way to specify this is to directly pass in the 
-key-value pairs as raw literals as follows:
-
-```go
-log.InfoS(ctx, "Channel open performed", "user_id", userID, "amount", 0.00154)
-```
-This does work, but it becomes easy to make a mistake and accidentally leave out
-a value for each key provided leading to a nonsensical log line. To avoid this, 
-it is suggested to make use of the various `slog.Attr` helper functions as 
-follows:
-
-```go
-log.InfoS(ctx, "Channel open performed",
-        slog.Int("user_id", userID),
-        btclog.Fmt("amount", "%.8f", 0.00154))
-```
-
-3) **Line wrapping**: Structured log lines are an exception to the 80-character
-line wrapping rule. This is so that the key-value pairs can be easily read and
-reasoned about. If it is the case that there is only a single key-value pair
-and the entire log line is still less than 80 characters, it is acceptable to
-have the key-value pair on the same line as the log message. However, if there
-are multiple key-value pairs, it is suggested to use the one line per key-value
-pair format. Due to this suggestion, it is acceptable for any single key-value
-pair line to exceed 80 characters for the sake of readability.
-
-**WRONG**
-```go
-// Example 1.
-log.InfoS(ctx, "User connected", 
-        "user_id", userID)
-
-// Example 2.
-log.InfoS(ctx, "Channel open performed", "user_id", userID,
-        btclog.Fmt("amount", "%.8f", 0.00154), "channel_id", channelID)
-
-// Example 3.
-log.InfoS(ctx, "Bytes received", 
-        "user_id", userID, 
-        btclog.Hex("peer_id", peerID.SerializeCompressed()),
-        btclog.Hex("message", []bytes{
-                0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
-        })))
-```
-
-**RIGHT**
-```go
-// Example 1.
-log.InfoS(ctx, "User connected", "user_id", userID)
-
-// Example 2.
-log.InfoS(ctx, "Channel open performed",
-        slog.Int("user_id", userID),
-        btclog.Fmt("amount", "%.8f", 0.00154),
-        slog.String("channel_id", channelID))
-
-// Example 3.
-log.InfoS(ctx, "Bytes received", 
-        "user_id", userID,
-        btclog.Hex("peer_id", peerID.SerializeCompressed()),
-        btclog.Hex("message", []bytes{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08})))
-```
-
-### Wrapping long function definitions
-
-If one is forced to wrap lines of function arguments that exceed the 
-80-character limit, then indentation must be kept on the following lines. Also,
-lines should not end with an open parenthesis if the function definition isn't
-finished yet.
-
-**WRONG**
-```go
-func foo(a, b, c,
-) (d, error) {
-
-func bar(a, b, c) (
-	d, error,
-) {
-
-func baz(a, b, c) (
-	d, error) {
-```
-**RIGHT**
-```go
-func foo(a, b,
-	c) (d, error) {
-
-func baz(a, b, c) (d,
-	error) {
-
-func longFunctionName(
-	a, b, c) (d, error) {
-```
-
-If a function declaration spans multiple lines the body should start with an
-empty line to help visually distinguishing the two elements.
-
-**WRONG**
-```go
-func foo(a, b, c,
-	d, e) error {
-	var a int
-}
-```
-**RIGHT**
-```go
-func foo(a, b, c,
-	d, e) error {
-
-	var a int
-}
-```
-
-## Recommended settings for your editor
-
-To make it easier to follow the rules outlined above, we recommend setting up
-your editor with at least the following two settings:
-
-1. Set your tabulator width (also called "tab size") to **8 spaces**.
-2. Set a ruler or visual guide at 80 character.
-
-Note that the two above settings are automatically applied in editors that
-support the `EditorConfig` scheme (for example GoLand, GitHub, GitLab,
-VisualStudio). In addition, specific settings for Visual Studio Code are checked
-into the code base as well.
-
-Other editors (for example Atom, Notepad++, Vim, Emacs and so on) might install
-a plugin to understand the rules in the `.editorconfig` file.
-
-In Vim, you might want to use `set colorcolumn=80`.
-
-
-## Testing
-
-One of the major design goals of all of `lnd`'s packages and the daemon itself is
-to aim for a high degree of test coverage.  This is financial software so bugs
-and regressions in the core logic can cost people real money.  For this reason
-every effort must be taken to ensure the code is as accurate and bug-free as
-possible.  Thorough testing is a good way to help achieve that goal.
-
-Unless a new feature you submit is completely trivial, it will probably be
-rejected unless it is also accompanied by adequate test coverage for both
-positive and negative conditions.  That is to say, the tests must ensure your
-code works correctly when it is fed correct data as well as incorrect data
-(error paths).
-
-
-Go provides an excellent test framework that makes writing test code and
-checking coverage statistics straightforward.  For more information about the
-test coverage tools, see the [golang cover blog post](https://blog.golang.org/cover).
-
-A quick summary of test practices follows:
-
-- All new code should be accompanied by tests that ensure the code behaves
-  correctly when given expected values, and, perhaps even more importantly, that
-  it handles errors gracefully.
-- When you fix a bug, it should be accompanied by tests which exercise the bug
-  to both prove it has been resolved and to prevent future regressions.
-- Changes to publicly exported packages such as
-  [brontide](https://github.com/lightningnetwork/lnd/tree/master/brontide) should
-  be accompanied by unit tests exercising the new or changed behavior.
-- Changes to behavior within the daemon's interaction with the P2P protocol,
-  or RPC's will need to be accompanied by integration tests which use the
-  [`networkHarness`framework](https://github.com/lightningnetwork/lnd/blob/master/lntest/harness.go)
-  contained within `lnd`. For example integration tests, see
-  [`lnd_test.go`](https://github.com/lightningnetwork/lnd/blob/master/itest/lnd_test.go).
-- The itest log files are automatically scanned for `[ERR]` lines. There
-  shouldn't be any of those in the logs, see [Use of Log Levels](#use-of-log-levels).
-
-Throughout the process of contributing to `lnd`, you'll likely also be
-extensively using the commands within our `Makefile`. As a result, we recommend
-[perusing the make file documentation](https://github.com/lightningnetwork/lnd/blob/master/docs/MAKEFILE.md).
-
-
-Before committing the changes made, you should run unit tests to validate the
-changes, and provide a new integration test when necessary. The unit tests
-should pass at two levels,
-
-- Run `make unit-debug log="stdlog trace" pkg=$pkg case=$case timeout=10s`,
-  where `pkg` is the package that's updated, and `case` is the newly added or
-  affected unit test. This command should run against all the newly added and
-  affected test cases. In addition, you should pay attention to the logs added
-  here, and make sure they are correctly formatted and no spammy. Also notice
-  the timeout - 10 seconds should be more than enough to run a single unit test
-  case, if it takes longer than that, consider break the test to make it more
-  "unit".
-
-- Run `make unit pkg=$pkg timeout=5m` to make sure all existing unit tests still
-  pass.
-
-- If there are newly added integration tests, or the changes may alter the
-  workflow of specific areas, run `make itest icase=$icase` to validate the
-  behavior, where `icase` is the affected test case.
-
-
-## Model Git Commit Messages
-
-This project prefers to keep a clean commit history with well-formed commit
-messages.  This section illustrates a model commit message and provides a bit
-of background for it.  This content was originally created by Tim Pope and made
-available on his website, however that website is no longer active, so it is
-being provided here.
-
-Here’s a model Git commit message:
-
-```text
-Short (50 chars or less) summary of changes
-
-More detailed explanatory text, if necessary.  Wrap it to about 72
-characters or so.  In some contexts, the first line is treated as the
-subject of an email and the rest of the text as the body.  The blank
-line separating the summary from the body is critical (unless you omit
-the body entirely); tools like rebase can get confused if you run the
-two together.
-
-Write your commit message in the present tense: "Fix bug" and not "Fixed
-bug."  This convention matches up with commit messages generated by
-commands like git merge and git revert.
-
-Further paragraphs come after blank lines.
-
-- Bullet points are okay, too
-- Typically a hyphen or asterisk is used for the bullet, preceded by a
-  single space, with blank lines in between, but conventions vary here
-- Use a hanging indent
-```
-
-Here are some of the reasons why wrapping your commit messages to 72 columns is
-a good thing.
-
-- git log doesn't do any special wrapping of the commit messages. With
-  the default pager of less -S, this means your paragraphs flow far off the edge
-  of the screen, making them difficult to read. On an 80 column terminal, if we
-  subtract 4 columns for the indent on the left and 4 more for symmetry on the
-  right, we’re left with 72 columns.
-- git format-patch --stdout converts a series of commits to a series of emails,
-  using the messages for the message body.  Good email netiquette dictates we
-  wrap our plain text emails such that there’s room for a few levels of nested
-  reply indicators without overflow in an 80 column terminal.
-  
-
-In addition to the Git commit message structure adhered to within the daemon
-all short-[commit messages are to be prefixed according to the convention
-outlined in the Go project](https://golang.org/doc/contribute.html#change). All
-commits should begin with the subsystem or package primarily affected by the
-change. In the case of a widespread change, the packages are to be delimited by
-either a '+' or a ','. This prefix seems minor but can be extremely helpful in
-determining the scope of a commit at a glance, or when bug hunting to find a
-commit which introduced a bug or regression. 
-
-## Ideal Git Commit Structure
-
-Within the project we prefer small, contained commits for a pull request over a
-single giant commit that touches several files/packages. Ideal commits build on
-their own, in order to facilitate easy usage of tools like `git bisect` to `git
-cherry-pick`. It's preferred that commits contain an isolated change in a
-single package. In this case, the commit header message should begin with the
-prefix of the modified package. For example, if a commit was made to modify the
-`lnwallet` package, it should start with `lnwallet: `. 
-
-In the case of changes that only build in tandem with changes made in other
-packages, it is permitted for a single commit to be made which contains several
-prefixes such as: `lnwallet+htlcswitch`. This prefix structure along with the
-requirement for atomic contained commits (when possible) make things like
-scanning the set of commits and debugging easier. In the case of changes that
-touch several packages, and can only compile with the change across several
-packages, a `multi: ` prefix should be used.
-
-Examples of common patterns w.r.t commit structures within the project:
-
-  * It is common that during the work on a PR, existing bugs are found and
-    fixed. If they can be fixed in isolation, they should have their own
-    commit. 
-  * File restructuring like moving a function to another file or changing order
-    of functions: with a separate commit because it is much easier to review
-    the real changes that go on top of the restructuring.
-  * Preparatory refactorings that are functionally equivalent: own commit.
-  * Project or package wide file renamings should be in their own commit.
-  * Ideally if a new package/struct/sub-system is added in a PR, there should
-    be a single commit which adds the new functionality, with follow up
-    individual commits that begin to integrate the functionality within the
-    codebase.
-  * If a PR only fixes a trivial issue, such as updating documentation on a
-    small scale, fix typos, or any changes that do not modify the code, the
-    commit message of the HEAD commit of the PR should end with `[skip ci]` to
-    skip the CI checks. When pushing to such an existing PR, the latest commit
-    being pushed should end with `[skip ci]` as to not inadvertently trigger the
-    CI checks.
-    
-## Sign your git commits
-
-When contributing to `lnd` it is recommended to sign your git commits. This is
-easy to do and will help in assuring the integrity of the tree. See [mailing
-list entry](https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2014-May/005877.html)
-for more information.
-
-### How to sign your commits?
-
-Provide the `-S` flag (or `--gpg-sign`) to git commit when you commit
-your changes, for example
-
-```shell
-$  git commit -m "Commit message" -S
-```
-
-Optionally you can provide a key id after the `-S` option to sign with a
-specific key.
-
-To instruct `git` to auto-sign every commit, add the following lines to your
-`~/.gitconfig` file:
-
-```text
-[commit]
-        gpgsign = true
-```
-
-### What if I forgot?
-
-You can retroactively sign your previous commit using `--amend`, for example
-
-```shell
-$  git commit -S --amend
-```
-
-If you need to go further back, you can use the interactive rebase
-command with 'edit'. Replace `HEAD~3` with the base commit from which
-you want to start.
-
-```shell
-$  git rebase -i HEAD~3
-```
-
-Replace 'pick' by 'edit' for the commit that you want to sign and the
-rebasing will stop after that commit. Then you can amend the commit as
-above. Afterwards, do
-
-```shell
-$  git rebase --continue
-```
-
-As this will rewrite history, you cannot do this when your commit is
-already merged. In that case, too bad, better luck next time.
-
-If you rewrite history for another reason - for example when squashing
-commits - make sure that you re-sign as the signatures will be lost.
-
-Multiple commits can also be re-signed with `git rebase`. For example, signing
-the last three commits can be done with:
-
-```shell
-$  git rebase --exec 'git commit --amend --no-edit -n -S' -i HEAD~3
-```
-
-### How to check if commits are signed?
-
-Use `git log` with `--show-signature`,
-
-```shell
-$  git log --show-signature
-```
-
-You can also pass the `--show-signature` option to `git show` to check a single
-commit.
-
-
-## Pointing to Remote Dependent Branches in Go Modules
-
-It's common that a developer may need to make a change in a dependent project
-of `lnd` such as `btcd`, `neutrino`, `btcwallet`, etc. In order to test changes
-without testing infrastructure, or simply make a PR into `lnd` that will build
-without any further work, the `go.mod` and `go.sum` files will need to be
-updated. Luckily, the `go mod` command has a handy tool to do this
-automatically so developers don't need to manually edit the `go.mod` file:
-```shell
-$  go mod edit -replace=IMPORT-PATH-IN-LND@LND-VERSION=DEV-FORK-IMPORT-PATH@DEV-FORK-VERSION
-```
-
-Here's an example replacing the `lightning-onion` version checked into `lnd` with a version in roasbeef's fork:
-```shell
-$  go mod edit -replace=github.com/lightningnetwork/lightning-onion@v0.0.0-20180605012408-ac4d9da8f1d6=github.com/roasbeef/lightning-onion@2e5ae87696046298365ab43bcd1cf3a7a1d69695
-```
-
-## Use of Log Levels
-
-There are six log levels available: `trace`, `debug`, `info`, `warn`, `error` and `critical`.
-
-Only use `error` for internal errors that are never expected to happen during
-normal operation. No event triggered by external sources (rpc, chain backend,
-etc) should lead to an `error` log.
-
-## Use of Golang submodules
-
-Changes to packages that are their own submodules (e.g. they contain a `go.mod`
-and `go.sum` file, for example `tor/go.mod`) require a specific process.
-We want to avoid the use of local replace directives in the root `go.mod`,
-therefore changes to a submodule are a bit involved.
-
-The main process for updating and then using code in a submodule is as follows:
- - Create a PR for the changes to the submodule itself (e.g. edit something in
-   the `tor` package)
- - Wait for the PR to be merged and a new tag (for example `tor/v1.0.x`) to be
-   pushed.
- - Create a second PR that bumps the updated submodule in the root `go.mod` and
-   uses the new functionality in the main module.
-
-Of course the two PRs can be opened at the same time and be built on top of each
-other. But the merge and tag push order should always be maintained.
diff --git a/docs/release-notes/release-notes-0.19.0.md b/docs/release-notes/release-notes-0.19.0.md
index 0228d234d..7ebc54037 100644
--- a/docs/release-notes/release-notes-0.19.0.md
+++ b/docs/release-notes/release-notes-0.19.0.md
@@ -111,9 +111,6 @@ keysend payment validation is stricter.
   process of the node won't be interrupted if a non-fatal error is returned from
   the subsystems.
 
-* [Fixed](https://github.com/lightningnetwork/lnd/pull/9703) a possible panic
-  when reloading legacy inflight payments which don't have the MPP feature.
-
 # New Features
 
 * Add support for [archiving channel backup](https://github.com/lightningnetwork/lnd/pull/9232)
@@ -171,11 +168,6 @@ close transaction.
   [changed](https://github.com/lightningnetwork/lnd/pull/9627) so the sweeper
   will always attempt the sweep as long as the budget can be partially covered.
 
-* [Add](https://github.com/lightningnetwork/lnd/pull/9603) validation to ensure
-  that MPP parameters are compatible with the payment amount before attempting
-  the payment. This prevents payments from entering a path finding loop that
-  would eventually timeout.
-
 ## RPC Additions
 
 * [Add a new rpc endpoint](https://github.com/lightningnetwork/lnd/pull/8843)
diff --git a/docs/review.md b/docs/review.md
index d613d6b54..d3be64420 100644
--- a/docs/review.md
+++ b/docs/review.md
@@ -85,7 +85,7 @@ entry point for anyone not yet familiar with the process:
 - Bonus: Write a unit test (or test case) that you can propose to add to the PR.
 - Go through the PR commit by commit and line by line
     - Is the flow of the commits easy to understand? Does the order make sense?
-      See the [code contribution guidelines](development_guidelines.md#ideal-git-commit-structure)
+      See the [code contribution guidelines](code_contribution_guidelines.md)
       for more information on this.
     - Is the granularity of the commit structure easy to understand? Should
       commits be split up or be combined?
diff --git a/htlcswitch/interfaces.go b/htlcswitch/interfaces.go
index 30a336085..786788e09 100644
--- a/htlcswitch/interfaces.go
+++ b/htlcswitch/interfaces.go
@@ -10,6 +10,7 @@ import (
 	"github.com/lightningnetwork/lnd/graph/db/models"
 	"github.com/lightningnetwork/lnd/invoices"
 	"github.com/lightningnetwork/lnd/lntypes"
+	"github.com/lightningnetwork/lnd/lnwallet"
 	"github.com/lightningnetwork/lnd/lnwallet/chainfee"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/record"
@@ -516,8 +517,8 @@ type AuxTrafficShaper interface {
 	// is a custom channel that should be handled by the traffic shaper, the
 	// ShouldHandleTraffic method should be called first.
 	PaymentBandwidth(htlcBlob, commitmentBlob fn.Option[tlv.Blob],
-		linkBandwidth,
-		htlcAmt lnwire.MilliSatoshi) (lnwire.MilliSatoshi, error)
+		linkBandwidth, htlcAmt lnwire.MilliSatoshi,
+		htlcView lnwallet.AuxHtlcView) (lnwire.MilliSatoshi, error)
 
 	// IsCustomHTLC returns true if the HTLC carries the set of relevant
 	// custom records to put it under the purview of the traffic shaper,
diff --git a/htlcswitch/link.go b/htlcswitch/link.go
index ec26b0918..bddf8d95e 100644
--- a/htlcswitch/link.go
+++ b/htlcswitch/link.go
@@ -3505,6 +3505,7 @@ func (l *channelLink) AuxBandwidth(amount lnwire.MilliSatoshi,
 	commitmentBlob := l.CommitmentCustomBlob()
 	auxBandwidth, err := ts.PaymentBandwidth(
 		htlcBlob, commitmentBlob, l.Bandwidth(), amount,
+		l.channel.FetchLatestAuxHTLCView(),
 	)
 	if err != nil {
 		return fn.Err[OptionalBandwidth](fmt.Errorf("failed to get "+
diff --git a/lnrpc/chainrpc/chain_server.go b/lnrpc/chainrpc/chain_server.go
index da68e034b..3644d57f5 100644
--- a/lnrpc/chainrpc/chain_server.go
+++ b/lnrpc/chainrpc/chain_server.go
@@ -360,7 +360,7 @@ func (s *Server) GetBlockHash(_ context.Context,
 // particular transaction by its hash or for an output script by specifying a
 // zero hash.
 //
-// NOTE: This is part of the chainrpc.ChainNotifierServer interface.
+// NOTE: This is part of the chainrpc.ChainNotifierService interface.
 func (s *Server) RegisterConfirmationsNtfn(in *ConfRequest,
 	confStream ChainNotifier_RegisterConfirmationsNtfnServer) error {
 
@@ -483,7 +483,7 @@ func (s *Server) RegisterConfirmationsNtfn(in *ConfRequest,
 // A client can specify whether the spend request should be for a particular
 // outpoint  or for an output script by specifying a zero outpoint.
 //
-// NOTE: This is part of the chainrpc.ChainNotifierServer interface.
+// NOTE: This is part of the chainrpc.ChainNotifierService interface.
 func (s *Server) RegisterSpendNtfn(in *SpendRequest,
 	spendStream ChainNotifier_RegisterSpendNtfnServer) error {
 
@@ -597,7 +597,7 @@ func (s *Server) RegisterSpendNtfn(in *SpendRequest,
 // point. This allows clients to be idempotent by ensuring that they do not
 // missing processing a single block within the chain.
 //
-// NOTE: This is part of the chainrpc.ChainNotifierServer interface.
+// NOTE: This is part of the chainrpc.ChainNotifierService interface.
 func (s *Server) RegisterBlockEpochNtfn(in *BlockEpoch,
 	epochStream ChainNotifier_RegisterBlockEpochNtfnServer) error {
 
diff --git a/lnrpc/routerrpc/router_backend.go b/lnrpc/routerrpc/router_backend.go
index b74484d5c..59d933648 100644
--- a/lnrpc/routerrpc/router_backend.go
+++ b/lnrpc/routerrpc/router_backend.go
@@ -36,10 +36,6 @@ const (
 	// TODO(roasbeef): make this value dynamic based on expected number of
 	// attempts for given amount.
 	DefaultMaxParts = 16
-
-	// MaxPartsUpperLimit defines the maximum allowable number of splits
-	// for MPP/AMP when the user is attempting to send a payment.
-	MaxPartsUpperLimit = 1000
 )
 
 // RouterBackend contains the backend implementation of the router rpc sub
@@ -872,16 +868,6 @@ func (r *RouterBackend) extractIntentFromSendRequest(
 	if rpcPayReq.MaxShardSizeMsat > 0 {
 		shardAmtMsat := lnwire.MilliSatoshi(rpcPayReq.MaxShardSizeMsat)
 		payIntent.MaxShardAmt = &shardAmtMsat
-
-		// If the requested max_parts exceeds the allowed limit, then we
-		// cannot send the payment amount.
-		if payIntent.MaxParts > MaxPartsUpperLimit {
-			return nil, fmt.Errorf("requested max_parts (%v) "+
-				"exceeds the allowed upper limit of %v; cannot"+
-				" send payment amount with max_shard_size_msat"+
-				"=%v", payIntent.MaxParts, MaxPartsUpperLimit,
-				*payIntent.MaxShardAmt)
-		}
 	}
 
 	// Take fee limit from request.
@@ -1217,23 +1203,6 @@ func (r *RouterBackend) extractIntentFromSendRequest(
 		payIntent.DestFeatures = features
 	}
 
-	// Validate that the MPP parameters are compatible with the
-	// payment amount. In other words, the parameters are invalid if
-	// they do not permit sending the full payment amount.
-	if payIntent.MaxShardAmt != nil {
-		maxPossibleAmount := (*payIntent.MaxShardAmt) *
-			lnwire.MilliSatoshi(payIntent.MaxParts)
-
-		if payIntent.Amount > maxPossibleAmount {
-			return nil, fmt.Errorf("payment amount %v exceeds "+
-				"maximum possible amount %v with max_parts=%v "+
-				"and max_shard_size_msat=%v", payIntent.Amount,
-				maxPossibleAmount, payIntent.MaxParts,
-				*payIntent.MaxShardAmt,
-			)
-		}
-	}
-
 	// Do bounds checking with the block padding so the router isn't
 	// left with a zombie payment in case the user messes up.
 	err = routing.ValidateCLTVLimit(
diff --git a/lnwallet/aux_leaf_store.go b/lnwallet/aux_leaf_store.go
index 28a78e09d..aa2cc3b3e 100644
--- a/lnwallet/aux_leaf_store.go
+++ b/lnwallet/aux_leaf_store.go
@@ -133,7 +133,7 @@ type CommitDiffAuxInput struct {
 	// UnfilteredView is the unfiltered, original HTLC view of the channel.
 	// Unfiltered in this context means that the view contains all HTLCs,
 	// including the canceled ones.
-	UnfilteredView *HtlcView
+	UnfilteredView AuxHtlcView
 
 	// WhoseCommit denotes whose commitment transaction we are computing the
 	// diff for.
@@ -177,9 +177,8 @@ type AuxLeafStore interface {
 	// correspond to the passed aux blob, and an existing channel
 	// commitment.
 	FetchLeavesFromCommit(chanState AuxChanState,
-		commit channeldb.ChannelCommitment,
-		keyRing CommitmentKeyRing, whoseCommit lntypes.ChannelParty,
-	) fn.Result[CommitDiffAuxResult]
+		commit channeldb.ChannelCommitment, keyRing CommitmentKeyRing,
+		whoseCommit lntypes.ChannelParty) fn.Result[CommitDiffAuxResult]
 
 	// FetchLeavesFromRevocation attempts to fetch the auxiliary leaves
 	// from a channel revocation that stores balance + blob information.
@@ -206,7 +205,7 @@ func auxLeavesFromView(leafStore AuxLeafStore, chanState *channeldb.OpenChannel,
 			return leafStore.FetchLeavesFromView(CommitDiffAuxInput{
 				ChannelState:   NewAuxChanState(chanState),
 				PrevBlob:       blob,
-				UnfilteredView: originalView,
+				UnfilteredView: newAuxHtlcView(originalView),
 				WhoseCommit:    whoseCommit,
 				OurBalance:     ourBalance,
 				TheirBalance:   theirBalance,
@@ -227,13 +226,15 @@ func updateAuxBlob(leafStore AuxLeafStore, chanState *channeldb.OpenChannel,
 	return fn.MapOptionZ(
 		prevBlob, func(blob tlv.Blob) fn.Result[fn.Option[tlv.Blob]] {
 			return leafStore.ApplyHtlcView(CommitDiffAuxInput{
-				ChannelState:   NewAuxChanState(chanState),
-				PrevBlob:       blob,
-				UnfilteredView: nextViewUnfiltered,
-				WhoseCommit:    whoseCommit,
-				OurBalance:     ourBalance,
-				TheirBalance:   theirBalance,
-				KeyRing:        keyRing,
+				ChannelState: NewAuxChanState(chanState),
+				PrevBlob:     blob,
+				UnfilteredView: newAuxHtlcView(
+					nextViewUnfiltered,
+				),
+				WhoseCommit:  whoseCommit,
+				OurBalance:   ourBalance,
+				TheirBalance: theirBalance,
+				KeyRing:      keyRing,
 			})
 		},
 	)
diff --git a/lnwallet/aux_signer.go b/lnwallet/aux_signer.go
index 510b64b5d..90a4325f6 100644
--- a/lnwallet/aux_signer.go
+++ b/lnwallet/aux_signer.go
@@ -5,6 +5,7 @@ import (
 	"github.com/lightningnetwork/lnd/fn/v2"
 	"github.com/lightningnetwork/lnd/input"
 	"github.com/lightningnetwork/lnd/lntypes"
+	"github.com/lightningnetwork/lnd/lnwallet/chainfee"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/tlv"
 )
@@ -13,6 +14,37 @@ import (
 // signatures within the custom data for an existing HTLC.
 var htlcCustomSigType tlv.TlvType65543
 
+// AuxHtlcView is a struct that contains a safe copy of an HTLC view that can
+// be used by aux components.
+type AuxHtlcView struct {
+	// NextHeight is the height of the commitment transaction that will be
+	// created using this view.
+	NextHeight uint64
+
+	// Updates is a Dual of the Local and Remote HTLCs.
+	Updates lntypes.Dual[[]AuxHtlcDescriptor]
+
+	// FeePerKw is the fee rate in sat/kw of the commitment transaction.
+	FeePerKw chainfee.SatPerKWeight
+}
+
+// newAuxHtlcView creates a new safe copy of the HTLC view that can be used by
+// aux components.
+//
+// NOTE: This function should only be called while holding the channel's read
+// lock, since the underlying local/remote payment descriptors are accessed
+// directly.
+func newAuxHtlcView(v *HtlcView) AuxHtlcView {
+	return AuxHtlcView{
+		NextHeight: v.NextHeight,
+		Updates: lntypes.Dual[[]AuxHtlcDescriptor]{
+			Local:  fn.Map(v.Updates.Local, newAuxHtlcDescriptor),
+			Remote: fn.Map(v.Updates.Remote, newAuxHtlcDescriptor),
+		},
+		FeePerKw: v.FeePerKw,
+	}
+}
+
 // AuxHtlcDescriptor is a struct that contains the information needed to sign or
 // verify an HTLC for custom channels.
 type AuxHtlcDescriptor struct {
@@ -99,6 +131,9 @@ func (a *AuxHtlcDescriptor) RemoveHeight(
 
 // newAuxHtlcDescriptor creates a new AuxHtlcDescriptor from a payment
 // descriptor.
+//
+// NOTE: This function should only be called while holding the channel's read
+// lock, since the underlying payment descriptors are accessed directly.
 func newAuxHtlcDescriptor(p *paymentDescriptor) AuxHtlcDescriptor {
 	return AuxHtlcDescriptor{
 		ChanID:                   p.ChanID,
diff --git a/lnwallet/channel.go b/lnwallet/channel.go
index ab064fbd4..2c1e3a252 100644
--- a/lnwallet/channel.go
+++ b/lnwallet/channel.go
@@ -2704,6 +2704,20 @@ func (v *HtlcView) AuxTheirUpdates() []AuxHtlcDescriptor {
 	return fn.Map(v.Updates.Remote, newAuxHtlcDescriptor)
 }
 
+// FetchLatestAuxHTLCView returns the latest HTLC view of the lightning channel
+// as a safe copy that can be used outside the wallet code in concurrent access.
+func (lc *LightningChannel) FetchLatestAuxHTLCView() AuxHtlcView {
+	// This read lock is important, because we access both the local and
+	// remote log indexes as well as the underlying payment descriptors of
+	// the HTLCs when creating the view.
+	lc.RLock()
+	defer lc.RUnlock()
+
+	return newAuxHtlcView(lc.fetchHTLCView(
+		lc.updateLogs.Remote.logIndex, lc.updateLogs.Local.logIndex,
+	))
+}
+
 // fetchHTLCView returns all the candidate HTLC updates which should be
 // considered for inclusion within a commitment based on the passed HTLC log
 // indexes.
diff --git a/routing/payment_lifecycle.go b/routing/payment_lifecycle.go
index a64c8c87e..9a7414079 100644
--- a/routing/payment_lifecycle.go
+++ b/routing/payment_lifecycle.go
@@ -14,7 +14,6 @@ import (
 	"github.com/lightningnetwork/lnd/graph/db/models"
 	"github.com/lightningnetwork/lnd/htlcswitch"
 	"github.com/lightningnetwork/lnd/lntypes"
-	"github.com/lightningnetwork/lnd/lnutils"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/routing/route"
 	"github.com/lightningnetwork/lnd/routing/shards"
@@ -501,8 +500,7 @@ func (p *paymentLifecycle) collectResultAsync(attempt *channeldb.HTLCAttempt) {
 func (p *paymentLifecycle) collectResult(
 	attempt *channeldb.HTLCAttempt) (*htlcswitch.PaymentResult, error) {
 
-	log.Tracef("Collecting result for attempt %v",
-		lnutils.SpewLogClosure(attempt))
+	log.Tracef("Collecting result for attempt %v", spew.Sdump(attempt))
 
 	result := &htlcswitch.PaymentResult{}
 
@@ -1062,38 +1060,6 @@ func marshallError(sendError error, time time.Time) *channeldb.HTLCFailInfo {
 	return response
 }
 
-// patchLegacyPaymentHash will make a copy of the passed attempt and sets its
-// Hash field to be the payment hash if it's nil.
-//
-// NOTE: For legacy payments, which were created before the AMP feature was
-// enabled, the `Hash` field in their HTLC attempts is nil. In that case, we use
-// the payment hash as the `attempt.Hash` as they are identical.
-func (p *paymentLifecycle) patchLegacyPaymentHash(
-	a channeldb.HTLCAttempt) channeldb.HTLCAttempt {
-
-	// Exit early if this is not a legacy attempt.
-	if a.Hash != nil {
-		return a
-	}
-
-	// Log a warning if the user is still using legacy payments, which has
-	// weaker support.
-	log.Warnf("Found legacy htlc attempt %v in payment %v", a.AttemptID,
-		p.identifier)
-
-	// Set the attempt's hash to be the payment hash, which is the payment's
-	// `PaymentHash`` in the `PaymentCreationInfo`. For legacy payments
-	// before AMP feature, the `Hash` field was not set so we use the
-	// payment hash instead.
-	//
-	// NOTE: During the router's startup, we have a similar logic in
-	// `resumePayments`, in which we will use the payment hash instead if
-	// the attempt's hash is nil.
-	a.Hash = &p.identifier
-
-	return a
-}
-
 // reloadInflightAttempts is called when the payment lifecycle is resumed after
 // a restart. It reloads all inflight attempts from the control tower and
 // collects the results of the attempts that have been sent before.
@@ -1109,10 +1075,6 @@ func (p *paymentLifecycle) reloadInflightAttempts() (DBMPPayment, error) {
 		log.Infof("Resuming HTLC attempt %v for payment %v",
 			a.AttemptID, p.identifier)
 
-		// Potentially attach the payment hash to the `Hash` field if
-		// it's a legacy payment.
-		a = p.patchLegacyPaymentHash(a)
-
 		p.resultCollector(&a)
 	}
 


Test patch:
diff --git a/lnrpc/routerrpc/router_backend_test.go b/lnrpc/routerrpc/router_backend_test.go
index 5a633d9e6..877a3cc17 100644
--- a/lnrpc/routerrpc/router_backend_test.go
+++ b/lnrpc/routerrpc/router_backend_test.go
@@ -7,10 +7,8 @@ import (
 	"testing"
 
 	"github.com/btcsuite/btcd/btcutil"
-	"github.com/btcsuite/btcd/chaincfg"
 	"github.com/lightningnetwork/lnd/lnrpc"
 	"github.com/lightningnetwork/lnd/lnwire"
-	"github.com/lightningnetwork/lnd/record"
 	"github.com/lightningnetwork/lnd/routing"
 	"github.com/lightningnetwork/lnd/routing/route"
 	"github.com/stretchr/testify/require"
@@ -477,370 +475,3 @@ func testUnmarshalAMP(t *testing.T, test unmarshalAMPTest) {
 		t.Fatalf("test case has non-standard outcome")
 	}
 }
-
-// extractIntentTestCase defines a test case for the
-// TestExtractIntentFromSendRequest function. It includes the test name, the
-// RouterBackend instance, the SendPaymentRequest to be tested, a boolean
-// indicating if the test case is valid, and the expected error message if
-// applicable.
-type extractIntentTestCase struct {
-	name             string
-	backend          *RouterBackend
-	sendReq          *SendPaymentRequest
-	valid            bool
-	expectedErrorMsg string
-}
-
-// TestExtractIntentFromSendRequest verifies that extractIntentFromSendRequest
-// correctly translates a SendPaymentRequest from an RPC client into a
-// LightningPayment intent.
-func TestExtractIntentFromSendRequest(t *testing.T) {
-	const paymentAmount = btcutil.Amount(300_000)
-
-	const paymentReq = "lnbcrt500u1pnh0xflpp56w08q26t896vg2e9mtdkrem320tp" +
-		"wws9z9sfr7dw86dx97d90u4sdqqcqzzsxqyz5vqsp5z9945kvfy5g9afmakz" +
-		"yrur2t4hhn2tr87un8j0r0e6l5m5zm0fus9qxpqysgqk98c6j7qefdpdmzt4" +
-		"g6aykds4ydvf2x9lpngqcfux3hv8qlraan9v3s9296r5w5eh959yzadgh5ck" +
-		"gjydgyfxdpumxtuk3p3caugmlqpz5necs"
-
-	destNodeBytes, err := hex.DecodeString(destKey)
-	require.NoError(t, err)
-
-	target, err := route.NewVertexFromBytes(destNodeBytes)
-	require.NoError(t, err)
-
-	testCases := []extractIntentTestCase{
-		{
-			name:    "Time preference out of range",
-			backend: &RouterBackend{},
-			sendReq: &SendPaymentRequest{
-				TimePref: 2,
-			},
-			valid:            false,
-			expectedErrorMsg: "time preference out of range",
-		},
-		{
-			name:    "Outgoing channel exclusivity violation",
-			backend: &RouterBackend{},
-			sendReq: &SendPaymentRequest{
-				OutgoingChanId:  38484,
-				OutgoingChanIds: []uint64{383322},
-			},
-			valid: false,
-			expectedErrorMsg: "outgoing_chan_id and " +
-				"outgoing_chan_ids are mutually exclusive",
-		},
-		{
-			name:    "Invalid last hop pubkey length",
-			backend: &RouterBackend{},
-			sendReq: &SendPaymentRequest{
-				LastHopPubkey: []byte{1},
-			},
-			valid:            false,
-			expectedErrorMsg: "invalid vertex length",
-		},
-		{
-			name: "total time lock exceeds max allowed",
-			backend: &RouterBackend{
-				MaxTotalTimelock: 1000,
-			},
-			sendReq: &SendPaymentRequest{
-				CltvLimit: 1001,
-			},
-			valid: false,
-			expectedErrorMsg: "total time lock of 1001 exceeds " +
-				"max allowed 1000",
-		},
-		{
-			name:    "Max parts exceed allowed limit",
-			backend: &RouterBackend{},
-			sendReq: &SendPaymentRequest{
-				MaxParts:         1001,
-				MaxShardSizeMsat: 300_000,
-			},
-			valid: false,
-			expectedErrorMsg: "requested max_parts (1001) exceeds" +
-				" the allowed upper limit",
-		},
-		{
-			name: "Fee limit conflict, both sat and msat " +
-				"specified",
-			backend: &RouterBackend{},
-			sendReq: &SendPaymentRequest{
-				FeeLimitSat:  1000000,
-				FeeLimitMsat: 1000000000,
-			},
-			valid: false,
-			expectedErrorMsg: "sat and msat arguments are " +
-				"mutually exclusive",
-		},
-		{
-			name:    "Fee limit cannot be negative",
-			backend: &RouterBackend{},
-			sendReq: &SendPaymentRequest{
-				FeeLimitSat: -1,
-			},
-			valid:            false,
-			expectedErrorMsg: "amount cannot be negative",
-		},
-		{
-			name: "Dest custom records with type below minimum" +
-				" range",
-			backend: &RouterBackend{},
-			sendReq: &SendPaymentRequest{
-				DestCustomRecords: map[uint64][]byte{
-					65530: {1, 2},
-				},
-			},
-			valid:            false,
-			expectedErrorMsg: "no custom records with types below",
-		},
-		{
-			name:    "MPP params with keysend payments",
-			backend: &RouterBackend{},
-			sendReq: &SendPaymentRequest{
-				DestCustomRecords: map[uint64][]byte{
-					record.KeySendType: {1, 2},
-				},
-				MaxShardSizeMsat: 300_000,
-			},
-			valid: false,
-			expectedErrorMsg: "MPP not supported with keysend " +
-				"payments",
-		},
-		{
-			name: "Custom record entry with TLV type below " +
-				"minimum range",
-			backend: &RouterBackend{},
-			sendReq: &SendPaymentRequest{
-				FirstHopCustomRecords: map[uint64][]byte{
-					65530: {1, 2},
-				},
-			},
-			valid:            false,
-			expectedErrorMsg: "custom records entry with TLV type",
-		},
-		{
-			name: "Amount conflict, both sat and msat specified",
-			backend: &RouterBackend{
-				ShouldSetExpEndorsement: func() bool {
-					return true
-				},
-			},
-			sendReq: &SendPaymentRequest{
-				Amt:     int64(paymentAmount),
-				AmtMsat: int64(paymentAmount) * 1000,
-			},
-			valid: false,
-			expectedErrorMsg: "sat and msat arguments are " +
-				"mutually exclusive",
-		},
-		{
-			name: "Both dest and payment_request provided",
-			backend: &RouterBackend{
-				ShouldSetExpEndorsement: func() bool {
-					return false
-				},
-			},
-			sendReq: &SendPaymentRequest{
-				Amt:            int64(paymentAmount),
-				PaymentRequest: "test",
-				Dest:           destNodeBytes,
-			},
-			valid: false,
-			expectedErrorMsg: "dest and payment_request " +
-				"cannot appear together",
-		},
-		{
-			name: "Both payment_hash and payment_request provided",
-			backend: &RouterBackend{
-				ShouldSetExpEndorsement: func() bool {
-					return false
-				},
-			},
-			sendReq: &SendPaymentRequest{
-				Amt:            int64(paymentAmount),
-				PaymentRequest: "test",
-				PaymentHash:    make([]byte, 32),
-			},
-			valid: false,
-			expectedErrorMsg: "payment_hash and payment_request " +
-				"cannot appear together",
-		},
-		{
-			name: "Both final_cltv_delta and payment_request " +
-				"provided",
-			backend: &RouterBackend{
-				ShouldSetExpEndorsement: func() bool {
-					return false
-				},
-			},
-			sendReq: &SendPaymentRequest{
-				Amt:            int64(paymentAmount),
-				PaymentRequest: "test",
-				FinalCltvDelta: 100,
-			},
-			valid: false,
-			expectedErrorMsg: "final_cltv_delta and " +
-				"payment_request cannot appear together",
-		},
-		{
-			name: "Invalid payment request length",
-			backend: &RouterBackend{
-				ShouldSetExpEndorsement: func() bool {
-					return false
-				},
-				ActiveNetParams: &chaincfg.RegressionNetParams,
-			},
-			sendReq: &SendPaymentRequest{
-				Amt:            int64(paymentAmount),
-				PaymentRequest: "test",
-			},
-			valid:            false,
-			expectedErrorMsg: "invalid bech32 string length",
-		},
-		{
-			name: "Expired invoice payment request",
-			backend: &RouterBackend{
-				ShouldSetExpEndorsement: func() bool {
-					return false
-				},
-				ActiveNetParams: &chaincfg.RegressionNetParams,
-			},
-			sendReq: &SendPaymentRequest{
-				Amt:            int64(paymentAmount),
-				PaymentRequest: paymentReq,
-			},
-			valid:            false,
-			expectedErrorMsg: "invoice expired.",
-		},
-		{
-			name: "Invalid dest vertex length",
-			backend: &RouterBackend{
-				ShouldSetExpEndorsement: func() bool {
-					return false
-				},
-			},
-			sendReq: &SendPaymentRequest{
-				Amt:  int64(paymentAmount),
-				Dest: []byte{1},
-			},
-			valid:            false,
-			expectedErrorMsg: "invalid vertex length",
-		},
-		{
-			name: "Payment request with missing amount",
-			backend: &RouterBackend{
-				ShouldSetExpEndorsement: func() bool {
-					return false
-				},
-			},
-			sendReq: &SendPaymentRequest{
-				Dest:           destNodeBytes,
-				FinalCltvDelta: 100,
-			},
-			valid:            false,
-			expectedErrorMsg: "amount must be specified",
-		},
-		{
-			name: "Destination lacks AMP support",
-			backend: &RouterBackend{
-				ShouldSetExpEndorsement: func() bool {
-					return false
-				},
-			},
-			sendReq: &SendPaymentRequest{
-				Dest:         destNodeBytes,
-				Amt:          int64(paymentAmount),
-				Amp:          true,
-				DestFeatures: []lnrpc.FeatureBit{},
-			},
-			valid: false,
-			expectedErrorMsg: "destination doesn't " +
-				"support AMP payments",
-		},
-		{
-			name: "Invalid payment hash length",
-			backend: &RouterBackend{
-				ShouldSetExpEndorsement: func() bool {
-					return false
-				},
-			},
-			sendReq: &SendPaymentRequest{
-				Dest:        destNodeBytes,
-				Amt:         int64(paymentAmount),
-				PaymentHash: make([]byte, 1),
-			},
-			valid:            false,
-			expectedErrorMsg: "invalid hash length",
-		},
-		{
-			name: "Payment amount exceeds maximum possible amount",
-			backend: &RouterBackend{
-				ShouldSetExpEndorsement: func() bool {
-					return false
-				},
-			},
-			sendReq: &SendPaymentRequest{
-				Dest:             destNodeBytes,
-				Amt:              int64(paymentAmount),
-				PaymentHash:      make([]byte, 32),
-				MaxParts:         10,
-				MaxShardSizeMsat: 300_000,
-			},
-			valid: false,
-			expectedErrorMsg: "payment amount 300000000 mSAT " +
-				"exceeds maximum possible amount",
-		},
-		{
-			name: "Reject self-payments if not permitted",
-			backend: &RouterBackend{
-				MaxTotalTimelock: 1000,
-				ShouldSetExpEndorsement: func() bool {
-					return false
-				},
-				SelfNode: target,
-			},
-			sendReq: &SendPaymentRequest{
-				Dest:        destNodeBytes,
-				Amt:         int64(paymentAmount),
-				PaymentHash: make([]byte, 32),
-			},
-			valid:            false,
-			expectedErrorMsg: "self-payments not allowed",
-		},
-		{
-			name: "Valid send req parameters, payment settled",
-			backend: &RouterBackend{
-				MaxTotalTimelock: 1000,
-				ShouldSetExpEndorsement: func() bool {
-					return false
-				},
-			},
-			sendReq: &SendPaymentRequest{
-				Dest:             destNodeBytes,
-				Amt:              int64(paymentAmount),
-				PaymentHash:      make([]byte, 32),
-				MaxParts:         10,
-				MaxShardSizeMsat: 30_000_000,
-			},
-			valid: true,
-		},
-	}
-
-	for _, test := range testCases {
-		t.Run(test.name, func(t *testing.T) {
-			t.Parallel()
-
-			_, err := test.backend.
-				extractIntentFromSendRequest(test.sendReq)
-
-			if test.valid {
-				require.NoError(t, err)
-			} else {
-				require.ErrorContains(t, err,
-					test.expectedErrorMsg)
-			}
-		})
-	}
-}
diff --git a/lntest/node/harness_node.go b/lntest/node/harness_node.go
index 4cfd9a501..5f0f5da96 100644
--- a/lntest/node/harness_node.go
+++ b/lntest/node/harness_node.go
@@ -671,18 +671,14 @@ func (hn *HarnessNode) WaitForProcessExit() error {
 		hn.printErrf("timeout waiting for process to exit")
 	}
 
-	// If the node has an open log file handle, inspect the log file
-	// to verify that the node shut down correctly.
-	if hn.logFile != nil {
-		// Make sure log file is closed and renamed if necessary.
-		filename := finalizeLogfile(hn)
-
-		// Assert the node has shut down from the log file.
-		err1 := assertNodeShutdown(filename)
-		if err1 != nil {
-			return fmt.Errorf("[%s]: assert shutdown failed in "+
-				"log[%s]: %w", hn.Name(), filename, err1)
-		}
+	// Make sure log file is closed and renamed if necessary.
+	filename := finalizeLogfile(hn)
+
+	// Assert the node has shut down from the log file.
+	err1 := assertNodeShutdown(filename)
+	if err1 != nil {
+		return fmt.Errorf("[%s]: assert shutdown failed in log[%s]: %w",
+			hn.Name(), filename, err1)
 	}
 
 	// Rename the etcd.log file if the node was running on embedded etcd.
diff --git a/routing/bandwidth_test.go b/routing/bandwidth_test.go
index 259f347ba..726275a6c 100644
--- a/routing/bandwidth_test.go
+++ b/routing/bandwidth_test.go
@@ -7,6 +7,7 @@ import (
 	"github.com/go-errors/errors"
 	"github.com/lightningnetwork/lnd/fn/v2"
 	"github.com/lightningnetwork/lnd/htlcswitch"
+	"github.com/lightningnetwork/lnd/lnwallet"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/tlv"
 	"github.com/stretchr/testify/require"
@@ -150,7 +151,8 @@ func (*mockTrafficShaper) ShouldHandleTraffic(_ lnwire.ShortChannelID,
 // is a custom channel that should be handled by the traffic shaper, the
 // HandleTraffic method should be called first.
 func (*mockTrafficShaper) PaymentBandwidth(_, _ fn.Option[tlv.Blob],
-	linkBandwidth, _ lnwire.MilliSatoshi) (lnwire.MilliSatoshi, error) {
+	linkBandwidth, _ lnwire.MilliSatoshi,
+	_ lnwallet.AuxHtlcView) (lnwire.MilliSatoshi, error) {
 
 	return linkBandwidth, nil
 }
diff --git a/routing/payment_lifecycle_test.go b/routing/payment_lifecycle_test.go
index 9df02ec32..b04b04d4b 100644
--- a/routing/payment_lifecycle_test.go
+++ b/routing/payment_lifecycle_test.go
@@ -1809,61 +1809,3 @@ func TestHandleAttemptResultSuccess(t *testing.T) {
 	require.NoError(t, err, "expected no error")
 	require.Equal(t, attempt, attemptResult.attempt)
 }
-
-// TestReloadInflightAttemptsLegacy checks that when handling a legacy HTLC
-// attempt, `collectResult` behaves as expected.
-func TestReloadInflightAttemptsLegacy(t *testing.T) {
-	t.Parallel()
-
-	// Create a test paymentLifecycle with the initial two calls mocked.
-	p, m := newTestPaymentLifecycle(t)
-
-	// Mount the resultCollector to check the full call path.
-	p.resultCollector = p.collectResultAsync
-
-	// Create testing params.
-	paymentAmt := 10_000
-	preimage := lntypes.Preimage{1}
-	attempt := makeSettledAttempt(t, paymentAmt, preimage)
-
-	// Make the attempt.Hash to be nil to mock a legacy payment.
-	attempt.Hash = nil
-
-	// Create a mock result returned from the switch.
-	result := &htlcswitch.PaymentResult{
-		Preimage: preimage,
-	}
-
-	// 1. calls `FetchPayment` and return the payment.
-	m.control.On("FetchPayment", p.identifier).Return(m.payment, nil).Once()
-
-	// 2. calls `InFlightHTLCs` and return the attempt.
-	attempts := []channeldb.HTLCAttempt{*attempt}
-	m.payment.On("InFlightHTLCs").Return(attempts).Once()
-
-	// 3. Mock the htlcswitch to return a the result chan.
-	resultChan := make(chan *htlcswitch.PaymentResult, 1)
-	m.payer.On("GetAttemptResult",
-		attempt.AttemptID, p.identifier, mock.Anything,
-	).Return(resultChan, nil).Once().Run(func(args mock.Arguments) {
-		// Send the preimage to the result chan.
-		resultChan <- result
-	})
-
-	// Now call the method under test.
-	payment, err := p.reloadInflightAttempts()
-	require.NoError(t, err)
-	require.Equal(t, m.payment, payment)
-
-	var r *switchResult
-
-	// Assert the result is returned within testTimeout.
-	waitErr := wait.NoError(func() error {
-		r = <-p.resultCollected
-		return nil
-	}, testTimeout)
-	require.NoError(t, waitErr, "timeout waiting for result")
-
-	// Assert the result is received as expected.
-	require.Equal(t, result, r.result)
-}