Introduce the WaitingProofInner interface and two concrete
implementations — V1WaitingProof (AnnounceSignatures1) and
V2WaitingProof (AnnounceSignatures2 + optional aggregate MuSig2 nonce).
WaitingProof.Encode/Decode now dispatch on the type prefix byte added
in the previous commit, so the store can transparently persist either
proof variant.
The gossiper is updated with a V1 type assertion to maintain existing
behaviour; full V2 gossiper integration will follow when taproot channel
announcements are wired up.
No live code path creates V2 waiting proofs yet — this commit only
lands the codec and storage readiness so the schema is in place before
new writers are introduced.
Existing waiting proof records encode a bare isRemote flag followed by a
raw AnnounceSignatures1 payload. A future gossip v2 implementation will
store AnnounceSignatures2 (taproot) proofs in the same bucket, so each
record needs a discriminator byte to select the correct decoder.
This commit:
1. Defines WaitingProofTypeV1 (= 0x00) for the current
AnnounceSignatures1-based proofs.
2. Updates WaitingProofKey to 10 bytes [proofType(1) || scid(8) ||
isRemote(1)] to avoid cross-version key collisions.
3. Adds migration 35, which rewrites every existing record to prepend
the type byte and rewrites keys to the new format.
4. Updates WaitingProof.Encode/Decode to always write/expect the prefix.
The migration, codec changes, and tests are kept in one atomic commit so
there is no intermediate revision where the new Decode can encounter
unmigrated records.
There is validation which requires "read-only" middle ware
not specify a caveat name. But when you try to register a second
read-only middleware, there's validation which prevents double
registration for same caveat (though in this case the caveat name
is the empty string).
Update the dameon to permit the registration of multiple
read-only rpc middleware.
Add a gossip version parameter to ChannelView in the Store interface,
KV and SQL implementations, and the ChannelGraph wrapper. The KVStore
guards v2 requests with ErrVersionNotSupportedForKVDB; the SQLStore
filters by the requested version.
Add three new SQL queries to support version-scoped channel lookups:
- GetPublicV1ChannelsBySCID: public v1 channels in a SCID range,
ordered by SCID.
- GetPublicV2ChannelsBySCID: public v2 channels in a SCID range,
ordered by SCID.
- ListChannelsPaginatedV2: paginate v2 channels by internal DB ID,
used by ChanUpdateRange.
Add TestVersionedDBs/channel_view to verify that v1 and v2 channel
views each return only their respective channels.
Add a gossip version parameter to ForEachNode, ForEachNodeCached, and
NumZombies in the Store interface and propagate it through the KV and
SQL implementations and the ChannelGraph wrapper.
The KVStore gates each method against GossipVersion1, returning
ErrVersionNotSupportedForKVDB for any other version. The SQLStore uses
the version to filter the underlying queries.
All call sites—routing graph, autopilot, RPC server, and the graph
migration integration test—are updated to pass the appropriate version
explicitly.
Add version-free shadow methods to VersionedGraph so it satisfies the
routing.Graph, graphdb.NodeTraverser, and related interfaces used by the
channel router and RPC layer.
FetchNodeFeatures and ForEachNodeDirectedChannel delegate to the graph
cache when available, falling back to the store with the baked-in
version. ForEachNode, ForEachNodeCached, ChannelView, and
NodeUpdatesInHorizon all forward to the embedded ChannelGraph with the
version pre-applied.
Update server.go and rpcserver.go to pass s.v1Graph (a *VersionedGraph
wrapping the main graphDB with GossipVersion1) wherever the routing and
session interfaces are needed, replacing direct *ChannelGraph references
that no longer satisfy those interfaces after the version parameters were
added.
Propagate the gossip version parameter through DeleteChannelEdges,
IsPublicNode, and IsZombieEdge on ChannelGraph, passing it down to the
underlying Store. Previously these methods hard-coded GossipVersion1
internally; surfacing the parameter lets callers operate on the version
appropriate for the channel.
Also fix two call sites that were still passing *ChannelGraph where a
version-aware interface was expected:
- rpcserver.go AddInvoice now uses s.v1Graph (a *VersionedGraph) so
that the invoicesrpc.GraphSource interface—whose IsPublicNode method
does not take a version parameter—is satisfied.
- subrpcserver_config.go wraps graphDB in NewVersionedGraph with
GossipVersion1 when populating the invoicesrpc config Graph field
via reflection, for the same reason.
Add a gossip version parameter to FilterChannelRange in the Store interface,
both KV and SQL implementations, and the ChannelGraph wrapper.
KVStore guards against non-v1 versions with ErrVersionNotSupportedForKVDB.
SQLStore accepts any known gossip version, filtering the channel results by
version and using it in policy lookups. The SQL query still uses
GetPublicV1ChannelsBySCID for now (a TODO marks where a version-aware query
will be substituted in a follow-up).
VersionedGraph.FilterChannelRange shadows the ChannelGraph method with a
version-free signature, passing its baked-in version to the store. This keeps
the ChannelGraphTimeSeries interface and ChanSeries implementation unchanged.
Add TestFilterChannelRangeVersionGuard to verify that the KV store returns
ErrVersionNotSupportedForKVDB for v2 requests while the SQL store handles
them gracefully.
Add a gossip version parameter to MarkEdgeZombie in the Store interface,
both KV and SQL implementations, and the ChannelGraph wrapper, following
the same pattern established for MarkEdgeLive.
KVStore guards against non-v1 versions with ErrVersionNotSupportedForKVDB.
SQLStore accepts any known gossip version and uses it in the UpsertZombieChannel
call and cache invalidation.
Builder.MarkZombieEdge (the ad-hoc path for validation failures) passes
GossipVersion1 as all channels in that path are v1.
Change the IsZombieChannel / isStillZombieChannel function signature
throughout the gossip and routing stacks from
func(time.Time, time.Time) bool to func(ChannelUpdateInfo) bool.
This allows zombie detection to inspect the full channel update info—
including version and freshness type—rather than receiving two raw unix
timestamps that carry no meaning for v2 channels.
Builder.IsZombieChannel is updated to extract version-appropriate
freshness from the ChannelUpdateInfo: unix-time expiry for v1, and a
block-count expiry (derived from ChannelPruneExpiry and
avgBitcoinBlockTime) for v2. The gossipSyncer, SyncManager, and gossiper
Config fields are updated to use the new signature.
Replace the separate Node1UpdateTimestamp/Node2UpdateTimestamp (time.Time)
and Node1BlockHeight/Node2BlockHeight (uint32) fields in ChannelUpdateInfo
with a unified Node1Freshness/Node2Freshness pair typed as lnwire.Timestamp.
The lnwire.Timestamp interface (added in the previous commit) is either a
UnixTimestamp (v1) or BlockHeightTimestamp (v2), making it structurally
impossible to pass block-height values into a v1 constructor or vice versa.
Two version-specific constructors replace the old single constructor:
- NewV1ChannelUpdateInfo(scid, node1Time, node2Time time.Time)
- NewV2ChannelUpdateInfo(scid, node1BlockHeight, node2BlockHeight uint32)
Add Node1FreshnessTime/Node2FreshnessTime helper methods on ChannelUpdateInfo
to extract the underlying time.Time from a UnixTimestamp, which the discovery
syncer needs for its v1-only isStale/isSkewed and isStillZombieChannel checks.
All call sites in kv_store, sql_store, graph_test, and syncer are updated
accordingly.
Add a Timestamp interface for channel and node update ordering values
that abstracts over the two freshness semantics used by the gossip
versions:
- UnixTimestamp (uint64): seconds-since-epoch, used by v1 gossip
channels and nodes.
- BlockHeightTimestamp (uint32): block height, used by v2 gossip
channels and nodes.
Both concrete types implement IsZero() and Cmp(). Cmp returns an error
if the two operands are of different concrete types, preventing
accidental cross-version comparisons.
Change the isZombieChan callback in FilterKnownChanIDs (and its
ChannelGraphTimeSeries interface counterpart) from
func(time.Time, time.Time) bool to func(ChannelUpdateInfo) bool.
This allows callers to make version-aware zombie decisions using the full
ChannelUpdateInfo—including freshness type—rather than two raw time.Time
values that are meaningless for v2 channels.
The GossipSyncer adapts its v1-only isStillZombieChannel check by
wrapping it in a closure that extracts Node1/Node2FreshnessTime from the
ChannelUpdateInfo. All other call sites are updated accordingly.
Add a gossip version parameter to MarkEdgeLive throughout the stack:
- Store interface and KVStore/SQLStore implementations now take
lnwire.GossipVersion; KVStore rejects non-v1 with
ErrVersionNotSupportedForKVDB, SQLStore uses the version in the
DeleteZombieChannel query and cache invalidation.
- ChannelGraph.MarkEdgeLive passes the version through to both the
store call and the FetchChanInfos cache repopulation.
- FilterKnownChanIDs uses GossipVersion1 explicitly for its internal
MarkEdgeLive call; this site will be properly versioned when
FilterKnownChanIDs itself is versioned.
- graph.ChannelGraphSource interface and Builder.MarkEdgeLive updated
accordingly.
- Discovery gossiper and test mock updated to pass GossipVersion1 at
their (v1-only) call sites.
Introduce isPolicyZombie to handle version-specific channel staleness.
For v1 policies, staleness is measured by wall-clock time since the last
update (unchanged behaviour). For v2 policies, staleness is measured by
the number of blocks elapsed since LastBlockHeight, using
avgBitcoinBlockTime to convert the configured prune expiry into an
equivalent block count.
isZombieChannel is simplified to call isPolicyZombie for each edge and
inline the strict/non-strict pruning logic directly, removing the prior
indirect call through IsZombieChannel.
The native SQL payments migrations were previously gated behind test
build tags in migrations_dev.go. This commit promotes them into the
main migration sequence in migrations.go, making them available in
production builds.
The following migrations are moved to mainline:
- 000010_payments (v12): initial payments SQL schema
- 000011_payment_duplicates (v13): duplicate payment support
- kv_payments_migration (v14): optional KV to SQL payment migration
- 000012_drop_redundant_invoice_indexes (v15): index cleanup
- 000013_payments_index_improvements (v16): payment index optimizations
In this commit, we move the release note for the improved confirmation
scaling for cooperative closes (PR #10331) from the 0.20.1 release notes
to 0.21.0, where the change actually landed.
Replace direct `err != sql.ErrNoRows` comparison with `errors.Is` and
extract the repeated fetch-and-check logic into a helper to reduce
duplication across the sequential and concurrent benchmarks.
Before this change the migration progress log only showed absolute counts
and a cumulative average rate. This adds two improvements:
- A cheap pre-count pass over the payments index bucket before migration
starts, giving an upper-bound estimate of the total entries to migrate
(noted as approximate since duplicates are also indexed). This allows
showing a percentage complete on each progress line.
- A rolling 300s window rate for the ETA calculation instead of the
cumulative average rate. The cumulative rate is slow to react when
throughput changes mid-migration; the rolling window makes the ETA
responsive to recent conditions. On window reset the previous window's
rate is used as a fallback for one tick to avoid a gap in the ETA.
Example progress line after this change:
Progress: 500000 payments (~48.5%), 499860 attempts | Rate: 399.2
pmt/s | Elapsed: 20m50s | ETA: ~22m8s
The previous query used an IN subquery that scanned all failed
resolutions across all payments (O(N) where N = total failed attempts
globally). Replace with a correlated EXISTS subquery that only checks
resolutions for the specific payment's attempts, making it O(k) where
k = attempts for this payment (typically 1-5).
Add a new omit_hops field to ListPaymentsRequest that allows clients
to skip loading hop-level route data for HTLC attempts, reducing both
query cost and response size. When set, the route is returned with
only route-level fields (TotalTimeLock, TotalAmount, SourcePubKey)
and no individual hop data or hop-level custom records.
Since migration 10 is already merged into master it cannot be edited.
Add a new migration (000013_payments_index_improvements) that carries
forward two index improvements:
- Drop idx_htlc_attempt_index on payment_htlc_attempts(attempt_index)
and idx_route_hops_htlc_attempt_index on
payment_route_hops(htlc_attempt_index). Both are redundant with
existing UNIQUE constraints and only add write/maintenance overhead.
- Add idx_htlc_payment_id_attempt_time on
payment_htlc_attempts(payment_id, attempt_time) to optimise batched
attempt reads that filter by payment_id and order by attempt_time
(FetchHtlcAttemptsForPayments).
- Add idx_htlc_resolutions_type_attempt_index on
payment_htlc_attempt_resolutions(resolution_type, attempt_index) to
optimise the failed-attempt cleanup path that filters by
resolution_type before joining on attempt_index
(DeleteFailedAttempts).
The previous commit stopped setting the channel capacity when
retrieving the route. This commit makes sure that in the next
release we remove the entries from the rpc interface.
During route retrieval don't query for the channel capacity. We
default to the static incomingAmt of the route. That was already
done previously when the channel was closed or private. The
channel capacity has been deprecated for quite a while so it is
acceptable to avoid the performance hit querying the graph db.
In the next release this field will be removed.
The invoice tombstone acts as a system wide kv db tombstone so
there is no need for a specific payment tombstone. Moreover a
TODO is added to redesign the current setting of the invoice
tombstone because it is also fragile to crashes after
the tombstone is set and the sql transaction of the migration
fails to commit.
Additionally the missing cleanup calls are added in case we return
early because of an error.
Add regression tests that compare every ScriptTemplate-based function
against the original ScriptBuilder implementations extracted from git
history. This ensures the template migration produces identical script
bytes for all 22 script types (segwit v0 + taproot).
The legacy builder functions are kept as private test helpers in
script_utils_legacy_test.go, extracted verbatim from the pre-template
commit.
In this commit, we add an initial cut out to allow us to swap in the
taproot scripts (which changed slightly) once we start using the final
"production" feature bit.
The changes to the scripts are pretty mechanic: we avoid using `OP_DROP`
and instead use a `_VERIFY` earlier in the script to consume the stack
item.
In this commit, we switch to using the new txscript.ScriptTemplate
function. This allows us to write the script in plain text, using some
hidden template operations to swap in items like keys or sigs.
This reduces in less code and boiler plate over all, the code that
defines the script now reads as if it was a comment.
In this commit, we update the CI workflow pinned dependency check
for google.golang.org/grpc from v1.59.0 to v1.79.1. While btcwallet
only requires v1.73.0, we upgrade to the latest stable release for
bug fixes and improvements.
In this commit, we regenerate all protobuf Go stubs to match the
updated grpc and protobuf library versions. This is a mechanical
change with no functional differences; the generated code simply
uses the newer protobuf runtime APIs.