What changed, and why it matters
This commit is a straightforward internal code reorganization: it moves some data types and helper functions related to forwarding packages from one package (channeldb) to another (chanstate), while keeping aliases in the original package so existing code continues to compile. There is no functional change, no bug fix, and no security-relevant behavior change visible in the diff.
No security action required. Treat as routine refactoring.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change relocates FwdState, PkgFilter, FwdPkg, their constructors, and methods from channeldb/forwarding_package.go to chanstate/forwarding.go. channeldb now uses type aliases (e.g., type FwdPkg = cstate.FwdPkg) and variable aliases for constructors/constants to preserve backward compatibility for callers. The serialization logic, bitvector operations, and struct definitions are copied verbatim. One minor implementation difference: FwdPkg.ID() now uses binary.BigEndian.PutUint64 directly instead of the package-level byteOrder helper, but this is semantically equivalent. No security logic was added, removed, or altered.
Changed components
channeldb/forwarding_package.gochanstate/forwarding.goInspect captured patch +279 / −248
diff --git a/channeldb/forwarding_package.go b/channeldb/forwarding_package.go
index 8ba2c95..31ec1cc 100644
--- a/channeldb/forwarding_package.go
+++ b/channeldb/forwarding_package.go
@@ -2,10 +2,7 @@ package channeldb
import (
"bytes"
- "encoding/binary"
"errors"
- "fmt"
- "io"
cstate "github.com/lightningnetwork/lnd/chanstate"
"github.com/lightningnetwork/lnd/kvdb"
@@ -19,33 +16,44 @@ type (
// SettleFailRef is used to locate a Settle/Fail in another channel's
// FwdPkg.
SettleFailRef = cstate.SettleFailRef
-)
-// ErrCorruptedFwdPkg signals that the on-disk structure of the forwarding
-// package has potentially been mangled.
-var ErrCorruptedFwdPkg = errors.New("fwding package db has been corrupted")
+ // FwdState is an enum used to describe the lifecycle of a FwdPkg.
+ FwdState = cstate.FwdState
+
+ // PkgFilter is used to compactly represent a particular subset of the
+ // Adds in a forwarding package.
+ PkgFilter = cstate.PkgFilter
-// FwdState is an enum used to describe the lifecycle of a FwdPkg.
-type FwdState byte
+ // FwdPkg records all adds, settles, and fails that were locked in as a
+ // result of the remote peer sending us a revocation.
+ FwdPkg = cstate.FwdPkg
+)
const (
// FwdStateLockedIn is the starting state for all forwarding packages.
- // Packages in this state have not yet committed to the exact set of
- // Adds to forward to the switch.
- FwdStateLockedIn FwdState = iota
+ FwdStateLockedIn = cstate.FwdStateLockedIn
// FwdStateProcessed marks the state in which all Adds have been
- // locally processed and the forwarding decision to the switch has been
- // persisted.
- FwdStateProcessed
-
- // FwdStateCompleted signals that all Adds have been acked, and that all
- // settles and fails have been delivered to their sources. Packages in
- // this state can be removed permanently.
- FwdStateCompleted
+ // locally processed.
+ FwdStateProcessed = cstate.FwdStateProcessed
+
+ // FwdStateCompleted signals that all Adds have been acked, and that
+ // all settles and fails have been delivered to their sources.
+ FwdStateCompleted = cstate.FwdStateCompleted
)
var (
+ // NewPkgFilter initializes an empty PkgFilter supporting `count`
+ // elements.
+ NewPkgFilter = cstate.NewPkgFilter
+
+ // NewFwdPkg initializes a new forwarding package in FwdStateLockedIn.
+ NewFwdPkg = cstate.NewFwdPkg
+
+ // ErrCorruptedFwdPkg signals that the on-disk structure of the
+ // forwarding package has potentially been mangled.
+ ErrCorruptedFwdPkg = errors.New("fwding package db has been corrupted")
+
// fwdPackagesKey is the root-level bucket that all forwarding packages
// are written. This bucket is further subdivided based on the short
// channel ID of each channel.
@@ -109,234 +117,6 @@ var (
settleFailFilterKey = []byte("settle-fail-filter-key")
)
-// PkgFilter is used to compactly represent a particular subset of the Adds in a
-// forwarding package. Each filter is represented as a simple, statically-sized
-// bitvector, where the elements are intended to be the indices of the Adds as
-// they are written in the FwdPkg.
-type PkgFilter struct {
- count uint16
- filter []byte
-}
-
-// NewPkgFilter initializes an empty PkgFilter supporting `count` elements.
-func NewPkgFilter(count uint16) *PkgFilter {
- // We add 7 to ensure that the integer division yields properly rounded
- // values.
- filterLen := (count + 7) / 8
-
- return &PkgFilter{
- count: count,
- filter: make([]byte, filterLen),
- }
-}
-
-// Count returns the number of elements represented by this PkgFilter.
-func (f *PkgFilter) Count() uint16 {
- return f.count
-}
-
-// Set marks the `i`-th element as included by this filter.
-// NOTE: It is assumed that i is always less than count.
-func (f *PkgFilter) Set(i uint16) {
- byt := i / 8
- bit := i % 8
-
- // Set the i-th bit in the filter.
- // TODO(conner): ignore if > count to prevent panic?
- f.filter[byt] |= byte(1 << (7 - bit))
-}
-
-// Contains queries the filter for membership of index `i`.
-// NOTE: It is assumed that i is always less than count.
-func (f *PkgFilter) Contains(i uint16) bool {
- byt := i / 8
- bit := i % 8
-
- // Read the i-th bit in the filter.
- // TODO(conner): ignore if > count to prevent panic?
- return f.filter[byt]&(1<<(7-bit)) != 0
-}
-
-// Equal checks two PkgFilters for equality.
-func (f *PkgFilter) Equal(f2 *PkgFilter) bool {
- if f == f2 {
- return true
- }
- if f.count != f2.count {
- return false
- }
-
- return bytes.Equal(f.filter, f2.filter)
-}
-
-// IsFull returns true if every element in the filter has been Set, and false
-// otherwise.
-func (f *PkgFilter) IsFull() bool {
- // Batch validate bytes that are fully used.
- for i := uint16(0); i < f.count/8; i++ {
- if f.filter[i] != 0xFF {
- return false
- }
- }
-
- // If the count is not a multiple of 8, check that the filter contains
- // all remaining bits.
- rem := f.count % 8
- for idx := f.count - rem; idx < f.count; idx++ {
- if !f.Contains(idx) {
- return false
- }
- }
-
- return true
-}
-
-// Size returns number of bytes produced when the PkgFilter is serialized.
-func (f *PkgFilter) Size() uint16 {
- // 2 bytes for uint16 `count`, then round up number of bytes required to
- // represent `count` bits.
- return 2 + (f.count+7)/8
-}
-
-// Encode writes the filter to the provided io.Writer.
-func (f *PkgFilter) Encode(w io.Writer) error {
- if err := binary.Write(w, binary.BigEndian, f.count); err != nil {
- return err
- }
-
- _, err := w.Write(f.filter)
-
- return err
-}
-
-// Decode reads the filter from the provided io.Reader.
-func (f *PkgFilter) Decode(r io.Reader) error {
- if err := binary.Read(r, binary.BigEndian, &f.count); err != nil {
- return err
- }
-
- f.filter = make([]byte, f.Size()-2)
- _, err := io.ReadFull(r, f.filter)
-
- return err
-}
-
-// String returns a human-readable string.
-func (f *PkgFilter) String() string {
- return fmt.Sprintf("count=%v, filter=%v", f.count, f.filter)
-}
-
-// FwdPkg records all adds, settles, and fails that were locked in as a result
-// of the remote peer sending us a revocation. Each package is identified by
-// the short chanid and remote commitment height corresponding to the revocation
-// that locked in the HTLCs. For everything except a locally initiated payment,
-// settles and fails in a forwarding package must have a corresponding Add in
-// another package, and can be removed individually once the source link has
-// received the fail/settle.
-//
-// Adds cannot be removed, as we need to present the same batch of Adds to
-// properly handle replay protection. Instead, we use a PkgFilter to mark that
-// we have finished processing a particular Add. A FwdPkg should only be deleted
-// after the AckFilter is full and all settles and fails have been persistently
-// removed.
-type FwdPkg struct {
- // Source identifies the channel that wrote this forwarding package.
- Source lnwire.ShortChannelID
-
- // Height is the height of the remote commitment chain that locked in
- // this forwarding package.
- Height uint64
-
- // State signals the persistent condition of the package and directs how
- // to reprocess the package in the event of failures.
- State FwdState
-
- // Adds contains all add messages which need to be processed and
- // forwarded to the switch. Adds does not change over the life of a
- // forwarding package.
- Adds []LogUpdate
-
- // FwdFilter is a filter containing the indices of all Adds that were
- // forwarded to the switch.
- //
- // NOTE: This value signals when persisted to disk that the fwd package
- // has been processed and garbage collection can happen. So it also
- // has to be set for packages with no adds (empty packages or only
- // settle/fail packages) so that they can be garbage collected as well.
- FwdFilter *PkgFilter
-
- // AckFilter is a filter containing the indices of all Adds for which
- // the source has received a settle or fail and is reflected in the next
- // commitment txn. A package should not be removed until IsFull()
- // returns true.
- AckFilter *PkgFilter
-
- // SettleFails contains all settle and fail messages that should be
- // forwarded to the switch.
- SettleFails []LogUpdate
-
- // SettleFailFilter is a filter containing the indices of all Settle or
- // Fails originating in this package that have been received and locked
- // into the incoming link's commitment state.
- SettleFailFilter *PkgFilter
-}
-
-// NewFwdPkg initializes a new forwarding package in FwdStateLockedIn. This
-// should be used to create a package at the time we receive a revocation.
-func NewFwdPkg(source lnwire.ShortChannelID, height uint64,
- addUpdates, settleFailUpdates []LogUpdate) *FwdPkg {
-
- nAddUpdates := uint16(len(addUpdates))
- nSettleFailUpdates := uint16(len(settleFailUpdates))
-
- return &FwdPkg{
- Source: source,
- Height: height,
- State: FwdStateLockedIn,
- Adds: addUpdates,
- FwdFilter: NewPkgFilter(nAddUpdates),
- AckFilter: NewPkgFilter(nAddUpdates),
- SettleFails: settleFailUpdates,
- SettleFailFilter: NewPkgFilter(nSettleFailUpdates),
- }
-}
-
-// SourceRef is a convenience method that returns an AddRef to this forwarding
-// package for the index in the argument. It is the caller's responsibility
-// to ensure that the index is in bounds.
-func (f *FwdPkg) SourceRef(i uint16) AddRef {
- return AddRef{
- Height: f.Height,
- Index: i,
- }
-}
-
-// DestRef is a convenience method that returns a SettleFailRef to this
-// forwarding package for the index in the argument. It is the caller's
-// responsibility to ensure that the index is in bounds.
-func (f *FwdPkg) DestRef(i uint16) SettleFailRef {
- return SettleFailRef{
- Source: f.Source,
- Height: f.Height,
- Index: i,
- }
-}
-
-// ID returns an unique identifier for this package, used to ensure that sphinx
-// replay processing of this batch is idempotent.
-func (f *FwdPkg) ID() []byte {
- var id = make([]byte, 16)
- byteOrder.PutUint64(id[:8], f.Source.ToUint64())
- byteOrder.PutUint64(id[8:], f.Height)
- return id
-}
-
-// String returns a human-readable description of the forwarding package.
-func (f *FwdPkg) String() string {
- return fmt.Sprintf("%T(src=%v, height=%v, nadds=%v, nfailsettles=%v)",
- f, f.Source, f.Height, len(f.Adds), len(f.SettleFails))
-}
-
// SettleFailAcker is a generic interface providing the ability to acknowledge
// settle/fail HTLCs stored in forwarding packages.
type SettleFailAcker interface {
diff --git a/chanstate/forwarding.go b/chanstate/forwarding.go
index 9cc830f..49728fe 100644
--- a/chanstate/forwarding.go
+++ b/chanstate/forwarding.go
@@ -1,7 +1,9 @@
package chanstate
import (
+ "bytes"
"encoding/binary"
+ "fmt"
"io"
"github.com/lightningnetwork/lnd/lnwire"
@@ -55,3 +57,252 @@ type SettleFailRef struct {
// NOTE: This index is static over the lifetime of a forwarding package.
Index uint16
}
+
+// FwdState is an enum used to describe the lifecycle of a FwdPkg.
+type FwdState byte
+
+const (
+ // FwdStateLockedIn is the starting state for all forwarding packages.
+ // Packages in this state have not yet committed to the exact set of
+ // Adds to forward to the switch.
+ FwdStateLockedIn FwdState = iota
+
+ // FwdStateProcessed marks the state in which all Adds have been
+ // locally processed and the forwarding decision to the switch has been
+ // persisted.
+ FwdStateProcessed
+
+ // FwdStateCompleted signals that all Adds have been acked, and that all
+ // settles and fails have been delivered to their sources. Packages in
+ // this state can be removed permanently.
+ FwdStateCompleted
+)
+
+// PkgFilter is used to compactly represent a particular subset of the Adds in a
+// forwarding package. Each filter is represented as a simple, statically-sized
+// bitvector, where the elements are intended to be the indices of the Adds as
+// they are written in the FwdPkg.
+type PkgFilter struct {
+ count uint16
+ filter []byte
+}
+
+// NewPkgFilter initializes an empty PkgFilter supporting `count` elements.
+func NewPkgFilter(count uint16) *PkgFilter {
+ // We add 7 to ensure that the integer division yields properly rounded
+ // values.
+ filterLen := (count + 7) / 8
+
+ return &PkgFilter{
+ count: count,
+ filter: make([]byte, filterLen),
+ }
+}
+
+// Count returns the number of elements represented by this PkgFilter.
+func (f *PkgFilter) Count() uint16 {
+ return f.count
+}
+
+// Set marks the `i`-th element as included by this filter.
+// NOTE: It is assumed that i is always less than count.
+func (f *PkgFilter) Set(i uint16) {
+ byt := i / 8
+ bit := i % 8
+
+ // Set the i-th bit in the filter.
+ // TODO(conner): ignore if > count to prevent panic?
+ f.filter[byt] |= byte(1 << (7 - bit))
+}
+
+// Contains queries the filter for membership of index `i`.
+// NOTE: It is assumed that i is always less than count.
+func (f *PkgFilter) Contains(i uint16) bool {
+ byt := i / 8
+ bit := i % 8
+
+ // Read the i-th bit in the filter.
+ // TODO(conner): ignore if > count to prevent panic?
+ return f.filter[byt]&(1<<(7-bit)) != 0
+}
+
+// Equal checks two PkgFilters for equality.
+func (f *PkgFilter) Equal(f2 *PkgFilter) bool {
+ if f == f2 {
+ return true
+ }
+ if f.count != f2.count {
+ return false
+ }
+
+ return bytes.Equal(f.filter, f2.filter)
+}
+
+// IsFull returns true if every element in the filter has been Set, and false
+// otherwise.
+func (f *PkgFilter) IsFull() bool {
+ // Batch validate bytes that are fully used.
+ for i := uint16(0); i < f.count/8; i++ {
+ if f.filter[i] != 0xFF {
+ return false
+ }
+ }
+
+ // If the count is not a multiple of 8, check that the filter contains
+ // all remaining bits.
+ rem := f.count % 8
+ for idx := f.count - rem; idx < f.count; idx++ {
+ if !f.Contains(idx) {
+ return false
+ }
+ }
+
+ return true
+}
+
+// Size returns number of bytes produced when the PkgFilter is serialized.
+func (f *PkgFilter) Size() uint16 {
+ // 2 bytes for uint16 `count`, then round up number of bytes required to
+ // represent `count` bits.
+ return 2 + (f.count+7)/8
+}
+
+// Encode writes the filter to the provided io.Writer.
+func (f *PkgFilter) Encode(w io.Writer) error {
+ if err := binary.Write(w, binary.BigEndian, f.count); err != nil {
+ return err
+ }
+
+ _, err := w.Write(f.filter)
+
+ return err
+}
+
+// Decode reads the filter from the provided io.Reader.
+func (f *PkgFilter) Decode(r io.Reader) error {
+ if err := binary.Read(r, binary.BigEndian, &f.count); err != nil {
+ return err
+ }
+
+ f.filter = make([]byte, f.Size()-2)
+ _, err := io.ReadFull(r, f.filter)
+
+ return err
+}
+
+// String returns a human-readable string.
+func (f *PkgFilter) String() string {
+ return fmt.Sprintf("count=%v, filter=%v", f.count, f.filter)
+}
+
+// FwdPkg records all adds, settles, and fails that were locked in as a result
+// of the remote peer sending us a revocation. Each package is identified by
+// the short chanid and remote commitment height corresponding to the revocation
+// that locked in the HTLCs. For everything except a locally initiated payment,
+// settles and fails in a forwarding package must have a corresponding Add in
+// another package, and can be removed individually once the source link has
+// received the fail/settle.
+//
+// Adds cannot be removed, as we need to present the same batch of Adds to
+// properly handle replay protection. Instead, we use a PkgFilter to mark that
+// we have finished processing a particular Add. A FwdPkg should only be deleted
+// after the AckFilter is full and all settles and fails have been persistently
+// removed.
+type FwdPkg struct {
+ // Source identifies the channel that wrote this forwarding package.
+ Source lnwire.ShortChannelID
+
+ // Height is the height of the remote commitment chain that locked in
+ // this forwarding package.
+ Height uint64
+
+ // State signals the persistent condition of the package and directs how
+ // to reprocess the package in the event of failures.
+ State FwdState
+
+ // Adds contains all add messages which need to be processed and
+ // forwarded to the switch. Adds does not change over the life of a
+ // forwarding package.
+ Adds []LogUpdate
+
+ // FwdFilter is a filter containing the indices of all Adds that were
+ // forwarded to the switch.
+ //
+ // NOTE: This value signals when persisted to disk that the fwd package
+ // has been processed and garbage collection can happen. So it also
+ // has to be set for packages with no adds (empty packages or only
+ // settle/fail packages) so that they can be garbage collected as well.
+ FwdFilter *PkgFilter
+
+ // AckFilter is a filter containing the indices of all Adds for which
+ // the source has received a settle or fail and is reflected in the next
+ // commitment txn. A package should not be removed until IsFull()
+ // returns true.
+ AckFilter *PkgFilter
+
+ // SettleFails contains all settle and fail messages that should be
+ // forwarded to the switch.
+ SettleFails []LogUpdate
+
+ // SettleFailFilter is a filter containing the indices of all Settle or
+ // Fails originating in this package that have been received and locked
+ // into the incoming link's commitment state.
+ SettleFailFilter *PkgFilter
+}
+
+// NewFwdPkg initializes a new forwarding package in FwdStateLockedIn. This
+// should be used to create a package at the time we receive a revocation.
+func NewFwdPkg(source lnwire.ShortChannelID, height uint64,
+ addUpdates, settleFailUpdates []LogUpdate) *FwdPkg {
+
+ nAddUpdates := uint16(len(addUpdates))
+ nSettleFailUpdates := uint16(len(settleFailUpdates))
+
+ return &FwdPkg{
+ Source: source,
+ Height: height,
+ State: FwdStateLockedIn,
+ Adds: addUpdates,
+ FwdFilter: NewPkgFilter(nAddUpdates),
+ AckFilter: NewPkgFilter(nAddUpdates),
+ SettleFails: settleFailUpdates,
+ SettleFailFilter: NewPkgFilter(nSettleFailUpdates),
+ }
+}
+
+// SourceRef is a convenience method that returns an AddRef to this forwarding
+// package for the index in the argument. It is the caller's responsibility
+// to ensure that the index is in bounds.
+func (f *FwdPkg) SourceRef(i uint16) AddRef {
+ return AddRef{
+ Height: f.Height,
+ Index: i,
+ }
+}
+
+// DestRef is a convenience method that returns a SettleFailRef to this
+// forwarding package for the index in the argument. It is the caller's
+// responsibility to ensure that the index is in bounds.
+func (f *FwdPkg) DestRef(i uint16) SettleFailRef {
+ return SettleFailRef{
+ Source: f.Source,
+ Height: f.Height,
+ Index: i,
+ }
+}
+
+// ID returns an unique identifier for this package, used to ensure that sphinx
+// replay processing of this batch is idempotent.
+func (f *FwdPkg) ID() []byte {
+ var id = make([]byte, 16)
+ binary.BigEndian.PutUint64(id[:8], f.Source.ToUint64())
+ binary.BigEndian.PutUint64(id[8:], f.Height)
+
+ return id
+}
+
+// String returns a human-readable description of the forwarding package.
+func (f *FwdPkg) String() string {
+ return fmt.Sprintf("%T(src=%v, height=%v, nadds=%v, nfailsettles=%v)",
+ f, f.Source, f.Height, len(f.Adds), len(f.SettleFails))
+}
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.