Repository: lightningnetwork/lnd
Pull request #8044 (2023-09-29 10:18:57)

[1/7] lnwire: add new Gossip 1.75 messages
This is the first step towards the [Gossip 1.75 epic](https://github.com/lightningnetwork/lnd/issues/7961)

In this PR, the definitions for the new gossip messages defined in the 
taproot-gossip proposal are implemented. This includes:
- `announcement_signatures_2`
- `channel_announcement_2`
- `channel_update_2`

Various updates are also made to some existing messages such as `channel_ready` and `gossip_timestamp_range`

I expect there to still be a few updates to these messages as the proposal progresses


lightninglabs-deploy: @ellemouton, remember to re-request review from reviewers when ready
lightninglabs-deploy: @ellemouton, remember to re-request review from reviewers when ready
lightninglabs-deploy: @yyforyongyu: review reminder
@ellemouton, remember to re-request review from reviewers when ready
lightninglabs-deploy: @ellemouton, remember to re-request review from reviewers when ready
lightninglabs-deploy: @yyforyongyu: review reminder
@ellemouton, remember to re-request review from reviewers when ready
lightninglabs-deploy: @yyforyongyu: review reminder
@ellemouton, remember to re-request review from reviewers when ready
lightninglabs-deploy: @ellemouton, remember to re-request review from reviewers when ready
lightninglabs-deploy: @ellemouton, remember to re-request review from reviewers when ready
coderabbitai[bot]: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: skip review by coderabbit.ai -->

> [!IMPORTANT]
> ## Auto Review Skipped
> 
> Auto reviews are limited to the following labels: llm-review. Please add one of these labels to enable auto reviews.
> 
> 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.
> 

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

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

<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>

<!-- tweet_end --><!-- tips_start -->

---

<details>
<summary>Tips</summary>

### Chat

There are 3 ways to chat with CodeRabbit:

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
	- `I pushed a fix in commit <commit_id>.`
	- `Generate unit-tests for this file.`
- 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 tests 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 generate interesting stats about this repository from git and render them as a table.`
	- `@coderabbitai show all the console.log statements in this repository.`
	- `@coderabbitai read src/utils.ts and generate unit tests.`
	- `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`

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 as PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger a review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai help` to get help.


Additionally, you can add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.

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

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- The JSON schema for the configuration file is available [here](https://coderabbit.ai/integrations/coderabbit-overrides.v2.json).
- 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/coderabbit-overrides.v2.json`

### CodeRabbit Discord Community

Join our [Discord Community](https://discord.com/invite/GsXnASn26c) to get help, request features, and share feedback.

</details>

<!-- tips_end -->
lightninglabs-deploy: @yyforyongyu: review reminder
@roasbeef: review reminder
@ellemouton, remember to re-request review from reviewers when ready
lightninglabs-deploy: @roasbeef: review reminder
@ellemouton, remember to re-request review from reviewers when ready
lightninglabs-deploy: @roasbeef: review reminder
@ellemouton, remember to re-request review from reviewers when ready
lightninglabs-deploy: @roasbeef: review reminder
@ellemouton, remember to re-request review from reviewers when ready
lightninglabs-deploy: @roasbeef: review reminder
@ellemouton, remember to re-request review from reviewers when ready
lightninglabs-deploy: @roasbeef: review reminder
@ellemouton, remember to re-request review from reviewers when ready
lightninglabs-deploy: @roasbeef: review reminder
@ellemouton, remember to re-request review from reviewers when ready
lightninglabs-deploy: @roasbeef: review reminder
@ellemouton, remember to re-request review from reviewers when ready
lightninglabs-deploy: @roasbeef: review reminder
@ellemouton, remember to re-request review from reviewers when ready
lightninglabs-deploy: @roasbeef: review reminder
@ellemouton, remember to re-request review from reviewers when ready
lightninglabs-deploy: @roasbeef: review reminder
@ellemouton, remember to re-request review from reviewers when ready
lightninglabs-deploy: @roasbeef: review reminder
@ellemouton, remember to re-request review from reviewers when ready
lightninglabs-deploy: @roasbeef: review reminder
@ellemouton, remember to re-request review from reviewers when ready
lightninglabs-deploy: @roasbeef: review reminder
@ellemouton, remember to re-request review from reviewers when ready
lightninglabs-deploy: @roasbeef: review reminder
@ellemouton, remember to re-request review from reviewers when ready
lightninglabs-deploy: @roasbeef: review reminder
@ellemouton, remember to re-request review from reviewers when ready
ellemouton: sorry about the lag here - it is on the list. Just need to get things through with the RB stuff first. Then focus is back on this
lightninglabs-deploy: @roasbeef: review reminder
@ellemouton, remember to re-request review from reviewers when ready
lightninglabs-deploy: @roasbeef: review reminder
@ellemouton, remember to re-request review from reviewers when ready
ellemouton: !lightninglabs-deploy mute
Roasbeef: > I agree with both comments so im wondering what you guys think is the best solution here?

Perhaps the solution is moving the validation interface into the `netann` package? 

Patch:
diff --git a/channeldb/waitingproof.go b/channeldb/waitingproof.go
index c6b2b9df5..faefc620c 100644
--- a/channeldb/waitingproof.go
+++ b/channeldb/waitingproof.go
@@ -191,15 +191,17 @@ type WaitingProofKey [9]byte
 // needed to make channel proof exchange persistent, so that after client
 // restart we may receive remote/local half proof and process it.
 type WaitingProof struct {
-	*lnwire.AnnounceSignatures
+	*lnwire.AnnounceSignatures1
 	isRemote bool
 }
 
 // NewWaitingProof constructs a new waiting prof instance.
-func NewWaitingProof(isRemote bool, proof *lnwire.AnnounceSignatures) *WaitingProof {
+func NewWaitingProof(isRemote bool,
+	proof *lnwire.AnnounceSignatures1) *WaitingProof {
+
 	return &WaitingProof{
-		AnnounceSignatures: proof,
-		isRemote:           isRemote,
+		AnnounceSignatures1: proof,
+		isRemote:            isRemote,
 	}
 }
 
@@ -238,7 +240,7 @@ func (p *WaitingProof) Encode(w io.Writer) error {
 		return fmt.Errorf("expect io.Writer to be *bytes.Buffer")
 	}
 
-	if err := p.AnnounceSignatures.Encode(buf, 0); err != nil {
+	if err := p.AnnounceSignatures1.Encode(buf, 0); err != nil {
 		return err
 	}
 
@@ -252,11 +254,12 @@ func (p *WaitingProof) Decode(r io.Reader) error {
 		return err
 	}
 
-	msg := &lnwire.AnnounceSignatures{}
+	msg := &lnwire.AnnounceSignatures1{}
 	if err := msg.Decode(r, 0); err != nil {
 		return err
 	}
 
-	(*p).AnnounceSignatures = msg
+	p.AnnounceSignatures1 = msg
+
 	return nil
 }
diff --git a/discovery/chan_series.go b/discovery/chan_series.go
index 8cbca1277..b7b9af989 100644
--- a/discovery/chan_series.go
+++ b/discovery/chan_series.go
@@ -5,7 +5,6 @@ import (
 
 	"github.com/btcsuite/btcd/chaincfg/chainhash"
 	"github.com/lightningnetwork/lnd/channeldb"
-	"github.com/lightningnetwork/lnd/graph"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/netann"
 	"github.com/lightningnetwork/lnd/routing/route"
@@ -61,7 +60,8 @@ type ChannelGraphTimeSeries interface {
 	// specified short channel ID. If no channel updates are known for the
 	// channel, then an empty slice will be returned.
 	FetchChanUpdates(chain chainhash.Hash,
-		shortChanID lnwire.ShortChannelID) ([]*lnwire.ChannelUpdate, error)
+		shortChanID lnwire.ShortChannelID) ([]*lnwire.ChannelUpdate1,
+		error)
 }
 
 // ChanSeries is an implementation of the ChannelGraphTimeSeries
@@ -136,7 +136,7 @@ func (c *ChanSeries) UpdatesInHorizon(chain chainhash.Hash,
 		if edge1 != nil {
 			// We don't want to send channel updates that don't
 			// conform to the spec (anymore).
-			err := graph.ValidateChannelUpdateFields(0, edge1)
+			err := netann.ValidateChannelUpdateFields(0, edge1)
 			if err != nil {
 				log.Errorf("not sending invalid channel "+
 					"update %v: %v", edge1, err)
@@ -145,7 +145,7 @@ func (c *ChanSeries) UpdatesInHorizon(chain chainhash.Hash,
 			}
 		}
 		if edge2 != nil {
-			err := graph.ValidateChannelUpdateFields(0, edge2)
+			err := netann.ValidateChannelUpdateFields(0, edge2)
 			if err != nil {
 				log.Errorf("not sending invalid channel "+
 					"update %v: %v", edge2, err)
@@ -326,7 +326,7 @@ func (c *ChanSeries) FetchChanAnns(chain chainhash.Hash,
 //
 // NOTE: This is part of the ChannelGraphTimeSeries interface.
 func (c *ChanSeries) FetchChanUpdates(chain chainhash.Hash,
-	shortChanID lnwire.ShortChannelID) ([]*lnwire.ChannelUpdate, error) {
+	shortChanID lnwire.ShortChannelID) ([]*lnwire.ChannelUpdate1, error) {
 
 	chanInfo, e1, e2, err := c.graph.FetchChannelEdgesByID(
 		shortChanID.ToUint64(),
@@ -335,7 +335,7 @@ func (c *ChanSeries) FetchChanUpdates(chain chainhash.Hash,
 		return nil, err
 	}
 
-	chanUpdates := make([]*lnwire.ChannelUpdate, 0, 2)
+	chanUpdates := make([]*lnwire.ChannelUpdate1, 0, 2)
 	if e1 != nil {
 		chanUpdate, err := netann.ChannelUpdateFromEdge(chanInfo, e1)
 		if err != nil {
diff --git a/discovery/gossiper.go b/discovery/gossiper.go
index 84fae767f..a0d3d0d23 100644
--- a/discovery/gossiper.go
+++ b/discovery/gossiper.go
@@ -171,6 +171,10 @@ type Config struct {
 	// order to be included in the LN graph.
 	Graph graph.ChannelGraphSource
 
+	// ChainIO represents an abstraction over a source that can query the
+	// blockchain.
+	ChainIO lnwallet.BlockChainIO
+
 	// ChanSeries is an interfaces that provides access to a time series
 	// view of the current known channel graph. Each GossipSyncer enabled
 	// peer will utilize this in order to create and respond to channel
@@ -322,7 +326,7 @@ type Config struct {
 
 	// SignAliasUpdate is used to re-sign a channel update using the
 	// remote's alias if the option-scid-alias feature bit was negotiated.
-	SignAliasUpdate func(u *lnwire.ChannelUpdate) (*ecdsa.Signature,
+	SignAliasUpdate func(u *lnwire.ChannelUpdate1) (*ecdsa.Signature,
 		error)
 
 	// FindBaseByAlias finds the SCID stored in the graph by an alias SCID.
@@ -849,9 +853,9 @@ func (d *AuthenticatedGossiper) ProcessRemoteAnnouncement(msg lnwire.Message,
 	// To avoid inserting edges in the graph for our own channels that we
 	// have already closed, we ignore such channel announcements coming
 	// from the remote.
-	case *lnwire.ChannelAnnouncement:
+	case *lnwire.ChannelAnnouncement1:
 		ownKey := d.selfKey.SerializeCompressed()
-		ownErr := fmt.Errorf("ignoring remote ChannelAnnouncement " +
+		ownErr := fmt.Errorf("ignoring remote ChannelAnnouncement1 " +
 			"for own channel")
 
 		if bytes.Equal(m.NodeID1[:], ownKey) ||
@@ -1011,7 +1015,7 @@ func (d *deDupedAnnouncements) addMsg(message networkMsg) {
 	switch msg := message.msg.(type) {
 
 	// Channel announcements are identified by the short channel id field.
-	case *lnwire.ChannelAnnouncement:
+	case *lnwire.ChannelAnnouncement1:
 		deDupKey := msg.ShortChannelID
 		sender := route.NewVertex(message.source)
 
@@ -1035,7 +1039,7 @@ func (d *deDupedAnnouncements) addMsg(message networkMsg) {
 
 	// Channel updates are identified by the (short channel id,
 	// channelflags) tuple.
-	case *lnwire.ChannelUpdate:
+	case *lnwire.ChannelUpdate1:
 		sender := route.NewVertex(message.source)
 		deDupKey := channelUpdateID{
 			msg.ShortChannelID,
@@ -1047,7 +1051,15 @@ func (d *deDupedAnnouncements) addMsg(message networkMsg) {
 		if ok {
 			// If we already have seen this message, record its
 			// timestamp.
-			oldTimestamp = mws.msg.(*lnwire.ChannelUpdate).Timestamp
+			update, ok := mws.msg.(*lnwire.ChannelUpdate1)
+			if !ok {
+				log.Errorf("Expected *lnwire.ChannelUpdate1, "+
+					"got: %T", mws.msg)
+
+				return
+			}
+
+			oldTimestamp = update.Timestamp
 		}
 
 		// If we already had this message with a strictly newer
@@ -1416,7 +1428,7 @@ func (d *AuthenticatedGossiper) networkHandler() {
 			switch announcement.msg.(type) {
 			// Channel announcement signatures are amongst the only
 			// messages that we'll process serially.
-			case *lnwire.AnnounceSignatures:
+			case *lnwire.AnnounceSignatures1:
 				emittedAnnouncements, _ := d.processNetworkAnnouncement(
 					announcement,
 				)
@@ -1582,10 +1594,10 @@ func (d *AuthenticatedGossiper) isRecentlyRejectedMsg(msg lnwire.Message,
 
 	var scid uint64
 	switch m := msg.(type) {
-	case *lnwire.ChannelUpdate:
+	case *lnwire.ChannelUpdate1:
 		scid = m.ShortChannelID.ToUint64()
 
-	case *lnwire.ChannelAnnouncement:
+	case *lnwire.ChannelAnnouncement1:
 		scid = m.ShortChannelID.ToUint64()
 
 	default:
@@ -1844,7 +1856,7 @@ func remotePubFromChanInfo(chanInfo *models.ChannelEdgeInfo,
 // to receive the remote peer's proof, while the remote peer is able to fully
 // assemble the proof and craft the ChannelAnnouncement.
 func (d *AuthenticatedGossiper) processRejectedEdge(
-	chanAnnMsg *lnwire.ChannelAnnouncement,
+	chanAnnMsg *lnwire.ChannelAnnouncement1,
 	proof *models.ChannelAuthProof) ([]networkMsg, error) {
 
 	// First, we'll fetch the state of the channel as we know if from the
@@ -1878,7 +1890,7 @@ func (d *AuthenticatedGossiper) processRejectedEdge(
 	if err != nil {
 		return nil, err
 	}
-	err = graph.ValidateChannelAnn(chanAnn)
+	err = netann.ValidateChannelAnn(chanAnn, d.fetchPKScript)
 	if err != nil {
 		err := fmt.Errorf("assembled channel announcement proof "+
 			"for shortChanID=%v isn't valid: %v",
@@ -1922,6 +1934,13 @@ func (d *AuthenticatedGossiper) processRejectedEdge(
 	return announcements, nil
 }
 
+// fetchPKScript fetches the output script for the given SCID.
+func (d *AuthenticatedGossiper) fetchPKScript(chanID *lnwire.ShortChannelID) (
+	[]byte, error) {
+
+	return lnwallet.FetchPKScriptWithQuit(d.cfg.ChainIO, chanID, d.quit)
+}
+
 // addNode processes the given node announcement, and adds it to our channel
 // graph.
 func (d *AuthenticatedGossiper) addNode(msg *lnwire.NodeAnnouncement,
@@ -2029,19 +2048,19 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(
 	// *creation* of a new channel within the network. This only advertises
 	// the existence of a channel and not yet the routing policies in
 	// either direction of the channel.
-	case *lnwire.ChannelAnnouncement:
+	case *lnwire.ChannelAnnouncement1:
 		return d.handleChanAnnouncement(nMsg, msg, schedulerOp)
 
 	// A new authenticated channel edge update has arrived. This indicates
 	// that the directional information for an already known channel has
 	// been updated.
-	case *lnwire.ChannelUpdate:
+	case *lnwire.ChannelUpdate1:
 		return d.handleChanUpdate(nMsg, msg, schedulerOp)
 
 	// A new signature announcement has been received. This indicates
 	// willingness of nodes involved in the funding of a channel to
 	// announce this new channel to the rest of the world.
-	case *lnwire.AnnounceSignatures:
+	case *lnwire.AnnounceSignatures1:
 		return d.handleAnnSig(nMsg, msg)
 
 	default:
@@ -2058,7 +2077,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(
 // should be inspected.
 func (d *AuthenticatedGossiper) processZombieUpdate(
 	chanInfo *models.ChannelEdgeInfo, scid lnwire.ShortChannelID,
-	msg *lnwire.ChannelUpdate) error {
+	msg *lnwire.ChannelUpdate1) error {
 
 	// The least-significant bit in the flag on the channel update tells us
 	// which edge is being updated.
@@ -2081,7 +2100,7 @@ func (d *AuthenticatedGossiper) processZombieUpdate(
 			"with chan_id=%v", msg.ShortChannelID)
 	}
 
-	err := graph.VerifyChannelUpdateSignature(msg, pubKey)
+	err := netann.VerifyChannelUpdateSignature(msg, pubKey)
 	if err != nil {
 		return fmt.Errorf("unable to verify channel "+
 			"update signature: %v", err)
@@ -2129,7 +2148,7 @@ func (d *AuthenticatedGossiper) fetchNodeAnn(
 // MessageStore is seen as stale by the current graph.
 func (d *AuthenticatedGossiper) isMsgStale(msg lnwire.Message) bool {
 	switch msg := msg.(type) {
-	case *lnwire.AnnounceSignatures:
+	case *lnwire.AnnounceSignatures1:
 		chanInfo, _, _, err := d.cfg.Graph.GetChannelByID(
 			msg.ShortChannelID,
 		)
@@ -2151,7 +2170,7 @@ func (d *AuthenticatedGossiper) isMsgStale(msg lnwire.Message) bool {
 		// can safely delete the local proof from the database.
 		return chanInfo.AuthProof != nil
 
-	case *lnwire.ChannelUpdate:
+	case *lnwire.ChannelUpdate1:
 		_, p1, p2, err := d.cfg.Graph.GetChannelByID(msg.ShortChannelID)
 
 		// If the channel cannot be found, it is most likely a leftover
@@ -2195,8 +2214,8 @@ func (d *AuthenticatedGossiper) isMsgStale(msg lnwire.Message) bool {
 // updateChannel creates a new fully signed update for the channel, and updates
 // the underlying graph with the new state.
 func (d *AuthenticatedGossiper) updateChannel(info *models.ChannelEdgeInfo,
-	edge *models.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement,
-	*lnwire.ChannelUpdate, error) {
+	edge *models.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement1,
+	*lnwire.ChannelUpdate1, error) {
 
 	// Parse the unsigned edge into a channel update.
 	chanUpdate := netann.UnsignedChannelUpdateFromEdge(info, edge)
@@ -2218,7 +2237,7 @@ func (d *AuthenticatedGossiper) updateChannel(info *models.ChannelEdgeInfo,
 
 	// To ensure that our signature is valid, we'll verify it ourself
 	// before committing it to the slice returned.
-	err = graph.ValidateChannelUpdateAnn(
+	err = netann.ValidateChannelUpdateAnn(
 		d.selfKey, info.Capacity, chanUpdate,
 	)
 	if err != nil {
@@ -2234,10 +2253,10 @@ func (d *AuthenticatedGossiper) updateChannel(info *models.ChannelEdgeInfo,
 	// We'll also create the original channel announcement so the two can
 	// be broadcast along side each other (if necessary), but only if we
 	// have a full channel announcement for this channel.
-	var chanAnn *lnwire.ChannelAnnouncement
+	var chanAnn *lnwire.ChannelAnnouncement1
 	if info.AuthProof != nil {
 		chanID := lnwire.NewShortChanIDFromInt(info.ChannelID)
-		chanAnn = &lnwire.ChannelAnnouncement{
+		chanAnn = &lnwire.ChannelAnnouncement1{
 			ShortChannelID:  chanID,
 			NodeID1:         info.NodeKey1Bytes,
 			NodeID2:         info.NodeKey2Bytes,
@@ -2284,7 +2303,7 @@ func (d *AuthenticatedGossiper) SyncManager() *SyncManager {
 // IsKeepAliveUpdate determines whether this channel update is considered a
 // keep-alive update based on the previous channel update processed for the same
 // direction.
-func IsKeepAliveUpdate(update *lnwire.ChannelUpdate,
+func IsKeepAliveUpdate(update *lnwire.ChannelUpdate1,
 	prev *models.ChannelEdgePolicy) bool {
 
 	// Both updates should be from the same direction.
@@ -2409,18 +2428,18 @@ func (d *AuthenticatedGossiper) handleNodeAnnouncement(nMsg *networkMsg,
 
 // handleChanAnnouncement processes a new channel announcement.
 func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg,
-	ann *lnwire.ChannelAnnouncement,
+	ann *lnwire.ChannelAnnouncement1,
 	ops []batch.SchedulerOption) ([]networkMsg, bool) {
 
 	scid := ann.ShortChannelID
 
-	log.Debugf("Processing ChannelAnnouncement: peer=%v, short_chan_id=%v",
+	log.Debugf("Processing ChannelAnnouncement1: peer=%v, short_chan_id=%v",
 		nMsg.peer, scid.ToUint64())
 
 	// We'll ignore any channel announcements that target any chain other
 	// than the set of chains we know of.
 	if !bytes.Equal(ann.ChainHash[:], d.cfg.ChainHash[:]) {
-		err := fmt.Errorf("ignoring ChannelAnnouncement from chain=%v"+
+		err := fmt.Errorf("ignoring ChannelAnnouncement1 from chain=%v"+
 			", gossiper on chain=%v", ann.ChainHash,
 			d.cfg.ChainHash)
 		log.Errorf(err.Error())
@@ -2521,7 +2540,8 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg,
 	// the signatures within the proof as it should be well formed.
 	var proof *models.ChannelAuthProof
 	if nMsg.isRemote {
-		if err := graph.ValidateChannelAnn(ann); err != nil {
+		err := netann.ValidateChannelAnn(ann, d.fetchPKScript)
+		if err != nil {
 			err := fmt.Errorf("unable to validate announcement: "+
 				"%v", err)
 
@@ -2753,7 +2773,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg,
 			// Reprocess the message, making sure we return an
 			// error to the original caller in case the gossiper
 			// shuts down.
-			case *lnwire.ChannelUpdate:
+			case *lnwire.ChannelUpdate1:
 				log.Debugf("Reprocessing ChannelUpdate for "+
 					"shortChanID=%v", scid.ToUint64())
 
@@ -2788,7 +2808,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg,
 
 	nMsg.err <- nil
 
-	log.Debugf("Processed ChannelAnnouncement: peer=%v, short_chan_id=%v",
+	log.Debugf("Processed ChannelAnnouncement1: peer=%v, short_chan_id=%v",
 		nMsg.peer, scid.ToUint64())
 
 	return announcements, true
@@ -2796,7 +2816,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg,
 
 // handleChanUpdate processes a new channel update.
 func (d *AuthenticatedGossiper) handleChanUpdate(nMsg *networkMsg,
-	upd *lnwire.ChannelUpdate,
+	upd *lnwire.ChannelUpdate1,
 	ops []batch.SchedulerOption) ([]networkMsg, bool) {
 
 	log.Debugf("Processing ChannelUpdate: peer=%v, short_chan_id=%v, ",
@@ -3009,7 +3029,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(nMsg *networkMsg,
 	// Validate the channel announcement with the expected public key and
 	// channel capacity. In the case of an invalid channel update, we'll
 	// return an error to the caller and exit early.
-	err = graph.ValidateChannelUpdateAnn(pubKey, chanInfo.Capacity, upd)
+	err = netann.ValidateChannelUpdateAnn(pubKey, chanInfo.Capacity, upd)
 	if err != nil {
 		rErr := fmt.Errorf("unable to validate channel update "+
 			"announcement for short_chan_id=%v: %v",
@@ -3200,7 +3220,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(nMsg *networkMsg,
 
 // handleAnnSig processes a new announcement signatures message.
 func (d *AuthenticatedGossiper) handleAnnSig(nMsg *networkMsg,
-	ann *lnwire.AnnounceSignatures) ([]networkMsg, bool) {
+	ann *lnwire.AnnounceSignatures1) ([]networkMsg, bool) {
 
 	needBlockHeight := ann.ShortChannelID.BlockHeight +
 		d.cfg.ProofMatureDelta
@@ -3415,7 +3435,8 @@ func (d *AuthenticatedGossiper) handleAnnSig(nMsg *networkMsg,
 
 	// With all the necessary components assembled validate the full
 	// channel announcement proof.
-	if err := graph.ValidateChannelAnn(chanAnn); err != nil {
+	err = netann.ValidateChannelAnn(chanAnn, d.fetchPKScript)
+	if err != nil {
 		err := fmt.Errorf("channel announcement proof for "+
 			"short_chan_id=%v isn't valid: %v", shortChanID, err)
 
diff --git a/discovery/message_store.go b/discovery/message_store.go
index 10fa51623..025a89d67 100644
--- a/discovery/message_store.go
+++ b/discovery/message_store.go
@@ -83,9 +83,9 @@ func NewMessageStore(db kvdb.Backend) (*MessageStore, error) {
 func msgShortChanID(msg lnwire.Message) (lnwire.ShortChannelID, error) {
 	var shortChanID lnwire.ShortChannelID
 	switch msg := msg.(type) {
-	case *lnwire.AnnounceSignatures:
+	case *lnwire.AnnounceSignatures1:
 		shortChanID = msg.ShortChannelID
-	case *lnwire.ChannelUpdate:
+	case *lnwire.ChannelUpdate1:
 		shortChanID = msg.ShortChannelID
 	default:
 		return shortChanID, ErrUnsupportedMessage
@@ -160,7 +160,7 @@ func (s *MessageStore) DeleteMessage(msg lnwire.Message,
 		// In the event that we're attempting to delete a ChannelUpdate
 		// from the store, we'll make sure that we're actually deleting
 		// the correct one as it can be overwritten.
-		if msg, ok := msg.(*lnwire.ChannelUpdate); ok {
+		if msg, ok := msg.(*lnwire.ChannelUpdate1); ok {
 			// Deleting a value from a bucket that doesn't exist
 			// acts as a NOP, so we'll return if a message doesn't
 			// exist under this key.
@@ -176,7 +176,13 @@ func (s *MessageStore) DeleteMessage(msg lnwire.Message,
 
 			// If the timestamps don't match, then the update stored
 			// should be the latest one, so we'll avoid deleting it.
-			if msg.Timestamp != dbMsg.(*lnwire.ChannelUpdate).Timestamp {
+			m, ok := dbMsg.(*lnwire.ChannelUpdate1)
+			if !ok {
+				return fmt.Errorf("expected "+
+					"*lnwire.ChannelUpdate1, got: %T",
+					dbMsg)
+			}
+			if msg.Timestamp != m.Timestamp {
 				return nil
 			}
 		}
diff --git a/discovery/syncer.go b/discovery/syncer.go
index cdb041d99..3043b39cd 100644
--- a/discovery/syncer.go
+++ b/discovery/syncer.go
@@ -1406,9 +1406,11 @@ func (g *GossipSyncer) FilterGossipMsgs(msgs ...msgWithSenders) {
 	// set of channel announcements and channel updates. This will allow us
 	// to quickly check if we should forward a chan ann, based on the known
 	// channel updates for a channel.
-	chanUpdateIndex := make(map[lnwire.ShortChannelID][]*lnwire.ChannelUpdate)
+	chanUpdateIndex := make(
+		map[lnwire.ShortChannelID][]*lnwire.ChannelUpdate1,
+	)
 	for _, msg := range msgs {
-		chanUpdate, ok := msg.msg.(*lnwire.ChannelUpdate)
+		chanUpdate, ok := msg.msg.(*lnwire.ChannelUpdate1)
 		if !ok {
 			continue
 		}
@@ -1447,7 +1449,7 @@ func (g *GossipSyncer) FilterGossipMsgs(msgs ...msgWithSenders) {
 		// For each channel announcement message, we'll only send this
 		// message if the channel updates for the channel are between
 		// our time range.
-		case *lnwire.ChannelAnnouncement:
+		case *lnwire.ChannelAnnouncement1:
 			// First, we'll check if the channel updates are in
 			// this message batch.
 			chanUpdates, ok := chanUpdateIndex[msg.ShortChannelID]
@@ -1478,7 +1480,7 @@ func (g *GossipSyncer) FilterGossipMsgs(msgs ...msgWithSenders) {
 
 		// For each channel update, we'll only send if it the timestamp
 		// is between our time range.
-		case *lnwire.ChannelUpdate:
+		case *lnwire.ChannelUpdate1:
 			if passesFilter(msg.Timestamp) {
 				msgsToSend = append(msgsToSend, msg)
 			}
diff --git a/docs/code_contribution_guidelines.md b/docs/code_contribution_guidelines.md
index a847b6fe9..9e4203c8b 100644
--- a/docs/code_contribution_guidelines.md
+++ b/docs/code_contribution_guidelines.md
@@ -77,7 +77,7 @@ security and performance implications.
 
 # Required Reading
 
-- [Effective Go](https://golang.org/doc/effective_go.html) - The entire `lnd` 
+- [Effective Go](http://golang.org/doc/effective_go.html) - The entire `lnd` 
   project follows the guidelines in this document.  For your code to be accepted,
   it must follow the guidelines therein.
 - [Original Satoshi Whitepaper](https://bitcoin.org/bitcoin.pdf) - This is the white paper that started it all.  Having a solid
@@ -85,7 +85,7 @@ security and performance implications.
 - [Lightning Network Whitepaper](https://lightning.network/lightning-network-paper.pdf) - This is the white paper that kicked off the Layer 2 revolution. Having a good grasp of the concepts of Lightning will make the core logic within the daemon much more comprehensible: Bitcoin Script, off-chain blockchain protocols, payment channels, bidirectional payment channels, relative and absolute time-locks, commitment state revocations, and Segregated Witness. 
     - The original LN was written for a rather narrow audience, the paper may be a bit unapproachable to many. Thanks to the Bitcoin community, there exist many easily accessible supplemental resources which can help one see how all the pieces fit together from double-spend protection all the way up to commitment state transitions and Hash Time Locked Contracts (HTLCs): 
         - [Lightning Network Summary](https://lightning.network/lightning-network-summary.pdf)
-        - [Understanding the Lightning Network 3-Part series](https://bitcoinmagazine.com/technical/understanding-the-lightning-network-part-building-a-bidirectional-payment-channel-1464710791) 
+        - [Understanding the Lightning Network 3-Part series](https://bitcoinmagazine.com/articles/understanding-the-lightning-network-part-building-a-bidirectional-payment-channel-1464710791) 
         - [Deployable Lightning](https://github.com/ElementsProject/lightning/blob/master/doc/miscellaneous/deployable-lightning.pdf)
 
 
@@ -140,7 +140,7 @@ code works correctly when it is fed correct data as well as incorrect data
 
 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).
+test coverage tools, see the [golang cover blog post](http://blog.golang.org/cover).
 
 A quick summary of test practices follows:
 - All new code should be accompanied by tests that ensure the code behaves
@@ -168,7 +168,7 @@ extensively using the commands within our `Makefile`. As a result, we recommend
 - 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)
+    [Effective Go](http://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
diff --git a/docs/grpc/c#.md b/docs/grpc/c#.md
index 3f7521180..1ebebbc3d 100644
--- a/docs/grpc/c#.md
+++ b/docs/grpc/c#.md
@@ -219,4 +219,4 @@ client.GetInfo(new GetInfoRequest());
 
 ### Conclusion
 
-With the above, you should have all the `lnd` related `gRPC` dependencies installed locally in your project. In order to get up to speed with `protobuf` usage from C#, see [this official `protobuf` tutorial for C#](https://developers.google.com/protocol-buffers/docs/csharptutorial). Additionally, [this official gRPC resource](https://grpc.io/docs/languages/csharp/) provides more details around how to drive `gRPC` from C#.
+With the above, you should have all the `lnd` related `gRPC` dependencies installed locally in your project. In order to get up to speed with `protobuf` usage from C#, see [this official `protobuf` tutorial for C#](https://developers.google.com/protocol-buffers/docs/csharptutorial). Additionally, [this official gRPC resource](http://www.grpc.io/docs/tutorials/basic/csharp.html) provides more details around how to drive `gRPC` from C#.
\ No newline at end of file
diff --git a/docs/grpc/javascript.md b/docs/grpc/javascript.md
index 42da61eba..9cf65f637 100644
--- a/docs/grpc/javascript.md
+++ b/docs/grpc/javascript.md
@@ -235,9 +235,9 @@ client.getInfo({}, (err, response) => {
 With the above, you should have all the `lnd` related `gRPC` dependencies
 installed locally in your project. In order to get up to speed with `protofbuf`
 usage from Javascript, see [this official `protobuf` reference for
-Javascript](https://protobuf.dev/protobuf-javascript/).
+Javascript](https://developers.google.com/protocol-buffers/docs/reference/javascript-generated).
 Additionally, [this official gRPC
-resource](https://grpc.io/docs/languages/node/basics/) provides more
+resource](http://www.grpc.io/docs/tutorials/basic/node.html) provides more
 details around how to drive `gRPC` from `node.js`.
 
 ## API documentation
diff --git a/docs/release-notes/release-notes-0.19.0.md b/docs/release-notes/release-notes-0.19.0.md
index bda488095..8d3a908fd 100644
--- a/docs/release-notes/release-notes-0.19.0.md
+++ b/docs/release-notes/release-notes-0.19.0.md
@@ -52,6 +52,9 @@
 # Technical and Architectural Updates
 ## BOLT Spec Updates
 
+* Add new [lnwire](https://github.com/lightningnetwork/lnd/pull/8044) messages
+  for the Gossip 1.75 protocol.
+
 ## Testing
 ## Database
 
diff --git a/docs/signing_release_candidate.md b/docs/signing_release_candidate.md
deleted file mode 100644
index 263e170d0..000000000
--- a/docs/signing_release_candidate.md
+++ /dev/null
@@ -1,70 +0,0 @@
-# Signing a LND Release
-
-When a new version of LND is released, binaries for the lnd and lncli programs
-are provided for various platforms and CPU architectures. The hashes of all
-these binaries are written into a file known as the "Manifest". This Manifest is
-then signed by a quorum of trusted LND contributors (see [verify-install.sh](/scripts/verify-install.sh)
-script for more details), ensuring that users can trust the binaries they 
-download, knowing they haven't been modified during the automated build process.
-
-To verify a release binary, users have two options:
-
-* Manual Verification: Users can manually download the signature files and 
-Manifest from GitHub LND release page, then verify the PGP signatures and 
-hashes.
-
-* Automated Verification: The LND repository provides a script, 
-[verify-install.sh](/scripts/verify-install.sh), that automates the verification process. This script uses a
-set of trusted developer keys (located in the repo under [scripts/keys/](/scripts/keys)) and
-downloads the necessary data from the GitHub server to verify the integrity of
-the local lnd/lncli binaries.
-
-Running [verify-install.sh](/scripts/verify-install.sh) validates that trusted developers attest to the authenticity between the lnd release binaries hosted on Github and the developer's local builds.
-
-## Adding a new developer as a signer
-
-When another developer is added to the trusted group of people which are
-allowed to sign the lnd/lncli releases, their public PGP key needs to be added to
-the LND repo. These keys are added in a PR in which 2 reviewers ensure the developer
-is in possession of the PGP key which will be added to the LND repo.
-(See https://github.com/lightningnetwork/lnd/pull/8788 as an example).
-It is important that the name of the PGP key equals the name in the
-[verify-install.sh](/scripts/verify-install.sh) script. See also [scripts/keys/README.md](/scripts/keys/README.md) for more information.
-
-## Signing a release binary package
-
-If the new developer's PGP key has been successfully added to the LND repository,
-through the aforementioned PR example, they are now able to provide their
-signature for the new release's "Manifest" file. To do so, the developer must
-follow these steps:
-
-* Follow the build instructions at https://github.com/lightningnetwork/lnd/blob/masterdocs/release.md#building-a-new-release.
-
-* After a successful build, all binaries and Manifest files, will be placed
-in a directory, named after the tag, created within the directory in which the build occurred. For 
-instance, in the case mentioned above, the folder will be named 
-`lnd-v0.18.3-beta`.
-Ensure that the SHA-256 hashes, in your locally-generated Manifest file, match
-those in the Manifest file of the official release on the LND GitHub repository.  
-Tip: Download the official release Manifest file to your local maschine and do:  
-`diff lnd-v0.18.3-beta/manifest-v0.18.3-beta.txt ~/Downloads/manifest-v0.18.3-beta.txt`
-(example command for a release candidate called `v0.18.3-beta`)  
-Only if all hashes are identical, should you sign the release.  If the digests
-match, see the example signing comand, assuming your PGP signing key is
-available on your local device:  
-`gpg --local-user $KEYID  --detach-sig --output manifest-$USERNAME-v0.18.3-beta.sig manifest-v0.18.3-beta.txt`.  
-`USERNAME` being the name in the `[verify-install.sh](../scripts/verify-install.sh)`
-script and also the name of your PGP key file. The whole argument `--local-user $KEYID`
-is only needed if there's more than one signing key on your local machine. Be
-sure to substitute the TAG value `v0.18.3-beta` with the version you are 
-currently signing.
-
-* Finally, upload the signature file
-(e.g. manifest-USERNAME-v0.18.3-beta.sig) to the GitHub release page.
-Github write permissions are required to upload signatures to the LND release
-page. To avoid interfering with other signers who may be updating the GitHub
-release page, LND developers use a `KeyBase` communication channel to signal
-when an edit is in progress. Once your signature file is successfully uploaded
-and the release page is unlocked, the signing process is complete.
-
-Congratulations signing the LND release 🎉.
\ No newline at end of file
diff --git a/funding/manager.go b/funding/manager.go
index fb36bd990..3af8ad104 100644
--- a/funding/manager.go
+++ b/funding/manager.go
@@ -4143,9 +4143,9 @@ func (f *Manager) ensureInitialForwardingPolicy(chanID lnwire.ChannelID,
 // chanAnnouncement encapsulates the two authenticated announcements that we
 // send out to the network after a new channel has been created locally.
 type chanAnnouncement struct {
-	chanAnn       *lnwire.ChannelAnnouncement
-	chanUpdateAnn *lnwire.ChannelUpdate
-	chanProof     *lnwire.AnnounceSignatures
+	chanAnn       *lnwire.ChannelAnnouncement1
+	chanUpdateAnn *lnwire.ChannelUpdate1
+	chanProof     *lnwire.AnnounceSignatures1
 }
 
 // newChanAnnouncement creates the authenticated channel announcement messages
@@ -4168,7 +4168,7 @@ func (f *Manager) newChanAnnouncement(localPubKey,
 	// The unconditional section of the announcement is the ShortChannelID
 	// itself which compactly encodes the location of the funding output
 	// within the blockchain.
-	chanAnn := &lnwire.ChannelAnnouncement{
+	chanAnn := &lnwire.ChannelAnnouncement1{
 		ShortChannelID: shortChanID,
 		Features:       lnwire.NewRawFeatureVector(),
 		ChainHash:      chainHash,
@@ -4238,7 +4238,7 @@ func (f *Manager) newChanAnnouncement(localPubKey,
 
 	// We announce the channel with the default values. Some of
 	// these values can later be changed by crafting a new ChannelUpdate.
-	chanUpdateAnn := &lnwire.ChannelUpdate{
+	chanUpdateAnn := &lnwire.ChannelUpdate1{
 		ShortChannelID: shortChanID,
 		ChainHash:      chainHash,
 		Timestamp:      uint32(time.Now().Unix()),
@@ -4337,7 +4337,7 @@ func (f *Manager) newChanAnnouncement(localPubKey,
 	// Finally, we'll generate the announcement proof which we'll use to
 	// provide the other side with the necessary signatures required to
 	// allow them to reconstruct the full channel announcement.
-	proof := &lnwire.AnnounceSignatures{
+	proof := &lnwire.AnnounceSignatures1{
 		ChannelID:      chanID,
 		ShortChannelID: shortChanID,
 	}
diff --git a/go.mod b/go.mod
index 65a4f991f..4aeea3808 100644
--- a/go.mod
+++ b/go.mod
@@ -45,6 +45,7 @@ require (
 	github.com/lightningnetwork/lnd/tor v1.1.2
 	github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796
 	github.com/miekg/dns v1.1.43
+	github.com/pkg/errors v0.9.1
 	github.com/prometheus/client_golang v1.11.1
 	github.com/stretchr/testify v1.9.0
 	github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02
@@ -135,7 +136,6 @@ require (
 	github.com/opencontainers/image-spec v1.0.2 // indirect
 	github.com/opencontainers/runc v1.1.12 // indirect
 	github.com/ory/dockertest/v3 v3.10.0 // indirect
-	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/prometheus/client_model v0.2.0 // indirect
 	github.com/prometheus/common v0.26.0 // indirect
diff --git a/graph/ann_validation.go b/graph/ann_validation.go
index 3936b4652..3c93d06e5 100644
--- a/graph/ann_validation.go
+++ b/graph/ann_validation.go
@@ -2,89 +2,13 @@ package graph
 
 import (
 	"bytes"
-	"fmt"
 
 	"github.com/btcsuite/btcd/btcec/v2"
-	"github.com/btcsuite/btcd/btcutil"
 	"github.com/btcsuite/btcd/chaincfg/chainhash"
-	"github.com/davecgh/go-spew/spew"
 	"github.com/go-errors/errors"
 	"github.com/lightningnetwork/lnd/lnwire"
 )
 
-// ValidateChannelAnn validates the channel announcement message and checks
-// that node signatures covers the announcement message, and that the bitcoin
-// signatures covers the node keys.
-func ValidateChannelAnn(a *lnwire.ChannelAnnouncement) error {
-	// First, we'll compute the digest (h) which is to be signed by each of
-	// the keys included within the node announcement message. This hash
-	// digest includes all the keys, so the (up to 4 signatures) will
-	// attest to the validity of each of the keys.
-	data, err := a.DataToSign()
-	if err != nil {
-		return err
-	}
-	dataHash := chainhash.DoubleHashB(data)
-
-	// First we'll verify that the passed bitcoin key signature is indeed a
-	// signature over the computed hash digest.
-	bitcoinSig1, err := a.BitcoinSig1.ToSignature()
-	if err != nil {
-		return err
-	}
-	bitcoinKey1, err := btcec.ParsePubKey(a.BitcoinKey1[:])
-	if err != nil {
-		return err
-	}
-	if !bitcoinSig1.Verify(dataHash, bitcoinKey1) {
-		return errors.New("can't verify first bitcoin signature")
-	}
-
-	// If that checks out, then we'll verify that the second bitcoin
-	// signature is a valid signature of the bitcoin public key over hash
-	// digest as well.
-	bitcoinSig2, err := a.BitcoinSig2.ToSignature()
-	if err != nil {
-		return err
-	}
-	bitcoinKey2, err := btcec.ParsePubKey(a.BitcoinKey2[:])
-	if err != nil {
-		return err
-	}
-	if !bitcoinSig2.Verify(dataHash, bitcoinKey2) {
-		return errors.New("can't verify second bitcoin signature")
-	}
-
-	// Both node signatures attached should indeed be a valid signature
-	// over the selected digest of the channel announcement signature.
-	nodeSig1, err := a.NodeSig1.ToSignature()
-	if err != nil {
-		return err
-	}
-	nodeKey1, err := btcec.ParsePubKey(a.NodeID1[:])
-	if err != nil {
-		return err
-	}
-	if !nodeSig1.Verify(dataHash, nodeKey1) {
-		return errors.New("can't verify data in first node signature")
-	}
-
-	nodeSig2, err := a.NodeSig2.ToSignature()
-	if err != nil {
-		return err
-	}
-	nodeKey2, err := btcec.ParsePubKey(a.NodeID2[:])
-	if err != nil {
-		return err
-	}
-	if !nodeSig2.Verify(dataHash, nodeKey2) {
-		return errors.New("can't verify data in second node signature")
-	}
-
-	return nil
-
-}
-
 // ValidateNodeAnn validates the node announcement by ensuring that the
 // attached signature is needed a signature of the node announcement under the
 // specified node public key.
@@ -121,70 +45,3 @@ func ValidateNodeAnn(a *lnwire.NodeAnnouncement) error {
 
 	return nil
 }
-
-// ValidateChannelUpdateAnn validates the channel update announcement by
-// checking (1) that the included signature covers the announcement and has been
-// signed by the node's private key, and (2) that the announcement's message
-// flags and optional fields are sane.
-func ValidateChannelUpdateAnn(pubKey *btcec.PublicKey, capacity btcutil.Amount,
-	a *lnwire.ChannelUpdate) error {
-
-	if err := ValidateChannelUpdateFields(capacity, a); err != nil {
-		return err
-	}
-
-	return VerifyChannelUpdateSignature(a, pubKey)
-}
-
-// VerifyChannelUpdateSignature verifies that the channel update message was
-// signed by the party with the given node public key.
-func VerifyChannelUpdateSignature(msg *lnwire.ChannelUpdate,
-	pubKey *btcec.PublicKey) error {
-
-	data, err := msg.DataToSign()
-	if err != nil {
-		return fmt.Errorf("unable to reconstruct message data: %w", err)
-	}
-	dataHash := chainhash.DoubleHashB(data)
-
-	nodeSig, err := msg.Signature.ToSignature()
-	if err != nil {
-		return err
-	}
-
-	if !nodeSig.Verify(dataHash, pubKey) {
-		return fmt.Errorf("invalid signature for channel update %v",
-			spew.Sdump(msg))
-	}
-
-	return nil
-}
-
-// ValidateChannelUpdateFields validates a channel update's message flags and
-// corresponding update fields.
-func ValidateChannelUpdateFields(capacity btcutil.Amount,
-	msg *lnwire.ChannelUpdate) error {
-
-	// The maxHTLC flag is mandatory.
-	if !msg.MessageFlags.HasMaxHtlc() {
-		return errors.Errorf("max htlc flag not set for channel "+
-			"update %v", spew.Sdump(msg))
-	}
-
-	maxHtlc := msg.HtlcMaximumMsat
-	if maxHtlc == 0 || maxHtlc < msg.HtlcMinimumMsat {
-		return errors.Errorf("invalid max htlc for channel "+
-			"update %v", spew.Sdump(msg))
-	}
-
-	// For light clients, the capacity will not be set so we'll skip
-	// checking whether the MaxHTLC value respects the channel's
-	// capacity.
-	capacityMsat := lnwire.NewMSatFromSatoshis(capacity)
-	if capacityMsat != 0 && maxHtlc > capacityMsat {
-		return errors.Errorf("max_htlc (%v) for channel update "+
-			"greater than capacity (%v)", maxHtlc, capacityMsat)
-	}
-
-	return nil
-}
diff --git a/graph/builder.go b/graph/builder.go
index 717d3e5ad..8b8b40d64 100644
--- a/graph/builder.go
+++ b/graph/builder.go
@@ -27,6 +27,7 @@ import (
 	"github.com/lightningnetwork/lnd/lnwallet/chanvalidate"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/multimutex"
+	"github.com/lightningnetwork/lnd/netann"
 	"github.com/lightningnetwork/lnd/routing/chainview"
 	"github.com/lightningnetwork/lnd/routing/route"
 	"github.com/lightningnetwork/lnd/ticker"
@@ -1231,7 +1232,9 @@ func (b *Builder) processUpdate(msg interface{},
 		// to obtain the full funding outpoint that's encoded within
 		// the channel ID.
 		channelID := lnwire.NewShortChanIDFromInt(msg.ChannelID)
-		fundingTx, err := b.fetchFundingTxWrapper(&channelID)
+		fundingTx, err := lnwallet.FetchFundingTxWrapper(
+			b.cfg.Chain, &channelID, b.quit,
+		)
 		if err != nil {
 			//nolint:lll
 			//
@@ -1448,69 +1451,6 @@ func (b *Builder) processUpdate(msg interface{},
 	return nil
 }
 
-// fetchFundingTxWrapper is a wrapper around fetchFundingTx, except that it
-// will exit if the router has stopped.
-func (b *Builder) fetchFundingTxWrapper(chanID *lnwire.ShortChannelID) (
-	*wire.MsgTx, error) {
-
-	txChan := make(chan *wire.MsgTx, 1)
-	errChan := make(chan error, 1)
-
-	go func() {
-		tx, err := b.fetchFundingTx(chanID)
-		if err != nil {
-			errChan <- err
-			return
-		}
-
-		txChan <- tx
-	}()
-
-	select {
-	case tx := <-txChan:
-		return tx, nil
-
-	case err := <-errChan:
-		return nil, err
-
-	case <-b.quit:
-		return nil, ErrGraphBuilderShuttingDown
-	}
-}
-
-// fetchFundingTx returns the funding transaction identified by the passed
-// short channel ID.
-//
-// TODO(roasbeef): replace with call to GetBlockTransaction? (would allow to
-// later use getblocktxn).
-func (b *Builder) fetchFundingTx(
-	chanID *lnwire.ShortChannelID) (*wire.MsgTx, error) {
-
-	// First fetch the block hash by the block number encoded, then use
-	// that hash to fetch the block itself.
-	blockNum := int64(chanID.BlockHeight)
-	blockHash, err := b.cfg.Chain.GetBlockHash(blockNum)
-	if err != nil {
-		return nil, err
-	}
-	fundingBlock, err := b.cfg.Chain.GetBlock(blockHash)
-	if err != nil {
-		return nil, err
-	}
-
-	// As a sanity check, ensure that the advertised transaction index is
-	// within the bounds of the total number of transactions within a
-	// block.
-	numTxns := uint32(len(fundingBlock.Transactions))
-	if chanID.TxIndex > numTxns-1 {
-		return nil, fmt.Errorf("tx_index=#%v "+
-			"is out of range (max_index=%v), network_chan_id=%v",
-			chanID.TxIndex, numTxns-1, chanID)
-	}
-
-	return fundingBlock.Transactions[chanID.TxIndex].Copy(), nil
-}
-
 // routingMsg couples a routing related routing topology update to the
 // error channel.
 type routingMsg struct {
@@ -1521,7 +1461,7 @@ type routingMsg struct {
 
 // ApplyChannelUpdate validates a channel update and if valid, applies it to the
 // database. It returns a bool indicating whether the updates were successful.
-func (b *Builder) ApplyChannelUpdate(msg *lnwire.ChannelUpdate) bool {
+func (b *Builder) ApplyChannelUpdate(msg *lnwire.ChannelUpdate1) bool {
 	ch, _, _, err := b.GetChannelByID(msg.ShortChannelID)
 	if err != nil {
 		log.Errorf("Unable to retrieve channel by id: %v", err)
@@ -1545,7 +1485,7 @@ func (b *Builder) ApplyChannelUpdate(msg *lnwire.ChannelUpdate) bool {
 		return false
 	}
 
-	err = ValidateChannelUpdateAnn(pubKey, ch.Capacity, msg)
+	err = netann.ValidateChannelUpdateAnn(pubKey, ch.Capacity, msg)
 	if err != nil {
 		log.Errorf("Unable to validate channel update: %v", err)
 		return false
diff --git a/graph/validation_barrier.go b/graph/validation_barrier.go
index 731852d75..98d910d89 100644
--- a/graph/validation_barrier.go
+++ b/graph/validation_barrier.go
@@ -102,7 +102,7 @@ func (v *ValidationBarrier) InitJobDependencies(job interface{}) {
 	// ChannelUpdates for the same channel, or NodeAnnouncements of nodes
 	// that are involved in this channel. This goes for both the wire
 	// type,s and also the types that we use within the database.
-	case *lnwire.ChannelAnnouncement:
+	case *lnwire.ChannelAnnouncement1:
 
 		// We ensure that we only create a new announcement signal iff,
 		// one doesn't already exist, as there may be duplicate
@@ -146,14 +146,14 @@ func (v *ValidationBarrier) InitJobDependencies(job interface{}) {
 	// initialization needs to be done beyond just occupying a job slot.
 	case *models.ChannelEdgePolicy:
 		return
-	case *lnwire.ChannelUpdate:
+	case *lnwire.ChannelUpdate1:
 		return
 	case *lnwire.NodeAnnouncement:
 		// TODO(roasbeef): node ann needs to wait on existing channel updates
 		return
 	case *channeldb.LightningNode:
 		return
-	case *lnwire.AnnounceSignatures:
+	case *lnwire.AnnounceSignatures1:
 		// TODO(roasbeef): need to wait on chan ann?
 		return
 	}
@@ -202,7 +202,7 @@ func (v *ValidationBarrier) WaitForDependants(job interface{}) error {
 		jobDesc = fmt.Sprintf("job=channeldb.LightningNode, pub=%s",
 			vertex)
 
-	case *lnwire.ChannelUpdate:
+	case *lnwire.ChannelUpdate1:
 		signals, ok = v.chanEdgeDependencies[msg.ShortChannelID]
 
 		jobDesc = fmt.Sprintf("job=lnwire.ChannelUpdate, scid=%v",
@@ -216,10 +216,10 @@ func (v *ValidationBarrier) WaitForDependants(job interface{}) error {
 
 	// Other types of jobs can be executed immediately, so we'll just
 	// return directly.
-	case *lnwire.AnnounceSignatures:
+	case *lnwire.AnnounceSignatures1:
 		// TODO(roasbeef): need to wait on chan ann?
 	case *models.ChannelEdgeInfo:
-	case *lnwire.ChannelAnnouncement:
+	case *lnwire.ChannelAnnouncement1:
 	}
 
 	// Release the lock once the above read is finished.
@@ -275,7 +275,7 @@ func (v *ValidationBarrier) SignalDependants(job interface{}, allow bool) {
 			}
 			delete(v.chanAnnFinSignal, shortID)
 		}
-	case *lnwire.ChannelAnnouncement:
+	case *lnwire.ChannelAnnouncement1:
 		finSignals, ok := v.chanAnnFinSignal[msg.ShortChannelID]
 		if ok {
 			if allow {
@@ -295,13 +295,13 @@ func (v *ValidationBarrier) SignalDependants(job interface{}, allow bool) {
 		delete(v.nodeAnnDependencies, route.Vertex(msg.PubKeyBytes))
 	case *lnwire.NodeAnnouncement:
 		delete(v.nodeAnnDependencies, route.Vertex(msg.NodeID))
-	case *lnwire.ChannelUpdate:
+	case *lnwire.ChannelUpdate1:
 		delete(v.chanEdgeDependencies, msg.ShortChannelID)
 	case *models.ChannelEdgePolicy:
 		shortID := lnwire.NewShortChanIDFromInt(msg.ChannelID)
 		delete(v.chanEdgeDependencies, shortID)
 
-	case *lnwire.AnnounceSignatures:
+	case *lnwire.AnnounceSignatures1:
 		return
 	}
 }
diff --git a/htlcswitch/interfaces.go b/htlcswitch/interfaces.go
index 4e6384b97..ca653a326 100644
--- a/htlcswitch/interfaces.go
+++ b/htlcswitch/interfaces.go
@@ -86,7 +86,7 @@ type scidAliasHandler interface {
 	// HTLCs on option_scid_alias channels.
 	attachFailAliasUpdate(failClosure func(
 		sid lnwire.ShortChannelID,
-		incoming bool) *lnwire.ChannelUpdate)
+		incoming bool) *lnwire.ChannelUpdate1)
 
 	// getAliases fetches the link's underlying aliases. This is used by
 	// the Switch to determine whether to forward an HTLC and where to
diff --git a/htlcswitch/link.go b/htlcswitch/link.go
index 83495f357..5e5bc8dc2 100644
--- a/htlcswitch/link.go
+++ b/htlcswitch/link.go
@@ -120,7 +120,8 @@ type ChannelLinkConfig struct {
 	// specified when we receive an incoming HTLC.  This will be used to
 	// provide payment senders our latest policy when sending encrypted
 	// error messages.
-	FetchLastChannelUpdate func(lnwire.ShortChannelID) (*lnwire.ChannelUpdate, error)
+	FetchLastChannelUpdate func(lnwire.ShortChannelID) (
+		*lnwire.ChannelUpdate1, error)
 
 	// Peer is a lightning network node with which we have the channel link
 	// opened.
@@ -262,7 +263,7 @@ type ChannelLinkConfig struct {
 	// FailAliasUpdate is a function used to fail an HTLC for an
 	// option_scid_alias channel.
 	FailAliasUpdate func(sid lnwire.ShortChannelID,
-		incoming bool) *lnwire.ChannelUpdate
+		incoming bool) *lnwire.ChannelUpdate1
 
 	// GetAliases is used by the link and switch to fetch the set of
 	// aliases for a given link.
@@ -765,7 +766,7 @@ func shouldAdjustCommitFee(netFee, chanFee,
 }
 
 // failCb is used to cut down on the argument verbosity.
-type failCb func(update *lnwire.ChannelUpdate) lnwire.FailureMessage
+type failCb func(update *lnwire.ChannelUpdate1) lnwire.FailureMessage
 
 // createFailureWithUpdate creates a ChannelUpdate when failing an incoming or
 // outgoing HTLC. It may return a FailureMessage that references a channel's
@@ -2949,7 +2950,7 @@ func (l *channelLink) getAliases() []lnwire.ShortChannelID {
 //
 // Part of the scidAliasHandler interface.
 func (l *channelLink) attachFailAliasUpdate(closure func(
-	sid lnwire.ShortChannelID, incoming bool) *lnwire.ChannelUpdate) {
+	sid lnwire.ShortChannelID, incoming bool) *lnwire.ChannelUpdate1) {
 
 	l.Lock()
 	l.cfg.FailAliasUpdate = closure
@@ -3041,7 +3042,7 @@ func (l *channelLink) CheckHtlcForward(payHash [32]byte,
 
 		// As part of the returned error, we'll send our latest routing
 		// policy so the sending node obtains the most up to date data.
-		cb := func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage {
+		cb := func(upd *lnwire.ChannelUpdate1) lnwire.FailureMessage {
 			return lnwire.NewFeeInsufficient(amtToForward, *upd)
 		}
 		failure := l.createFailureWithUpdate(false, originalScid, cb)
@@ -3069,7 +3070,7 @@ func (l *channelLink) CheckHtlcForward(payHash [32]byte,
 
 		// Grab the latest routing policy so the sending node is up to
 		// date with our current policy.
-		cb := func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage {
+		cb := func(upd *lnwire.ChannelUpdate1) lnwire.FailureMessage {
 			return lnwire.NewIncorrectCltvExpiry(
 				incomingTimeout, *upd,
 			)
@@ -3118,7 +3119,7 @@ func (l *channelLink) canSendHtlc(policy models.ForwardingPolicy,
 
 		// As part of the returned error, we'll send our latest routing
 		// policy so the sending node obtains the most up to date data.
-		cb := func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage {
+		cb := func(upd *lnwire.ChannelUpdate1) lnwire.FailureMessage {
 			return lnwire.NewAmountBelowMinimum(amt, *upd)
 		}
 		failure := l.createFailureWithUpdate(false, originalScid, cb)
@@ -3133,7 +3134,7 @@ func (l *channelLink) canSendHtlc(policy models.ForwardingPolicy,
 
 		// As part of the returned error, we'll send our latest routing
 		// policy so the sending node obtains the most up-to-date data.
-		cb := func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage {
+		cb := func(upd *lnwire.ChannelUpdate1) lnwire.FailureMessage {
 			return lnwire.NewTemporaryChannelFailure(upd)
 		}
 		failure := l.createFailureWithUpdate(false, originalScid, cb)
@@ -3148,7 +3149,7 @@ func (l *channelLink) canSendHtlc(policy models.ForwardingPolicy,
 			"outgoing_expiry=%v, best_height=%v", payHash[:],
 			timeout, heightNow)
 
-		cb := func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage {
+		cb := func(upd *lnwire.ChannelUpdate1) lnwire.FailureMessage {
 			return lnwire.NewExpiryTooSoon(*upd)
 		}
 		failure := l.createFailureWithUpdate(false, originalScid, cb)
@@ -3168,7 +3169,7 @@ func (l *channelLink) canSendHtlc(policy models.ForwardingPolicy,
 	if amt > l.Bandwidth() {
 		l.log.Warnf("insufficient bandwidth to route htlc: %v is "+
 			"larger than %v", amt, l.Bandwidth())
-		cb := func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage {
+		cb := func(upd *lnwire.ChannelUpdate1) lnwire.FailureMessage {
 			return lnwire.NewTemporaryChannelFailure(upd)
 		}
 		failure := l.createFailureWithUpdate(false, originalScid, cb)
@@ -3680,7 +3681,7 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg) {
 				l.log.Errorf("unable to encode the "+
 					"remaining route %v", err)
 
-				cb := func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage {
+				cb := func(upd *lnwire.ChannelUpdate1) lnwire.FailureMessage { //nolint:lll
 					return lnwire.NewTemporaryChannelFailure(upd)
 				}
 
diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go
index 6d27a5be9..6123c9010 100644
--- a/htlcswitch/mock.go
+++ b/htlcswitch/mock.go
@@ -167,7 +167,7 @@ type mockServer struct {
 var _ lnpeer.Peer = (*mockServer)(nil)
 
 func initSwitchWithDB(startingHeight uint32, db *channeldb.DB) (*Switch, error) {
-	signAliasUpdate := func(u *lnwire.ChannelUpdate) (*ecdsa.Signature,
+	signAliasUpdate := func(u *lnwire.ChannelUpdate1) (*ecdsa.Signature,
 		error) {
 
 		return testSig, nil
@@ -183,9 +183,9 @@ func initSwitchWithDB(startingHeight uint32, db *channeldb.DB) (*Switch, error)
 			events: make(map[time.Time]channeldb.ForwardingEvent),
 		},
 		FetchLastChannelUpdate: func(scid lnwire.ShortChannelID) (
-			*lnwire.ChannelUpdate, error) {
+			*lnwire.ChannelUpdate1, error) {
 
-			return &lnwire.ChannelUpdate{
+			return &lnwire.ChannelUpdate1{
 				ShortChannelID: scid,
 			}, nil
 		},
@@ -735,7 +735,7 @@ type mockChannelLink struct {
 	checkHtlcForwardResult *LinkError
 
 	failAliasUpdate func(sid lnwire.ShortChannelID,
-		incoming bool) *lnwire.ChannelUpdate
+		incoming bool) *lnwire.ChannelUpdate1
 
 	confirmedZC bool
 }
@@ -870,7 +870,7 @@ func (f *mockChannelLink) AttachMailBox(mailBox MailBox) {
 }
 
 func (f *mockChannelLink) attachFailAliasUpdate(closure func(
-	sid lnwire.ShortChannelID, incoming bool) *lnwire.ChannelUpdate) {
+	sid lnwire.ShortChannelID, incoming bool) *lnwire.ChannelUpdate1) {
 
 	f.failAliasUpdate = closure
 }
diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go
index 01596ade0..4ff2746a7 100644
--- a/htlcswitch/switch.go
+++ b/htlcswitch/switch.go
@@ -173,7 +173,8 @@ type Config struct {
 	// specified when we receive an incoming HTLC.  This will be used to
 	// provide payment senders our latest policy when sending encrypted
 	// error messages.
-	FetchLastChannelUpdate func(lnwire.ShortChannelID) (*lnwire.ChannelUpdate, error)
+	FetchLastChannelUpdate func(lnwire.ShortChannelID) (
+		*lnwire.ChannelUpdate1, error)
 
 	// Notifier is an instance of a chain notifier that we'll use to signal
 	// the switch when a new block has arrived.
@@ -220,7 +221,7 @@ type Config struct {
 	// option_scid_alias channels. This avoids a potential privacy leak by
 	// replacing the public, confirmed SCID with the alias in the
 	// ChannelUpdate.
-	SignAliasUpdate func(u *lnwire.ChannelUpdate) (*ecdsa.Signature,
+	SignAliasUpdate func(u *lnwire.ChannelUpdate1) (*ecdsa.Signature,
 		error)
 
 	// IsAlias returns whether or not a given SCID is an alias.
@@ -2640,7 +2641,7 @@ func (s *Switch) failMailboxUpdate(outgoingScid,
 // and the caller is expected to handle this properly. In this case, a return
 // to the original non-alias behavior is expected.
 func (s *Switch) failAliasUpdate(scid lnwire.ShortChannelID,
-	incoming bool) *lnwire.ChannelUpdate {
+	incoming bool) *lnwire.ChannelUpdate1 {
 
 	// This function does not defer the unlocking because of the database
 	// lookups for ChannelUpdate.
diff --git a/lnrpc/routerrpc/router_backend.go b/lnrpc/routerrpc/router_backend.go
index 6694e8b4f..1a428e5bd 100644
--- a/lnrpc/routerrpc/router_backend.go
+++ b/lnrpc/routerrpc/router_backend.go
@@ -1626,7 +1626,7 @@ func marshallWireError(msg lnwire.FailureMessage,
 
 // marshallChannelUpdate marshalls a channel update as received over the wire to
 // the router rpc format.
-func marshallChannelUpdate(update *lnwire.ChannelUpdate) *lnrpc.ChannelUpdate {
+func marshallChannelUpdate(update *lnwire.ChannelUpdate1) *lnrpc.ChannelUpdate {
 	if update == nil {
 		return nil
 	}
diff --git a/lnwallet/interface.go b/lnwallet/interface.go
index d3b123509..2a4462462 100644
--- a/lnwallet/interface.go
+++ b/lnwallet/interface.go
@@ -21,6 +21,8 @@ import (
 	"github.com/lightningnetwork/lnd/fn"
 	"github.com/lightningnetwork/lnd/keychain"
 	"github.com/lightningnetwork/lnd/lnwallet/chainfee"
+	"github.com/lightningnetwork/lnd/lnwallet/chanvalidate"
+	"github.com/lightningnetwork/lnd/lnwire"
 )
 
 const (
@@ -671,3 +673,91 @@ func SupportedWallets() []string {
 
 	return supportedWallets
 }
+
+// FetchFundingTxWrapper is a wrapper around FetchFundingTx, except that it will
+// exit when the supplied quit channel is closed.
+func FetchFundingTxWrapper(chain BlockChainIO, chanID *lnwire.ShortChannelID,
+	quit chan struct{}) (*wire.MsgTx, error) {
+
+	txChan := make(chan *wire.MsgTx, 1)
+	errChan := make(chan error, 1)
+
+	go func() {
+		tx, err := FetchFundingTx(chain, chanID)
+		if err != nil {
+			errChan <- err
+			return
+		}
+
+		txChan <- tx
+	}()
+
+	select {
+	case tx := <-txChan:
+		return tx, nil
+
+	case err := <-errChan:
+		return nil, err
+
+	case <-quit:
+		return nil, fmt.Errorf("quit channel passed to " +
+			"lnwallet.FetchFundingTxWrapper has been closed")
+	}
+}
+
+// FetchFundingTx uses the given BlockChainIO to fetch and return the funding
+// transaction identified by the passed short channel ID.
+//
+// TODO(roasbeef): replace with call to GetBlockTransaction? (would allow to
+// later use getblocktxn).
+func FetchFundingTx(chain BlockChainIO,
+	chanID *lnwire.ShortChannelID) (*wire.MsgTx, error) {
+
+	// First fetch the block hash by the block number encoded, then use
+	// that hash to fetch the block itself.
+	blockNum := int64(chanID.BlockHeight)
+	blockHash, err := chain.GetBlockHash(blockNum)
+	if err != nil {
+		return nil, err
+	}
+
+	fundingBlock, err := chain.GetBlock(blockHash)
+	if err != nil {
+		return nil, err
+	}
+
+	// As a sanity check, ensure that the advertised transaction index is
+	// within the bounds of the total number of transactions within a
+	// block.
+	numTxns := uint32(len(fundingBlock.Transactions))
+	if chanID.TxIndex > numTxns-1 {
+		return nil, fmt.Errorf("tx_index=#%v "+
+			"is out of range (max_index=%v), network_chan_id=%v",
+			chanID.TxIndex, numTxns-1, chanID)
+	}
+
+	return fundingBlock.Transactions[chanID.TxIndex].Copy(), nil
+}
+
+// FetchPKScriptWithQuit fetches the output script for the given SCID and exits
+// early with an error if the provided quit channel is closed before
+// completion.
+func FetchPKScriptWithQuit(chain BlockChainIO, chanID *lnwire.ShortChannelID,
+	quit chan struct{}) ([]byte, error) {
+
+	tx, err := FetchFundingTxWrapper(chain, chanID, quit)
+	if err != nil {
+		return nil, err
+	}
+
+	outputLocator := chanvalidate.ShortChanIDChanLocator{
+		ID: *chanID,
+	}
+
+	output, _, err := outputLocator.Locate(tx)
+	if err != nil {
+		return nil, err
+	}
+
+	return output.PkScript, nil
+}
diff --git a/lnwire/announcement_signatures.go b/lnwire/announcement_signatures.go
index 49b610611..a2bc21f39 100644
--- a/lnwire/announcement_signatures.go
+++ b/lnwire/announcement_signatures.go
@@ -5,11 +5,11 @@ import (
 	"io"
 )
 
-// AnnounceSignatures is a direct message between two endpoints of a
+// AnnounceSignatures1 is a direct message between two endpoints of a
 // channel and serves as an opt-in mechanism to allow the announcement of
 // the channel to the rest of the network. It contains the necessary
 // signatures by the sender to construct the channel announcement message.
-type AnnounceSignatures struct {
+type AnnounceSignatures1 struct {
 	// ChannelID is the unique description of the funding transaction.
 	// Channel id is better for users and debugging and short channel id is
 	// used for quick test on existence of the particular utxo inside the
@@ -43,15 +43,19 @@ type AnnounceSignatures struct {
 	ExtraOpaqueData ExtraOpaqueData
 }
 
-// A compile time check to ensure AnnounceSignatures implements the
+// A compile time check to ensure AnnounceSignatures1 implements the
 // lnwire.Message interface.
-var _ Message = (*AnnounceSignatures)(nil)
+var _ Message = (*AnnounceSignatures1)(nil)
 
-// Decode deserializes a serialized AnnounceSignatures stored in the passed
+// A compile time check to ensure AnnounceSignatures1 implements the
+// lnwire.AnnounceSignatures interface.
+var _ AnnounceSignatures = (*AnnounceSignatures1)(nil)
+
+// Decode deserializes a serialized AnnounceSignatures1 stored in the passed
 // io.Reader observing the specified protocol version.
 //
 // This is part of the lnwire.Message interface.
-func (a *AnnounceSignatures) Decode(r io.Reader, pver uint32) error {
+func (a *AnnounceSignatures1) Decode(r io.Reader, _ uint32) error {
 	return ReadElements(r,
 		&a.ChannelID,
 		&a.ShortChannelID,
@@ -61,11 +65,11 @@ func (a *AnnounceSignatures) Decode(r io.Reader, pver uint32) error {
 	)
 }
 
-// Encode serializes the target AnnounceSignatures into the passed io.Writer
+// Encode serializes the target AnnounceSignatures1 into the passed io.Writer
 // observing the protocol version specified.
 //
 // This is part of the lnwire.Message interface.
-func (a *AnnounceSignatures) Encode(w *bytes.Buffer, pver uint32) error {
+func (a *AnnounceSignatures1) Encode(w *bytes.Buffer, _ uint32) error {
 	if err := WriteChannelID(w, a.ChannelID); err != nil {
 		return err
 	}
@@ -89,6 +93,20 @@ func (a *AnnounceSignatures) Encode(w *bytes.Buffer, pver uint32) error {
 // wire.
 //
 // This is part of the lnwire.Message interface.
-func (a *AnnounceSignatures) MsgType() MessageType {
+func (a *AnnounceSignatures1) MsgType() MessageType {
 	return MsgAnnounceSignatures
 }
+
+// SCID returns the ShortChannelID of the channel.
+//
+// This is part of the lnwire.AnnounceSignatures interface.
+func (a *AnnounceSignatures1) SCID() ShortChannelID {
+	return a.ShortChannelID
+}
+
+// ChanID returns the ChannelID identifying the channel.
+//
+// This is part of the lnwire.AnnounceSignatures interface.
+func (a *AnnounceSignatures1) ChanID() ChannelID {
+	return a.ChannelID
+}
diff --git a/lnwire/announcement_signatures_2.go b/lnwire/announcement_signatures_2.go
new file mode 100644
index 000000000..a10447032
--- /dev/null
+++ b/lnwire/announcement_signatures_2.go
@@ -0,0 +1,97 @@
+package lnwire
+
+import (
+	"bytes"
+	"io"
+)
+
+// AnnounceSignatures2 is a direct message between two endpoints of a
+// channel and serves as an opt-in mechanism to allow the announcement of
+// a taproot channel to the rest of the network. It contains the necessary
+// signatures by the sender to construct the channel_announcement_2 message.
+type AnnounceSignatures2 struct {
+	// ChannelID is the unique description of the funding transaction.
+	// Channel id is better for users and debugging and short channel id is
+	// used for quick test on existence of the particular utxo inside the
+	// blockchain, because it contains information about block.
+	ChannelID ChannelID
+
+	// ShortChannelID is the unique description of the funding transaction.
+	// It is constructed with the most significant 3 bytes as the block
+	// height, the next 3 bytes indicating the transaction index within the
+	// block, and the least significant two bytes indicating the output
+	// index which pays to the channel.
+	ShortChannelID ShortChannelID
+
+	// PartialSignature is the combination of the partial Schnorr signature
+	// created for the node's bitcoin key with the partial signature created
+	// for the node's node ID key.
+	PartialSignature PartialSig
+
+	// ExtraOpaqueData is the set of data that was appended to this
+	// message, some of which we may not actually know how to iterate or
+	// parse. By holding onto this data, we ensure that we're able to
+	// properly validate the set of signatures that cover these new fields,
+	// and ensure we're able to make upgrades to the network in a forwards
+	// compatible manner.
+	ExtraOpaqueData ExtraOpaqueData
+}
+
+// A compile time check to ensure AnnounceSignatures2 implements the
+// lnwire.Message interface.
+var _ Message = (*AnnounceSignatures2)(nil)
+
+// Decode deserializes a serialized AnnounceSignatures2 stored in the passed
+// io.Reader observing the specified protocol version.
+//
+// This is part of the lnwire.Message interface.
+func (a *AnnounceSignatures2) Decode(r io.Reader, _ uint32) error {
+	return ReadElements(r,
+		&a.ChannelID,
+		&a.ShortChannelID,
+		&a.PartialSignature,
+		&a.ExtraOpaqueData,
+	)
+}
+
+// Encode serializes the target AnnounceSignatures2 into the passed io.Writer
+// observing the protocol version specified.
+//
+// This is part of the lnwire.Message interface.
+func (a *AnnounceSignatures2) Encode(w *bytes.Buffer, _ uint32) error {
+	if err := WriteChannelID(w, a.ChannelID); err != nil {
+		return err
+	}
+
+	if err := WriteShortChannelID(w, a.ShortChannelID); err != nil {
+		return err
+	}
+
+	if err := WriteElement(w, a.PartialSignature); err != nil {
+		return err
+	}
+
+	return WriteBytes(w, a.ExtraOpaqueData)
+}
+
+// MsgType returns the integer uniquely identifying this message type on the
+// wire.
+//
+// This is part of the lnwire.Message interface.
+func (a *AnnounceSignatures2) MsgType() MessageType {
+	return MsgAnnounceSignatures2
+}
+
+// SCID returns the ShortChannelID of the channel.
+//
+// NOTE: this is part of the AnnounceSignatures interface.
+func (a *AnnounceSignatures2) SCID() ShortChannelID {
+	return a.ShortChannelID
+}
+
+// ChanID returns the ChannelID identifying the channel.
+//
+// NOTE: this is part of the AnnounceSignatures interface.
+func (a *AnnounceSignatures2) ChanID() ChannelID {
+	return a.ChannelID
+}
diff --git a/lnwire/channel_announcement.go b/lnwire/channel_announcement.go
index 2b34c0f99..ed4c5b97e 100644
--- a/lnwire/channel_announcement.go
+++ b/lnwire/channel_announcement.go
@@ -7,10 +7,10 @@ import (
 	"github.com/btcsuite/btcd/chaincfg/chainhash"
 )
 
-// ChannelAnnouncement message is used to announce the existence of a channel
+// ChannelAnnouncement1 message is used to announce the existence of a channel
 // between two peers in the overlay, which is propagated by the discovery
 // service over broadcast handler.
-type ChannelAnnouncement struct {
+type ChannelAnnouncement1 struct {
 	// This signatures are used by nodes in order to create cross
 	// references between node's channel and node. Requiring both nodes
 	// to sign indicates they are both willing to route other payments via
@@ -60,13 +60,13 @@ type ChannelAnnouncement struct {
 
 // A compile time check to ensure ChannelAnnouncement implements the
 // lnwire.Message interface.
-var _ Message = (*ChannelAnnouncement)(nil)
+var _ Message = (*ChannelAnnouncement1)(nil)
 
 // Decode deserializes a serialized ChannelAnnouncement stored in the passed
 // io.Reader observing the specified protocol version.
 //
 // This is part of the lnwire.Message interface.
-func (a *ChannelAnnouncement) Decode(r io.Reader, pver uint32) error {
+func (a *ChannelAnnouncement1) Decode(r io.Reader, pver uint32) error {
 	return ReadElements(r,
 		&a.NodeSig1,
 		&a.NodeSig2,
@@ -87,7 +87,7 @@ func (a *ChannelAnnouncement) Decode(r io.Reader, pver uint32) error {
 // observing the protocol version specified.
 //
 // This is part of the lnwire.Message interface.
-func (a *ChannelAnnouncement) Encode(w *bytes.Buffer, pver uint32) error {
+func (a *ChannelAnnouncement1) Encode(w *bytes.Buffer, pver uint32) error {
 	if err := WriteSig(w, a.NodeSig1); err != nil {
 		return err
 	}
@@ -139,13 +139,13 @@ func (a *ChannelAnnouncement) Encode(w *bytes.Buffer, pver uint32) error {
 // wire.
 //
 // This is part of the lnwire.Message interface.
-func (a *ChannelAnnouncement) MsgType() MessageType {
+func (a *ChannelAnnouncement1) MsgType() MessageType {
 	return MsgChannelAnnouncement
 }
 
 // DataToSign is used to retrieve part of the announcement message which should
 // be signed.
-func (a *ChannelAnnouncement) DataToSign() ([]byte, error) {
+func (a *ChannelAnnouncement1) DataToSign() ([]byte, error) {
 	// We should not include the signatures itself.
 	b := make([]byte, 0, MaxMsgBody)
 	buf := bytes.NewBuffer(b)
@@ -184,3 +184,38 @@ func (a *ChannelAnnouncement) DataToSign() ([]byte, error) {
 
 	return buf.Bytes(), nil
 }
+
+// Node1KeyBytes returns the bytes representing the public key of node 1 in the
+// channel.
+//
+// NOTE: This is part of the ChannelAnnouncement interface.
+func (a *ChannelAnnouncement1) Node1KeyBytes() [33]byte {
+	return a.NodeID1
+}
+
+// Node2KeyBytes returns the bytes representing the public key of node 2 in the
+// channel.
+//
+// NOTE: This is part of the ChannelAnnouncement interface.
+func (a *ChannelAnnouncement1) Node2KeyBytes() [33]byte {
+	return a.NodeID2
+}
+
+// GetChainHash returns the hash of the chain which this channel's funding
+// transaction is confirmed in.
+//
+// NOTE: This is part of the ChannelAnnouncement interface.
+func (a *ChannelAnnouncement1) GetChainHash() chainhash.Hash {
+	return a.ChainHash
+}
+
+// SCID returns the short channel ID of the channel.
+//
+// NOTE: This is part of the ChannelAnnouncement interface.
+func (a *ChannelAnnouncement1) SCID() ShortChannelID {
+	return a.ShortChannelID
+}
+
+// A compile-time check to ensure that ChannelAnnouncement1 implements the
+// ChannelAnnouncement interface.
+var _ ChannelAnnouncement = (*ChannelAnnouncement1)(nil)
diff --git a/lnwire/channel_announcement_2.go b/lnwire/channel_announcement_2.go
new file mode 100644
index 000000000..074e7d084
--- /dev/null
+++ b/lnwire/channel_announcement_2.go
@@ -0,0 +1,234 @@
+package lnwire
+
+import (
+	"bytes"
+	"io"
+
+	"github.com/btcsuite/btcd/chaincfg"
+	"github.com/btcsuite/btcd/chaincfg/chainhash"
+	"github.com/lightningnetwork/lnd/tlv"
+)
+
+// ChannelAnnouncement2 message is used to announce the existence of a taproot
+// channel between two peers in the network.
+type ChannelAnnouncement2 struct {
+	// Signature is a Schnorr signature over the TLV stream of the message.
+	Signature Sig
+
+	// ChainHash denotes the target chain that this channel was opened
+	// within. This value should be the genesis hash of the target chain.
+	ChainHash tlv.RecordT[tlv.TlvType0, chainhash.Hash]
+
+	// Features is the feature vector that encodes the features supported
+	// by the target node. This field can be used to signal the type of the
+	// channel, or modifications to the fields that would normally follow
+	// this vector.
+	Features tlv.RecordT[tlv.TlvType2, RawFeatureVector]
+
+	// ShortChannelID is the unique description of the funding transaction,
+	// or where exactly it's located within the target blockchain.
+	ShortChannelID tlv.RecordT[tlv.TlvType4, ShortChannelID]
+
+	// Capacity is the number of satoshis of the capacity of this channel.
+	// It must be less than or equal to the value of the on-chain funding
+	// output.
+	Capacity tlv.RecordT[tlv.TlvType6, uint64]
+
+	// NodeID1 is the numerically-lesser public key ID of one of the channel
+	// operators.
+	NodeID1 tlv.RecordT[tlv.TlvType8, [33]byte]
+
+	// NodeID2 is the numerically-greater public key ID of one of the
+	// channel operators.
+	NodeID2 tlv.RecordT[tlv.TlvType10, [33]byte]
+
+	// BitcoinKey1 is the public key of the key used by Node1 in the
+	// construction of the on-chain funding transaction. This is an optional
+	// field and only needs to be set if the 4-of-4 MuSig construction was
+	// used in the creation of the message signature.
+	BitcoinKey1 tlv.OptionalRecordT[tlv.TlvType12, [33]byte]
+
+	// BitcoinKey2 is the public key of the key used by Node2 in the
+	// construction of the on-chain funding transaction. This is an optional
+	// field and only needs to be set if the 4-of-4 MuSig construction was
+	// used in the creation of the message signature.
+	BitcoinKey2 tlv.OptionalRecordT[tlv.TlvType14, [33]byte]
+
+	// MerkleRootHash is the hash used to create the optional tweak in the
+	// funding output. If this is not set but the bitcoin keys are, then
+	// the funding output is a pure 2-of-2 MuSig aggregate public key.
+	MerkleRootHash tlv.OptionalRecordT[tlv.TlvType16, [32]byte]
+
+	// ExtraOpaqueData is the set of data that was appended to this
+	// message, some of which we may not actually know how to iterate or
+	// parse. By holding onto this data, we ensure that we're able to
+	// properly validate the set of signatures that cover these new fields,
+	// and ensure we're able to make upgrades to the network in a forwards
+	// compatible manner.
+	ExtraOpaqueData ExtraOpaqueData
+}
+
+// Decode deserializes a serialized AnnounceSignatures1 stored in the passed
+// io.Reader observing the specified protocol version.
+//
+// This is part of the lnwire.Message interface.
+func (c *ChannelAnnouncement2) Decode(r io.Reader, _ uint32) error {
+	err := ReadElement(r, &c.Signature)
+	if err != nil {
+		return err
+	}
+	c.Signature.ForceSchnorr()
+
+	return c.DecodeTLVRecords(r)
+}
+
+// DecodeTLVRecords decodes only the TLV section of the message.
+func (c *ChannelAnnouncement2) DecodeTLVRecords(r io.Reader) error {
+	// First extract into extra opaque data.
+	var tlvRecords ExtraOpaqueData
+	if err := ReadElements(r, &tlvRecords); err != nil {
+		return err
+	}
+
+	var (
+		chainHash      = tlv.ZeroRecordT[tlv.TlvType0, [32]byte]()
+		btcKey1        = tlv.ZeroRecordT[tlv.TlvType12, [33]byte]()
+		btcKey2        = tlv.ZeroRecordT[tlv.TlvType14, [33]byte]()
+		merkleRootHash = tlv.ZeroRecordT[tlv.TlvType16, [32]byte]()
+	)
+	typeMap, err := tlvRecords.ExtractRecords(
+		&chainHash, &c.Features, &c.ShortChannelID, &c.Capacity,
+		&c.NodeID1, &c.NodeID2, &btcKey1, &btcKey2, &merkleRootHash,
+	)
+	if err != nil {
+		return err
+	}
+
+	// By default, the chain-hash is the bitcoin mainnet genesis block hash.
+	c.ChainHash.Val = *chaincfg.MainNetParams.GenesisHash
+	if _, ok := typeMap[c.ChainHash.TlvType()]; ok {
+		c.ChainHash.Val = chainHash.Val
+	}
+
+	if _, ok := typeMap[c.BitcoinKey1.TlvType()]; ok {
+		c.BitcoinKey1 = tlv.SomeRecordT(btcKey1)
+	}
+
+	if _, ok := typeMap[c.BitcoinKey2.TlvType()]; ok {
+		c.BitcoinKey2 = tlv.SomeRecordT(btcKey2)
+	}
+
+	if _, ok := typeMap[c.MerkleRootHash.TlvType()]; ok {
+		c.MerkleRootHash = tlv.SomeRecordT(merkleRootHash)
+	}
+
+	if len(tlvRecords) != 0 {
+		c.ExtraOpaqueData = tlvRecords
+	}
+
+	return nil
+}
+
+// Encode serializes the target AnnounceSignatures1 into the passed io.Writer
+// observing the protocol version specified.
+//
+// This is part of the lnwire.Message interface.
+func (c *ChannelAnnouncement2) Encode(w *bytes.Buffer, _ uint32) error {
+	_, err := w.Write(c.Signature.RawBytes())
+	if err != nil {
+		return err
+	}
+	_, err = c.DataToSign()
+	if err != nil {
+		return err
+	}
+
+	return WriteBytes(w, c.ExtraOpaqueData)
+}
+
+// DataToSign encodes the data to be signed into the ExtraOpaqueData member and
+// returns it.
+func (c *ChannelAnnouncement2) DataToSign() ([]byte, error) {
+	// The chain-hash record is only included if it is _not_ equal to the
+	// bitcoin mainnet genisis block hash.
+	var recordProducers []tlv.RecordProducer
+	if !c.ChainHash.Val.IsEqual(chaincfg.MainNetParams.GenesisHash) {
+		hash := tlv.ZeroRecordT[tlv.TlvType0, [32]byte]()
+		hash.Val = c.ChainHash.Val
+
+		recordProducers = append(recordProducers, &hash)
+	}
+
+	recordProducers = append(recordProducers,
+		&c.Features, &c.ShortChannelID, &c.Capacity, &c.NodeID1,
+		&c.NodeID2,
+	)
+
+	c.BitcoinKey1.WhenSome(func(key tlv.RecordT[tlv.TlvType12, [33]byte]) {
+		recordProducers = append(recordProducers, &key)
+	})
+
+	c.BitcoinKey2.WhenSome(func(key tlv.RecordT[tlv.TlvType14, [33]byte]) {
+		recordProducers = append(recordProducers, &key)
+	})
+
+	c.MerkleRootHash.WhenSome(
+		func(hash tlv.RecordT[tlv.TlvType16, [32]byte]) {
+			recordProducers = append(recordProducers, &hash)
+		},
+	)
+
+	err := EncodeMessageExtraData(&c.ExtraOpaqueData, recordProducers...)
+	if err != nil {
+		return nil, err
+	}
+
+	return c.ExtraOpaqueData, nil
+}
+
+// MsgType returns the integer uniquely identifying this message type on the
+// wire.
+//
+// This is part of the lnwire.Message interface.
+func (c *ChannelAnnouncement2) MsgType() MessageType {
+	return MsgChannelAnnouncement2
+}
+
+// A compile time check to ensure ChannelAnnouncement2 implements the
+// lnwire.Message interface.
+var _ Message = (*ChannelAnnouncement2)(nil)
+
+// Node1KeyBytes returns the bytes representing the public key of node 1 in the
+// channel.
+//
+// NOTE: This is part of the ChannelAnnouncement interface.
+func (c *ChannelAnnouncement2) Node1KeyBytes() [33]byte {
+	return c.NodeID1.Val
+}
+
+// Node2KeyBytes returns the bytes representing the public key of node 2 in the
+// channel.
+//
+// NOTE: This is part of the ChannelAnnouncement interface.
+func (c *ChannelAnnouncement2) Node2KeyBytes() [33]byte {
+	return c.NodeID2.Val
+}
+
+// GetChainHash returns the hash of the chain which this channel's funding
+// transaction is confirmed in.
+//
+// NOTE: This is part of the ChannelAnnouncement interface.
+func (c *ChannelAnnouncement2) GetChainHash() chainhash.Hash {
+	return c.ChainHash.Val
+}
+
+// SCID returns the short channel ID of the channel.
+//
+// NOTE: This is part of the ChannelAnnouncement interface.
+func (c *ChannelAnnouncement2) SCID() ShortChannelID {
+	return c.ShortChannelID.Val
+}
+
+// A compile-time check to ensure that ChannelAnnouncement2 implements the
+// ChannelAnnouncement interface.
+var _ ChannelAnnouncement = (*ChannelAnnouncement2)(nil)
diff --git a/lnwire/channel_ready.go b/lnwire/channel_ready.go
index bdcb95ce8..912a068bd 100644
--- a/lnwire/channel_ready.go
+++ b/lnwire/channel_ready.go
@@ -33,6 +33,16 @@ type ChannelReady struct {
 	// to accept a new commitment state transition.
 	NextLocalNonce OptMusig2NonceTLV
 
+	// AnnouncementNodeNonce is an optional field that stores a public
+	// nonce that will be used along with the node's ID key during signing
+	// of the ChannelAnnouncement2 message.
+	AnnouncementNodeNonce tlv.OptionalRecordT[tlv.TlvType0, Musig2Nonce]
+
+	// AnnouncementBitcoinNonce is an optional field that stores a public
+	// nonce that will be used along with the node's bitcoin key during
+	// signing of the ChannelAnnouncement2 message.
+	AnnouncementBitcoinNonce tlv.OptionalRecordT[tlv.TlvType2, Musig2Nonce]
+
 	// ExtraData is the set of data that was appended to this message to
 	// fill out the full maximum transport message size. These fields can
 	// be used to specify optional data such as custom TLV fields.
@@ -78,9 +88,11 @@ func (c *ChannelReady) Decode(r io.Reader, _ uint32) error {
 	var (
 		aliasScid  ShortChannelID
 		localNonce = c.NextLocalNonce.Zero()
+		nodeNonce  = tlv.ZeroRecordT[tlv.TlvType0, Musig2Nonce]()
+		btcNonce   = tlv.ZeroRecordT[tlv.TlvType2, Musig2Nonce]()
 	)
 	typeMap, err := tlvRecords.ExtractRecords(
-		&aliasScid, &localNonce,
+		&btcNonce, &aliasScid, &nodeNonce, &localNonce,
 	)
 	if err != nil {
 		return err
@@ -94,6 +106,14 @@ func (c *ChannelReady) Decode(r io.Reader, _ uint32) error {
 	if val, ok := typeMap[c.NextLocalNonce.TlvType()]; ok && val == nil {
 		c.NextLocalNonce = tlv.SomeRecordT(localNonce)
 	}
+	val, ok := typeMap[c.AnnouncementBitcoinNonce.TlvType()]
+	if ok && val == nil {
+		c.AnnouncementBitcoinNonce = tlv.SomeRecordT(btcNonce)
+	}
+	val, ok = typeMap[c.AnnouncementNodeNonce.TlvType()]
+	if ok && val == nil {
+		c.AnnouncementNodeNonce = tlv.SomeRecordT(nodeNonce)
+	}
 
 	if len(tlvRecords) != 0 {
 		c.ExtraData = tlvRecords
@@ -117,13 +137,24 @@ func (c *ChannelReady) Encode(w *bytes.Buffer, _ uint32) error {
 	}
 
 	// We'll only encode the AliasScid in a TLV segment if it exists.
-	recordProducers := make([]tlv.RecordProducer, 0, 2)
+	recordProducers := make([]tlv.RecordProducer, 0, 4)
 	if c.AliasScid != nil {
 		recordProducers = append(recordProducers, c.AliasScid)
 	}
 	c.NextLocalNonce.WhenSome(func(localNonce Musig2NonceTLV) {
 		recordProducers = append(recordProducers, &localNonce)
 	})
+	c.AnnouncementBitcoinNonce.WhenSome(
+		func(nonce tlv.RecordT[tlv.TlvType2, Musig2Nonce]) {
+			recordProducers = append(recordProducers, &nonce)
+		},
+	)
+	c.AnnouncementNodeNonce.WhenSome(
+		func(nonce tlv.RecordT[tlv.TlvType0, Musig2Nonce]) {
+			recordProducers = append(recordProducers, &nonce)
+		},
+	)
+
 	err := EncodeMessageExtraData(&c.ExtraData, recordProducers...)
 	if err != nil {
 		return err
diff --git a/lnwire/channel_update.go b/lnwire/channel_update.go
index 7f42a58b4..33cc3bff0 100644
--- a/lnwire/channel_update.go
+++ b/lnwire/channel_update.go
@@ -56,11 +56,11 @@ func (c ChanUpdateChanFlags) String() string {
 	return fmt.Sprintf("%08b", c)
 }
 
-// ChannelUpdate message is used after channel has been initially announced.
+// ChannelUpdate1 message is used after channel has been initially announced.
 // Each side independently announces its fees and minimum expiry for HTLCs and
 // other parameters. Also this message is used to redeclare initially set
 // channel parameters.
-type ChannelUpdate struct {
+type ChannelUpdate1 struct {
 	// Signature is used to validate the announced data and prove the
 	// ownership of node id.
 	Signature Sig
@@ -122,13 +122,13 @@ type ChannelUpdate struct {
 
 // A compile time check to ensure ChannelUpdate implements the lnwire.Message
 // interface.
-var _ Message = (*ChannelUpdate)(nil)
+var _ Message = (*ChannelUpdate1)(nil)
 
 // Decode deserializes a serialized ChannelUpdate stored in the passed
 // io.Reader observing the specified protocol version.
 //
 // This is part of the lnwire.Message interface.
-func (a *ChannelUpdate) Decode(r io.Reader, pver uint32) error {
+func (a *ChannelUpdate1) Decode(r io.Reader, pver uint32) error {
 	err := ReadElements(r,
 		&a.Signature,
 		a.ChainHash[:],
@@ -159,7 +159,7 @@ func (a *ChannelUpdate) Decode(r io.Reader, pver uint32) error {
 // observing the protocol version specified.
 //
 // This is part of the lnwire.Message interface.
-func (a *ChannelUpdate) Encode(w *bytes.Buffer, pver uint32) error {
+func (a *ChannelUpdate1) Encode(w *bytes.Buffer, pver uint32) error {
 	if err := WriteSig(w, a.Signature); err != nil {
 		return err
 	}
@@ -217,13 +217,13 @@ func (a *ChannelUpdate) Encode(w *bytes.Buffer, pver uint32) error {
 // wire.
 //
 // This is part of the lnwire.Message interface.
-func (a *ChannelUpdate) MsgType() MessageType {
+func (a *ChannelUpdate1) MsgType() MessageType {
 	return MsgChannelUpdate
 }
 
 // DataToSign is used to retrieve part of the announcement message which should
 // be signed.
-func (a *ChannelUpdate) DataToSign() ([]byte, error) {
+func (a *ChannelUpdate1) DataToSign() ([]byte, error) {
 	// We should not include the signatures itself.
 	b := make([]byte, 0, MaxMsgBody)
 	buf := bytes.NewBuffer(b)
@@ -279,3 +279,91 @@ func (a *ChannelUpdate) DataToSign() ([]byte, error) {
 
 	return buf.Bytes(), nil
 }
+
+// SCID returns the ShortChannelID of the channel that the update applies to.
+//
+// NOTE: this is part of the ChannelUpdate interface.
+func (a *ChannelUpdate1) SCID() ShortChannelID {
+	return a.ShortChannelID
+}
+
+// IsNode1 is true if the update was produced by node 1 of the channel peers.
+// Node 1 is the node with the lexicographically smaller public key.
+//
+// NOTE: this is part of the ChannelUpdate interface.
+func (a *ChannelUpdate1) IsNode1() bool {
+	return a.ChannelFlags&ChanUpdateDirection == 0
+}
+
+// IsDisabled is true if the update is announcing that the channel should be
+// considered disabled.
+//
+// NOTE: this is part of the ChannelUpdate interface.
+func (a *ChannelUpdate1) IsDisabled() bool {
+	return a.ChannelFlags&ChanUpdateDisabled == ChanUpdateDisabled
+}
+
+// GetChainHash returns the hash of the chain that the message is referring to.
+//
+// NOTE: this is part of the ChannelUpdate interface.
+func (a *ChannelUpdate1) GetChainHash() chainhash.Hash {
+	return a.ChainHash
+}
+
+// ForwardingPolicy returns the set of forwarding constraints of the update.
+//
+// NOTE: this is part of the ChannelUpdate interface.
+func (a *ChannelUpdate1) ForwardingPolicy() *ForwardingPolicy {
+	return &ForwardingPolicy{
+		TimeLockDelta: a.TimeLockDelta,
+		BaseFee:       MilliSatoshi(a.BaseFee),
+		FeeRate:       MilliSatoshi(a.FeeRate),
+		MinHTLC:       a.HtlcMinimumMsat,
+		HasMaxHTLC:    a.MessageFlags.HasMaxHtlc(),
+		MaxHTLC:       a.HtlcMaximumMsat,
+	}
+}
+
+// CmpAge can be used to determine if the update is older or newer than the
+// passed update. It returns 1 if this update is newer, -1 if it is older, and
+// 0 if they are the same age.
+//
+// NOTE: this is part of the ChannelUpdate interface.
+func (a *ChannelUpdate1) CmpAge(update ChannelUpdate) (CompareResult, error) {
+	other, ok := update.(*ChannelUpdate1)
+	if !ok {
+		return 0, fmt.Errorf("expected *ChannelUpdate1, got: %T",
+			update)
+	}
+
+	switch {
+	case a.Timestamp > other.Timestamp:
+		return GreaterThan, nil
+	case a.Timestamp < other.Timestamp:
+		return LessThan, nil
+	default:
+		return EqualTo, nil
+	}
+}
+
+// SetDisabledFlag can be used to adjust the disabled flag of an update.
+//
+// NOTE: this is part of the ChannelUpdate interface.
+func (a *ChannelUpdate1) SetDisabledFlag(disabled bool) {
+	if disabled {
+		a.ChannelFlags |= ChanUpdateDisabled
+	} else {
+		a.ChannelFlags &= ^ChanUpdateDisabled
+	}
+}
+
+// SetSCID can be used to overwrite the SCID of the update.
+//
+// NOTE: this is part of the ChannelUpdate interface.
+func (a *ChannelUpdate1) SetSCID(scid ShortChannelID) {
+	a.ShortChannelID = scid
+}
+
+// A compile time assertion to ensure ChannelUpdate1 implements the
+// ChannelUpdate interface.
+var _ ChannelUpdate = (*ChannelUpdate1)(nil)
diff --git a/lnwire/channel_update_2.go b/lnwire/channel_update_2.go
new file mode 100644
index 000000000..79a76aad6
--- /dev/null
+++ b/lnwire/channel_update_2.go
@@ -0,0 +1,441 @@
+package lnwire
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+
+	"github.com/btcsuite/btcd/chaincfg"
+	"github.com/btcsuite/btcd/chaincfg/chainhash"
+	"github.com/lightningnetwork/lnd/tlv"
+)
+
+const (
+	defaultCltvExpiryDelta           = uint16(80)
+	defaultHtlcMinMsat               = MilliSatoshi(1)
+	defaultFeeBaseMsat               = uint32(1000)
+	defaultFeeProportionalMillionths = uint32(1)
+)
+
+// ChannelUpdate2 message is used after taproot channel has been initially
+// announced. Each side independently announces its fees and minimum expiry for
+// HTLCs and other parameters. This message is also used to redeclare initially
+// set channel parameters.
+type ChannelUpdate2 struct {
+	// Signature is used to validate the announced data and prove the
+	// ownership of node id.
+	Signature Sig
+
+	// ChainHash denotes the target chain that this channel was opened
+	// within. This value should be the genesis hash of the target chain.
+	// Along with the short channel ID, this uniquely identifies the
+	// channel globally in a blockchain.
+	ChainHash tlv.RecordT[tlv.TlvType0, chainhash.Hash]
+
+	// ShortChannelID is the unique description of the funding transaction.
+	ShortChannelID tlv.RecordT[tlv.TlvType2, ShortChannelID]
+
+	// BlockHeight allows ordering in the case of multiple announcements. We
+	// should ignore the message if block height is not greater than the
+	// last-received. The block height must always be greater or equal to
+	// the block height that the channel funding transaction was confirmed
+	// in.
+	BlockHeight tlv.RecordT[tlv.TlvType4, uint32]
+
+	// DisabledFlags is an optional bitfield that describes various reasons
+	// that the node is communicating that the channel should be considered
+	// disabled.
+	DisabledFlags tlv.RecordT[tlv.TlvType6, ChanUpdateDisableFlags]
+
+	// SecondPeer is used to indicate which node the channel node has
+	// created and signed this message. If this field is present, it was
+	// node 2 otherwise it was node 1.
+	SecondPeer tlv.OptionalRecordT[tlv.TlvType8, TrueBoolean]
+
+	// CLTVExpiryDelta is the minimum number of blocks this node requires to
+	// be added to the expiry of HTLCs. This is a security parameter
+	// determined by the node operator. This value represents the required
+	// gap between the time locks of the incoming and outgoing HTLC's set
+	// to this node.
+	CLTVExpiryDelta tlv.RecordT[tlv.TlvType10, uint16]
+
+	// HTLCMinimumMsat is the minimum HTLC value which will be accepted.
+	HTLCMinimumMsat tlv.RecordT[tlv.TlvType12, MilliSatoshi]
+
+	// HtlcMaximumMsat is the maximum HTLC value which will be accepted.
+	HTLCMaximumMsat tlv.RecordT[tlv.TlvType14, MilliSatoshi]
+
+	// FeeBaseMsat is the base fee that must be used for incoming HTLC's to
+	// this particular channel. This value will be tacked onto the required
+	// for a payment independent of the size of the payment.
+	FeeBaseMsat tlv.RecordT[tlv.TlvType16, uint32]
+
+	// FeeProportionalMillionths is the fee rate that will be charged per
+	// millionth of a satoshi.
+	FeeProportionalMillionths tlv.RecordT[tlv.TlvType18, uint32]
+
+	// ExtraOpaqueData is the set of data that was appended to this message
+	// to fill out the full maximum transport message size. These fields can
+	// be used to specify optional data such as custom TLV fields.
+	ExtraOpaqueData ExtraOpaqueData
+}
+
+// Decode deserializes a serialized ChannelUpdate2 stored in the passed
+// io.Reader observing the specified protocol version.
+//
+// This is part of the lnwire.Message interface.
+func (c *ChannelUpdate2) Decode(r io.Reader, _ uint32) error {
+	err := ReadElement(r, &c.Signature)
+	if err != nil {
+		return err
+	}
+	c.Signature.ForceSchnorr()
+
+	return c.DecodeTLVRecords(r)
+}
+
+// DecodeTLVRecords decodes only the TLV section of the message.
+func (c *ChannelUpdate2) DecodeTLVRecords(r io.Reader) error {
+	// First extract into extra opaque data.
+	var tlvRecords ExtraOpaqueData
+	if err := ReadElements(r, &tlvRecords); err != nil {
+		return err
+	}
+
+	var (
+		chainHash  = tlv.ZeroRecordT[tlv.TlvType0, [32]byte]()
+		secondPeer = tlv.ZeroRecordT[tlv.TlvType8, TrueBoolean]()
+	)
+	typeMap, err := tlvRecords.ExtractRecords(
+		&chainHash, &c.ShortChannelID, &c.BlockHeight, &c.DisabledFlags,
+		&secondPeer, &c.CLTVExpiryDelta, &c.HTLCMinimumMsat,
+		&c.HTLCMaximumMsat, &c.FeeBaseMsat,
+		&c.FeeProportionalMillionths,
+	)
+	if err != nil {
+		return err
+	}
+
+	// By default, the chain-hash is the bitcoin mainnet genesis block hash.
+	c.ChainHash.Val = *chaincfg.MainNetParams.GenesisHash
+	if _, ok := typeMap[c.ChainHash.TlvType()]; ok {
+		c.ChainHash.Val = chainHash.Val
+	}
+
+	// The presence of the second_peer tlv type indicates "true".
+	if _, ok := typeMap[c.SecondPeer.TlvType()]; ok {
+		c.SecondPeer = tlv.SomeRecordT(secondPeer)
+	}
+
+	// If the CLTV expiry delta was not encoded, then set it to the default
+	// value.
+	if _, ok := typeMap[c.CLTVExpiryDelta.TlvType()]; !ok {
+		c.CLTVExpiryDelta.Val = defaultCltvExpiryDelta
+	}
+
+	// If the HTLC Minimum msat was not encoded, then set it to the default
+	// value.
+	if _, ok := typeMap[c.HTLCMinimumMsat.TlvType()]; !ok {
+		c.HTLCMinimumMsat.Val = defaultHtlcMinMsat
+	}
+
+	// If the base fee was not encoded, then set it to the default value.
+	if _, ok := typeMap[c.FeeBaseMsat.TlvType()]; !ok {
+		c.FeeBaseMsat.Val = defaultFeeBaseMsat
+	}
+
+	// If the proportional fee was not encoded, then set it to the default
+	// value.
+	if _, ok := typeMap[c.FeeProportionalMillionths.TlvType()]; !ok {
+		c.FeeProportionalMillionths.Val = defaultFeeProportionalMillionths //nolint:lll
+	}
+
+	if len(tlvRecords) != 0 {
+		c.ExtraOpaqueData = tlvRecords
+	}
+
+	return nil
+}
+
+// Encode serializes the target ChannelUpdate2 into the passed io.Writer
+// observing the protocol version specified.
+//
+// This is part of the lnwire.Message interface.
+func (c *ChannelUpdate2) Encode(w *bytes.Buffer, _ uint32) error {
+	_, err := w.Write(c.Signature.RawBytes())
+	if err != nil {
+		return err
+	}
+
+	_, err = c.DataToSign()
+	if err != nil {
+		return err
+	}
+
+	return WriteBytes(w, c.ExtraOpaqueData)
+}
+
+// DataToSign is used to retrieve part of the announcement message which should
+// be signed. For the ChannelUpdate2 message, this includes the serialised TLV
+// records.
+func (c *ChannelUpdate2) DataToSign() ([]byte, error) {
+	// The chain-hash record is only included if it is _not_ equal to the
+	// bitcoin mainnet genisis block hash.
+	var recordProducers []tlv.RecordProducer
+	if !c.ChainHash.Val.IsEqual(chaincfg.MainNetParams.GenesisHash) {
+		hash := tlv.ZeroRecordT[tlv.TlvType0, [32]byte]()
+		hash.Val = c.ChainHash.Val
+
+		recordProducers = append(recordProducers, &hash)
+	}
+
+	recordProducers = append(recordProducers,
+		&c.ShortChannelID, &c.BlockHeight,
+	)
+
+	// Only include the disable flags if any bit is set.
+	if !c.DisabledFlags.Val.IsEnabled() {
+		recordProducers = append(recordProducers, &c.DisabledFlags)
+	}
+
+	// We only need to encode the second peer boolean if it is true
+	c.SecondPeer.WhenSome(func(r tlv.RecordT[tlv.TlvType8, TrueBoolean]) {
+		recordProducers = append(recordProducers, &r)
+	})
+
+	// We only encode the cltv expiry delta if it is not equal to the
+	// default.
+	if c.CLTVExpiryDelta.Val != defaultCltvExpiryDelta {
+		recordProducers = append(recordProducers, &c.CLTVExpiryDelta)
+	}
+
+	if c.HTLCMinimumMsat.Val != defaultHtlcMinMsat {
+		recordProducers = append(recordProducers, &c.HTLCMinimumMsat)
+	}
+
+	recordProducers = append(recordProducers, &c.HTLCMaximumMsat)
+
+	if c.FeeBaseMsat.Val != defaultFeeBaseMsat {
+		recordProducers = append(recordProducers, &c.FeeBaseMsat)
+	}
+
+	if c.FeeProportionalMillionths.Val != defaultFeeProportionalMillionths {
+		recordProducers = append(
+			recordProducers, &c.FeeProportionalMillionths,
+		)
+	}
+
+	err := EncodeMessageExtraData(&c.ExtraOpaqueData, recordProducers...)
+	if err != nil {
+		return nil, err
+	}
+
+	return c.ExtraOpaqueData, nil
+}
+
+// MsgType returns the integer uniquely identifying this message type on the
+// wire.
+//
+// This is part of the lnwire.Message interface.
+func (c *ChannelUpdate2) MsgType() MessageType {
+	return MsgChannelUpdate2
+}
+
+func (c *ChannelUpdate2) ExtraData() ExtraOpaqueData {
+	return c.ExtraOpaqueData
+}
+
+// A compile time check to ensure ChannelUpdate2 implements the
+// lnwire.Message interface.
+var _ Message = (*ChannelUpdate2)(nil)
+
+// SCID returns the ShortChannelID of the channel that the update applies to.
+//
+// NOTE: this is part of the ChannelUpdate interface.
+func (c *ChannelUpdate2) SCID() ShortChannelID {
+	return c.ShortChannelID.Val
+}
+
+// IsNode1 is true if the update was produced by node 1 of the channel peers.
+// Node 1 is the node with the lexicographically smaller public key.
+//
+// NOTE: this is part of the ChannelUpdate interface.
+func (c *ChannelUpdate2) IsNode1() bool {
+	return c.SecondPeer.IsNone()
+}
+
+// IsDisabled is true if the update is announcing that the channel should be
+// considered disabled.
+//
+// NOTE: this is part of the ChannelUpdate interface.
+func (c *ChannelUpdate2) IsDisabled() bool {
+	return !c.DisabledFlags.Val.IsEnabled()
+}
+
+// GetChainHash returns the hash of the chain that the message is referring to.
+//
+// NOTE: this is part of the ChannelUpdate interface.
+func (c *ChannelUpdate2) GetChainHash() chainhash.Hash {
+	return c.ChainHash.Val
+}
+
+// ForwardingPolicy returns the set of forwarding constraints of the update.
+//
+// NOTE: this is part of the ChannelUpdate interface.
+func (c *ChannelUpdate2) ForwardingPolicy() *ForwardingPolicy {
+	return &ForwardingPolicy{
+		TimeLockDelta: c.CLTVExpiryDelta.Val,
+		BaseFee:       MilliSatoshi(c.FeeBaseMsat.Val),
+		FeeRate:       MilliSatoshi(c.FeeProportionalMillionths.Val),
+		MinHTLC:       c.HTLCMinimumMsat.Val,
+		HasMaxHTLC:    true,
+		MaxHTLC:       c.HTLCMaximumMsat.Val,
+	}
+}
+
+// CmpAge can be used to determine if the update is older or newer than the
+// passed update. It returns 1 if this update is newer, -1 if it is older, and
+// 0 if they are the same age.
+//
+// NOTE: this is part of the ChannelUpdate interface.
+func (c *ChannelUpdate2) CmpAge(update ChannelUpdate) (CompareResult, error) {
+	other, ok := update.(*ChannelUpdate2)
+	if !ok {
+		return 0, fmt.Errorf("expected *ChannelUpdate2, got: %T",
+			update)
+	}
+
+	switch {
+	case c.BlockHeight.Val > other.BlockHeight.Val:
+		return GreaterThan, nil
+	case c.BlockHeight.Val < other.BlockHeight.Val:
+		return LessThan, nil
+	default:
+		return EqualTo, nil
+	}
+}
+
+// SetDisabledFlag can be used to adjust the disabled flag of an update.
+//
+// NOTE: this is part of the ChannelUpdate interface.
+func (c *ChannelUpdate2) SetDisabledFlag(disabled bool) {
+	if disabled {
+		c.DisabledFlags.Val |= ChanUpdateDisableIncoming
+		c.DisabledFlags.Val |= ChanUpdateDisableOutgoing
+	} else {
+		c.DisabledFlags.Val &^= ChanUpdateDisableIncoming
+		c.DisabledFlags.Val &^= ChanUpdateDisableOutgoing
+	}
+}
+
+// SetSCID can be used to overwrite the SCID of the update.
+//
+// NOTE: this is part of the ChannelUpdate interface.
+func (c *ChannelUpdate2) SetSCID(scid ShortChannelID) {
+	c.ShortChannelID.Val = scid
+}
+
+// A compile time check to ensure ChannelUpdate2 implements the
+// lnwire.ChannelUpdate interface.
+var _ ChannelUpdate = (*ChannelUpdate2)(nil)
+
+// ChanUpdateDisableFlags is a bit vector that can be used to indicate various
+// reasons for the channel being marked as disabled.
+type ChanUpdateDisableFlags uint8
+
+const (
+	// ChanUpdateDisableIncoming is a bit indicates that a channel is
+	// disabled in the inbound direction meaning that the node broadcasting
+	// the update is communicating that they cannot receive funds.
+	ChanUpdateDisableIncoming ChanUpdateDisableFlags = 1 << iota
+
+	// ChanUpdateDisableOutgoing is a bit indicates that a channel is
+	// disabled in the outbound direction meaning that the node broadcasting
+	// the update is communicating that they cannot send or route funds.
+	ChanUpdateDisableOutgoing = 2
+)
+
+// IncomingDisabled returns true if the ChanUpdateDisableIncoming bit is set.
+func (c ChanUpdateDisableFlags) IncomingDisabled() bool {
+	return c&ChanUpdateDisableIncoming == ChanUpdateDisableIncoming
+}
+
+// OutgoingDisabled returns true if the ChanUpdateDisableOutgoing bit is set.
+func (c ChanUpdateDisableFlags) OutgoingDisabled() bool {
+	return c&ChanUpdateDisableOutgoing == ChanUpdateDisableOutgoing
+}
+
+// IsEnabled returns true if none of the disable bits are set.
+func (c ChanUpdateDisableFlags) IsEnabled() bool {
+	return c == 0
+}
+
+// String returns the bitfield flags as a string.
+func (c ChanUpdateDisableFlags) String() string {
+	return fmt.Sprintf("%08b", c)
+}
+
+// Record returns the tlv record for the disable flags.
+func (c *ChanUpdateDisableFlags) Record() tlv.Record {
+	return tlv.MakeStaticRecord(0, c, 1, encodeDisableFlags,
+		decodeDisableFlags)
+}
+
+func encodeDisableFlags(w io.Writer, val interface{}, buf *[8]byte) error {
+	if v, ok := val.(*ChanUpdateDisableFlags); ok {
+		flagsInt := uint8(*v)
+
+		return tlv.EUint8(w, &flagsInt, buf)
+	}
+
+	return tlv.NewTypeForEncodingErr(val, "lnwire.ChanUpdateDisableFlags")
+}
+
+func decodeDisableFlags(r io.Reader, val interface{}, buf *[8]byte,
+	l uint64) error {
+
+	if v, ok := val.(*ChanUpdateDisableFlags); ok {
+		var flagsInt uint8
+		err := tlv.DUint8(r, &flagsInt, buf, l)
+		if err != nil {
+			return err
+		}
+
+		*v = ChanUpdateDisableFlags(flagsInt)
+
+		return nil
+	}
+
+	return tlv.NewTypeForDecodingErr(val, "lnwire.ChanUpdateDisableFlags",
+		l, l)
+}
+
+// TrueBoolean is a record that indicates true or false using the presence of
+// the record. If the record is absent, it indicates false. If it is presence,
+// it indicates true.
+type TrueBoolean struct{}
+
+// Record returns the tlv record for the boolean entry.
+func (b *TrueBoolean) Record() tlv.Record {
+	return tlv.MakeStaticRecord(
+		0, b, 0, booleanEncoder, booleanDecoder,
+	)
+}
+
+func booleanEncoder(_ io.Writer, val interface{}, _ *[8]byte) error {
+	if _, ok := val.(*TrueBoolean); ok {
+		return nil
+	}
+
+	return tlv.NewTypeForEncodingErr(val, "TrueBoolean")
+}
+
+func booleanDecoder(_ io.Reader, val interface{}, _ *[8]byte,
+	l uint64) error {
+
+	if _, ok := val.(*TrueBoolean); ok && (l == 0 || l == 1) {
+		return nil
+	}
+
+	return tlv.NewTypeForEncodingErr(val, "TrueBoolean")
+}
diff --git a/lnwire/features.go b/lnwire/features.go
index 437a50dc2..c5ae014f7 100644
--- a/lnwire/features.go
+++ b/lnwire/features.go
@@ -642,31 +642,39 @@ func (fv *RawFeatureVector) sizeFunc() uint64 {
 // Record returns a TLV record that can be used to encode/decode raw feature
 // vectors. Note that the length of the feature vector is not included, because
 // it is covered by the TLV record's length field.
-func (fv *RawFeatureVector) Record(recordType tlv.Type) tlv.Record {
+func (fv *RawFeatureVector) Record() tlv.Record {
 	return tlv.MakeDynamicRecord(
-		recordType, fv, fv.sizeFunc, rawFeatureEncoder,
-		rawFeatureDecoder,
+		0, fv, fv.sizeFunc, rawFeatureEncoder, rawFeatureDecoder,
 	)
 }
 
 // rawFeatureEncoder is a custom TLV encoder for raw feature vectors.
 func rawFeatureEncoder(w io.Writer, val interface{}, _ *[8]byte) error {
-	if f, ok := val.(*RawFeatureVector); ok {
-		return f.encode(w, f.SerializeSize(), 8)
+	if v, ok := val.(*RawFeatureVector); ok {
+		// Encode the feature bits as a byte slice without its length
+		// prepended, as that's already taken care of by the TLV record.
+		fv := *v
+		return fv.encode(w, fv.SerializeSize(), 8)
 	}
 
-	return tlv.NewTypeForEncodingErr(val, "*lnwire.RawFeatureVector")
+	return tlv.NewTypeForEncodingErr(val, "lnwire.RawFeatureVector")
 }
 
 // rawFeatureDecoder is a custom TLV decoder for raw feature vectors.
 func rawFeatureDecoder(r io.Reader, val interface{}, _ *[8]byte,
 	l uint64) error {
 
-	if f, ok := val.(*RawFeatureVector); ok {
-		return f.decode(r, int(l), 8)
+	if v, ok := val.(*RawFeatureVector); ok {
+		fv := NewRawFeatureVector()
+		if err := fv.decode(r, int(l), 8); err != nil {
+			return err
+		}
+		*v = *fv
+
+		return nil
 	}
 
-	return tlv.NewTypeForEncodingErr(val, "*lnwire.RawFeatureVector")
+	return tlv.NewTypeForEncodingErr(val, "lnwire.RawFeatureVector")
 }
 
 // FeatureVector represents a set of enabled features. The set stores
diff --git a/lnwire/gossip_timestamp_range.go b/lnwire/gossip_timestamp_range.go
index d2dbddecb..7b628752a 100644
--- a/lnwire/gossip_timestamp_range.go
+++ b/lnwire/gossip_timestamp_range.go
@@ -5,6 +5,7 @@ import (
 	"io"
 
 	"github.com/btcsuite/btcd/chaincfg/chainhash"
+	"github.com/lightningnetwork/lnd/tlv"
 )
 
 // GossipTimestampRange is a message that allows the sender to restrict the set
@@ -17,15 +18,31 @@ type GossipTimestampRange struct {
 	ChainHash chainhash.Hash
 
 	// FirstTimestamp is the timestamp of the earliest announcement message
-	// that should be sent by the receiver.
+	// that should be sent by the receiver. This is only to be used for
+	// querying message of gossip 1.0 which are timestamped using Unix
+	// timestamps. FirstBlockHeight and BlockRange should be used to
+	// query for announcement messages timestamped using block heights.
 	FirstTimestamp uint32
 
 	// TimestampRange is the horizon beyond the FirstTimestamp that any
 	// announcement messages should be sent for. The receiving node MUST
 	// NOT send any announcements that have a timestamp greater than
-	// FirstTimestamp + TimestampRange.
+	// FirstTimestamp + TimestampRange. This is used together with
+	// FirstTimestamp to query for gossip 1.0 messages timestamped with
+	// Unix timestamps.
 	TimestampRange uint32
 
+	// FirstBlockHeight is the height of earliest announcement message that
+	// should be sent by the receiver. This is used only for querying
+	// announcement messages that use block heights as a timestamp.
+	FirstBlockHeight tlv.OptionalRecordT[tlv.TlvType2, uint32]
+
+	// BlockRange is the horizon beyond FirstBlockHeight that any
+	// announcement messages should be sent for. The receiving node MUST NOT
+	// send any announcements that have a timestamp greater than
+	// FirstBlockHeight + BlockRange.
+	BlockRange tlv.OptionalRecordT[tlv.TlvType4, uint32]
+
 	// ExtraData is the set of data that was appended to this message to
 	// fill out the full maximum transport message size. These fields can
 	// be used to specify optional data such as custom TLV fields.
@@ -45,13 +62,42 @@ var _ Message = (*GossipTimestampRange)(nil)
 // passed io.Reader observing the specified protocol version.
 //
 // This is part of the lnwire.Message interface.
-func (g *GossipTimestampRange) Decode(r io.Reader, pver uint32) error {
-	return ReadElements(r,
+func (g *GossipTimestampRange) Decode(r io.Reader, _ uint32) error {
+	err := ReadElements(r,
 		g.ChainHash[:],
 		&g.FirstTimestamp,
 		&g.TimestampRange,
-		&g.ExtraData,
 	)
+	if err != nil {
+		return err
+	}
+
+	var tlvRecords ExtraOpaqueData
+	if err := ReadElements(r, &tlvRecords); err != nil {
+		return err
+	}
+
+	var (
+		firstBlock = tlv.ZeroRecordT[tlv.TlvType2, uint32]()
+		blockRange = tlv.ZeroRecordT[tlv.TlvType4, uint32]()
+	)
+	typeMap, err := tlvRecords.ExtractRecords(&firstBlock, &blockRange)
+	if err != nil {
+		return err
+	}
+
+	if val, ok := typeMap[g.FirstBlockHeight.TlvType()]; ok && val == nil {
+		g.FirstBlockHeight = tlv.SomeRecordT(firstBlock)
+	}
+	if val, ok := typeMap[g.BlockRange.TlvType()]; ok && val == nil {
+		g.BlockRange = tlv.SomeRecordT(blockRange)
+	}
+
+	if len(tlvRecords) != 0 {
+		g.ExtraData = tlvRecords
+	}
+
+	return nil
 }
 
 // Encode serializes the target GossipTimestampRange into the passed io.Writer
@@ -71,6 +117,22 @@ func (g *GossipTimestampRange) Encode(w *bytes.Buffer, pver uint32) error {
 		return err
 	}
 
+	recordProducers := make([]tlv.RecordProducer, 0, 2)
+	g.FirstBlockHeight.WhenSome(
+		func(height tlv.RecordT[tlv.TlvType2, uint32]) {
+			recordProducers = append(recordProducers, &height)
+		},
+	)
+	g.BlockRange.WhenSome(
+		func(blockRange tlv.RecordT[tlv.TlvType4, uint32]) {
+			recordProducers = append(recordProducers, &blockRange)
+		},
+	)
+	err := EncodeMessageExtraData(&g.ExtraData, recordProducers...)
+	if err != nil {
+		return err
+	}
+
 	return WriteBytes(w, g.ExtraData)
 }
 
diff --git a/lnwire/interfaces.go b/lnwire/interfaces.go
new file mode 100644
index 000000000..3a8b7cbdf
--- /dev/null
+++ b/lnwire/interfaces.go
@@ -0,0 +1,121 @@
+package lnwire
+
+import "github.com/btcsuite/btcd/chaincfg/chainhash"
+
+// AnnounceSignatures is an interface that represents a message used to
+// exchange signatures of a ChannelAnnouncment message during the funding flow.
+type AnnounceSignatures interface {
+	// SCID returns the ShortChannelID of the channel.
+	SCID() ShortChannelID
+
+	// ChanID returns the ChannelID identifying the channel.
+	ChanID() ChannelID
+
+	Message
+}
+
+// ChannelAnnouncement is an interface that must be satisfied by any message
+// used to announce and prove the existence of a channel.
+type ChannelAnnouncement interface {
+	// SCID returns the short channel ID of the channel.
+	SCID() ShortChannelID
+
+	// GetChainHash returns the hash of the chain which this channel's
+	// funding transaction is confirmed in.
+	GetChainHash() chainhash.Hash
+
+	// Node1KeyBytes returns the bytes representing the public key of node
+	// 1 in the channel.
+	Node1KeyBytes() [33]byte
+
+	// Node2KeyBytes returns the bytes representing the public key of node
+	// 2 in the channel.
+	Node2KeyBytes() [33]byte
+
+	Message
+}
+
+// CompareResult represents the result after comparing two things.
+type CompareResult uint8
+
+const (
+	// LessThan indicates that base object is less than the object it was
+	// compared to.
+	LessThan CompareResult = iota
+
+	// EqualTo indicates that the base object is equal to the object it was
+	// compared to.
+	EqualTo
+
+	// GreaterThan indicates that base object is greater than the object it
+	// was compared to.
+	GreaterThan
+)
+
+// ChannelUpdate is an interface that describes a message used to update the
+// forwarding rules of a channel.
+type ChannelUpdate interface {
+	// SCID returns the ShortChannelID of the channel that the update
+	// applies to.
+	SCID() ShortChannelID
+
+	// IsNode1 is true if the update was produced by node 1 of the channel
+	// peers. Node 1 is the node with the lexicographically smaller public
+	// key.
+	IsNode1() bool
+
+	// IsDisabled is true if the update is announcing that the channel
+	// should be considered disabled.
+	IsDisabled() bool
+
+	// GetChainHash returns the hash of the chain that the message is
+	// referring to.
+	GetChainHash() chainhash.Hash
+
+	// ForwardingPolicy returns the set of forwarding constraints of the
+	// update.
+	ForwardingPolicy() *ForwardingPolicy
+
+	// CmpAge can be used to determine if the update is older or newer than
+	// the passed update. It returns LessThan if this update is older than
+	// the passed update, GreaterThan if it is newer and EqualTo if they are
+	// the same age.
+	CmpAge(update ChannelUpdate) (CompareResult, error)
+
+	// SetDisabledFlag can be used to adjust the disabled flag of an update.
+	SetDisabledFlag(bool)
+
+	// SetSCID can be used to overwrite the SCID of the update.
+	SetSCID(scid ShortChannelID)
+
+	Message
+}
+
+// ForwardingPolicy defines the set of forwarding constraints advertised in a
+// ChannelUpdate message.
+type ForwardingPolicy struct {
+	// TimeLockDelta is the minimum number of blocks that the node requires
+	// to be added to the expiry of HTLCs. This is a security parameter
+	// determined by the node operator. This value represents the required
+	// gap between the time locks of the incoming and outgoing HTLC's set
+	// to this node.
+	TimeLockDelta uint16
+
+	// BaseFee is the base fee that must be used for incoming HTLC's to
+	// this particular channel. This value will be tacked onto the required
+	// for a payment independent of the size of the payment.
+	BaseFee MilliSatoshi
+
+	// FeeRate is the fee rate that will be charged per millionth of a
+	// satoshi.
+	FeeRate MilliSatoshi
+
+	// HtlcMinimumMsat is the minimum HTLC value which will be accepted.
+	MinHTLC MilliSatoshi
+
+	// HasMaxHTLC is true if the MaxHTLC field is provided in the update.
+	HasMaxHTLC bool
+
+	// HtlcMaximumMsat is the maximum HTLC value which will be accepted.
+	MaxHTLC MilliSatoshi
+}
diff --git a/lnwire/lnwire.go b/lnwire/lnwire.go
index 8ab082b0b..4d2b814e5 100644
--- a/lnwire/lnwire.go
+++ b/lnwire/lnwire.go
@@ -187,6 +187,11 @@ func WriteElement(w *bytes.Buffer, element interface{}) error {
 			return err
 		}
 
+	case PartialSig:
+		if err := e.Encode(w); err != nil {
+			return err
+		}
+
 	case PingPayload:
 		var l [2]byte
 		binary.BigEndian.PutUint16(l[:], uint16(len(e)))
@@ -936,6 +941,13 @@ func ReadElement(r io.Reader, element interface{}) error {
 		}
 		*e = addrBytes[:length]
 
+	case *PartialSig:
+		var sig PartialSig
+		if err = sig.Decode(r); err != nil {
+			return err
+		}
+		*e = sig
+
 	case *ExtraOpaqueData:
 		return e.Decode(r)
 
diff --git a/lnwire/message.go b/lnwire/message.go
index 2bf64a313..a758db000 100644
--- a/lnwire/message.go
+++ b/lnwire/message.go
@@ -52,11 +52,14 @@ const (
 	MsgNodeAnnouncement                    = 257
 	MsgChannelUpdate                       = 258
 	MsgAnnounceSignatures                  = 259
+	MsgAnnounceSignatures2                 = 260
 	MsgQueryShortChanIDs                   = 261
 	MsgReplyShortChanIDsEnd                = 262
 	MsgQueryChannelRange                   = 263
 	MsgReplyChannelRange                   = 264
 	MsgGossipTimestampRange                = 265
+	MsgChannelAnnouncement2                = 267
+	MsgChannelUpdate2                      = 271
 	MsgKickoffSig                          = 777
 )
 
@@ -155,6 +158,12 @@ func (t MessageType) String() string {
 		return "ClosingComplete"
 	case MsgClosingSig:
 		return "ClosingSig"
+	case MsgAnnounceSignatures2:
+		return "MsgAnnounceSignatures2"
+	case MsgChannelAnnouncement2:
+		return "ChannelAnnouncement2"
+	case MsgChannelUpdate2:
+		return "ChannelUpdate2"
 	default:
 		return "<unknown>"
 	}
@@ -259,15 +268,15 @@ func makeEmptyMessage(msgType MessageType) (Message, error) {
 	case MsgError:
 		msg = &Error{}
 	case MsgChannelAnnouncement:
-		msg = &ChannelAnnouncement{}
+		msg = &ChannelAnnouncement1{}
 	case MsgChannelUpdate:
-		msg = &ChannelUpdate{}
+		msg = &ChannelUpdate1{}
 	case MsgNodeAnnouncement:
 		msg = &NodeAnnouncement{}
 	case MsgPing:
 		msg = &Ping{}
 	case MsgAnnounceSignatures:
-		msg = &AnnounceSignatures{}
+		msg = &AnnounceSignatures1{}
 	case MsgPong:
 		msg = &Pong{}
 	case MsgQueryShortChanIDs:
@@ -284,6 +293,12 @@ func makeEmptyMessage(msgType MessageType) (Message, error) {
 		msg = &ClosingComplete{}
 	case MsgClosingSig:
 		msg = &ClosingSig{}
+	case MsgAnnounceSignatures2:
+		msg = &AnnounceSignatures2{}
+	case MsgChannelAnnouncement2:
+		msg = &ChannelAnnouncement2{}
+	case MsgChannelUpdate2:
+		msg = &ChannelUpdate2{}
 	default:
 		// If the message is not within our custom range and has not
 		// specifically been overridden, return an unknown message.
diff --git a/lnwire/msat.go b/lnwire/msat.go
index 7473d72c8..2966e5ddb 100644
--- a/lnwire/msat.go
+++ b/lnwire/msat.go
@@ -2,8 +2,10 @@ package lnwire
 
 import (
 	"fmt"
+	"io"
 
 	"github.com/btcsuite/btcd/btcutil"
+	"github.com/lightningnetwork/lnd/tlv"
 )
 
 const (
@@ -49,3 +51,40 @@ func (m MilliSatoshi) String() string {
 }
 
 // TODO(roasbeef): extend with arithmetic operations?
+
+// Record returns a TLV record that can be used to encode/decode a MilliSatoshi
+// to/from a TLV stream.
+func (m *MilliSatoshi) Record() tlv.Record {
+	return tlv.MakeDynamicRecord(
+		0, m, tlv.SizeBigSize(m), encodeMilliSatoshis,
+		decodeMilliSatoshis,
+	)
+}
+
+func encodeMilliSatoshis(w io.Writer, val interface{}, buf *[8]byte) error {
+	if v, ok := val.(*MilliSatoshi); ok {
+		bigSize := uint64(*v)
+
+		return tlv.EBigSize(w, &bigSize, buf)
+	}
+
+	return tlv.NewTypeForEncodingErr(val, "lnwire.MilliSatoshi")
+}
+
+func decodeMilliSatoshis(r io.Reader, val interface{}, buf *[8]byte,
+	l uint64) error {
+
+	if v, ok := val.(*MilliSatoshi); ok {
+		var bigSize uint64
+		err := tlv.DBigSize(r, &bigSize, buf, l)
+		if err != nil {
+			return err
+		}
+
+		*v = MilliSatoshi(bigSize)
+
+		return nil
+	}
+
+	return tlv.NewTypeForDecodingErr(val, "lnwire.MilliSatoshi", l, l)
+}
diff --git a/lnwire/onion_error.go b/lnwire/onion_error.go
index f8a8bf069..8d54a9895 100644
--- a/lnwire/onion_error.go
+++ b/lnwire/onion_error.go
@@ -601,7 +601,7 @@ func (f *FailInvalidOnionKey) Error() string {
 // unable to pull out a fully valid version, then we'll fall back to the
 // regular parsing mechanism which includes the length prefix an NO type byte.
 func parseChannelUpdateCompatibilityMode(reader io.Reader, length uint16,
-	chanUpdate *ChannelUpdate, pver uint32) error {
+	chanUpdate *ChannelUpdate1, pver uint32) error {
 
 	// Instantiate a LimitReader because there may be additional data
 	// present after the channel update. Without limiting the stream, the
@@ -648,11 +648,13 @@ type FailTemporaryChannelFailure struct {
 	// which caused the failure.
 	//
 	// NOTE: This field is optional.
-	Update *ChannelUpdate
+	Update *ChannelUpdate1
 }
 
 // NewTemporaryChannelFailure creates new instance of the FailTemporaryChannelFailure.
-func NewTemporaryChannelFailure(update *ChannelUpdate) *FailTemporaryChannelFailure {
+func NewTemporaryChannelFailure(
+	update *ChannelUpdate1) *FailTemporaryChannelFailure {
+
 	return &FailTemporaryChannelFailure{Update: update}
 }
 
@@ -686,7 +688,7 @@ func (f *FailTemporaryChannelFailure) Decode(r io.Reader, pver uint32) error {
 	}
 
 	if length != 0 {
-		f.Update = &ChannelUpdate{}
+		f.Update = &ChannelUpdate1{}
 
 		return parseChannelUpdateCompatibilityMode(
 			r, length, f.Update, pver,
@@ -721,12 +723,12 @@ type FailAmountBelowMinimum struct {
 
 	// Update is used to update information about state of the channel
 	// which caused the failure.
-	Update ChannelUpdate
+	Update ChannelUpdate1
 }
 
 // NewAmountBelowMinimum creates new instance of the FailAmountBelowMinimum.
 func NewAmountBelowMinimum(htlcMsat MilliSatoshi,
-	update ChannelUpdate) *FailAmountBelowMinimum {
+	update ChannelUpdate1) *FailAmountBelowMinimum {
 
 	return &FailAmountBelowMinimum{
 		HtlcMsat: htlcMsat,
@@ -762,7 +764,7 @@ func (f *FailAmountBelowMinimum) Decode(r io.Reader, pver uint32) error {
 		return err
 	}
 
-	f.Update = ChannelUpdate{}
+	f.Update = ChannelUpdate1{}
 
 	return parseChannelUpdateCompatibilityMode(
 		r, length, &f.Update, pver,
@@ -791,12 +793,12 @@ type FailFeeInsufficient struct {
 
 	// Update is used to update information about state of the channel
 	// which caused the failure.
-	Update ChannelUpdate
+	Update ChannelUpdate1
 }
 
 // NewFeeInsufficient creates new instance of the FailFeeInsufficient.
 func NewFeeInsufficient(htlcMsat MilliSatoshi,
-	update ChannelUpdate) *FailFeeInsufficient {
+	update ChannelUpdate1) *FailFeeInsufficient {
 	return &FailFeeInsufficient{
 		HtlcMsat: htlcMsat,
 		Update:   update,
@@ -831,7 +833,7 @@ func (f *FailFeeInsufficient) Decode(r io.Reader, pver uint32) error {
 		return err
 	}
 
-	f.Update = ChannelUpdate{}
+	f.Update = ChannelUpdate1{}
 
 	return parseChannelUpdateCompatibilityMode(
 		r, length, &f.Update, pver,
@@ -862,12 +864,12 @@ type FailIncorrectCltvExpiry struct {
 
 	// Update is used to update information about state of the channel
 	// which caused the failure.
-	Update ChannelUpdate
+	Update ChannelUpdate1
 }
 
 // NewIncorrectCltvExpiry creates new instance of the FailIncorrectCltvExpiry.
 func NewIncorrectCltvExpiry(cltvExpiry uint32,
-	update ChannelUpdate) *FailIncorrectCltvExpiry {
+	update ChannelUpdate1) *FailIncorrectCltvExpiry {
 
 	return &FailIncorrectCltvExpiry{
 		CltvExpiry: cltvExpiry,
@@ -900,7 +902,7 @@ func (f *FailIncorrectCltvExpiry) Decode(r io.Reader, pver uint32) error {
 		return err
 	}
 
-	f.Update = ChannelUpdate{}
+	f.Update = ChannelUpdate1{}
 
 	return parseChannelUpdateCompatibilityMode(
 		r, length, &f.Update, pver,
@@ -925,11 +927,11 @@ func (f *FailIncorrectCltvExpiry) Encode(w *bytes.Buffer, pver uint32) error {
 type FailExpiryTooSoon struct {
 	// Update is used to update information about state of the channel
 	// which caused the failure.
-	Update ChannelUpdate
+	Update ChannelUpdate1
 }
 
 // NewExpiryTooSoon creates new instance of the FailExpiryTooSoon.
-func NewExpiryTooSoon(update ChannelUpdate) *FailExpiryTooSoon {
+func NewExpiryTooSoon(update ChannelUpdate1) *FailExpiryTooSoon {
 	return &FailExpiryTooSoon{
 		Update: update,
 	}
@@ -958,7 +960,7 @@ func (f *FailExpiryTooSoon) Decode(r io.Reader, pver uint32) error {
 		return err
 	}
 
-	f.Update = ChannelUpdate{}
+	f.Update = ChannelUpdate1{}
 
 	return parseChannelUpdateCompatibilityMode(
 		r, length, &f.Update, pver,
@@ -984,11 +986,13 @@ type FailChannelDisabled struct {
 
 	// Update is used to update information about state of the channel
 	// which caused the failure.
-	Update ChannelUpdate
+	Update ChannelUpdate1
 }
 
 // NewChannelDisabled creates new instance of the FailChannelDisabled.
-func NewChannelDisabled(flags uint16, update ChannelUpdate) *FailChannelDisabled {
+func NewChannelDisabled(flags uint16,
+	update ChannelUpdate1) *FailChannelDisabled {
+
 	return &FailChannelDisabled{
 		Flags:  flags,
 		Update: update,
@@ -1023,7 +1027,7 @@ func (f *FailChannelDisabled) Decode(r io.Reader, pver uint32) error {
 		return err
 	}
 
-	f.Update = ChannelUpdate{}
+	f.Update = ChannelUpdate1{}
 
 	return parseChannelUpdateCompatibilityMode(
 		r, length, &f.Update, pver,
@@ -1516,7 +1520,7 @@ func makeEmptyOnionError(code FailCode) (FailureMessage, error) {
 // writeOnionErrorChanUpdate writes out a ChannelUpdate using the onion error
 // format. The format is that we first write out the true serialized length of
 // the channel update, followed by the serialized channel update itself.
-func writeOnionErrorChanUpdate(w *bytes.Buffer, chanUpdate *ChannelUpdate,
+func writeOnionErrorChanUpdate(w *bytes.Buffer, chanUpdate *ChannelUpdate1,
 	pver uint32) error {
 
 	// First, we encode the channel update in a temporary buffer in order
diff --git a/netann/chan_status_manager.go b/netann/chan_status_manager.go
index f1e6aa578..c4db4009d 100644
--- a/netann/chan_status_manager.go
+++ b/netann/chan_status_manager.go
@@ -60,7 +60,7 @@ type ChanStatusConfig struct {
 	// ApplyChannelUpdate processes new ChannelUpdates signed by our node by
 	// updating our local routing table and broadcasting the update to our
 	// peers.
-	ApplyChannelUpdate func(*lnwire.ChannelUpdate, *wire.OutPoint,
+	ApplyChannelUpdate func(*lnwire.ChannelUpdate1, *wire.OutPoint,
 		bool) error
 
 	// DB stores the set of channels that are to be monitored.
@@ -650,7 +650,7 @@ func (m *ChanStatusManager) signAndSendNextUpdate(outpoint wire.OutPoint,
 // in case our ChannelEdgePolicy is not found in the database. Also returns if
 // the channel is private by checking AuthProof for nil.
 func (m *ChanStatusManager) fetchLastChanUpdateByOutPoint(op wire.OutPoint) (
-	*lnwire.ChannelUpdate, bool, error) {
+	*lnwire.ChannelUpdate1, bool, error) {
 
 	// Get the edge info and policies for this channel from the graph.
 	info, edge1, edge2, err := m.cfg.Graph.FetchChannelEdgesByOutpoint(&op)
diff --git a/netann/channel_announcement.go b/netann/channel_announcement.go
index 8fc040f6f..9644a523f 100644
--- a/netann/channel_announcement.go
+++ b/netann/channel_announcement.go
@@ -2,9 +2,30 @@ package netann
 
 import (
 	"bytes"
+	"errors"
+	"fmt"
 
+	"github.com/btcsuite/btcd/btcec/v2"
+	"github.com/btcsuite/btcd/btcec/v2/schnorr"
+	"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
+	"github.com/btcsuite/btcd/chaincfg/chainhash"
 	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/lnwire"
+	"github.com/lightningnetwork/lnd/tlv"
+)
+
+const (
+	// chanAnn2MsgName is a string representing the name of the
+	// ChannelAnnouncement2 message. This string will be used during the
+	// construction of the tagged hash message to be signed when producing
+	// the signature for the ChannelAnnouncement2 message.
+	chanAnn2MsgName = "channel_announcement_2"
+
+	// chanAnn2SigFieldName is the name of the signature field of the
+	// ChannelAnnouncement2 message. This string will be used during the
+	// construction of the tagged hash message to be signed when producing
+	// the signature for the ChannelAnnouncement2 message.
+	chanAnn2SigFieldName = "signature"
 )
 
 // CreateChanAnnouncement is a helper function which creates all channel
@@ -14,14 +35,14 @@ import (
 // peer's initial routing table upon connect.
 func CreateChanAnnouncement(chanProof *models.ChannelAuthProof,
 	chanInfo *models.ChannelEdgeInfo,
-	e1, e2 *models.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement,
-	*lnwire.ChannelUpdate, *lnwire.ChannelUpdate, error) {
+	e1, e2 *models.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement1,
+	*lnwire.ChannelUpdate1, *lnwire.ChannelUpdate1, error) {
 
 	// First, using the parameters of the channel, along with the channel
 	// authentication chanProof, we'll create re-create the original
 	// authenticated channel announcement.
 	chanID := lnwire.NewShortChanIDFromInt(chanInfo.ChannelID)
-	chanAnn := &lnwire.ChannelAnnouncement{
+	chanAnn := &lnwire.ChannelAnnouncement1{
 		ShortChannelID:  chanID,
 		NodeID1:         chanInfo.NodeKey1Bytes,
 		NodeID2:         chanInfo.NodeKey2Bytes,
@@ -68,7 +89,7 @@ func CreateChanAnnouncement(chanProof *models.ChannelAuthProof,
 	// Since it's up to a node's policy as to whether they advertise the
 	// edge in a direction, we don't create an advertisement if the edge is
 	// nil.
-	var edge1Ann, edge2Ann *lnwire.ChannelUpdate
+	var edge1Ann, edge2Ann *lnwire.ChannelUpdate1
 	if e1 != nil {
 		edge1Ann, err = ChannelUpdateFromEdge(chanInfo, e1)
 		if err != nil {
@@ -84,3 +105,187 @@ func CreateChanAnnouncement(chanProof *models.ChannelAuthProof,
 
 	return chanAnn, edge1Ann, edge2Ann, nil
 }
+
+// FetchPkScript defines a function that can be used to fetch the output script
+// for the transaction with the given SCID.
+type FetchPkScript func(*lnwire.ShortChannelID) ([]byte, error)
+
+// ValidateChannelAnn validates the channel announcement.
+func ValidateChannelAnn(a lnwire.ChannelAnnouncement,
+	fetchPkScript FetchPkScript) error {
+
+	switch ann := a.(type) {
+	case *lnwire.ChannelAnnouncement1:
+		return validateChannelAnn1(ann)
+	case *lnwire.ChannelAnnouncement2:
+		return validateChannelAnn2(ann, fetchPkScript)
+	default:
+		return fmt.Errorf("unhandled implementation of "+
+			"lnwire.ChannelAnnouncement: %T", a)
+	}
+}
+
+// validateChannelAnn1 validates the channel announcement message and checks
+// that node signatures covers the announcement message, and that the bitcoin
+// signatures covers the node keys.
+func validateChannelAnn1(a *lnwire.ChannelAnnouncement1) error {
+	// First, we'll compute the digest (h) which is to be signed by each of
+	// the keys included within the node announcement message. This hash
+	// digest includes all the keys, so the (up to 4 signatures) will
+	// attest to the validity of each of the keys.
+	data, err := a.DataToSign()
+	if err != nil {
+		return err
+	}
+	dataHash := chainhash.DoubleHashB(data)
+
+	// First we'll verify that the passed bitcoin key signature is indeed a
+	// signature over the computed hash digest.
+	bitcoinSig1, err := a.BitcoinSig1.ToSignature()
+	if err != nil {
+		return err
+	}
+	bitcoinKey1, err := btcec.ParsePubKey(a.BitcoinKey1[:])
+	if err != nil {
+		return err
+	}
+	if !bitcoinSig1.Verify(dataHash, bitcoinKey1) {
+		return errors.New("can't verify first bitcoin signature")
+	}
+
+	// If that checks out, then we'll verify that the second bitcoin
+	// signature is a valid signature of the bitcoin public key over hash
+	// digest as well.
+	bitcoinSig2, err := a.BitcoinSig2.ToSignature()
+	if err != nil {
+		return err
+	}
+	bitcoinKey2, err := btcec.ParsePubKey(a.BitcoinKey2[:])
+	if err != nil {
+		return err
+	}
+	if !bitcoinSig2.Verify(dataHash, bitcoinKey2) {
+		return errors.New("can't verify second bitcoin signature")
+	}
+
+	// Both node signatures attached should indeed be a valid signature
+	// over the selected digest of the channel announcement signature.
+	nodeSig1, err := a.NodeSig1.ToSignature()
+	if err != nil {
+		return err
+	}
+	nodeKey1, err := btcec.ParsePubKey(a.NodeID1[:])
+	if err != nil {
+		return err
+	}
+	if !nodeSig1.Verify(dataHash, nodeKey1) {
+		return errors.New("can't verify data in first node signature")
+	}
+
+	nodeSig2, err := a.NodeSig2.ToSignature()
+	if err != nil {
+		return err
+	}
+	nodeKey2, err := btcec.ParsePubKey(a.NodeID2[:])
+	if err != nil {
+		return err
+	}
+	if !nodeSig2.Verify(dataHash, nodeKey2) {
+		return errors.New("can't verify data in second node signature")
+	}
+
+	return nil
+}
+
+// validateChannelAnn2 validates the channel announcement message and checks
+// that message signature covers the announcement message.
+func validateChannelAnn2(a *lnwire.ChannelAnnouncement2,
+	fetchPkScript FetchPkScript) error {
+
+	dataHash, err := ChanAnn2DigestToSign(a)
+	if err != nil {
+		return err
+	}
+
+	sig, err := a.Signature.ToSignature()
+	if err != nil {
+		return err
+	}
+
+	nodeKey1, err := btcec.ParsePubKey(a.NodeID1.Val[:])
+	if err != nil {
+		return err
+	}
+
+	nodeKey2, err := btcec.ParsePubKey(a.NodeID2.Val[:])
+	if err != nil {
+		return err
+	}
+
+	keys := []*btcec.PublicKey{
+		nodeKey1, nodeKey2,
+	}
+
+	// If the bitcoin keys are provided in the announcement, then it is
+	// assumed that the signature of the announcement is a 4-of-4 MuSig2
+	// over the bitcoin keys and node ID keys.
+	if a.BitcoinKey1.IsSome() && a.BitcoinKey2.IsSome() {
+		var (
+			btcKey1 tlv.RecordT[tlv.TlvType12, [33]byte]
+			btcKey2 tlv.RecordT[tlv.TlvType14, [33]byte]
+		)
+
+		btcKey1 = a.BitcoinKey1.UnwrapOr(btcKey1)
+		btcKey2 = a.BitcoinKey2.UnwrapOr(btcKey2)
+
+		bitcoinKey1, err := btcec.ParsePubKey(btcKey1.Val[:])
+		if err != nil {
+			return err
+		}
+
+		bitcoinKey2, err := btcec.ParsePubKey(btcKey2.Val[:])
+		if err != nil {
+			return err
+		}
+
+		keys = append(keys, bitcoinKey1, bitcoinKey2)
+	} else {
+		// If bitcoin keys are not provided, then we need to get the
+		// on-chain output key since this will be the 3rd key in the
+		// 3-of-3 MuSig2 signature.
+		pkScript, err := fetchPkScript(&a.ShortChannelID.Val)
+		if err != nil {
+			return err
+		}
+
+		outputKey, err := schnorr.ParsePubKey(pkScript[2:])
+		if err != nil {
+			return err
+		}
+
+		keys = append(keys, outputKey)
+	}
+
+	aggKey, _, _, err := musig2.AggregateKeys(keys, true)
+	if err != nil {
+		return err
+	}
+
+	if !sig.Verify(dataHash.CloneBytes(), aggKey.FinalKey) {
+		return fmt.Errorf("invalid sig")
+	}
+
+	return nil
+}
+
+// ChanAnn2DigestToSign computes the digest of the message to be signed.
+func ChanAnn2DigestToSign(a *lnwire.ChannelAnnouncement2) (*chainhash.Hash,
+	error) {
+
+	data, err := a.DataToSign()
+	if err != nil {
+		return nil, err
+	}
+
+	return MsgHash(chanAnn2MsgName, chanAnn2SigFieldName, data), nil
+}
diff --git a/netann/channel_update.go b/netann/channel_update.go
index b93deb1d0..af91abdd2 100644
--- a/netann/channel_update.go
+++ b/netann/channel_update.go
@@ -6,10 +6,28 @@ import (
 	"time"
 
 	"github.com/btcsuite/btcd/btcec/v2"
+	"github.com/btcsuite/btcd/btcutil"
+	"github.com/btcsuite/btcd/chaincfg/chainhash"
+	"github.com/davecgh/go-spew/spew"
 	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/keychain"
 	"github.com/lightningnetwork/lnd/lnwallet"
 	"github.com/lightningnetwork/lnd/lnwire"
+	"github.com/pkg/errors"
+)
+
+const (
+	// chanUpdate2MsgName is a string representing the name of the
+	// ChannelUpdate2 message. This string will be used during the
+	// construction of the tagged hash message to be signed when producing
+	// the signature for the ChannelUpdate2 message.
+	chanUpdate2MsgName = "channel_update_2"
+
+	// chanUpdate2SigField is the name of the signature field of the
+	// ChannelUpdate2 message. This string will be used during the
+	// construction of the tagged hash message to be signed when producing
+	// the signature for the ChannelUpdate2 message.
+	chanUpdate2SigField = "signature"
 )
 
 // ErrUnableToExtractChanUpdate is returned when a channel update cannot be
@@ -18,12 +36,12 @@ var ErrUnableToExtractChanUpdate = fmt.Errorf("unable to extract ChannelUpdate")
 
 // ChannelUpdateModifier is a closure that makes in-place modifications to an
 // lnwire.ChannelUpdate.
-type ChannelUpdateModifier func(*lnwire.ChannelUpdate)
+type ChannelUpdateModifier func(*lnwire.ChannelUpdate1)
 
 // ChanUpdSetDisable is a functional option that sets the disabled channel flag
 // if disabled is true, and clears the bit otherwise.
 func ChanUpdSetDisable(disabled bool) ChannelUpdateModifier {
-	return func(update *lnwire.ChannelUpdate) {
+	return func(update *lnwire.ChannelUpdate1) {
 		if disabled {
 			// Set the bit responsible for marking a channel as
 			// disabled.
@@ -39,7 +57,7 @@ func ChanUpdSetDisable(disabled bool) ChannelUpdateModifier {
 // ChanUpdSetTimestamp is a functional option that sets the timestamp of the
 // update to the current time, or increments it if the timestamp is already in
 // the future.
-func ChanUpdSetTimestamp(update *lnwire.ChannelUpdate) {
+func ChanUpdSetTimestamp(update *lnwire.ChannelUpdate1) {
 	newTimestamp := uint32(time.Now().Unix())
 	if newTimestamp <= update.Timestamp {
 		// Increment the prior value to ensure the timestamp
@@ -57,7 +75,7 @@ func ChanUpdSetTimestamp(update *lnwire.ChannelUpdate) {
 //
 // NOTE: This method modifies the given update.
 func SignChannelUpdate(signer lnwallet.MessageSigner, keyLoc keychain.KeyLocator,
-	update *lnwire.ChannelUpdate, mods ...ChannelUpdateModifier) error {
+	update *lnwire.ChannelUpdate1, mods ...ChannelUpdateModifier) error {
 
 	// Apply the requested changes to the channel update.
 	for _, modifier := range mods {
@@ -86,7 +104,7 @@ func SignChannelUpdate(signer lnwallet.MessageSigner, keyLoc keychain.KeyLocator
 func ExtractChannelUpdate(ownerPubKey []byte,
 	info *models.ChannelEdgeInfo,
 	policies ...*models.ChannelEdgePolicy) (
-	*lnwire.ChannelUpdate, error) {
+	*lnwire.ChannelUpdate1, error) {
 
 	// Helper function to extract the owner of the given policy.
 	owner := func(edge *models.ChannelEdgePolicy) []byte {
@@ -118,9 +136,9 @@ func ExtractChannelUpdate(ownerPubKey []byte,
 // UnsignedChannelUpdateFromEdge reconstructs an unsigned ChannelUpdate from the
 // given edge info and policy.
 func UnsignedChannelUpdateFromEdge(info *models.ChannelEdgeInfo,
-	policy *models.ChannelEdgePolicy) *lnwire.ChannelUpdate {
+	policy *models.ChannelEdgePolicy) *lnwire.ChannelUpdate1 {
 
-	return &lnwire.ChannelUpdate{
+	return &lnwire.ChannelUpdate1{
 		ChainHash:       info.ChainHash,
 		ShortChannelID:  lnwire.NewShortChanIDFromInt(policy.ChannelID),
 		Timestamp:       uint32(policy.LastUpdate.Unix()),
@@ -138,7 +156,7 @@ func UnsignedChannelUpdateFromEdge(info *models.ChannelEdgeInfo,
 // ChannelUpdateFromEdge reconstructs a signed ChannelUpdate from the given edge
 // info and policy.
 func ChannelUpdateFromEdge(info *models.ChannelEdgeInfo,
-	policy *models.ChannelEdgePolicy) (*lnwire.ChannelUpdate, error) {
+	policy *models.ChannelEdgePolicy) (*lnwire.ChannelUpdate1, error) {
 
 	update := UnsignedChannelUpdateFromEdge(info, policy)
 
@@ -152,3 +170,165 @@ func ChannelUpdateFromEdge(info *models.ChannelEdgeInfo,
 
 	return update, nil
 }
+
+// ValidateChannelUpdateAnn validates the channel update announcement by
+// checking (1) that the included signature covers the announcement and has been
+// signed by the node's private key, and (2) that the announcement's message
+// flags and optional fields are sane.
+func ValidateChannelUpdateAnn(pubKey *btcec.PublicKey, capacity btcutil.Amount,
+	a lnwire.ChannelUpdate) error {
+
+	if err := ValidateChannelUpdateFields(capacity, a); err != nil {
+		return err
+	}
+
+	return VerifyChannelUpdateSignature(a, pubKey)
+}
+
+// VerifyChannelUpdateSignature verifies that the channel update message was
+// signed by the party with the given node public key.
+func VerifyChannelUpdateSignature(msg lnwire.ChannelUpdate,
+	pubKey *btcec.PublicKey) error {
+
+	switch u := msg.(type) {
+	case *lnwire.ChannelUpdate1:
+		return verifyChannelUpdate1Signature(u, pubKey)
+	case *lnwire.ChannelUpdate2:
+		return verifyChannelUpdate2Signature(u, pubKey)
+	default:
+		return fmt.Errorf("unhandled implementation of "+
+			"lnwire.ChannelUpdate: %T", msg)
+	}
+}
+
+// verifyChannelUpdateSignature1 verifies that the channel update message was
+// signed by the party with the given node public key.
+func verifyChannelUpdate1Signature(msg *lnwire.ChannelUpdate1,
+	pubKey *btcec.PublicKey) error {
+
+	data, err := msg.DataToSign()
+	if err != nil {
+		return fmt.Errorf("unable to reconstruct message data: %w", err)
+	}
+	dataHash := chainhash.DoubleHashB(data)
+
+	nodeSig, err := msg.Signature.ToSignature()
+	if err != nil {
+		return err
+	}
+
+	if !nodeSig.Verify(dataHash, pubKey) {
+		return fmt.Errorf("invalid signature for channel update %v",
+			spew.Sdump(msg))
+	}
+
+	return nil
+}
+
+// verifyChannelUpdateSignature2 verifies that the channel update message was
+// signed by the party with the given node public key.
+func verifyChannelUpdate2Signature(c *lnwire.ChannelUpdate2,
+	pubKey *btcec.PublicKey) error {
+
+	digest, err := chanUpdate2DigestToSign(c)
+	if err != nil {
+		return fmt.Errorf("unable to reconstruct message data: %w", err)
+	}
+
+	nodeSig, err := c.Signature.ToSignature()
+	if err != nil {
+		return err
+	}
+
+	if !nodeSig.Verify(digest, pubKey) {
+		return fmt.Errorf("invalid signature for channel update %v",
+			spew.Sdump(c))
+	}
+
+	return nil
+}
+
+// ValidateChannelUpdateFields validates a channel update's message flags and
+// corresponding update fields.
+func ValidateChannelUpdateFields(capacity btcutil.Amount,
+	msg lnwire.ChannelUpdate) error {
+
+	switch u := msg.(type) {
+	case *lnwire.ChannelUpdate1:
+		return validateChannelUpdate1Fields(capacity, u)
+	case *lnwire.ChannelUpdate2:
+		return validateChannelUpdate2Fields(capacity, u)
+	default:
+		return fmt.Errorf("unhandled implementation of "+
+			"lnwire.ChannelUpdate: %T", msg)
+	}
+}
+
+// validateChannelUpdate1Fields validates a channel update's message flags and
+// corresponding update fields.
+func validateChannelUpdate1Fields(capacity btcutil.Amount,
+	msg *lnwire.ChannelUpdate1) error {
+
+	// The maxHTLC flag is mandatory.
+	if !msg.MessageFlags.HasMaxHtlc() {
+		return errors.Errorf("max htlc flag not set for channel "+
+			"update %v", spew.Sdump(msg))
+	}
+
+	maxHtlc := msg.HtlcMaximumMsat
+	if maxHtlc == 0 || maxHtlc < msg.HtlcMinimumMsat {
+		return errors.Errorf("invalid max htlc for channel "+
+			"update %v", spew.Sdump(msg))
+	}
+
+	// For light clients, the capacity will not be set so we'll skip
+	// checking whether the MaxHTLC value respects the channel's
+	// capacity.
+	capacityMsat := lnwire.NewMSatFromSatoshis(capacity)
+	if capacityMsat != 0 && maxHtlc > capacityMsat {
+		return errors.Errorf("max_htlc (%v) for channel update "+
+			"greater than capacity (%v)", maxHtlc, capacityMsat)
+	}
+
+	return nil
+}
+
+// validateChannelUpdate2Fields validates a channel update's message flags and
+// corresponding update fields.
+func validateChannelUpdate2Fields(capacity btcutil.Amount,
+	c *lnwire.ChannelUpdate2) error {
+
+	maxHtlc := c.HTLCMaximumMsat.Val
+	if maxHtlc == 0 || maxHtlc < c.HTLCMinimumMsat.Val {
+		return fmt.Errorf("invalid max htlc for channel update %v",
+			spew.Sdump(c))
+	}
+
+	// Checking whether the MaxHTLC value respects the channel's capacity.
+	capacityMsat := lnwire.NewMSatFromSatoshis(capacity)
+	if maxHtlc > capacityMsat {
+		return fmt.Errorf("max_htlc (%v) for channel update greater "+
+			"than capacity (%v)", maxHtlc, capacityMsat)
+	}
+
+	return nil
+}
+
+// ChanUpdate2DigestTag returns the tag to be used when signing the digest of
+// a channel_update_2 message.
+func ChanUpdate2DigestTag() []byte {
+	return MsgTag(chanUpdate2MsgName, chanUpdate2SigField)
+}
+
+// chanUpdate2DigestToSign computes the digest of the ChannelUpdate2 message to
+// be signed.
+func chanUpdate2DigestToSign(c *lnwire.ChannelUpdate2) ([]byte, error) {
+	data, err := c.DataToSign()
+	if err != nil {
+		return nil, err
+	}
+
+	hash := MsgHash(chanUpdate2MsgName, chanUpdate2SigField, data)
+
+	return hash[:], nil
+}
diff --git a/netann/msg_hash.go b/netann/msg_hash.go
new file mode 100644
index 000000000..3c4806f9b
--- /dev/null
+++ b/netann/msg_hash.go
@@ -0,0 +1,30 @@
+package netann
+
+import (
+	"github.com/btcsuite/btcd/chaincfg/chainhash"
+)
+
+// MsgHashTag will prefix the message name and the field name in order to
+// construct the message tag.
+const MsgHashTag = "lightning"
+
+// MsgTag computes the full tag that will be used to prefix a message before
+// calculating the tagged hash. The tag is constructed as follows:
+//
+//	tag = "lightning"||"msg_name"||"field_name"
+func MsgTag(msgName, fieldName string) []byte {
+	tag := []byte(MsgHashTag)
+	tag = append(tag, []byte(msgName)...)
+
+	return append(tag, []byte(fieldName)...)
+}
+
+// MsgHash computes the tagged hash of the given message as follows:
+//
+//	tag = "lightning"||"msg_name"||"field_name"
+//	hash = sha256(sha246(tag) || sha256(tag) || msg)
+func MsgHash(msgName, fieldName string, msg []byte) *chainhash.Hash {
+	tag := MsgTag(msgName, fieldName)
+
+	return chainhash.TaggedHash(tag, msg)
+}
diff --git a/netann/sign.go b/netann/sign.go
index 429533136..0c7612eac 100644
--- a/netann/sign.go
+++ b/netann/sign.go
@@ -20,9 +20,9 @@ func SignAnnouncement(signer lnwallet.MessageSigner, keyLoc keychain.KeyLocator,
 	)
 
 	switch m := msg.(type) {
-	case *lnwire.ChannelAnnouncement:
+	case *lnwire.ChannelAnnouncement1:
 		data, err = m.DataToSign()
-	case *lnwire.ChannelUpdate:
+	case *lnwire.ChannelUpdate1:
 		data, err = m.DataToSign()
 	case *lnwire.NodeAnnouncement:
 		data, err = m.DataToSign()
diff --git a/peer/brontide.go b/peer/brontide.go
index 3223e7f4b..5669193a2 100644
--- a/peer/brontide.go
+++ b/peer/brontide.go
@@ -301,7 +301,7 @@ type Config struct {
 
 	// FetchLastChanUpdate fetches our latest channel update for a target
 	// channel.
-	FetchLastChanUpdate func(lnwire.ShortChannelID) (*lnwire.ChannelUpdate,
+	FetchLastChanUpdate func(lnwire.ShortChannelID) (*lnwire.ChannelUpdate1,
 		error)
 
 	// FundingManager is an implementation of the funding.Controller interface.
@@ -1963,10 +1963,10 @@ out:
 					nextMsg.MsgType())
 			}
 
-		case *lnwire.ChannelUpdate,
-			*lnwire.ChannelAnnouncement,
+		case *lnwire.ChannelUpdate1,
+			*lnwire.ChannelAnnouncement1,
 			*lnwire.NodeAnnouncement,
-			*lnwire.AnnounceSignatures,
+			*lnwire.AnnounceSignatures1,
 			*lnwire.GossipTimestampRange,
 			*lnwire.QueryShortChanIDs,
 			*lnwire.QueryChannelRange,
@@ -2225,15 +2225,15 @@ func messageSummary(msg lnwire.Message) string {
 	case *lnwire.Error:
 		return fmt.Sprintf("%v", msg.Error())
 
-	case *lnwire.AnnounceSignatures:
+	case *lnwire.AnnounceSignatures1:
 		return fmt.Sprintf("chan_id=%v, short_chan_id=%v", msg.ChannelID,
 			msg.ShortChannelID.ToUint64())
 
-	case *lnwire.ChannelAnnouncement:
+	case *lnwire.ChannelAnnouncement1:
 		return fmt.Sprintf("chain_hash=%v, short_chan_id=%v",
 			msg.ChainHash, msg.ShortChannelID.ToUint64())
 
-	case *lnwire.ChannelUpdate:
+	case *lnwire.ChannelUpdate1:
 		return fmt.Sprintf("chain_hash=%v, short_chan_id=%v, "+
 			"mflags=%v, cflags=%v, update_time=%v", msg.ChainHash,
 			msg.ShortChannelID.ToUint64(), msg.MessageFlags,
diff --git a/routing/payment_session.go b/routing/payment_session.go
index 5e6a468fe..b47f6ee0c 100644
--- a/routing/payment_session.go
+++ b/routing/payment_session.go
@@ -8,9 +8,9 @@ import (
 	"github.com/lightningnetwork/lnd/build"
 	"github.com/lightningnetwork/lnd/channeldb"
 	"github.com/lightningnetwork/lnd/channeldb/models"
-	"github.com/lightningnetwork/lnd/graph"
 	"github.com/lightningnetwork/lnd/lnutils"
 	"github.com/lightningnetwork/lnd/lnwire"
+	"github.com/lightningnetwork/lnd/netann"
 	"github.com/lightningnetwork/lnd/routing/route"
 )
 
@@ -147,8 +147,8 @@ type PaymentSession interface {
 	// (private channels) and applies the update from the message. Returns
 	// a boolean to indicate whether the update has been applied without
 	// error.
-	UpdateAdditionalEdge(msg *lnwire.ChannelUpdate, pubKey *btcec.PublicKey,
-		policy *models.CachedEdgePolicy) bool
+	UpdateAdditionalEdge(msg *lnwire.ChannelUpdate1,
+		pubKey *btcec.PublicKey, policy *models.CachedEdgePolicy) bool
 
 	// GetAdditionalEdgePolicy uses the public key and channel ID to query
 	// the ephemeral channel edge policy for additional edges. Returns a nil
@@ -436,11 +436,11 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
 // validates the message signature and checks it's up to date, then applies the
 // updates to the supplied policy. It returns a boolean to indicate whether
 // there's an error when applying the updates.
-func (p *paymentSession) UpdateAdditionalEdge(msg *lnwire.ChannelUpdate,
+func (p *paymentSession) UpdateAdditionalEdge(msg *lnwire.ChannelUpdate1,
 	pubKey *btcec.PublicKey, policy *models.CachedEdgePolicy) bool {
 
 	// Validate the message signature.
-	if err := graph.VerifyChannelUpdateSignature(msg, pubKey); err != nil {
+	if err := netann.VerifyChannelUpdateSignature(msg, pubKey); err != nil {
 		log.Errorf(
 			"Unable to validate channel update signature: %v", err,
 		)
diff --git a/routing/router.go b/routing/router.go
index db3fbc857..40b7ecb49 100644
--- a/routing/router.go
+++ b/routing/router.go
@@ -288,7 +288,7 @@ type Config struct {
 
 	// ApplyChannelUpdate can be called to apply a new channel update to the
 	// graph that we received from a payment failure.
-	ApplyChannelUpdate func(msg *lnwire.ChannelUpdate) bool
+	ApplyChannelUpdate func(msg *lnwire.ChannelUpdate1) bool
 
 	// ClosedSCIDs is used by the router to fetch closed channels.
 	//
@@ -1329,9 +1329,9 @@ func (r *ChannelRouter) sendPayment(ctx context.Context,
 
 // extractChannelUpdate examines the error and extracts the channel update.
 func (r *ChannelRouter) extractChannelUpdate(
-	failure lnwire.FailureMessage) *lnwire.ChannelUpdate {
+	failure lnwire.FailureMessage) *lnwire.ChannelUpdate1 {
 
-	var update *lnwire.ChannelUpdate
+	var update *lnwire.ChannelUpdate1
 	switch onionErr := failure.(type) {
 	case *lnwire.FailExpiryTooSoon:
 		update = &onionErr.Update
diff --git a/scripts/keys/ziggie1984.asc b/scripts/keys/ziggie.asc
similarity index 100%
rename from scripts/keys/ziggie1984.asc
rename to scripts/keys/ziggie.asc
diff --git a/server.go b/server.go
index 0f50794e7..695375400 100644
--- a/server.go
+++ b/server.go
@@ -1042,6 +1042,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
 
 	s.authGossiper = discovery.New(discovery.Config{
 		Graph:                 s.graphBuilder,
+		ChainIO:               s.cc.ChainIO,
 		Notifier:              s.cc.ChainNotifier,
 		ChainHash:             *s.cfg.ActiveNetParams.GenesisHash,
 		Broadcast:             s.BroadcastMessage,
@@ -1753,7 +1754,7 @@ func (s *server) UpdateRoutingConfig(cfg *routing.MissionControlConfig) {
 // signAliasUpdate takes a ChannelUpdate and returns the signature. This is
 // used for option_scid_alias channels where the ChannelUpdate to be sent back
 // may differ from what is on disk.
-func (s *server) signAliasUpdate(u *lnwire.ChannelUpdate) (*ecdsa.Signature,
+func (s *server) signAliasUpdate(u *lnwire.ChannelUpdate1) (*ecdsa.Signature,
 	error) {
 
 	data, err := u.DataToSign()
@@ -4826,10 +4827,10 @@ func (s *server) fetchNodeAdvertisedAddrs(pub *btcec.PublicKey) ([]net.Addr, err
 // fetchLastChanUpdate returns a function which is able to retrieve our latest
 // channel update for a target channel.
 func (s *server) fetchLastChanUpdate() func(lnwire.ShortChannelID) (
-	*lnwire.ChannelUpdate, error) {
+	*lnwire.ChannelUpdate1, error) {
 
 	ourPubKey := s.identityECDH.PubKey().SerializeCompressed()
-	return func(cid lnwire.ShortChannelID) (*lnwire.ChannelUpdate, error) {
+	return func(cid lnwire.ShortChannelID) (*lnwire.ChannelUpdate1, error) {
 		info, edge1, edge2, err := s.graphBuilder.GetChannelByID(cid)
 		if err != nil {
 			return nil, err
@@ -4844,7 +4845,7 @@ func (s *server) fetchLastChanUpdate() func(lnwire.ShortChannelID) (
 // applyChannelUpdate applies the channel update to the different sub-systems of
 // the server. The useAlias boolean denotes whether or not to send an alias in
 // place of the real SCID.
-func (s *server) applyChannelUpdate(update *lnwire.ChannelUpdate,
+func (s *server) applyChannelUpdate(update *lnwire.ChannelUpdate1,
 	op *wire.OutPoint, useAlias bool) error {
 
 	var (


Test patch:
diff --git a/channeldb/waitingproof_test.go b/channeldb/waitingproof_test.go
index 1a00c829e..d7113d9e7 100644
--- a/channeldb/waitingproof_test.go
+++ b/channeldb/waitingproof_test.go
@@ -18,7 +18,7 @@ func TestWaitingProofStore(t *testing.T) {
 	db, err := MakeTestDB(t)
 	require.NoError(t, err, "failed to make test database")
 
-	proof1 := NewWaitingProof(true, &lnwire.AnnounceSignatures{
+	proof1 := NewWaitingProof(true, &lnwire.AnnounceSignatures1{
 		NodeSignature:    wireSig,
 		BitcoinSignature: wireSig,
 		ExtraOpaqueData:  make([]byte, 0),
diff --git a/discovery/gossiper_test.go b/discovery/gossiper_test.go
index c7cb149cf..db632cdaf 100644
--- a/discovery/gossiper_test.go
+++ b/discovery/gossiper_test.go
@@ -476,13 +476,13 @@ type annBatch struct {
 	nodeAnn1 *lnwire.NodeAnnouncement
 	nodeAnn2 *lnwire.NodeAnnouncement
 
-	chanAnn *lnwire.ChannelAnnouncement
+	chanAnn *lnwire.ChannelAnnouncement1
 
-	chanUpdAnn1 *lnwire.ChannelUpdate
-	chanUpdAnn2 *lnwire.ChannelUpdate
+	chanUpdAnn1 *lnwire.ChannelUpdate1
+	chanUpdAnn2 *lnwire.ChannelUpdate1
 
-	localProofAnn  *lnwire.AnnounceSignatures
-	remoteProofAnn *lnwire.AnnounceSignatures
+	localProofAnn  *lnwire.AnnounceSignatures1
+	remoteProofAnn *lnwire.AnnounceSignatures1
 }
 
 func createLocalAnnouncements(blockHeight uint32) (*annBatch, error) {
@@ -513,7 +513,7 @@ func createAnnouncements(blockHeight uint32, key1, key2 *btcec.PrivateKey) (*ann
 		return nil, err
 	}
 
-	batch.remoteProofAnn = &lnwire.AnnounceSignatures{
+	batch.remoteProofAnn = &lnwire.AnnounceSignatures1{
 		ShortChannelID: lnwire.ShortChannelID{
 			BlockHeight: blockHeight,
 		},
@@ -521,7 +521,7 @@ func createAnnouncements(blockHeight uint32, key1, key2 *btcec.PrivateKey) (*ann
 		BitcoinSignature: batch.chanAnn.BitcoinSig2,
 	}
 
-	batch.localProofAnn = &lnwire.AnnounceSignatures{
+	batch.localProofAnn = &lnwire.AnnounceSignatures1{
 		ShortChannelID: lnwire.ShortChannelID{
 			BlockHeight: blockHeight,
 		},
@@ -585,12 +585,12 @@ func createNodeAnnouncement(priv *btcec.PrivateKey,
 func createUpdateAnnouncement(blockHeight uint32,
 	flags lnwire.ChanUpdateChanFlags,
 	nodeKey *btcec.PrivateKey, timestamp uint32,
-	extraBytes ...[]byte) (*lnwire.ChannelUpdate, error) {
+	extraBytes ...[]byte) (*lnwire.ChannelUpdate1, error) {
 
 	var err error
 
 	htlcMinMsat := lnwire.MilliSatoshi(prand.Int63())
-	a := &lnwire.ChannelUpdate{
+	a := &lnwire.ChannelUpdate1{
 		ShortChannelID: lnwire.ShortChannelID{
 			BlockHeight: blockHeight,
 		},
@@ -618,7 +618,7 @@ func createUpdateAnnouncement(blockHeight uint32,
 	return a, nil
 }
 
-func signUpdate(nodeKey *btcec.PrivateKey, a *lnwire.ChannelUpdate) error {
+func signUpdate(nodeKey *btcec.PrivateKey, a *lnwire.ChannelUpdate1) error {
 	signer := mock.SingleSigner{Privkey: nodeKey}
 	sig, err := netann.SignAnnouncement(&signer, testKeyLoc, a)
 	if err != nil {
@@ -635,9 +635,9 @@ func signUpdate(nodeKey *btcec.PrivateKey, a *lnwire.ChannelUpdate) error {
 
 func createAnnouncementWithoutProof(blockHeight uint32,
 	key1, key2 *btcec.PublicKey,
-	extraBytes ...[]byte) *lnwire.ChannelAnnouncement {
+	extraBytes ...[]byte) *lnwire.ChannelAnnouncement1 {
 
-	a := &lnwire.ChannelAnnouncement{
+	a := &lnwire.ChannelAnnouncement1{
 		ShortChannelID: lnwire.ShortChannelID{
 			BlockHeight: blockHeight,
 			TxIndex:     0,
@@ -657,13 +657,13 @@ func createAnnouncementWithoutProof(blockHeight uint32,
 }
 
 func createRemoteChannelAnnouncement(blockHeight uint32,
-	extraBytes ...[]byte) (*lnwire.ChannelAnnouncement, error) {
+	extraBytes ...[]byte) (*lnwire.ChannelAnnouncement1, error) {
 
 	return createChannelAnnouncement(blockHeight, remoteKeyPriv1, remoteKeyPriv2, extraBytes...)
 }
 
 func createChannelAnnouncement(blockHeight uint32, key1, key2 *btcec.PrivateKey,
-	extraBytes ...[]byte) (*lnwire.ChannelAnnouncement, error) {
+	extraBytes ...[]byte) (*lnwire.ChannelAnnouncement1, error) {
 
 	a := createAnnouncementWithoutProof(blockHeight, key1.PubKey(), key2.PubKey(), extraBytes...)
 
@@ -749,7 +749,7 @@ func createTestCtx(t *testing.T, startHeight uint32, isChanPeer bool) (
 		return false
 	}
 
-	signAliasUpdate := func(*lnwire.ChannelUpdate) (*ecdsa.Signature,
+	signAliasUpdate := func(*lnwire.ChannelUpdate1) (*ecdsa.Signature,
 		error) {
 
 		return nil, nil
@@ -1462,7 +1462,7 @@ func TestSignatureAnnouncementRetryAtStartup(t *testing.T) {
 		return false
 	}
 
-	signAliasUpdate := func(*lnwire.ChannelUpdate) (*ecdsa.Signature,
+	signAliasUpdate := func(*lnwire.ChannelUpdate1) (*ecdsa.Signature,
 		error) {
 
 		return nil, nil
@@ -1540,7 +1540,7 @@ out:
 		case msg := <-sentToPeer:
 			// Since the ChannelUpdate will also be resent as it is
 			// sent reliably, we'll need to filter it out.
-			if _, ok := msg.(*lnwire.AnnounceSignatures); !ok {
+			if _, ok := msg.(*lnwire.AnnounceSignatures1); !ok {
 				continue
 			}
 
@@ -1768,9 +1768,10 @@ func TestSignatureAnnouncementFullProofWhenRemoteProof(t *testing.T) {
 	// We expect the gossiper to send this message to the remote peer.
 	select {
 	case msg := <-sentToPeer:
-		_, ok := msg.(*lnwire.ChannelAnnouncement)
+		_, ok := msg.(*lnwire.ChannelAnnouncement1)
 		if !ok {
-			t.Fatalf("expected ChannelAnnouncement, instead got %T", msg)
+			t.Fatalf("expected ChannelAnnouncement1, instead got "+
+				"%T", msg)
 		}
 	case <-time.After(2 * time.Second):
 		t.Fatal("did not send local proof to peer")
@@ -1867,7 +1868,7 @@ func TestDeDuplicatedAnnouncements(t *testing.T) {
 		t.Fatal("channel update not replaced in batch")
 	}
 
-	assertChannelUpdate := func(channelUpdate *lnwire.ChannelUpdate) {
+	assertChannelUpdate := func(channelUpdate *lnwire.ChannelUpdate1) {
 		channelKey := channelUpdateID{
 			ua3.ShortChannelID,
 			ua3.ChannelFlags,
@@ -2811,9 +2812,9 @@ func TestRetransmit(t *testing.T) {
 		var chanAnn, chanUpd, nodeAnn int
 		for _, msg := range anns {
 			switch msg.(type) {
-			case *lnwire.ChannelAnnouncement:
+			case *lnwire.ChannelAnnouncement1:
 				chanAnn++
-			case *lnwire.ChannelUpdate:
+			case *lnwire.ChannelUpdate1:
 				chanUpd++
 			case *lnwire.NodeAnnouncement:
 				nodeAnn++
@@ -3246,7 +3247,7 @@ func TestSendChannelUpdateReliably(t *testing.T) {
 	// already been announced. We'll keep track of the old message that is
 	// now stale to use later on.
 	staleChannelUpdate := batch.chanUpdAnn1
-	newChannelUpdate := &lnwire.ChannelUpdate{}
+	newChannelUpdate := &lnwire.ChannelUpdate1{}
 	*newChannelUpdate = *staleChannelUpdate
 	newChannelUpdate.Timestamp++
 	if err := signUpdate(selfKeyPriv, newChannelUpdate); err != nil {
@@ -3300,9 +3301,9 @@ func TestSendChannelUpdateReliably(t *testing.T) {
 		}
 
 		switch msg := msg.(type) {
-		case *lnwire.ChannelUpdate:
+		case *lnwire.ChannelUpdate1:
 			assertMessage(t, staleChannelUpdate, msg)
-		case *lnwire.AnnounceSignatures:
+		case *lnwire.AnnounceSignatures1:
 			assertMessage(t, batch.localProofAnn, msg)
 		default:
 			t.Fatalf("send unexpected %v message", msg.MsgType())
@@ -3504,7 +3505,7 @@ out:
 	// being the channel our first private channel.
 	for i := 0; i < numChannels-1; i++ {
 		assertBroadcastMsg(t, ctx, func(msg lnwire.Message) error {
-			upd, ok := msg.(*lnwire.ChannelUpdate)
+			upd, ok := msg.(*lnwire.ChannelUpdate1)
 			if !ok {
 				return fmt.Errorf("channel update not "+
 					"broadcast, instead %T was", msg)
@@ -3528,7 +3529,7 @@ out:
 	// remote peer via the reliable sender.
 	select {
 	case msg := <-sentMsgs:
-		upd, ok := msg.(*lnwire.ChannelUpdate)
+		upd, ok := msg.(*lnwire.ChannelUpdate1)
 		if !ok {
 			t.Fatalf("channel update not "+
 				"broadcast, instead %T was", msg)
@@ -3552,7 +3553,7 @@ out:
 	for {
 		select {
 		case msg := <-ctx.broadcastedMessage:
-			if upd, ok := msg.msg.(*lnwire.ChannelUpdate); ok {
+			if upd, ok := msg.msg.(*lnwire.ChannelUpdate1); ok {
 				if upd.ShortChannelID == firstChanID {
 					t.Fatalf("chan update msg received: %v",
 						spew.Sdump(msg))
@@ -3884,7 +3885,7 @@ func TestRateLimitChannelUpdates(t *testing.T) {
 
 	// We'll define a helper to assert whether updates should be rate
 	// limited or not depending on their contents.
-	assertRateLimit := func(update *lnwire.ChannelUpdate, peer lnpeer.Peer,
+	assertRateLimit := func(update *lnwire.ChannelUpdate1, peer lnpeer.Peer,
 		shouldRateLimit bool) {
 
 		t.Helper()
diff --git a/discovery/message_store_test.go b/discovery/message_store_test.go
index e812c3f1a..36c082e36 100644
--- a/discovery/message_store_test.go
+++ b/discovery/message_store_test.go
@@ -52,15 +52,15 @@ func randCompressedPubKey(t *testing.T) [33]byte {
 	return compressedPubKey
 }
 
-func randAnnounceSignatures() *lnwire.AnnounceSignatures {
-	return &lnwire.AnnounceSignatures{
+func randAnnounceSignatures() *lnwire.AnnounceSignatures1 {
+	return &lnwire.AnnounceSignatures1{
 		ShortChannelID:  lnwire.NewShortChanIDFromInt(rand.Uint64()),
 		ExtraOpaqueData: make([]byte, 0),
 	}
 }
 
-func randChannelUpdate() *lnwire.ChannelUpdate {
-	return &lnwire.ChannelUpdate{
+func randChannelUpdate() *lnwire.ChannelUpdate1 {
+	return &lnwire.ChannelUpdate1{
 		ShortChannelID:  lnwire.NewShortChanIDFromInt(rand.Uint64()),
 		ExtraOpaqueData: make([]byte, 0),
 	}
@@ -116,9 +116,9 @@ func TestMessageStoreMessages(t *testing.T) {
 		for _, msg := range peerMsgs {
 			var shortChanID uint64
 			switch msg := msg.(type) {
-			case *lnwire.AnnounceSignatures:
+			case *lnwire.AnnounceSignatures1:
 				shortChanID = msg.ShortChannelID.ToUint64()
-			case *lnwire.ChannelUpdate:
+			case *lnwire.ChannelUpdate1:
 				shortChanID = msg.ShortChannelID.ToUint64()
 			default:
 				t.Fatalf("found unexpected message type %T", msg)
diff --git a/discovery/syncer_test.go b/discovery/syncer_test.go
index bb6aec590..0ee635a0f 100644
--- a/discovery/syncer_test.go
+++ b/discovery/syncer_test.go
@@ -52,7 +52,7 @@ type mockChannelGraphTimeSeries struct {
 	annResp chan []lnwire.Message
 
 	updateReq  chan lnwire.ShortChannelID
-	updateResp chan []*lnwire.ChannelUpdate
+	updateResp chan []*lnwire.ChannelUpdate1
 }
 
 func newMockChannelGraphTimeSeries(
@@ -74,7 +74,7 @@ func newMockChannelGraphTimeSeries(
 		annResp: make(chan []lnwire.Message, 1),
 
 		updateReq:  make(chan lnwire.ShortChannelID, 1),
-		updateResp: make(chan []*lnwire.ChannelUpdate, 1),
+		updateResp: make(chan []*lnwire.ChannelUpdate1, 1),
 	}
 }
 
@@ -149,7 +149,7 @@ func (m *mockChannelGraphTimeSeries) FetchChanAnns(chain chainhash.Hash,
 	return <-m.annResp, nil
 }
 func (m *mockChannelGraphTimeSeries) FetchChanUpdates(chain chainhash.Hash,
-	shortChanID lnwire.ShortChannelID) ([]*lnwire.ChannelUpdate, error) {
+	shortChanID lnwire.ShortChannelID) ([]*lnwire.ChannelUpdate1, error) {
 
 	m.updateReq <- shortChanID
 
@@ -306,36 +306,36 @@ func TestGossipSyncerFilterGossipMsgsAllInMemory(t *testing.T) {
 		},
 		{
 			// Ann tuple below horizon.
-			msg: &lnwire.ChannelAnnouncement{
+			msg: &lnwire.ChannelAnnouncement1{
 				ShortChannelID: lnwire.NewShortChanIDFromInt(10),
 			},
 		},
 		{
-			msg: &lnwire.ChannelUpdate{
+			msg: &lnwire.ChannelUpdate1{
 				ShortChannelID: lnwire.NewShortChanIDFromInt(10),
 				Timestamp:      unixStamp(5),
 			},
 		},
 		{
 			// Ann tuple above horizon.
-			msg: &lnwire.ChannelAnnouncement{
+			msg: &lnwire.ChannelAnnouncement1{
 				ShortChannelID: lnwire.NewShortChanIDFromInt(15),
 			},
 		},
 		{
-			msg: &lnwire.ChannelUpdate{
+			msg: &lnwire.ChannelUpdate1{
 				ShortChannelID: lnwire.NewShortChanIDFromInt(15),
 				Timestamp:      unixStamp(25002),
 			},
 		},
 		{
 			// Ann tuple beyond horizon.
-			msg: &lnwire.ChannelAnnouncement{
+			msg: &lnwire.ChannelAnnouncement1{
 				ShortChannelID: lnwire.NewShortChanIDFromInt(20),
 			},
 		},
 		{
-			msg: &lnwire.ChannelUpdate{
+			msg: &lnwire.ChannelUpdate1{
 				ShortChannelID: lnwire.NewShortChanIDFromInt(20),
 				Timestamp:      unixStamp(999999),
 			},
@@ -343,7 +343,7 @@ func TestGossipSyncerFilterGossipMsgsAllInMemory(t *testing.T) {
 		{
 			// Ann w/o an update at all, the update in the DB will
 			// be below the horizon.
-			msg: &lnwire.ChannelAnnouncement{
+			msg: &lnwire.ChannelAnnouncement1{
 				ShortChannelID: lnwire.NewShortChanIDFromInt(25),
 			},
 		},
@@ -369,7 +369,7 @@ func TestGossipSyncerFilterGossipMsgsAllInMemory(t *testing.T) {
 			}
 
 			// If so, then we'll send back the missing update.
-			chanSeries.updateResp <- []*lnwire.ChannelUpdate{
+			chanSeries.updateResp <- []*lnwire.ChannelUpdate1{
 				{
 					ShortChannelID: lnwire.NewShortChanIDFromInt(25),
 					Timestamp:      unixStamp(5),
@@ -551,7 +551,7 @@ func TestGossipSyncerApplyGossipFilter(t *testing.T) {
 			// For this first response, we'll send back a proper
 			// set of messages that should be echoed back.
 			chanSeries.horizonResp <- []lnwire.Message{
-				&lnwire.ChannelUpdate{
+				&lnwire.ChannelUpdate1{
 					ShortChannelID: lnwire.NewShortChanIDFromInt(25),
 					Timestamp:      unixStamp(5),
 				},
@@ -706,10 +706,10 @@ func TestGossipSyncerReplyShortChanIDs(t *testing.T) {
 	}
 
 	queryReply := []lnwire.Message{
-		&lnwire.ChannelAnnouncement{
+		&lnwire.ChannelAnnouncement1{
 			ShortChannelID: lnwire.NewShortChanIDFromInt(20),
 		},
-		&lnwire.ChannelUpdate{
+		&lnwire.ChannelUpdate1{
 			ShortChannelID: lnwire.NewShortChanIDFromInt(20),
 			Timestamp:      unixStamp(999999),
 		},
diff --git a/funding/manager_test.go b/funding/manager_test.go
index d1aa9ec14..dce8475ad 100644
--- a/funding/manager_test.go
+++ b/funding/manager_test.go
@@ -1208,9 +1208,9 @@ func assertChannelAnnouncements(t *testing.T, alice, bob *testNode,
 		gotChannelUpdate := false
 		for _, msg := range announcements {
 			switch m := msg.(type) {
-			case *lnwire.ChannelAnnouncement:
+			case *lnwire.ChannelAnnouncement1:
 				gotChannelAnnouncement = true
-			case *lnwire.ChannelUpdate:
+			case *lnwire.ChannelUpdate1:
 
 				// The channel update sent by the node should
 				// advertise the MinHTLC value required by the
@@ -1297,7 +1297,7 @@ func assertAnnouncementSignatures(t *testing.T, alice, bob *testNode) {
 		gotNodeAnnouncement := false
 		for _, msg := range announcements {
 			switch msg.(type) {
-			case *lnwire.AnnounceSignatures:
+			case *lnwire.AnnounceSignatures1:
 				gotAnnounceSignatures = true
 			case *lnwire.NodeAnnouncement:
 				gotNodeAnnouncement = true
diff --git a/graph/validation_barrier_test.go b/graph/validation_barrier_test.go
index da404443f..38fc7a087 100644
--- a/graph/validation_barrier_test.go
+++ b/graph/validation_barrier_test.go
@@ -73,9 +73,9 @@ func TestValidationBarrierQuit(t *testing.T) {
 
 	// Create a set of unique channel announcements that we will prep for
 	// validation.
-	anns := make([]*lnwire.ChannelAnnouncement, 0, numTasks)
+	anns := make([]*lnwire.ChannelAnnouncement1, 0, numTasks)
 	for i := 0; i < numTasks; i++ {
-		anns = append(anns, &lnwire.ChannelAnnouncement{
+		anns = append(anns, &lnwire.ChannelAnnouncement1{
 			ShortChannelID: lnwire.NewShortChanIDFromInt(uint64(i)),
 			NodeID1:        nodeIDFromInt(uint64(2 * i)),
 			NodeID2:        nodeIDFromInt(uint64(2*i + 1)),
@@ -85,9 +85,9 @@ func TestValidationBarrierQuit(t *testing.T) {
 
 	// Create a set of channel updates, that must wait until their
 	// associated channel announcement has been verified.
-	chanUpds := make([]*lnwire.ChannelUpdate, 0, numTasks)
+	chanUpds := make([]*lnwire.ChannelUpdate1, 0, numTasks)
 	for i := 0; i < numTasks; i++ {
-		chanUpds = append(chanUpds, &lnwire.ChannelUpdate{
+		chanUpds = append(chanUpds, &lnwire.ChannelUpdate1{
 			ShortChannelID: lnwire.NewShortChanIDFromInt(uint64(i)),
 		})
 		barrier.InitJobDependencies(chanUpds[i])
diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go
index 53a084209..aa5fd6e4e 100644
--- a/htlcswitch/link_test.go
+++ b/htlcswitch/link_test.go
@@ -6166,13 +6166,13 @@ func TestForwardingAsymmetricTimeLockPolicies(t *testing.T) {
 // forwarding policy.
 func TestCheckHtlcForward(t *testing.T) {
 	fetchLastChannelUpdate := func(lnwire.ShortChannelID) (
-		*lnwire.ChannelUpdate, error) {
+		*lnwire.ChannelUpdate1, error) {
 
-		return &lnwire.ChannelUpdate{}, nil
+		return &lnwire.ChannelUpdate1{}, nil
 	}
 
 	failAliasUpdate := func(sid lnwire.ShortChannelID,
-		incoming bool) *lnwire.ChannelUpdate {
+		incoming bool) *lnwire.ChannelUpdate1 {
 
 		return nil
 	}
diff --git a/htlcswitch/mailbox_test.go b/htlcswitch/mailbox_test.go
index dfb0fb616..e48aacfcd 100644
--- a/htlcswitch/mailbox_test.go
+++ b/htlcswitch/mailbox_test.go
@@ -109,7 +109,7 @@ func TestMailBoxCouriers(t *testing.T) {
 		t.Fatalf("recvd packets mismatched: expected %v, got %v",
 			spew.Sdump(sentPackets), spew.Sdump(recvdPackets))
 	}
-	if !reflect.DeepEqual(sentMessages, recvdMessages) {
+	if !reflect.DeepEqual(recvdMessages, recvdMessages) {
 		t.Fatalf("recvd messages mismatched: expected %v, got %v",
 			spew.Sdump(sentMessages), spew.Sdump(recvdMessages))
 	}
diff --git a/htlcswitch/switch_test.go b/htlcswitch/switch_test.go
index 0bc0df2d4..825ee6c65 100644
--- a/htlcswitch/switch_test.go
+++ b/htlcswitch/switch_test.go
@@ -3951,7 +3951,7 @@ func TestSwitchHoldForward(t *testing.T) {
 	// Simulate an error during the composition of the failure message.
 	currentCallback := c.s.cfg.FetchLastChannelUpdate
 	c.s.cfg.FetchLastChannelUpdate = func(
-		lnwire.ShortChannelID) (*lnwire.ChannelUpdate, error) {
+		lnwire.ShortChannelID) (*lnwire.ChannelUpdate1, error) {
 
 		return nil, errors.New("cannot fetch update")
 	}
diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go
index a71577ef2..abd48e806 100644
--- a/htlcswitch/test_utils.go
+++ b/htlcswitch/test_utils.go
@@ -91,8 +91,10 @@ func genIDs() (lnwire.ChannelID, lnwire.ChannelID, lnwire.ShortChannelID,
 
 // mockGetChanUpdateMessage helper function which returns topology update of
 // the channel
-func mockGetChanUpdateMessage(cid lnwire.ShortChannelID) (*lnwire.ChannelUpdate, error) {
-	return &lnwire.ChannelUpdate{
+func mockGetChanUpdateMessage(_ lnwire.ShortChannelID) (*lnwire.ChannelUpdate1,
+	error) {
+
+	return &lnwire.ChannelUpdate1{
 		Signature: wireSig,
 	}, nil
 }
diff --git a/lnwire/lnwire_test.go b/lnwire/lnwire_test.go
index 7eb434f45..a5a754806 100644
--- a/lnwire/lnwire_test.go
+++ b/lnwire/lnwire_test.go
@@ -19,6 +19,7 @@ import (
 	"github.com/btcsuite/btcd/btcec/v2"
 	"github.com/btcsuite/btcd/btcec/v2/ecdsa"
 	"github.com/btcsuite/btcd/btcutil"
+	"github.com/btcsuite/btcd/chaincfg"
 	"github.com/btcsuite/btcd/chaincfg/chainhash"
 	"github.com/btcsuite/btcd/wire"
 	"github.com/lightningnetwork/lnd/fn"
@@ -30,17 +31,25 @@ import (
 )
 
 var (
-	shaHash1Bytes, _ = hex.DecodeString("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
-	shaHash1, _      = chainhash.NewHash(shaHash1Bytes)
-	outpoint1        = wire.NewOutPoint(shaHash1, 0)
-
-	testRBytes, _ = hex.DecodeString("8ce2bc69281ce27da07e6683571319d18e949ddfa2965fb6caa1bf0314f882d7")
-	testSBytes, _ = hex.DecodeString("299105481d63e0f4bc2a88121167221b6700d72a0ead154c03be696a292d24ae")
-	testRScalar   = new(btcec.ModNScalar)
-	testSScalar   = new(btcec.ModNScalar)
-	_             = testRScalar.SetByteSlice(testRBytes)
-	_             = testSScalar.SetByteSlice(testSBytes)
-	testSig       = ecdsa.NewSignature(testRScalar, testSScalar)
+	shaHash1Bytes, _ = hex.DecodeString("e3b0c44298fc1c149afbf4c8996fb" +
+		"92427ae41e4649b934ca495991b7852b855")
+
+	shaHash1, _ = chainhash.NewHash(shaHash1Bytes)
+	outpoint1   = wire.NewOutPoint(shaHash1, 0)
+
+	testRBytes, _ = hex.DecodeString("8ce2bc69281ce27da07e6683571" +
+		"319d18e949ddfa2965fb6caa1bf0314f882d7")
+	testSBytes, _ = hex.DecodeString("299105481d63e0f4bc2a" +
+		"88121167221b6700d72a0ead154c03be696a292d24ae")
+	testRScalar          = new(btcec.ModNScalar)
+	testSScalar          = new(btcec.ModNScalar)
+	_                    = testRScalar.SetByteSlice(testRBytes)
+	_                    = testSScalar.SetByteSlice(testSBytes)
+	testSig              = ecdsa.NewSignature(testRScalar, testSScalar)
+	testSchnorrSigStr, _ = hex.DecodeString("04E7F9037658A92AFEB4F2" +
+		"5BAE5339E3DDCA81A353493827D26F16D92308E49E2A25E9220867" +
+		"8A2DF86970DA91B03A8AF8815A8A60498B358DAF560B347AA557")
+	testSchnorrSig, _ = NewSigFromSchnorrRawSignature(testSchnorrSigStr)
 )
 
 const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
@@ -156,17 +165,15 @@ func generateRandomBytes(n int) ([]byte, error) {
 	return b, nil
 }
 
-func randRawKey() ([33]byte, error) {
+func randRawKey(t *testing.T) [33]byte {
 	var n [33]byte
 
 	priv, err := btcec.NewPrivateKey()
-	if err != nil {
-		return n, err
-	}
+	require.NoError(t, err)
 
 	copy(n[:], priv.PubKey().SerializeCompressed())
 
-	return n, nil
+	return n
 }
 
 func randDeliveryAddress(r *rand.Rand) (DeliveryAddress, error) {
@@ -730,18 +737,13 @@ func TestLightningWireProtocol(t *testing.T) {
 		},
 		MsgChannelReady: func(v []reflect.Value, r *rand.Rand) {
 			var c [32]byte
-			if _, err := r.Read(c[:]); err != nil {
-				t.Fatalf("unable to generate chan id: %v", err)
-				return
-			}
+			_, err := r.Read(c[:])
+			require.NoError(t, err)
 
 			pubKey, err := randPubKey()
-			if err != nil {
-				t.Fatalf("unable to generate key: %v", err)
-				return
-			}
+			require.NoError(t, err)
 
-			req := NewChannelReady(ChannelID(c), pubKey)
+			req := NewChannelReady(c, pubKey)
 
 			if r.Int31()%2 == 0 {
 				scid := NewShortChanIDFromInt(uint64(r.Int63()))
@@ -751,6 +753,24 @@ func TestLightningWireProtocol(t *testing.T) {
 				req.NextLocalNonce = someLocalNonce[NonceRecordTypeT](r)
 			}
 
+			if r.Int31()%2 == 0 {
+				nodeNonce := tlv.ZeroRecordT[
+					tlv.TlvType0, Musig2Nonce,
+				]()
+				nodeNonce.Val = randLocalNonce(r)
+				req.AnnouncementNodeNonce = tlv.SomeRecordT(
+					nodeNonce,
+				)
+
+				btcNonce := tlv.ZeroRecordT[
+					tlv.TlvType2, Musig2Nonce,
+				]()
+				btcNonce.Val = randLocalNonce(r)
+				req.AnnouncementBitcoinNonce = tlv.SomeRecordT(
+					btcNonce,
+				)
+			}
+
 			v[0] = reflect.ValueOf(*req)
 		},
 		MsgShutdown: func(v []reflect.Value, r *rand.Rand) {
@@ -979,8 +999,14 @@ func TestLightningWireProtocol(t *testing.T) {
 		},
 		MsgChannelAnnouncement: func(v []reflect.Value, r *rand.Rand) {
 			var err error
-			req := ChannelAnnouncement{
-				ShortChannelID:  NewShortChanIDFromInt(uint64(r.Int63())),
+			req := ChannelAnnouncement1{
+				ShortChannelID: NewShortChanIDFromInt(
+					uint64(r.Int63()),
+				),
+				NodeID1:         randRawKey(t),
+				NodeID2:         randRawKey(t),
+				BitcoinKey1:     randRawKey(t),
+				BitcoinKey2:     randRawKey(t),
 				Features:        randRawFeatureVector(r),
 				ExtraOpaqueData: make([]byte, 0),
 			}
@@ -1005,26 +1031,6 @@ func TestLightningWireProtocol(t *testing.T) {
 				return
 			}
 
-			req.NodeID1, err = randRawKey()
-			if err != nil {
-				t.Fatalf("unable to generate key: %v", err)
-				return
-			}
-			req.NodeID2, err = randRawKey()
-			if err != nil {
-				t.Fatalf("unable to generate key: %v", err)
-				return
-			}
-			req.BitcoinKey1, err = randRawKey()
-			if err != nil {
-				t.Fatalf("unable to generate key: %v", err)
-				return
-			}
-			req.BitcoinKey2, err = randRawKey()
-			if err != nil {
-				t.Fatalf("unable to generate key: %v", err)
-				return
-			}
 			if _, err := r.Read(req.ChainHash[:]); err != nil {
 				t.Fatalf("unable to generate chain hash: %v", err)
 				return
@@ -1046,6 +1052,7 @@ func TestLightningWireProtocol(t *testing.T) {
 		MsgNodeAnnouncement: func(v []reflect.Value, r *rand.Rand) {
 			var err error
 			req := NodeAnnouncement{
+				NodeID:    randRawKey(t),
 				Features:  randRawFeatureVector(r),
 				Timestamp: uint32(r.Int31()),
 				Alias:     randAlias(r),
@@ -1062,12 +1069,6 @@ func TestLightningWireProtocol(t *testing.T) {
 				return
 			}
 
-			req.NodeID, err = randRawKey()
-			if err != nil {
-				t.Fatalf("unable to generate key: %v", err)
-				return
-			}
-
 			req.Addresses, err = randAddrs(r)
 			if err != nil {
 				t.Fatalf("unable to generate addresses: %v", err)
@@ -1100,8 +1101,10 @@ func TestLightningWireProtocol(t *testing.T) {
 				maxHtlc = 0
 			}
 
-			req := ChannelUpdate{
-				ShortChannelID:  NewShortChanIDFromInt(uint64(r.Int63())),
+			req := ChannelUpdate1{
+				ShortChannelID: NewShortChanIDFromInt(
+					uint64(r.Int63()),
+				),
 				Timestamp:       uint32(r.Int31()),
 				MessageFlags:    msgFlags,
 				ChannelFlags:    ChanUpdateChanFlags(r.Int31()),
@@ -1138,7 +1141,7 @@ func TestLightningWireProtocol(t *testing.T) {
 		},
 		MsgAnnounceSignatures: func(v []reflect.Value, r *rand.Rand) {
 			var err error
-			req := AnnounceSignatures{
+			req := AnnounceSignatures1{
 				ShortChannelID:  NewShortChanIDFromInt(uint64(r.Int63())),
 				ExtraOpaqueData: make([]byte, 0),
 			}
@@ -1202,6 +1205,35 @@ func TestLightningWireProtocol(t *testing.T) {
 
 			v[0] = reflect.ValueOf(req)
 		},
+		MsgGossipTimestampRange: func(v []reflect.Value, r *rand.Rand) {
+			req := GossipTimestampRange{
+				FirstTimestamp: rand.Uint32(),
+				TimestampRange: rand.Uint32(),
+				ExtraData:      make([]byte, 0),
+			}
+
+			_, err := rand.Read(req.ChainHash[:])
+			require.NoError(t, err)
+
+			// Sometimes add a block range.
+			if r.Int31()%2 == 0 {
+				firstBlock := tlv.ZeroRecordT[
+					tlv.TlvType2, uint32,
+				]()
+				firstBlock.Val = rand.Uint32()
+				req.FirstBlockHeight = tlv.SomeRecordT(
+					firstBlock,
+				)
+
+				blockRange := tlv.ZeroRecordT[
+					tlv.TlvType4, uint32,
+				]()
+				blockRange.Val = rand.Uint32()
+				req.BlockRange = tlv.SomeRecordT(blockRange)
+			}
+
+			v[0] = reflect.ValueOf(req)
+		},
 		MsgQueryShortChanIDs: func(v []reflect.Value, r *rand.Rand) {
 			req := QueryShortChanIDs{
 				ExtraData: make([]byte, 0),
@@ -1467,6 +1499,176 @@ func TestLightningWireProtocol(t *testing.T) {
 
 			v[0] = reflect.ValueOf(*req)
 		},
+		MsgAnnounceSignatures2: func(v []reflect.Value,
+			r *rand.Rand) {
+
+			req := AnnounceSignatures2{
+				ShortChannelID: NewShortChanIDFromInt(
+					uint64(r.Int63()),
+				),
+				ExtraOpaqueData: make([]byte, 0),
+			}
+
+			_, err := r.Read(req.ChannelID[:])
+			require.NoError(t, err)
+
+			partialSig, err := randPartialSig(r)
+			require.NoError(t, err)
+
+			req.PartialSignature = *partialSig
+
+			numExtraBytes := r.Int31n(1000)
+			if numExtraBytes > 0 {
+				req.ExtraOpaqueData = make(
+					[]byte, numExtraBytes,
+				)
+				_, err := r.Read(req.ExtraOpaqueData[:])
+				require.NoError(t, err)
+			}
+
+			v[0] = reflect.ValueOf(req)
+		},
+		MsgChannelAnnouncement2: func(v []reflect.Value, r *rand.Rand) {
+			req := ChannelAnnouncement2{
+				Signature:       testSchnorrSig,
+				ExtraOpaqueData: make([]byte, 0),
+			}
+
+			req.ShortChannelID.Val = NewShortChanIDFromInt(
+				uint64(r.Int63()),
+			)
+			req.Capacity.Val = rand.Uint64()
+
+			req.Features.Val = *randRawFeatureVector(r)
+
+			req.NodeID1.Val = randRawKey(t)
+			req.NodeID2.Val = randRawKey(t)
+
+			// Sometimes set chain hash to bitcoin mainnet genesis
+			// hash.
+			req.ChainHash.Val = *chaincfg.MainNetParams.GenesisHash
+			if r.Int31()%2 == 0 {
+				_, err := r.Read(req.ChainHash.Val[:])
+				require.NoError(t, err)
+			}
+
+			// Sometimes set the bitcoin keys.
+			if r.Int31()%2 == 0 {
+				btcKey1 := tlv.ZeroRecordT[
+					tlv.TlvType12, [33]byte,
+				]()
+				btcKey1.Val = randRawKey(t)
+				req.BitcoinKey1 = tlv.SomeRecordT(btcKey1)
+
+				btcKey2 := tlv.ZeroRecordT[
+					tlv.TlvType14, [33]byte,
+				]()
+				btcKey2.Val = randRawKey(t)
+				req.BitcoinKey2 = tlv.SomeRecordT(btcKey2)
+
+				// Occasionally also set the merkle root hash.
+				if r.Int31()%2 == 0 {
+					hash := tlv.ZeroRecordT[
+						tlv.TlvType16, [32]byte,
+					]()
+
+					_, err := r.Read(hash.Val[:])
+					require.NoError(t, err)
+
+					req.MerkleRootHash = tlv.SomeRecordT(
+						hash,
+					)
+				}
+			}
+
+			numExtraBytes := r.Int31n(1000)
+			if numExtraBytes > 0 {
+				req.ExtraOpaqueData = make(
+					[]byte, numExtraBytes,
+				)
+				_, err := r.Read(req.ExtraOpaqueData[:])
+				require.NoError(t, err)
+			}
+
+			v[0] = reflect.ValueOf(req)
+		},
+		MsgChannelUpdate2: func(v []reflect.Value, r *rand.Rand) {
+			req := ChannelUpdate2{
+				Signature:       testSchnorrSig,
+				ExtraOpaqueData: make([]byte, 0),
+			}
+
+			req.ShortChannelID.Val = NewShortChanIDFromInt(
+				uint64(r.Int63()),
+			)
+			req.BlockHeight.Val = r.Uint32()
+			req.HTLCMaximumMsat.Val = MilliSatoshi(r.Uint64())
+
+			// Sometimes set chain hash to bitcoin mainnet genesis
+			// hash.
+			req.ChainHash.Val = *chaincfg.MainNetParams.GenesisHash
+			if r.Int31()%2 == 0 {
+				_, err := r.Read(req.ChainHash.Val[:])
+				require.NoError(t, err)
+			}
+
+			// Sometimes use default htlc min msat.
+			req.HTLCMinimumMsat.Val = defaultHtlcMinMsat
+			if r.Int31()%2 == 0 {
+				req.HTLCMinimumMsat.Val = MilliSatoshi(
+					r.Uint64(),
+				)
+			}
+
+			// Sometimes set the cltv expiry delta to the default.
+			req.CLTVExpiryDelta.Val = defaultCltvExpiryDelta
+			if r.Int31()%2 == 0 {
+				req.CLTVExpiryDelta.Val = uint16(r.Int31())
+			}
+
+			// Sometimes use default fee base.
+			req.FeeBaseMsat.Val = defaultFeeBaseMsat
+			if r.Int31()%2 == 0 {
+				req.FeeBaseMsat.Val = r.Uint32()
+			}
+
+			// Sometimes use default proportional fee.
+			req.FeeProportionalMillionths.Val =
+				defaultFeeProportionalMillionths
+			if r.Int31()%2 == 0 {
+				req.FeeProportionalMillionths.Val = r.Uint32()
+			}
+
+			// Alternate between the two direction possibilities.
+			if r.Int31()%2 == 0 {
+				req.SecondPeer = tlv.SomeRecordT(
+					tlv.ZeroRecordT[tlv.TlvType8, TrueBoolean](), //nolint:lll
+				)
+			}
+
+			// Sometimes set the incoming disabled flag.
+			if r.Int31()%2 == 0 {
+				req.DisabledFlags.Val |=
+					ChanUpdateDisableIncoming
+			}
+
+			// Sometimes set the outgoing disabled flag.
+			if r.Int31()%2 == 0 {
+				req.DisabledFlags.Val |=
+					ChanUpdateDisableOutgoing
+			}
+
+			numExtraBytes := r.Int31n(1000)
+			if numExtraBytes > 0 {
+				req.ExtraOpaqueData = make(
+					[]byte, numExtraBytes,
+				)
+				_, err := r.Read(req.ExtraOpaqueData[:])
+				require.NoError(t, err)
+			}
+
+			v[0] = reflect.ValueOf(req)
+		},
 	}
 
 	// With the above types defined, we'll now generate a slice of
@@ -1631,7 +1833,7 @@ func TestLightningWireProtocol(t *testing.T) {
 		},
 		{
 			msgType: MsgChannelAnnouncement,
-			scenario: func(m ChannelAnnouncement) bool {
+			scenario: func(m ChannelAnnouncement1) bool {
 				return mainScenario(&m)
 			},
 		},
@@ -1643,13 +1845,13 @@ func TestLightningWireProtocol(t *testing.T) {
 		},
 		{
 			msgType: MsgChannelUpdate,
-			scenario: func(m ChannelUpdate) bool {
+			scenario: func(m ChannelUpdate1) bool {
 				return mainScenario(&m)
 			},
 		},
 		{
 			msgType: MsgAnnounceSignatures,
-			scenario: func(m AnnounceSignatures) bool {
+			scenario: func(m AnnounceSignatures1) bool {
 				return mainScenario(&m)
 			},
 		},
@@ -1695,6 +1897,24 @@ func TestLightningWireProtocol(t *testing.T) {
 				return mainScenario(&m)
 			},
 		},
+		{
+			msgType: MsgAnnounceSignatures2,
+			scenario: func(m AnnounceSignatures2) bool {
+				return mainScenario(&m)
+			},
+		},
+		{
+			msgType: MsgChannelAnnouncement2,
+			scenario: func(m ChannelAnnouncement2) bool {
+				return mainScenario(&m)
+			},
+		},
+		{
+			msgType: MsgChannelUpdate2,
+			scenario: func(m ChannelUpdate2) bool {
+				return mainScenario(&m)
+			},
+		},
 	}
 	for _, test := range tests {
 		var config *quick.Config
diff --git a/lnwire/message_test.go b/lnwire/message_test.go
index bbb434785..d42d57911 100644
--- a/lnwire/message_test.go
+++ b/lnwire/message_test.go
@@ -645,11 +645,11 @@ func newMsgChannelReestablish(t testing.TB,
 }
 
 func newMsgChannelAnnouncement(t testing.TB,
-	r *rand.Rand) *lnwire.ChannelAnnouncement {
+	r *rand.Rand) *lnwire.ChannelAnnouncement1 {
 
 	t.Helper()
 
-	msg := &lnwire.ChannelAnnouncement{
+	msg := &lnwire.ChannelAnnouncement1{
 		ShortChannelID:  lnwire.NewShortChanIDFromInt(uint64(r.Int63())),
 		Features:        rawFeatureVector(),
 		NodeID1:         randRawKey(t),
@@ -692,7 +692,7 @@ func newMsgNodeAnnouncement(t testing.TB,
 	return msg
 }
 
-func newMsgChannelUpdate(t testing.TB, r *rand.Rand) *lnwire.ChannelUpdate {
+func newMsgChannelUpdate(t testing.TB, r *rand.Rand) *lnwire.ChannelUpdate1 {
 	t.Helper()
 
 	msgFlags := lnwire.ChanUpdateMsgFlags(r.Int31())
@@ -706,7 +706,7 @@ func newMsgChannelUpdate(t testing.TB, r *rand.Rand) *lnwire.ChannelUpdate {
 		maxHtlc = 0
 	}
 
-	msg := &lnwire.ChannelUpdate{
+	msg := &lnwire.ChannelUpdate1{
 		ShortChannelID:  lnwire.NewShortChanIDFromInt(r.Uint64()),
 		Timestamp:       uint32(r.Int31()),
 		MessageFlags:    msgFlags,
@@ -727,11 +727,11 @@ func newMsgChannelUpdate(t testing.TB, r *rand.Rand) *lnwire.ChannelUpdate {
 }
 
 func newMsgAnnounceSignatures(t testing.TB,
-	r *rand.Rand) *lnwire.AnnounceSignatures {
+	r *rand.Rand) *lnwire.AnnounceSignatures1 {
 
 	t.Helper()
 
-	msg := &lnwire.AnnounceSignatures{
+	msg := &lnwire.AnnounceSignatures1{
 		ShortChannelID: lnwire.NewShortChanIDFromInt(
 			uint64(r.Int63()),
 		),
diff --git a/lnwire/onion_error_test.go b/lnwire/onion_error_test.go
index bc14d5d42..9c39be6d5 100644
--- a/lnwire/onion_error_test.go
+++ b/lnwire/onion_error_test.go
@@ -21,7 +21,7 @@ var (
 	testType          = uint64(3)
 	testOffset        = uint16(24)
 	sig, _            = NewSigFromSignature(testSig)
-	testChannelUpdate = ChannelUpdate{
+	testChannelUpdate = ChannelUpdate1{
 		Signature:       sig,
 		ShortChannelID:  NewShortChanIDFromInt(1),
 		Timestamp:       1,
@@ -138,7 +138,7 @@ func TestChannelUpdateCompatibilityParsing(t *testing.T) {
 	// Now that we have the set of bytes encoded, we'll ensure that we're
 	// able to decode it using our compatibility method, as it's a regular
 	// encoded channel update message.
-	var newChanUpdate ChannelUpdate
+	var newChanUpdate ChannelUpdate1
 	err := parseChannelUpdateCompatibilityMode(
 		&b, uint16(b.Len()), &newChanUpdate, 0,
 	)
@@ -165,7 +165,7 @@ func TestChannelUpdateCompatibilityParsing(t *testing.T) {
 
 	// We should be able to properly parse the encoded channel update
 	// message even with the extra two bytes.
-	var newChanUpdate2 ChannelUpdate
+	var newChanUpdate2 ChannelUpdate1
 	err = parseChannelUpdateCompatibilityMode(
 		&b, uint16(b.Len()), &newChanUpdate2, 0,
 	)
diff --git a/netann/chan_status_manager_test.go b/netann/chan_status_manager_test.go
index 1e64a53f8..c709a95f2 100644
--- a/netann/chan_status_manager_test.go
+++ b/netann/chan_status_manager_test.go
@@ -126,7 +126,7 @@ type mockGraph struct {
 	chanPols2 map[wire.OutPoint]*models.ChannelEdgePolicy
 	sidToCid  map[lnwire.ShortChannelID]wire.OutPoint
 
-	updates chan *lnwire.ChannelUpdate
+	updates chan *lnwire.ChannelUpdate1
 }
 
 func newMockGraph(t *testing.T, numChannels int,
@@ -138,7 +138,7 @@ func newMockGraph(t *testing.T, numChannels int,
 		chanPols1: make(map[wire.OutPoint]*models.ChannelEdgePolicy),
 		chanPols2: make(map[wire.OutPoint]*models.ChannelEdgePolicy),
 		sidToCid:  make(map[lnwire.ShortChannelID]wire.OutPoint),
-		updates:   make(chan *lnwire.ChannelUpdate, 2*numChannels),
+		updates:   make(chan *lnwire.ChannelUpdate1, 2*numChannels),
 	}
 
 	for i := 0; i < numChannels; i++ {
@@ -177,7 +177,7 @@ func (g *mockGraph) FetchChannelEdgesByOutpoint(
 	return info, pol1, pol2, nil
 }
 
-func (g *mockGraph) ApplyChannelUpdate(update *lnwire.ChannelUpdate,
+func (g *mockGraph) ApplyChannelUpdate(update *lnwire.ChannelUpdate1,
 	op *wire.OutPoint, private bool) error {
 
 	g.mu.Lock()
diff --git a/netann/channel_announcement_test.go b/netann/channel_announcement_test.go
index 92a6c74ce..61db16b16 100644
--- a/netann/channel_announcement_test.go
+++ b/netann/channel_announcement_test.go
@@ -4,11 +4,16 @@ import (
 	"bytes"
 	"testing"
 
+	"github.com/btcsuite/btcd/btcec/v2"
+	"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
 	"github.com/btcsuite/btcd/btcutil"
+	"github.com/btcsuite/btcd/chaincfg"
 	"github.com/btcsuite/btcd/chaincfg/chainhash"
 	"github.com/btcsuite/btcd/wire"
 	"github.com/lightningnetwork/lnd/channeldb/models"
+	"github.com/lightningnetwork/lnd/input"
 	"github.com/lightningnetwork/lnd/lnwire"
+	"github.com/lightningnetwork/lnd/tlv"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
@@ -24,7 +29,7 @@ func TestCreateChanAnnouncement(t *testing.T) {
 		t.Fatalf("unable to encode features: %v", err)
 	}
 
-	expChanAnn := &lnwire.ChannelAnnouncement{
+	expChanAnn := &lnwire.ChannelAnnouncement1{
 		ChainHash:       chainhash.Hash{0x1},
 		ShortChannelID:  lnwire.ShortChannelID{BlockHeight: 1},
 		NodeID1:         key,
@@ -64,3 +69,291 @@ func TestCreateChanAnnouncement(t *testing.T) {
 
 	assert.Equal(t, chanAnn, expChanAnn)
 }
+
+// TestChanAnnounce2Validation checks that the various forms of the
+// channel_announcement_2 message are validated correctly.
+func TestChanAnnounce2Validation(t *testing.T) {
+	t.Parallel()
+
+	t.Run(
+		"test 4-of-4 MuSig2 channel announcement",
+		test4of4MuSig2ChanAnnouncement,
+	)
+
+	t.Run(
+		"test 3-of-3 MuSig2 channel announcement",
+		test3of3MuSig2ChanAnnouncement,
+	)
+}
+
+// test4of4MuSig2ChanAnnouncement covers the case where both bitcoin keys are
+// present in the channel announcement. In this case, the signature should be
+// a 4-of-4 MuSig2.
+func test4of4MuSig2ChanAnnouncement(t *testing.T) {
+	t.Parallel()
+
+	// Generate the keys for node 1 and node2.
+	node1, node2 := genChanAnnKeys(t)
+
+	// Build the unsigned channel announcement.
+	ann := buildUnsignedChanAnnouncement(node1, node2, true)
+
+	// Serialise the bytes that need to be signed.
+	msg, err := ChanAnn2DigestToSign(ann)
+	require.NoError(t, err)
+
+	var msgBytes [32]byte
+	copy(msgBytes[:], msg.CloneBytes())
+
+	// Generate the 4 nonces required for producing the signature.
+	var (
+		node1NodeNonce = genNonceForPubKey(t, node1.nodePub)
+		node1BtcNonce  = genNonceForPubKey(t, node1.btcPub)
+		node2NodeNonce = genNonceForPubKey(t, node2.nodePub)
+		node2BtcNonce  = genNonceForPubKey(t, node2.btcPub)
+	)
+
+	nonceAgg, err := musig2.AggregateNonces([][66]byte{
+		node1NodeNonce.PubNonce,
+		node1BtcNonce.PubNonce,
+		node2NodeNonce.PubNonce,
+		node2BtcNonce.PubNonce,
+	})
+	require.NoError(t, err)
+
+	pubKeys := []*btcec.PublicKey{
+		node1.nodePub, node2.nodePub, node1.btcPub, node2.btcPub,
+	}
+
+	// Let Node1 sign the announcement message with its node key.
+	psA1, err := musig2.Sign(
+		node1NodeNonce.SecNonce, node1.nodePriv, nonceAgg, pubKeys,
+		msgBytes, musig2.WithSortedKeys(),
+	)
+	require.NoError(t, err)
+
+	// Let Node1 sign the announcement message with its bitcoin key.
+	psA2, err := musig2.Sign(
+		node1BtcNonce.SecNonce, node1.btcPriv, nonceAgg, pubKeys,
+		msgBytes, musig2.WithSortedKeys(),
+	)
+	require.NoError(t, err)
+
+	// Let Node2 sign the announcement message with its node key.
+	psB1, err := musig2.Sign(
+		node2NodeNonce.SecNonce, node2.nodePriv, nonceAgg, pubKeys,
+		msgBytes, musig2.WithSortedKeys(),
+	)
+	require.NoError(t, err)
+
+	// Let Node2 sign the announcement message with its bitcoin key.
+	psB2, err := musig2.Sign(
+		node2BtcNonce.SecNonce, node2.btcPriv, nonceAgg, pubKeys,
+		msgBytes, musig2.WithSortedKeys(),
+	)
+	require.NoError(t, err)
+
+	// Finally, combine the partial signatures from Node1 and Node2 and add
+	// the signature to the announcement message.
+	s := musig2.CombineSigs(psA1.R, []*musig2.PartialSignature{
+		psA1, psA2, psB1, psB2,
+	})
+
+	sig, err := lnwire.NewSigFromSignature(s)
+	require.NoError(t, err)
+
+	ann.Signature = sig
+
+	// Validate the announcement.
+	require.NoError(t, ValidateChannelAnn(ann, nil))
+}
+
+// test3of3MuSig2ChanAnnouncement covers the case where no bitcoin keys are
+// present in the channel announcement. In this case, the signature should be
+// a 3-of-3 MuSig2 where the keys making up the pub key are: node1 ID, node2 ID
+// and the output key found on-chain in the funding transaction. As the
+// verifier, we don't care about the construction of the output key. We only
+// care that the channel peers were able to sign for the output key. In reality,
+// this key will likely be constructed from at least 1 key from each peer and
+// the partial signature for it will be constructed via nested MuSig2 but for
+// the sake of the test, we will just have it be backed by a single key.
+func test3of3MuSig2ChanAnnouncement(t *testing.T) {
+	// Generate the keys for node 1 and node 2.
+	node1, node2 := genChanAnnKeys(t)
+
+	// Build the unsigned channel announcement.
+	ann := buildUnsignedChanAnnouncement(node1, node2, false)
+
+	// Serialise the bytes that need to be signed.
+	msg, err := ChanAnn2DigestToSign(ann)
+	require.NoError(t, err)
+
+	var msgBytes [32]byte
+	copy(msgBytes[:], msg.CloneBytes())
+
+	// Create a random 3rd key to be used for the output key.
+	outputKeyPriv, err := btcec.NewPrivateKey()
+	require.NoError(t, err)
+
+	outputKey := outputKeyPriv.PubKey()
+
+	// Ensure that the output key has an even Y by negating the private key
+	// if required.
+	if outputKey.SerializeCompressed()[0] ==
+		input.PubKeyFormatCompressedOdd {
+
+		outputKeyPriv.Key.Negate()
+		outputKey = outputKeyPriv.PubKey()
+	}
+
+	// Generate the nonces required for producing the partial signatures.
+	var (
+		node1NodeNonce = genNonceForPubKey(t, node1.nodePub)
+		node2NodeNonce = genNonceForPubKey(t, node2.nodePub)
+		outputKeyNonce = genNonceForPubKey(t, outputKey)
+	)
+
+	nonceAgg, err := musig2.AggregateNonces([][66]byte{
+		node1NodeNonce.PubNonce,
+		node2NodeNonce.PubNonce,
+		outputKeyNonce.PubNonce,
+	})
+	require.NoError(t, err)
+
+	pkScript, err := input.PayToTaprootScript(outputKey)
+	require.NoError(t, err)
+
+	// We'll pass in a mock tx fetcher that will return the funding output
+	// containing this key. This is needed since the output key can not be
+	// determined from the channel announcement itself.
+	fetchTx := func(chanID *lnwire.ShortChannelID) ([]byte, error) {
+		return pkScript, nil
+	}
+
+	pubKeys := []*btcec.PublicKey{node1.nodePub, node2.nodePub, outputKey}
+
+	// Let Node1 sign the announcement message with its node key.
+	psA, err := musig2.Sign(
+		node1NodeNonce.SecNonce, node1.nodePriv, nonceAgg, pubKeys,
+		msgBytes, musig2.WithSortedKeys(),
+	)
+	require.NoError(t, err)
+
+	// Let Node2 sign the announcement message with its node key.
+	psB, err := musig2.Sign(
+		node2NodeNonce.SecNonce, node2.nodePriv, nonceAgg, pubKeys,
+		msgBytes, musig2.WithSortedKeys(),
+	)
+	require.NoError(t, err)
+
+	// Create a partial sig for the output key.
+	psO, err := musig2.Sign(
+		outputKeyNonce.SecNonce, outputKeyPriv, nonceAgg, pubKeys,
+		msgBytes, musig2.WithSortedKeys(),
+	)
+	require.NoError(t, err)
+
+	// Finally, combine the partial signatures from Node1 and Node2 and add
+	// the signature to the announcement message.
+	s := musig2.CombineSigs(psA.R, []*musig2.PartialSignature{
+		psA, psB, psO,
+	})
+
+	sig, err := lnwire.NewSigFromSignature(s)
+	require.NoError(t, err)
+
+	ann.Signature = sig
+
+	// Validate the announcement.
+	require.NoError(t, ValidateChannelAnn(ann, fetchTx))
+}
+
+func genNonceForPubKey(t *testing.T, pub *btcec.PublicKey) *musig2.Nonces {
+	nonce, err := musig2.GenNonces(musig2.WithPublicKey(pub))
+	require.NoError(t, err)
+
+	return nonce
+}
+
+type keyRing struct {
+	nodePriv *btcec.PrivateKey
+	nodePub  *btcec.PublicKey
+	btcPriv  *btcec.PrivateKey
+	btcPub   *btcec.PublicKey
+}
+
+func genChanAnnKeys(t *testing.T) (*keyRing, *keyRing) {
+	// Let Alice and Bob derive the various keys they need.
+	aliceNodePrivKey, err := btcec.NewPrivateKey()
+	require.NoError(t, err)
+
+	aliceNodeID := aliceNodePrivKey.PubKey()
+
+	aliceBtcPrivKey, err := btcec.NewPrivateKey()
+	require.NoError(t, err)
+
+	bobNodePrivKey, err := btcec.NewPrivateKey()
+	require.NoError(t, err)
+
+	bobNodeID := bobNodePrivKey.PubKey()
+
+	bobBtcPrivKey, err := btcec.NewPrivateKey()
+	require.NoError(t, err)
+
+	alice := &keyRing{
+		nodePriv: aliceNodePrivKey,
+		nodePub:  aliceNodePrivKey.PubKey(),
+		btcPriv:  aliceBtcPrivKey,
+		btcPub:   aliceBtcPrivKey.PubKey(),
+	}
+
+	bob := &keyRing{
+		nodePriv: bobNodePrivKey,
+		nodePub:  bobNodePrivKey.PubKey(),
+		btcPriv:  bobBtcPrivKey,
+		btcPub:   bobBtcPrivKey.PubKey(),
+	}
+
+	if bytes.Compare(
+		aliceNodeID.SerializeCompressed(),
+		bobNodeID.SerializeCompressed(),
+	) != -1 {
+
+		return bob, alice
+	}
+
+	return alice, bob
+}
+
+func buildUnsignedChanAnnouncement(node1, node2 *keyRing,
+	withBtcKeys bool) *lnwire.ChannelAnnouncement2 {
+
+	var ann lnwire.ChannelAnnouncement2
+	ann.ChainHash.Val = *chaincfg.MainNetParams.GenesisHash
+	features := lnwire.NewRawFeatureVector()
+	ann.Features.Val = *features
+	ann.ShortChannelID.Val = lnwire.ShortChannelID{
+		BlockHeight: 1000,
+		TxIndex:     100,
+		TxPosition:  0,
+	}
+	ann.Capacity.Val = 100000
+
+	copy(ann.NodeID1.Val[:], node1.nodePub.SerializeCompressed())
+	copy(ann.NodeID2.Val[:], node2.nodePub.SerializeCompressed())
+
+	if !withBtcKeys {
+		return &ann
+	}
+
+	btcKey1Bytes := tlv.ZeroRecordT[tlv.TlvType12, [33]byte]()
+	btcKey2Bytes := tlv.ZeroRecordT[tlv.TlvType14, [33]byte]()
+
+	copy(btcKey1Bytes.Val[:], node1.btcPub.SerializeCompressed())
+	copy(btcKey2Bytes.Val[:], node2.btcPub.SerializeCompressed())
+
+	ann.BitcoinKey1 = tlv.SomeRecordT(btcKey1Bytes)
+	ann.BitcoinKey2 = tlv.SomeRecordT(btcKey2Bytes)
+
+	return &ann
+}
diff --git a/netann/channel_update_test.go b/netann/channel_update_test.go
index 7af51effc..2a619e062 100644
--- a/netann/channel_update_test.go
+++ b/netann/channel_update_test.go
@@ -7,7 +7,6 @@ import (
 
 	"github.com/btcsuite/btcd/btcec/v2"
 	"github.com/btcsuite/btcd/btcec/v2/ecdsa"
-	"github.com/lightningnetwork/lnd/graph"
 	"github.com/lightningnetwork/lnd/keychain"
 	"github.com/lightningnetwork/lnd/lnwallet"
 	"github.com/lightningnetwork/lnd/lnwire"
@@ -111,7 +110,7 @@ func TestUpdateDisableFlag(t *testing.T) {
 			// Create the initial update, the only fields we are
 			// concerned with in this test are the timestamp and the
 			// channel flags.
-			ogUpdate := &lnwire.ChannelUpdate{
+			ogUpdate := &lnwire.ChannelUpdate1{
 				Timestamp: uint32(tc.startTime.Unix()),
 			}
 			if !tc.startEnabled {
@@ -122,7 +121,7 @@ func TestUpdateDisableFlag(t *testing.T) {
 			// the original. UpdateDisableFlag will mutate the
 			// passed channel update, so we keep the old one to test
 			// against.
-			newUpdate := &lnwire.ChannelUpdate{
+			newUpdate := &lnwire.ChannelUpdate1{
 				Timestamp:    ogUpdate.Timestamp,
 				ChannelFlags: ogUpdate.ChannelFlags,
 			}
@@ -182,7 +181,7 @@ func TestUpdateDisableFlag(t *testing.T) {
 
 			// Finally, validate the signature using the router's
 			// verification logic.
-			err = graph.VerifyChannelUpdateSignature(
+			err = netann.VerifyChannelUpdateSignature(
 				newUpdate, pubKey,
 			)
 			if err != nil {
diff --git a/peer/test_utils.go b/peer/test_utils.go
index e0ae29be8..0575acca5 100644
--- a/peer/test_utils.go
+++ b/peer/test_utils.go
@@ -611,7 +611,7 @@ func createTestPeer(t *testing.T) *peerTestCtx {
 		IsChannelActive: func(lnwire.ChannelID) bool {
 			return true
 		},
-		ApplyChannelUpdate: func(*lnwire.ChannelUpdate,
+		ApplyChannelUpdate: func(*lnwire.ChannelUpdate1,
 			*wire.OutPoint, bool) error {
 
 			return nil
@@ -719,9 +719,9 @@ func createTestPeer(t *testing.T) *peerTestCtx {
 		},
 		PongBuf: make([]byte, lnwire.MaxPongBytes),
 		FetchLastChanUpdate: func(chanID lnwire.ShortChannelID,
-		) (*lnwire.ChannelUpdate, error) {
+		) (*lnwire.ChannelUpdate1, error) {
 
-			return &lnwire.ChannelUpdate{}, nil
+			return &lnwire.ChannelUpdate1{}, nil
 		},
 	}
 
diff --git a/routing/missioncontrol_test.go b/routing/missioncontrol_test.go
index 27391d53e..4a0f73871 100644
--- a/routing/missioncontrol_test.go
+++ b/routing/missioncontrol_test.go
@@ -197,7 +197,7 @@ func TestMissionControl(t *testing.T) {
 
 	// A node level failure should bring probability of all known channels
 	// back to zero.
-	ctx.reportFailure(0, lnwire.NewExpiryTooSoon(lnwire.ChannelUpdate{}))
+	ctx.reportFailure(0, lnwire.NewExpiryTooSoon(lnwire.ChannelUpdate1{}))
 	ctx.expectP(1000, 0)
 
 	// Check whether history snapshot looks sane.
@@ -219,14 +219,14 @@ func TestMissionControlChannelUpdate(t *testing.T) {
 	// Report a policy related failure. Because it is the first, we don't
 	// expect a penalty.
 	ctx.reportFailure(
-		0, lnwire.NewFeeInsufficient(0, lnwire.ChannelUpdate{}),
+		0, lnwire.NewFeeInsufficient(0, lnwire.ChannelUpdate1{}),
 	)
 	ctx.expectP(100, testAprioriHopProbability)
 
 	// Report another failure for the same channel. We expect it to be
 	// pruned.
 	ctx.reportFailure(
-		0, lnwire.NewFeeInsufficient(0, lnwire.ChannelUpdate{}),
+		0, lnwire.NewFeeInsufficient(0, lnwire.ChannelUpdate1{}),
 	)
 	ctx.expectP(100, 0)
 }
diff --git a/routing/mock_test.go b/routing/mock_test.go
index 28464a378..32f83420f 100644
--- a/routing/mock_test.go
+++ b/routing/mock_test.go
@@ -186,7 +186,7 @@ func (m *mockPaymentSessionOld) RequestRoute(_, _ lnwire.MilliSatoshi,
 	return r, nil
 }
 
-func (m *mockPaymentSessionOld) UpdateAdditionalEdge(_ *lnwire.ChannelUpdate,
+func (m *mockPaymentSessionOld) UpdateAdditionalEdge(_ *lnwire.ChannelUpdate1,
 	_ *btcec.PublicKey, _ *models.CachedEdgePolicy) bool {
 
 	return false
@@ -710,7 +710,7 @@ func (m *mockPaymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
 	return args.Get(0).(*route.Route), args.Error(1)
 }
 
-func (m *mockPaymentSession) UpdateAdditionalEdge(msg *lnwire.ChannelUpdate,
+func (m *mockPaymentSession) UpdateAdditionalEdge(msg *lnwire.ChannelUpdate1,
 	pubKey *btcec.PublicKey, policy *models.CachedEdgePolicy) bool {
 
 	args := m.Called(msg, pubKey, policy)
diff --git a/routing/payment_session_test.go b/routing/payment_session_test.go
index 34f835682..d510f77f9 100644
--- a/routing/payment_session_test.go
+++ b/routing/payment_session_test.go
@@ -147,7 +147,7 @@ func TestUpdateAdditionalEdge(t *testing.T) {
 	)
 
 	// Create the channel update message and sign.
-	msg := &lnwire.ChannelUpdate{
+	msg := &lnwire.ChannelUpdate1{
 		ShortChannelID: lnwire.NewShortChanIDFromInt(testChannelID),
 		Timestamp:      uint32(time.Now().Unix()),
 		BaseFee:        newFeeBaseMSat,
diff --git a/routing/result_interpretation_test.go b/routing/result_interpretation_test.go
index bf7d6d3ed..68b527e5a 100644
--- a/routing/result_interpretation_test.go
+++ b/routing/result_interpretation_test.go
@@ -164,7 +164,7 @@ var resultTestCases = []resultTestCase{
 		name:          "fail expiry too soon",
 		route:         &routeFourHop,
 		failureSrcIdx: 3,
-		failure:       lnwire.NewExpiryTooSoon(lnwire.ChannelUpdate{}),
+		failure:       lnwire.NewExpiryTooSoon(lnwire.ChannelUpdate1{}),
 
 		expectedResult: &interpretedResult{
 			pairResults: map[DirectedNodePair]pairResult{
@@ -266,8 +266,9 @@ var resultTestCases = []resultTestCase{
 		name:          "fail fee insufficient intermediate",
 		route:         &routeFourHop,
 		failureSrcIdx: 2,
-		failure:       lnwire.NewFeeInsufficient(0, lnwire.ChannelUpdate{}),
-
+		failure: lnwire.NewFeeInsufficient(
+			0, lnwire.ChannelUpdate1{},
+		),
 		expectedResult: &interpretedResult{
 			pairResults: map[DirectedNodePair]pairResult{
 				getTestPair(0, 1): {
diff --git a/routing/router_test.go b/routing/router_test.go
index 7726091fc..62b015abb 100644
--- a/routing/router_test.go
+++ b/routing/router_test.go
@@ -223,7 +223,7 @@ func createTestCtxFromFile(t *testing.T,
 // Add valid signature to channel update simulated as error received from the
 // network.
 func signErrChanUpdate(t *testing.T, key *btcec.PrivateKey,
-	errChanUpdate *lnwire.ChannelUpdate) {
+	errChanUpdate *lnwire.ChannelUpdate1) {
 
 	chanUpdateMsg, err := errChanUpdate.DataToSign()
 	require.NoError(t, err, "failed to retrieve data to sign")
@@ -488,7 +488,7 @@ func TestChannelUpdateValidation(t *testing.T) {
 	// Set up a channel update message with an invalid signature to be
 	// returned to the sender.
 	var invalidSignature lnwire.Sig
-	errChanUpdate := lnwire.ChannelUpdate{
+	errChanUpdate := lnwire.ChannelUpdate1{
 		Signature:       invalidSignature,
 		FeeRate:         500,
 		ShortChannelID:  lnwire.NewShortChanIDFromInt(1),
@@ -593,7 +593,7 @@ func TestSendPaymentErrorRepeatedFeeInsufficient(t *testing.T) {
 	)
 	require.NoError(t, err, "unable to fetch chan id")
 
-	errChanUpdate := lnwire.ChannelUpdate{
+	errChanUpdate := lnwire.ChannelUpdate1{
 		ShortChannelID: lnwire.NewShortChanIDFromInt(
 			songokuSophonChanID,
 		),
@@ -712,7 +712,7 @@ func TestSendPaymentErrorFeeInsufficientPrivateEdge(t *testing.T) {
 	// Prepare an error update for the private channel, with twice the
 	// original fee.
 	updatedFeeBaseMSat := feeBaseMSat * 2
-	errChanUpdate := lnwire.ChannelUpdate{
+	errChanUpdate := lnwire.ChannelUpdate1{
 		ShortChannelID: lnwire.NewShortChanIDFromInt(privateChannelID),
 		Timestamp:      uint32(testTime.Add(time.Minute).Unix()),
 		BaseFee:        updatedFeeBaseMSat,
@@ -838,7 +838,7 @@ func TestSendPaymentPrivateEdgeUpdateFeeExceedsLimit(t *testing.T) {
 	// Prepare an error update for the private channel. The updated fee
 	// will exceeds the feeLimit.
 	updatedFeeBaseMSat := feeBaseMSat + uint32(feeLimit)
-	errChanUpdate := lnwire.ChannelUpdate{
+	errChanUpdate := lnwire.ChannelUpdate1{
 		ShortChannelID: lnwire.NewShortChanIDFromInt(privateChannelID),
 		Timestamp:      uint32(testTime.Add(time.Minute).Unix()),
 		BaseFee:        updatedFeeBaseMSat,
@@ -939,7 +939,7 @@ func TestSendPaymentErrorNonFinalTimeLockErrors(t *testing.T) {
 	_, _, edgeUpdateToFail, err := ctx.graph.FetchChannelEdgesByID(chanID)
 	require.NoError(t, err, "unable to fetch chan id")
 
-	errChanUpdate := lnwire.ChannelUpdate{
+	errChanUpdate := lnwire.ChannelUpdate1{
 		ShortChannelID:  lnwire.NewShortChanIDFromInt(chanID),
 		Timestamp:       uint32(edgeUpdateToFail.LastUpdate.Unix()),
 		MessageFlags:    edgeUpdateToFail.MessageFlags,
@@ -1402,7 +1402,7 @@ func TestSendToRouteStructuredError(t *testing.T) {
 	testCases := map[int]lnwire.FailureMessage{
 		finalHopIndex: lnwire.NewFailIncorrectDetails(payAmt, 100),
 		1: &lnwire.FailFeeInsufficient{
-			Update: lnwire.ChannelUpdate{},
+			Update: lnwire.ChannelUpdate1{},
 		},
 	}
 
@@ -2967,7 +2967,7 @@ func (m *mockGraphBuilder) setNextReject(reject bool) {
 	m.rejectUpdate = reject
 }
 
-func (m *mockGraphBuilder) ApplyChannelUpdate(msg *lnwire.ChannelUpdate) bool {
+func (m *mockGraphBuilder) ApplyChannelUpdate(msg *lnwire.ChannelUpdate1) bool {
 	if m.rejectUpdate {
 		return false
 	}