Compare commits

...

1975 Commits

Author SHA1 Message Date
Nicolas Dorier
34cf59f2b9
bump go 2026-02-04 09:00:00 +09:00
Olaoluwa Osuntokun
f6ac04309e
lnrpc: add ValidatePayReqAmt helper for invoice amount validation
In this commit, we introduce a new ValidatePayReqAmt helper function
in the lnrpc package that consolidates the logic for validating a
caller-specified payment amount against a BOLT11 invoice amount. The
helper handles three cases: zero-amount invoices (caller must specify
an amount), fixed-amount invoices with no caller override (use invoice
amount), and fixed-amount invoices with a caller override (allow
overpayment, reject underpayment).

This helper will be used in the next commit to fix a bug where
amt_msat was silently ignored when paying fixed-amount invoices.
2026-02-04 08:57:57 +09:00
Olaoluwa Osuntokun
f5c1443c97
rpcserver+routerrpc: allow overpayment of fixed-amount invoices
Previously, the SendPaymentSync path in rpcserver.go silently ignored
the amt_msat field when paying a BOLT11 invoice that already contained
a fixed amount, and the SendPaymentV2 path in router_backend.go
rejected the request outright with "amount must not be specified when
paying a non-zero amount invoice". Both behaviors prevented users from
intentionally overpaying an invoice, which is permitted by the BOLT
spec.

In this commit, we replace the duplicated amount validation logic in
both extractPaymentIntent (rpcserver.go) and
extractIntentFromSendRequest (router_backend.go) with calls to the new
ValidatePayReqAmt helper. When a caller specifies amt_msat alongside a
fixed-amount invoice, the payment now proceeds as long as the
specified amount is at least the invoice amount. Underpayment is still
rejected with a clear error message.

Fixes https://github.com/lightningnetwork/lnd/issues/10541
2026-02-04 08:57:45 +09:00
Olaoluwa Osuntokun
63eefaf5ed
itest: add send payment overpay integration test
In this commit, we add an integration test that verifies a payer can
overpay a fixed-amount BOLT11 invoice by specifying a larger amt_msat
in the SendPaymentV2 request. The test creates a 1000 sat invoice,
pays it with 1100 sat (1,100,000 msat), and asserts that both the
sender's payment record and the receiver's invoice reflect the
overpaid amount.
2026-02-04 08:51:49 +09:00
rockstardev
fb3853be8f
Adding BtcPayServer related files and resources 2025-09-18 22:57:53 -05:00
Olaoluwa Osuntokun
2fb725e0e2
Merge pull request #10171 from Roasbeef/0-19-3-branch-final
release: bump version to v0.19.3
2025-08-20 11:52:35 -07:00
Olaoluwa Osuntokun
efc1bfc317
build: bump version to v0.19.3 2025-08-20 10:55:54 -07:00
Oliver Gugger
1a5fda6470
Merge pull request #10152 from lightningnetwork/0-19-3-branch-rc2
release: create v0.19.3-rc2 branch
2025-08-15 04:09:49 -06:00
Oliver Gugger
c113aa4a14
build: bump version to v0.19.3-beta.rc2 2025-08-15 08:48:18 +02:00
Oliver Gugger
c3226e8c22
Merge branch '0-19-3-branch-rc2-10139' into 0-19-3-branch-rc2 2025-08-12 21:43:15 +02:00
ziggie
5cfc0ea936
docs: add release notes 2025-08-12 21:42:32 +02:00
ziggie
40b40f7ae4
multi: bump Golang version to v1.23.12 2025-08-12 21:42:29 +02:00
Oliver Gugger
4498921091
Merge branch '0-19-3-branch-rc2-9871' into 0-19-3-branch-rc2 2025-08-12 21:28:51 +02:00
George Tsagkarelis
38a719d528
docs: update release notes for NoOp HTLCs 2025-08-12 21:28:35 +02:00
George Tsagkarelis
2e90d5c2d4
lnwallet: add table-driven test for evaluateNoOpHtlc helper 2025-08-12 21:26:35 +02:00
George Tsagkarelis
814bcb64dd
lnwallet: add noop case to retransmit test
To make sure we don't cause force-closures because of commit sig
mismatches, we add a test case to verify that the retransmitted HTLC
matches the original HTLC.
2025-08-12 21:26:35 +02:00
George Tsagkarelis
a3f19d5cfe
lnwallet: add noop HTLC tests
Adds some simple tests to check the noop HTLC logic of the lightning
channel.
2025-08-12 21:26:35 +02:00
George Tsagkarelis
776df9159e
lnwallet: detect and handle noop HTLCs
We update the lightning channel state machine in some key areas. If the
noop TLV is set in the update_add_htlc custom records then we change the
entry type to noop. When settling the HTLC if the type is noop we credit
the satoshi amount back to the sender.
2025-08-12 21:26:35 +02:00
George Tsagkarelis
c9d8e41e34
lnwallet: add IsAdd helper to AuxHtlcDescriptor
We also add the IsAdd helper to the AuxHtlcDescriptor, as external
software using the aux framework might want to know which type of HTLC
this is.
2025-08-12 21:26:34 +02:00
George Tsagkarelis
53554cd438
lnwallet: add noop updateType to paymendDescriptor
We add a new update type to the payment descriptor to describe this new
type of htlc. This type of HTLC will only end up being set if explicitly
signalled by external software.
2025-08-12 21:26:34 +02:00
Oliver Gugger
7eeafc1de7
Merge branch '0-19-3-branch-rc2-10141' into 0-19-3-branch-rc2 2025-08-12 21:25:16 +02:00
ziggie
039629b4af
docs: add release-notes 2025-08-12 21:25:16 +02:00
ziggie
dd1d57d82d
routing: make sure attempts are always resolved after a timeout
We check the context of the payment lifecycle at the beginning of
the `resumepayment` loop. This will make sure we have always the
latest state of the payment before deciding on the next steps in
the function `decideNextStep`.
2025-08-12 21:25:15 +02:00
Olaoluwa Osuntokun
985d29fd4a
Merge pull request #10134 from lightningnetwork/0-19-3-branch-rc1
release: create v0.19.3-rc1 branch
2025-08-06 12:03:25 -07:00
Oliver Gugger
eccdd3c966
build: bump version to v0.19.2-beta.rc1 2025-08-06 14:53:23 +02:00
Oliver Gugger
d5f451ae00
Merge branch '0-19-3-branch-rc1-10125' into 0-19-3-branch-rc1 2025-08-06 14:48:27 +02:00
ziggie
3710bc19a4
docs: add release-notes 2025-08-06 14:48:11 +02:00
ziggie
4a38f8f112
itest: add payment test with max htlc restriction 2025-08-06 14:47:43 +02:00
ziggie
97c4012753
multi: skip range check in pathfinder and switch for custom htlc payments 2025-08-06 14:47:41 +02:00
Oliver Gugger
9aa70e41c8
Merge branch '0-19-3-branch-rc1-10119' into 0-19-3-branch-rc1 2025-08-06 14:45:25 +02:00
Oliver Gugger
100feb7416
docs: add release notes 2025-08-06 14:45:11 +02:00
Oliver Gugger
b5257546f7
mod: update btcwallet to version with fix 2025-08-06 14:44:48 +02:00
Oliver Gugger
f18ae884b7
itest: run FundPsbt test with imported account
This demonstrates that the "imported" account behaves differently for a
wallet that's watch-only vs. normal. The testTaprootImportTapscriptFullKeyFundPsbt
test case succeeds in a normal run but fails in a remote-signing setup.

For some reason, an imported tapscript address ends up in the "default"
account when remote-signing but in the "imported" account for a normal
wallet.
2025-08-06 14:44:47 +02:00
Oliver Gugger
ccaeeb964e
itest: run normal FundPsbt test case in remote-signer mode
This makes sure there is no general issue with running the pay-join
FundPsbt test case in a remote signer setup.
2025-08-06 14:44:47 +02:00
Oliver Gugger
f275fd66ce
Merge branch '0-19-3-branch-rc1-10117' into 0-19-3-branch-rc1 2025-08-06 14:43:32 +02:00
yyforyongyu
b814428cce
docs: add and update release notes for 0.19.3 2025-08-06 14:43:18 +02:00
yyforyongyu
5a22549bee
sweep: fix typos 2025-08-06 14:42:43 +02:00
yyforyongyu
83d33f4b30
itest: fix tests re the new anchor behavior 2025-08-06 14:42:43 +02:00
yyforyongyu
3796c99bd0
sweep: only remove the other input from the exclusive group
We now make sure when removing inputs identified by the exclusive group
ID, we only remove the other one, not the one that invoked the removal.
2025-08-06 14:42:43 +02:00
yyforyongyu
28af39cd03
contractcourt+sweep: make anchor inputs exclusive
We now make sure to sweep each anchor input in its own sweeping tx, if
economically feasible.
2025-08-06 14:42:43 +02:00
Oliver Gugger
558ea0f895
Merge branch '0-19-3-branch-rc1-10108' into 0-19-3-branch-rc1 2025-08-06 14:27:35 +02:00
yyforyongyu
f12692e3f3
docs: add release notes 2025-08-06 14:27:25 +02:00
yyforyongyu
b0dca5f4a9
contractcourt: replace MarkChannelClosed with NotifyChannelResolved 2025-08-06 14:26:41 +02:00
yyforyongyu
91000b9b98
contractcourt: add a new chan resolvedChan to handle resolved channels
Thus we can mark channels as resolved in an async way to avoid deadlock.
2025-08-06 14:26:40 +02:00
Oliver Gugger
d6523d7490
Merge branch '0-19-3-branch-rc1-10107' into 0-19-3-branch-rc1 2025-08-06 11:37:18 +02:00
Elle Mouton
1345b47961
go.mod: bump btclog v2 version 2025-08-06 11:37:06 +02:00
Oliver Gugger
94c0208bd4
Merge branch '0-19-3-branch-rc1-10097' into 0-19-3-branch-rc1 2025-08-06 11:35:27 +02:00
Olaoluwa Osuntokun
6f092979a0
docs/release-notes: add release notes entry 2025-08-06 11:35:17 +02:00
Olaoluwa Osuntokun
ce4fdd3117
discovery: only permit a single gossip backlog goroutine per peer
In this commit, we add a new atomic bool to only permit a single gossip
backlog goroutine per peer. If we get a new reuqest that needs a backlog
while we're still processing the other, then we'll drop that request.
2025-08-06 11:34:43 +02:00
Olaoluwa Osuntokun
bb5825387e
discovery: add tests for for async timestamp range queue 2025-08-06 11:34:42 +02:00
Olaoluwa Osuntokun
a2fcfb02c9
docs: add comprehensive gossip rate limiting guide
In this commit, we add detailed documentation to help node operators
understand and configure the gossip rate limiting system effectively.
The new guide addresses a critical knowledge gap that has led to
misconfigured nodes experiencing synchronization failures.

The documentation covers the token bucket algorithm used for rate
limiting, providing clear formulas and examples for calculating
appropriate values based on node size and network position. We include
specific recommendations ranging from 50 KB/s for small nodes to
1 MB/s for large routing nodes, with detailed calculations showing
how these values are derived.

The guide explains the relationship between rate limiting and other
configuration options like num-restricted-slots and the new
filter-concurrency setting. We provide troubleshooting steps for
common issues like slow initial sync and peer disconnections, along
with debug commands and log patterns to identify problems.

Configuration examples are provided for conservative, balanced, and
performance-oriented setups, giving operators concrete starting points
they can adapt to their specific needs. The documentation emphasizes
the importance of not setting rate limits too low, warning that values
below 50 KB/s can cause synchronization to fail entirely.
2025-08-06 11:34:42 +02:00
Olaoluwa Osuntokun
27778cbe06
multi: wire up gossip filter concurrency config
In this commit, we complete the integration of the configurable gossip
filter concurrency by wiring the new FilterConcurrency configuration
through all layers of the application.

The changes connect the gossip.filter-concurrency configuration option
from the command-line interface through the server initialization code
to the gossiper and sync manager. This ensures that operators can
actually use the new configuration option to tune their node's
concurrent gossip filter processing capacity based on their specific
requirements and available resources.
2025-08-06 11:34:42 +02:00
Olaoluwa Osuntokun
8eda486227
discovery: integrate async queue in ProcessRemoteAnnouncement
In this commit, we complete the integration of the asynchronous
timestamp range queue by modifying ProcessRemoteAnnouncement to use
the new queuing mechanism instead of calling ApplyGossipFilter
synchronously.

This change ensures that when a peer sends a GossipTimestampRange
message, it is queued for asynchronous processing rather than
blocking the gossiper's main message processing loop. The modification
prevents the peer's readHandler from blocking on potentially slow
gossip filter operations, maintaining connection stability during
periods of high synchronization activity.

If the queue is full when attempting to enqueue a message, we log
a warning but return success to prevent peer disconnection. This
design choice prioritizes connection stability over guaranteed
delivery of every gossip filter request, which is acceptable since
peers can always resend timestamp range messages if needed.
2025-08-06 11:34:42 +02:00
Olaoluwa Osuntokun
80e0ea0d40
discovery: add async timestamp range queue to prevent blocking
In this commit, we introduce an asynchronous processing queue for
GossipTimestampRange messages in the GossipSyncer. This change addresses
a critical issue where the gossiper could block indefinitely when
processing timestamp range messages during periods of high load.

Previously, when a peer sent a GossipTimestampRange message, the
gossiper would synchronously call ApplyGossipFilter, which could block
on semaphore acquisition, database queries, and rate limiting. This
synchronous processing created a bottleneck where the entire peer
message processing pipeline would stall, potentially causing timeouts
and disconnections.

The new design adds a timestampRangeQueue channel with a capacity of 1
message and a dedicated goroutine for processing these messages
asynchronously. This follows the established pattern used for other
message types in the syncer. When the queue is full, we drop messages
and log a warning rather than blocking indefinitely, providing graceful
degradation under extreme load conditions.
2025-08-06 11:34:42 +02:00
Olaoluwa Osuntokun
57872b9cff
discovery: make gossip filter semaphore capacity configurable
In this commit, we make the gossip filter semaphore capacity configurable
through a new FilterConcurrency field. This change allows node operators
to tune the number of concurrent gossip filter applications based on
their node's resources and network position.

The previous hard-coded limit of 5 concurrent filter applications could
become a bottleneck when multiple peers attempt to synchronize
simultaneously. By making this value configurable via the new
gossip.filter-concurrency option, operators can increase this limit
for better performance on well-resourced nodes or maintain conservative
values on resource-constrained systems.

We keep the default value at 5 to maintain backward compatibility and
avoid unexpected resource usage increases for existing deployments. The
sample configuration file is updated to document this new option.
2025-08-06 11:34:42 +02:00
Oliver Gugger
f6c5cd7ffc
Merge branch '0-19-3-branch-rc1-10096' into 0-19-3-branch-rc1 2025-08-06 11:33:25 +02:00
yyforyongyu
f25bfb7866
docs: update release notes 2025-08-06 11:33:05 +02:00
yyforyongyu
173dbec389
lncfg: update docs about msg-rate-bytes and msg-burst-bytes 2025-08-06 11:30:38 +02:00
yyforyongyu
8746a6e204
discovery: increase default msg rates to 1MB 2025-08-06 11:30:37 +02:00
Oliver Gugger
a839456493
Merge pull request #10082 from lightningnetwork/0-19-2-final
release: create v0.19.2-beta final version
2025-07-16 02:44:11 -06:00
Oliver Gugger
9e052357dd
build: bump version to v0.19.2-beta 2025-07-15 18:50:25 +02:00
Oliver Gugger
051d171dee
Merge branch '0-19-2-final-10079' into 0-19-2-final 2025-07-15 18:48:00 +02:00
Oliver Gugger
2855288636
docs: add release notes 2025-07-15 18:47:45 +02:00
Oliver Gugger
3eced8a634
lnrpc+rpcserver: add missing channel update event type
Fixes the error "unexpected channel event update" in
SubscribeChannelEvents.
2025-07-15 18:47:25 +02:00
Oliver Gugger
61fd2ca425
channelnotifier: fix Godoc comment 2025-07-15 18:46:48 +02:00
Oliver Gugger
e4465daa60
build: bump version to v0.19.2-beta.rc2 2025-07-09 10:37:20 +02:00
Oliver Gugger
f16e795917
Merge pull request #10047 from lightningnetwork/0-19-2-branch-rc2
release: create v0.19.2-rc2 branch
2025-07-09 10:36:07 +02:00
Olaoluwa Osuntokun
0d64c3d379 Merge branch '0-19-2-branch-rc2-10044' into 0-19-2-branch-rc2 2025-07-08 18:41:26 -07:00
ziggie
bc4a12a346 docs: add release-notes 2025-07-08 18:41:00 -07:00
ziggie
370d254ba2 chanbackup: fix shutdown issue when backuper has not started yet 2025-07-08 18:40:21 -07:00
Olaoluwa Osuntokun
2d985c89d6 Merge branch '0-19-2-branch-rc2-10048' into 0-19-2-branch-rc2 2025-07-08 18:39:48 -07:00
ziggie
da1456f71b docs: add release notes 2025-07-08 18:34:39 -07:00
ziggie
151fabc945 contractcourt: fix encoding 2025-07-08 18:34:39 -07:00
Oliver Gugger
e4bdd7762e
Merge branch '0-19-2-branch-rc2-10041' into 0-19-2-branch-rc2 2025-07-08 18:33:36 +02:00
ziggie
15cd86c0aa
multi: revert back changes from 9911
always process remote ADDs even when they are empty to trigger
the gc process when loading them back into memory.
2025-07-08 18:33:35 +02:00
Oliver Gugger
7afaaffb3e
Merge branch '0-19-2-branch-rc2-10042' into 0-19-2-branch-rc2 2025-07-08 16:25:39 +02:00
ziggie
e1f9e87e47
routing: return error for getBandwidth and log it 2025-07-08 16:25:39 +02:00
ziggie
7bcb65d9e6
multi: add logs to debug potential payment sending issue 2025-07-08 16:25:39 +02:00
Oliver Gugger
103b24e7ff
Merge branch '0-19-2-branch-rc2-10045' into 0-19-2-branch-rc2 2025-07-07 15:29:40 +02:00
yyforyongyu
8fe0d0f7cd
docs: update release docs 2025-07-07 15:29:40 +02:00
yyforyongyu
93bede938e
contractcourt: only close quit in Stop
Make sure we don't prematurely close the `quit` chans.
2025-07-07 15:29:39 +02:00
Oliver Gugger
7b1ca7d273
Merge pull request #9986 from lightningnetwork/0-19-2-branch-rc1
release: create v0.19.2-rc1 branch
2025-07-04 20:36:07 +02:00
Oliver Gugger
1f336a07fa
Merge branch '0-19-2-branch-rc1-10035' into 0-19-2-branch-rc1 2025-07-04 15:38:53 +02:00
ziggie
d59036b48a
docs: add release-notes 2025-07-04 15:38:52 +02:00
ziggie
81e4160d67
switch: unlock mutex lock earlier
We now unlock the mutex lock of the switch as soon as possible to
avoid potetnial deadlock in the htlc switch.
2025-07-04 15:38:52 +02:00
Oliver Gugger
11bdccca47
docs: fix release notes, add contributors 2025-07-04 10:11:11 +02:00
Oliver Gugger
775a6dd8f8
build: bump version to v0.19.2-beta.rc1 2025-07-04 09:08:11 +02:00
Oliver Gugger
acac2a12d6
Merge branch '0-19-2-branch-rc1-10028' into 0-19-2-branch-rc1 2025-07-04 09:03:42 +02:00
ffranr
9345eb247d
chainntnfs: export NotifierOptions and internal field for interface use
Export NotifierOptions and its internal field to enable external
satisfaction of the protofsm.DaemonAdapters interface.
2025-07-04 09:03:41 +02:00
Oliver Gugger
8a185a12d1
server: fix rebase issue 2025-07-03 17:54:17 +02:00
Oliver Gugger
1cd42b62bd
Merge branch '0-19-2-branch-rc1-10012' into 0-19-2-branch-rc1 2025-07-03 16:18:04 +02:00
ziggie
be7c2b0cc7
docs: add release-notes 2025-07-03 16:18:04 +02:00
ziggie
db7e9e1c53
brontide: remove async goroutine to process gossip process result
We cannot rely on a response currently so we avoid spawning
goroutines. This is just a temporary fix to avoid the goroutine
leak.
2025-07-03 16:18:04 +02:00
ziggie
a68dec8c19
discovery: add comments 2025-07-03 16:18:03 +02:00
Oliver Gugger
99ef37f450
Merge branch '0-19-2-branch-rc1-9980' into 0-19-2-branch-rc1 2025-07-02 16:52:06 +02:00
George Tsagkarelis
2460bd17ac
docs: add release note 2025-07-02 16:52:06 +02:00
George Tsagkarelis
128b23e813
htlcswitch+routing: PaymentBandwidth accepts channel peer pubkey argument 2025-07-02 16:52:06 +02:00
George Tsagkarelis
ab036990b5
routing+htlcswitch: ProduceHtlcExtraData uses first hop pub key 2025-07-02 16:52:05 +02:00
Oliver Gugger
c91753eda1
Merge branch '0-19-2-branch-rc1-10016' into 0-19-2-branch-rc1 2025-07-01 20:11:05 +02:00
Elle Mouton
6bbc438077
server: make use of structured logging for peer connections 2025-07-01 20:09:43 +02:00
Elle Mouton
f6c41565d1
accessman: fix structured logging formatting 2025-07-01 20:09:07 +02:00
Oliver Gugger
5398229079
Merge branch '0-19-2-branch-rc1-10004' into 0-19-2-branch-rc1 2025-07-01 20:08:26 +02:00
Elle Mouton
2a578737f6
server: ensure newer node announcement timestamp
On startup, we currently blindly overwrite our node announcement info
for our source node. This includes overwriting the existing LastUpdate
timestamp. This can cause an issue with our new SQL backends in the case
that the new timestamp is not greater than the previously written
timestamp. So here, we first fetch the source node and make sure to use
a timestamp that is greater than the previosly set one.
2025-07-01 20:08:15 +02:00
Elle Mouton
9b877b94c3
multi: use the "errors" package everywhere
Replace all usages of the "github.com/go-errors/errors" and
"github.com/pkg/errors" packages with the standard lib's "errors"
package. This ensures that error wrapping and `errors.Is` checks will
work as expected.
2025-07-01 20:08:12 +02:00
Oliver Gugger
72d4ae0faa
Merge branch '0-19-2-branch-rc1-10009' into 0-19-2-branch-rc1 2025-07-01 08:46:29 +02:00
Elle Mouton
4efcb075de
discovery: fix log line panic
If a method returns an error, we should assume all other parameters to
be nil unless the documentation explicitly says otherwise. So here, we
fix a log line where a dereference is made to an object that will be nil
due to an error being returned.
2025-07-01 08:46:28 +02:00
Oliver Gugger
d90781e36a
Merge branch '0-19-2-branch-rc1-9996' into 0-19-2-branch-rc1 2025-06-27 12:32:33 +02:00
Calvin Zachman
a3e36133af
docs: update release notes 2025-06-27 12:32:33 +02:00
Calvin Zachman
09bcce5ba5
chainreg: use new lseed service
This points lnd at the new deployment of the lseed
service used to provide candidate peers during initial
network bootstrap.
2025-06-27 12:32:32 +02:00
Oliver Gugger
bc982499bd
Merge branch '0-19-2-branch-rc1-9978' into 0-19-2-branch-rc1 2025-06-26 08:49:21 +02:00
ziggie
d7b79eff38
lnd: improve code comment 2025-06-26 08:49:10 +02:00
ziggie
78313e3436
lnd: rename removePeer to removePeerUnsafe
rename `removePeer` to highlight that the mutex has to be acquired
before calling it.
2025-06-26 08:49:10 +02:00
ziggie
a656f2c8a6
docs: add release-notes 2025-06-26 08:49:08 +02:00
ziggie
67e7e29413
multi: fix deadlock in p2p race condition
In case we cannot guarantee that the peers has started up
completely we launch the disconnect method asynchronously.
2025-06-26 08:48:39 +02:00
Oliver Gugger
ddbd2e0c34
Merge branch '0-19-2-branch-rc1-9989' into 0-19-2-branch-rc1 2025-06-25 14:57:30 +02:00
Slyghtning
45b26cf2d4
docs: update release notes 2025-06-25 14:57:18 +02:00
Slyghtning
2937837fc0
chainfee: method to round up the fee for a given transaction weight 2025-06-25 14:56:47 +02:00
Oliver Gugger
911fbae52f
Merge branch '0-19-2-branch-rc1-9991' into 0-19-2-branch-rc1 2025-06-25 14:52:07 +02:00
ziggie
3565df870b
routerrpc: rename var and add more comments 2025-06-25 14:52:06 +02:00
ziggie
4f72354385
routerrpc: add log line for probing the invoice request 2025-06-25 14:52:06 +02:00
Oliver Gugger
4afa5e44b3
Merge branch '0-19-2-branch-rc1-9973' into 0-19-2-branch-rc1 2025-06-24 18:24:22 +02:00
ZZiigguurraatt
397984a9f2
sqldb: clarify native sql options 2025-06-24 18:24:21 +02:00
Olaoluwa Osuntokun
b48e2763a7 docs/release-notes: update release notes w/ all contributors and notes 2025-06-23 16:20:57 -07:00
Olaoluwa Osuntokun
a50a52865e Merge branch '0-19-2-branch-rc1-9945' into 0-19-2-branch-rc1 2025-06-23 16:20:27 -07:00
ziggie
970f789e85 docs: add release-notes 2025-06-23 16:20:27 -07:00
ziggie
988e78177a multi: Add decayedlog db migration code
This commit adds the migration code for the decayedlog db which
is optional and will default to true.
2025-06-23 16:20:27 -07:00
ziggie
684da273b0 channeldb: update optional migration code for multiple versions 2025-06-23 16:20:27 -07:00
ziggie
ef5ae79623 channeldb: add migration34
Migration34 garbage collects the decayed log database. This commit
only adds the migration code and does not use it.
2025-06-23 16:20:27 -07:00
Olaoluwa Osuntokun
bcd854cbf9 Merge branch '0-19-2-branch-rc1-9880' into 0-19-2-branch-rc1 2025-06-20 15:51:11 -07:00
yyforyongyu
f7af9701cc config: update docs for num-restricted-slots 2025-06-20 15:50:45 -07:00
yyforyongyu
da13c7ab66 lnd: remove peer from peerChanInfo when necessary
We now remove the peer from `peerChanInfo` if this peer doesn't have
channels with us. Also patched a unit test for `removePeerAccess`.
2025-06-20 15:50:45 -07:00
yyforyongyu
ceeb123925 channeldb+lnd: rename peerCounts to peerChanInfo for clarity 2025-06-20 15:50:45 -07:00
yyforyongyu
a8d1985fd6 funding+lnd: make sure accessman won't interrupt funding flow
If there's an error occured when updating the peer's status after the
channel status is changed, we now make sure we log the error instead of
letting it interrupt the channel open/close flow.
2025-06-20 15:50:45 -07:00
yyforyongyu
107b74d81d accessman+lnd: rename checkIncomingConnBanScore for clarity 2025-06-20 15:50:45 -07:00
yyforyongyu
c23c90ee9b docs: add release notes for improved accessman 2025-06-20 15:50:44 -07:00
yyforyongyu
c4116e1438 itest+lntest: add more itest for accessman 2025-06-20 15:50:25 -07:00
yyforyongyu
0b0513d88c accessman: make sure to decrement PendingOpenCount 2025-06-20 15:50:25 -07:00
yyforyongyu
d9e3412ed5 accessman: skip incrementing num of slots for existing peer
When a peer is already existing, we should skip incrementing the
`numRestricted` count.

Also patched unit test for method `addPeerAccess`.
2025-06-20 15:50:25 -07:00
yyforyongyu
d170ef3efe lnd: add string representation for peerAccessStatus 2025-06-20 15:50:25 -07:00
yyforyongyu
bd99924383 lnd: only restrict slots for inbound connections
For outbound connections, since they are initialized by the users, we
can relax on the restriction. A future global limit may be added - as
for now, we will let them to be managed by the users.
2025-06-20 15:50:25 -07:00
yyforyongyu
bc6008f854 lnd: move peer perms assignment into peerConnected
When the callback is called in `scheduledPeerConnection`, it is
referencing the old `access` variable which was created when the peer
was first connected. However, if this peer opens a channel with us and
goes offline, or another inbound connection is made from this peer, we
may still use the old `access` value. To fix it, we need to make sure we
always get the fresh perm by calling `assignPeerPerms` inside
`peerConnected`.
2025-06-20 15:50:25 -07:00
yyforyongyu
89a819db46 accessman: reduce lock span by excluding ctx 2025-06-20 15:50:25 -07:00
yyforyongyu
a0439155d4 accessman+lnd: check if a peer is found in peerScores
We need to also check this map to make sure the peer exists or not.
2025-06-20 15:50:25 -07:00
yyforyongyu
b527f19de7 accessman: skip restriction for existing peers
When a peer already has a connection with us, there's no need to check
for available slots as we will either close the old conn or refuse the
new conn.
2025-06-20 15:50:25 -07:00
yyforyongyu
5dfc5f4b42 lncfg+lnd: add new dev config unsafeconnect
This flag is added so we can use it in the itest to mimic racing inbound
and outbound connections.
2025-06-20 15:50:25 -07:00
yyforyongyu
7981b3b4de lncfg: fix typo 2025-06-20 15:50:25 -07:00
Olaoluwa Osuntokun
ba94755dfd Merge branch '0-19-2-branch-rc1-9929' into 0-19-2-branch-rc1 2025-06-20 15:49:04 -07:00
yyforyongyu
68cc4bc208 docs: update release notes 2025-06-20 15:49:04 -07:00
yyforyongyu
69df66f1f9 htlcswitch: skip decoding hop if the htlc is already acked
We now move the check earlier in the loop so we don't even need to
decode the hop for already processed ADDs.
2025-06-20 15:49:04 -07:00
yyforyongyu
13308644d0 htlcswitch: remove batchReplayBkt
This commit removes the `batchReplayBkt` as its only effect is to allow
reforwarding htlcs during startup.

Normally for every incoming htlc added, their shared secret is used as
the key to be saved into the `sharedHashBucket`, which will be used for
check for replays. In addition, the fwdPkg's ID, which is SCID+height is
also saved to the bucket `batchReplayBkt`. Since replays of HTLCs cannot
happen at the same commitment height, when a replay happens,
`batchReplayBkt` simply doesn't have this info, and we again rely on
`sharedHashBucket` to detect it. This means most of the time the
`batchReplayBkt` is a list of SCID+height with empty values.

The `batchReplayBkt` was previously used as a mechanism to check for
reforwardings during startup - when reforwarding htlcs, it quries this
bucket and finds an empty map, knowing this is a forwarding and skips
the check in `sharedHashBucket`. Given now we use a bool flag to
explicitly skip the replay check, this bucket is no longer useful.
2025-06-20 15:49:04 -07:00
yyforyongyu
c8ff379a9c htlcswitch: exit early if the fwdPkg is already completed 2025-06-20 15:49:04 -07:00
yyforyongyu
cb009119c4 htlcswitch: skip checking replays for reforwarded packets
We now rely on the forwarding package's state to decide whether a given
packet is a reforwarding or not. If we know it's a reforwarding packet,
there's no need to check for replays in the `sharedHashes` bucket, which
behaves the same as if we are querying the `batchReplayBkt`.
2025-06-20 15:49:04 -07:00
Olaoluwa Osuntokun
2b3c04ccaa Merge branch '0-19-2-branch-rc1-9726' into 0-19-2-branch-rc1 2025-06-20 15:48:41 -07:00
Olaoluwa Osuntokun
14ce4f17ab protofsm: add test for new full block conf behavior 2025-06-20 15:48:41 -07:00
Olaoluwa Osuntokun
3e29342353 protofsm: add option to allow conf resp to return full back
In this commit, we add an option to allow a conf req caller to receive
the full block. This is useful if the caller wants to be able to create
an SPV proof.
2025-06-20 15:48:41 -07:00
Olaoluwa Osuntokun
fd03675f79 Merge branch '0-19-2-branch-rc1-9725' into 0-19-2-branch-rc1 2025-06-20 15:48:29 -07:00
Olaoluwa Osuntokun
6cee18ff4a protofsm: add generic type assertion to state machine tests
This commit introduces a new generic type assertion function
`assertState` to the state machine tests. This function asserts that the
state machine is currently in the expected state type and returns the
state cast to that type. This allows us to directly access the fields of
the state without having to perform a type assertion manually.
2025-06-20 15:48:29 -07:00
Olaoluwa Osuntokun
f11d1d7460 protofsm: add unit tests for SpendMapper 2025-06-20 15:48:29 -07:00
Olaoluwa Osuntokun
1304182897 protofsm: add new ConfMapper similar to SpendMapper for conf events
In this commit, we add a new ConfMapper which is useful for state
machines that want to project some of the conf attributes into a new
event to be sent post conf.
2025-06-20 15:48:29 -07:00
Olaoluwa Osuntokun
6e6901ec61 Merge branch '0-19-2-branch-rc1-9962' into 0-19-2-branch-rc1 2025-06-20 15:46:10 -07:00
yyforyongyu
6bf9a38940 chainio: use package logger instead of instance logger
There's no guarantee that the `b.beat` is initialized when the
dispatcher shuts down, especially with the case in the remote signer
where no chainbackend is created.
2025-06-20 15:46:10 -07:00
Olaoluwa Osuntokun
ff6ea786ec Merge branch '0-19-2-branch-rc1-9921' into 0-19-2-branch-rc1 2025-06-20 15:45:56 -07:00
yyforyongyu
1d2217e9a9 docs: update release notes 2025-06-20 15:45:56 -07:00
yyforyongyu
732cd717bf chainntnfs: use spent height as height hint if found
Previously when deciding whether a UTXO is spent or not, we accept a
height hint as the starting block to look for the spending tx in the
rescan process. When it's already found spent before the rescan starts,
we will update the height hint to be the spent height, only if the
latter is greater. This means if the user-specified hint is greater than
the actual spending height, this UTXO will never be found as spent. We
now fix it by always using the spent height as the hint.
2025-06-20 15:45:56 -07:00
yyforyongyu
a806323035 chainntnfs: patch debug logs and fix StartHeight
Make sure we patch the `StartHeight` for btcd notifier.
2025-06-20 15:45:56 -07:00
Olaoluwa Osuntokun
270981eef0 Merge branch '0-19-2-branch-rc1-9653' into 0-19-2-branch-rc1 2025-06-20 15:45:39 -07:00
djkazic
2ffcefc947 channeldb: perform init of top level buckets first 2025-06-20 15:45:39 -07:00
Olaoluwa Osuntokun
ff95646d42 Merge branch '0-19-2-branch-rc1-9889' into 0-19-2-branch-rc1 2025-06-20 15:45:21 -07:00
yyforyongyu
0608cc0428 channeldb+lnwallet: fix typo 2025-06-20 15:45:13 -07:00
yyforyongyu
f326fbb840 docs: add release note entry 2025-06-20 15:45:12 -07:00
yyforyongyu
6947e0a87a channeldb: add customized encoding for HtlcIndex 2025-06-20 15:44:16 -07:00
yyforyongyu
938d78a28b lnwallet+channeldb: use BigSizeT to save space 2025-06-20 15:44:16 -07:00
Olaoluwa Osuntokun
6e8d5522ea Merge branch '0-19-2-branch-rc1-9813' into 0-19-2-branch-rc1 2025-06-20 15:43:53 -07:00
Abdullahi Yunus
473240533b docs: add release note 2025-06-20 15:42:20 -07:00
Abdullahi Yunus
768911c46f lnd+lnrpc: update fwdinghistory message
In this commit we update the returned message for fwdinghistory
to include the htlcindex for all forwarded htlcs.
2025-06-20 15:37:24 -07:00
Abdullahi Yunus
43f8bf288f htlcswitch+channeldb: add htlcidx to fwding log
In this commit we add htlcindex field to the forwardingevent
struct, which is persisted alongside the other event fields.
2025-06-20 15:37:02 -07:00
Olaoluwa Osuntokun
b27f401ccc Merge branch '0-19-2-branch-rc1-9911' into 0-19-2-branch-rc1 2025-06-20 15:31:00 -07:00
ziggie
21ed1360cd htlcswitch: exit early if no adds are in the fwd pkg
This lead to the case that we would always record a HTLC
two times in the decayed log protection which is not necessary
in the first place.
2025-06-20 15:31:00 -07:00
Olaoluwa Osuntokun
59a86b3b54
Merge pull request #9927 from Roasbeef/0-19-1-branch
build: bump version to v0.19.1
2025-06-10 18:45:01 -07:00
Olaoluwa Osuntokun
51bc76cc9f
build: bump version to v0.19.1 2025-06-10 17:22:53 -07:00
Oliver Gugger
a0b2764167
Merge pull request #9896 from lightningnetwork/0-19-1-rc1-branch
release: create branch for v0.19.1-beta.rc1
2025-06-04 14:05:39 +02:00
Oliver Gugger
fedc4c939f
build: bump version to v0.19.1-beta.rc1 2025-06-04 13:00:47 +02:00
Oliver Gugger
0aa53b74a4
docs: add missing release note entry for #9858 2025-06-04 13:00:47 +02:00
Oliver Gugger
48bdd3fc1f
Merge branch '0-19-1-rc1-branch-9876' into 0-19-1-rc1-branch 2025-06-04 12:22:11 +02:00
yyforyongyu
14fbba5a5d
docs: update release notes 2025-06-04 12:22:11 +02:00
yyforyongyu
9fcde4b452
lnd: increase DefaultNumRestrictedSlots to 100 2025-06-04 12:22:11 +02:00
yyforyongyu
643b1c8af7
accessman: patch unit tests 2025-06-04 12:22:11 +02:00
yyforyongyu
6d70e3607b
accessman: only check ban score for restricted peers
If a peer has, or used to have a channel with us there's no need to
check for the ban score.
2025-06-04 12:22:11 +02:00
Oliver Gugger
77882c2712
Merge branch '0-19-1-rc1-branch-9875' into 0-19-1-rc1-branch 2025-06-04 12:21:47 +02:00
ziggie
258ce4f107
docs: add release notes for 19.1 2025-06-04 12:21:34 +02:00
ziggie
48e440e560
discovery: increase syncer gossip chan buffer 2025-06-04 12:21:00 +02:00
ziggie
6f8a94c094
brontide: increase logging when processing gossip msgs
We add logging to we can draw conclusions how long the processing
of gossip message last and potentially see whether the syncer
buffer channel size is a bottleneck in processing.
2025-06-04 12:21:00 +02:00
Oliver Gugger
85689b18b3
Merge branch '0-19-1-rc1-branch-9858' into 0-19-1-rc1-branch 2025-06-04 12:20:23 +02:00
Olaoluwa Osuntokun
0484aa2496
peer+feature: start to signal the prod rbf coop close bit
In this commit, we start to signal the prod bit for the rbf coop close
feature. We keep our signaling of the staging bit in place to ensure
the protocol continues to work between those nodes in the wild that are
still signaling the bit.

Fixes https://github.com/lightningnetwork/lnd/issues/9852
2025-06-04 12:20:23 +02:00
Oliver Gugger
4db8b40e8d
Merge branch '0-19-1-rc1-branch-9890' into 0-19-1-rc1-branch 2025-06-04 12:20:03 +02:00
ziggie
a7634d5dc5
docs: update release-notes 2025-06-04 12:20:03 +02:00
ziggie
df2def1473
mod: update btclog library 2025-06-04 12:20:02 +02:00
Oliver Gugger
0815245cfc
Merge branch '0-19-1-rc1-branch-9872' into 0-19-1-rc1-branch 2025-06-04 12:19:41 +02:00
ziggie
0055f84732
docs: add release notes 2025-06-04 12:19:41 +02:00
ziggie
49d40c8ea1
brontide: fix peer disconnection issue
In case when the rbf coop close feature was active we would not
properly disconnect the peer.
2025-06-04 12:19:41 +02:00
ziggie
275ab7da1d
itest: add disconnect test
Add a connect/disconnect test when the peers both started up
with the rbf coop close feature.
2025-06-04 12:19:41 +02:00
Oliver Gugger
fdd2717c37
Merge branch '0-19-1-rc1-branch-9885' into 0-19-1-rc1-branch 2025-06-04 12:19:20 +02:00
ffranr
00f9589929
protofsm: exercise StateMachine.IsRunning() in unit test
Update unit test to call `StateMachine.IsRunning()` to ensure the
method has test coverage.
2025-06-04 12:19:20 +02:00
Oliver Gugger
6c3545ad53
Merge branch '0-19-1-rc1-branch-9883' into 0-19-1-rc1-branch 2025-06-04 12:19:03 +02:00
ffranr
6f4811fc14
protofsm: add thread-safe IsRunning method to StateMachine
This commit introduces a new `IsRunning()` method to the `StateMachine`.
This method allows callers to safely query whether the state machine
is currently active after it has been started and before it has been
stopped.

To ensure thread-safety, the internal `running` status flag is
implemented using `atomic.Bool` (from `sync/atomic`). Without atomic
operations, concurrent accesses to a simple boolean flag from different
goroutines (e.g., one goroutine calling `IsRunning()` while another
executes `Start()` or `Stop()`) could lead to stale reads or data races.
2025-06-04 12:19:03 +02:00
Oliver Gugger
19b2e42e18
Merge branch '0-19-1-rc1-branch-9854' into 0-19-1-rc1-branch 2025-06-04 12:18:38 +02:00
yyforyongyu
b703cf2042
docs: add release notes 19.1 2025-06-04 12:18:21 +02:00
yyforyongyu
9d5b103d09
docs: add release notes template 2025-06-04 12:17:55 +02:00
yyforyongyu
15d14ee1c4
gomod: update btcwallet 2025-06-04 12:17:54 +02:00
yyforyongyu
92279e87ae
lntest+itest: add testBumpFeeExternalInput 2025-06-04 12:17:54 +02:00
yyforyongyu
6afdda837c
itest: add new file lnd_bump_fee.go
Move bumpfee RPC related tests into one file - these tests focus on
testing the behaivor of the RPC only without any force close context.
2025-06-04 12:17:54 +02:00
Oliver Gugger
5f37da3f21
Merge branch '0-19-1-rc1-branch-9856' into 0-19-1-rc1-branch 2025-06-04 12:17:22 +02:00
Elle Mouton
2512677b08
docs: add release notes for 0.19.1 2025-06-04 12:17:00 +02:00
Elle Mouton
c79daf68fa
lnwire: add *OpaqueAddrs case in WriteElements
And then expand the chanbackup unit tests to cover such a case.
2025-06-04 12:16:09 +02:00
Elle Mouton
2530ab13c7
lnwire: remove duplicated logic
Use existing serialisation helpers for serialising TC Pand Onion
addresses in WriteElement.
2025-06-04 12:16:09 +02:00
Elle Mouton
659549a5fc
chanbackup: make sure onion addresses are covered in tests 2025-06-04 12:16:08 +02:00
Oliver Gugger
4b3fb1150b
Merge pull request #9893 from ziggie1984/fix-memory-leak-cherry-pick
fix memory leak cherry pick
2025-06-04 12:07:42 +02:00
ziggie
7477a91472
docs: add release-notes 19.1 2025-06-04 10:58:48 +02:00
ziggie
45ebb9b900
discovery: add comments to the ctx creation
We highlight why we do not use the returned cancel method of the
context guard.
2025-06-04 10:55:22 +02:00
ziggie
af47604cc3
fn: add comment to context create fn 2025-06-04 10:55:22 +02:00
Elle Mouton
fe3a862ec3
autopilot: revert passing ctx to Start methods 2025-06-04 10:54:36 +02:00
Elle Mouton
6202597eec
discovery: revert passing ctx through to Start methods 2025-06-04 10:54:35 +02:00
Elle Mouton
2f99706fa1
multi: remove kvdb.RTx from ForEachNodeChannel
Since we have not removed all call-sites that make use of this
parameter, we can remove it. This helps hide DB-specific details from
the interface we will introduce for the graph store.
2025-06-04 10:54:35 +02:00
Elle Mouton
de11997fad
graph/db: use only exported KVStore methods in tests
Replace all calls to bbolt specific methods on the KVStore to instead
use exported methods on the KVStore that are more db-agnostic.
2025-06-04 10:54:35 +02:00
Elle Mouton
9aab68a6f5
graph/db: unexport various methods that expose kvdb.RTx
Unexport the KVStore `FetchOtherNode` and `ForEachNodeChannelTx` methods
so that fewer exposed methods are leaking implementation details.
2025-06-04 10:54:35 +02:00
Elle Mouton
c15740ba09
graph/db: introduce ForEachSourceNodeChannel
In preparation for creating a clean interface for the graph store, we
want to hide anything that is DB specific from the exposed methods on
the interface. Currently the `ForEachNodeChannel` and the
`FetchOtherNode` methods of the `KVStore` expose a `kvdb.RTx` parameter
which is bbolt specific. There is only one call-site of
`ForEachNodeChannel` actually makes use of the passed `kvdb.RTx`
parameter, and that is in the `establishPersistentConnections` method of
the `server` which then passes the tx parameter to `FetchOtherNode`.

So to clean-up the interface such that the `kvdb.RTx` is no longer
exposed: we instead create one new method called
`ForEachSourceNodeChannel` which can be used to replace the above
mentioned call-site. So as of this commit, all the remaining call-site
of `ForEachNodeChannel` pass in a nil param for `kvdb.RTx` - meaning we
can remove the parameter in a future commit.
2025-06-04 10:54:35 +02:00
Elle Mouton
9c3c2b970c
graph/db: remove unused Wipe method
Later on we will create an interface for the persisted graph data. We
want this interface to be as small and as neat as possible. In
preparation for this, we remove this unused `Wipe` method.
2025-06-04 10:54:34 +02:00
Elle Mouton
1507a7fcc7
autopilot: update AttachmentHeuristics with context
Continue threading context through the autopilot system and remove the
remaining context.TODOs.
2025-06-04 10:54:34 +02:00
Elle Mouton
46f09ba1ff
autopilot: continue threading context
Remove one context.TODO and add one more.
2025-06-04 10:54:34 +02:00
Elle Mouton
cd4a59071d
autopilot: start threading contexts through
The `GraphSource` interface in the `autopilot` package is directly
implemented by the `graphdb.KVStore` and so we will eventually thread
contexts through to this interface. So in this commit, we start updating
the autopilot system to thread contexts through in preparation for
passing the context through to any calls made to the GraphSource.

Two context.TODOs are added here which will be addressed in follow up
commits.
2025-06-04 10:54:34 +02:00
Elle Mouton
ca52834795
graph/db: use only exported KVStore ForEachNode method in tests
Replace all tests calls to the private `forEachNode` method on the
`KVStore` with the exported ForEachNode method. This is in preparation
for having the tests run against an abstract DB backend.
2025-06-04 10:54:34 +02:00
Elle Mouton
861606d28b
graph/db: remove kvdb.Backend from test helpers
Remove unused kvdb.Backend param from `randEdgePolicy` and
`newEdgePolicy` test helpers.
2025-06-04 10:54:34 +02:00
Elle Mouton
29ce7627fd
graph/db: remove kvdb param from test helper
Remove the kvdb.Backend parameter from the `createChannelEdge` helper.
This is all in preparation for having the unit tests run against any DB
backend.
2025-06-04 10:54:33 +02:00
Elle Mouton
1b26b590b4
graph/db: test clean-up
This commit cleans up the graph test code by removing unused kvdb type
parameters from the `createTextVertex` and `createLightningNode` helper
methods. We also pass in the testing parameter now so that we dont need
to check the error each time we call `createTestVertex`.
2025-06-04 10:54:33 +02:00
Elle Mouton
0ab61e08fa
discovery: listen on ctx in any select
For any method that takes a context that has a select that listens on
the systems quit channel, we should also listen on the ctx since we
should not need to worry about if this context is derived internally or
externally.
2025-06-04 10:54:33 +02:00
Elle Mouton
f2fb4827c7
discovery: remove unnecessary context.Background() calls 2025-06-04 10:54:33 +02:00
Elle Mouton
6b95b7933c
discovery: pass context through to bootstrapper SampleNodeAddrs
Since the ChannelGraphBootstrapper implementation makes a call to the
graph DB.
2025-06-04 10:54:33 +02:00
Elle Mouton
1a8e7587f9
discovery: pass context to ProcessRemoteAnnouncement
With this, we move a context.TODO() out of the gossiper and into the
brontide package - this will be removed in a future PR which focuses on
threading contexts through that code.
2025-06-04 10:54:33 +02:00
Elle Mouton
1a5821a873
discovery: thread contexts through sync manager
Here, we remove one context.TODO() by threading a context through to the
SyncManager.
2025-06-04 10:54:32 +02:00
Elle Mouton
a1a7d771da
discovery: thread contexts to syncer
The `GossiperSyncer` makes various calls to the `ChannelGraphTimeSeries`
interface which threads through to the graph DB. So in preparation for
threading context through to all the methods on that interface, we
update the GossipSyncer accordingly by passing contexts through.

Two `context.TODO()`s are added in this commit. They will be removed in
the upcoming commits.
2025-06-04 10:54:32 +02:00
Elle Mouton
5430157d0c
discovery: pass context through to reliable sender
And remove a context.TODO() that was added in the previous commit.
2025-06-04 10:54:32 +02:00
Elle Mouton
e2c184f235
discovery: thread context through to gossiper
Pass the parent LND context to the gossiper, let it derive a child
context that gets cancelled on Stop. Pass the context through to any
methods that will eventually thread it through to any graph DB calls.

One `context.TODO()` is added here - this will be removed in the next
commit.

NOTE: for any internal methods that the context gets passed to, if those
methods already listen on the gossiper's `quit` channel, then then don't
need to also listen on the passed context's Done() channel because the
quit channel is closed at the same time that the context is cancelled.
2025-06-04 10:54:32 +02:00
Elle Mouton
4fca1f35ae
lnd: pass context to newServer and server.Start
In preparation for starting to thread a single parent context through
LND, we update the main `server.Start` method to take a context so that
it can later pass it to any subsytem's Start method it calls. We also
pass the context to `newServer` since it makes some calls that will
eventually reach the DB (for example the graph db).
2025-06-04 10:54:32 +02:00
Elle Mouton
76d2bec58f
kvdb/etcd: remove context.TODO() from test helpers
We want `context.TODO()` to be high signal in the code-base. It should
signal clearly that work is required to thread parent context through to
the call-site. So to keep the signal-to-noise ratio high, we remove any
context.TODO() calls from tests since these will never need to be
replace by a parent context.

After this commit, there is only a single context.TODO() left in the
code-base.
2025-06-04 10:54:32 +02:00
Elle Mouton
54f6b16f01
macaroons: remove context.TODO() in tests
We want `context.TODO()` to be high signal in the code-base. It should
signal clearly that work is required to thread parent context through to
the call-site. So to keep the signal-to-noise ratio high, we remove any
context.TODO() calls from tests since these will never need to be
replace by a parent context.
2025-06-04 10:54:31 +02:00
Oliver Gugger
c52a6ddeb8
Merge pull request #9840 from Roasbeef/0-19-final
build: bump version to 0.19
2025-05-20 18:11:13 +02:00
Oliver Gugger
182aab7ca4
Merge pull request #9841 from twofaktor/patch-1
docs: minor format & grammatical fixes on release notes 0.19.0
2025-05-20 17:19:49 +02:00
⚡️2FakTor⚡️
2680715832
Update release-notes-0.19.0.md 2025-05-20 16:59:59 +02:00
Olaoluwa Osuntokun
2b76e2fbd5
build: bump version to 0.19.0 2025-05-19 17:46:29 -07:00
Olaoluwa Osuntokun
30f3d7ce89 discovery: lower bandwidth rate limiting log to Debugf 2025-05-19 17:46:08 -07:00
Oliver Gugger
3707b1fb70
Merge pull request #9814 from ziggie1984/add-info-release-notes
Update release notes for LND 19 - lncli breaking change
2025-05-16 10:14:08 +02:00
ziggie
468c527556
docs: update release notes for 19.
The `lncli listchannels` command was changed and might cause breaking
changes for people relying on the `chan_id` return value in their
automation scripts.
2025-05-16 09:41:36 +02:00
Olaoluwa Osuntokun
5ea7232c31
Merge pull request #9811 from lightningnetwork/0-19-rc-5
build: bump version to v0.19.0-beta.rc5
2025-05-15 14:48:45 -07:00
Olaoluwa Osuntokun
71dbc189d4
Merge pull request #9801 from Roasbeef/pong-relax
peer+lnd: add new CLI option to control if we D/C on slow pongs
2025-05-15 10:25:29 -07:00
Oliver Gugger
1e8fcc5ea9
build: bump version to v0.19.0-beta.rc5 2025-05-15 16:38:26 +02:00
Olaoluwa Osuntokun
e2c56af519
docs/release-notes: add release notes entry for pong default change 2025-05-15 16:36:38 +02:00
Olaoluwa Osuntokun
c493f0c0a9
peer+lnd: add new CLI option to control if we D/C on slow pongs
In this commit, we add a new CLI option to control if we D/C on slow
pongs or not. Due to the existence of head-of-the-line blocking at
various levels of abstraction (app buffer, slow processing, TCP kernel
buffers, etc), if there's a flurry of gossip messages (eg: 1K channel
updates), then even with a reasonable processing latency, a peer may
still not read our ping in time.

To give users another option, we add a flag that allows users to disable
this behavior. The default remains.
2025-05-15 16:36:38 +02:00
Oliver Gugger
b0cba7dd08
Merge pull request #9804 from ellemouton/removeChanGraphCacheMu
graph/db: remove ChannelGraph cacheMu
2025-05-13 16:10:17 +02:00
Oliver Gugger
1db6c31e20
Merge pull request #9798 from ellemouton/graphFixNotificationSubs
graph/db: synchronous topology client subscriptions/cancellations
2025-05-12 17:10:44 +02:00
Elle Mouton
0155d5d7b0
graph/db: handle topology updates in a single location
In this commit, we ensure that any topology update is forced to go via
the `handleTopologySubscriptions` handler so that client subscriptions
and updates are handled correctly and in the correct order.

This removes a bug that could result from a client missing a
notification about a channel being closed if the client is subscribed
and shortly after, `PruneGraph` is called which would notify all
subscribed clients and possibly do so before the client subscription has
actually been persisted.
2025-05-12 14:42:22 +02:00
Oliver Gugger
8044891df4
Merge pull request #9799 from guggero/function-call-formatting
docs: document previously implicit formatting rule
2025-05-12 13:13:14 +02:00
Elle Mouton
765fc6c132
graph/db: remove ChannelGraph cacheMu
We remove the mutex that was previously held between DB calls and calls
that update the graphCache. This is done so that the underlying DB calls
can make use of any batch requests which they currently cannot since the
mutex prevents multiple requests from calling the methods at once.

The reason the cacheMu was originally added here was during a code
refactor that moved the `graphCache` out of the `KVStore` and into the
`ChannelGraph` and the aim was then to have a best effort way of
ensuring that updates to the DB and updates to the graphCache were as
consistent/atomic as possible.
2025-05-12 08:29:40 +02:00
Oliver Gugger
12dbeb99c3
docs: document previously implicit formatting rule
Adds a formatting rule to the code style documentation that is being
used widely throughout the codebase but isn't properly documented.
2025-05-12 07:00:51 +02:00
Oliver Gugger
ee25c228e9
Merge pull request #8330 from bitromortac/2401-bimodal-improvements
bimodal pathfinding probability improvements
2025-05-08 21:35:49 +02:00
bitromortac
86249fbe6c
docs: update release notes 2025-05-08 18:31:31 +02:00
Oliver Gugger
43e822c3b0
Merge pull request #9789 from ellemouton/updateTLVDep
multi: use updated TLV SizeFunc signature
2025-05-08 10:20:37 +02:00
Olaoluwa Osuntokun
1a5432368e
Merge pull request #9785 from Roasbeef/go-1-23-9
build: bump Go version to v1.23.9
2025-05-07 16:17:00 -07:00
Elle Mouton
ad38ed73c7
go.mod+lnwire: bump TLV dep and fix MilliSatoshi Record
In this commit, we update the `tlv` package version which includes type
constraints on the `tlv.SizeBigSize` method parameter. This exposes a
bug in the MilliSatoshi Record method which is fixed here.

This was not caught in tests before since currently only
our TLV encoding code makes use of this SizeFunc (so we would write 0
size to disk) but then when we read the bytes from disk and decode, we
dont use the SizeFunc and our MilliSatoshi decode method makes direct
use of the `tlv.DBigSize` function which _currently does not make use of
the `l` length variable passed to it_. So it currently does correctly
read the data.
2025-05-07 19:36:25 +02:00
Oliver Gugger
8b413e89f1
Merge pull request #9793 from ellemouton/tlvSizeBigSize
tlv: catch unhandled type in SizeBigSize
2025-05-07 19:21:57 +02:00
Elle Mouton
ecb9755e19
tlv: constrain the types passed to MakeBigSizeRecord and SizeBigSize
Protect against unhandled types being passed to these methods.
2025-05-07 15:45:12 +02:00
Olaoluwa Osuntokun
1c76c6198c
build: bump Go version to v1.23.9 2025-05-06 15:50:16 -07:00
Oliver Gugger
67a40c90ad
Merge pull request #9782 from Roasbeef/v19-rc4
build: bump to version v0.19 rc4
2025-05-06 18:36:20 +02:00
Oliver Gugger
221b7634a6
Merge pull request #9783 from bitromortac/2505-loadmc-fixes
lncli: establish connection after parsing of mc data for loadmc
2025-05-06 13:52:57 +02:00
bitromortac
306df7048f
docs: update release notes 2025-05-06 11:16:36 +02:00
bitromortac
d33c92bd19
lncli: option to skip prompts for loadmc
This makes the command scriptable.
2025-05-06 11:13:30 +02:00
bitromortac
725a80a3b7
lncli: remove possibility to discard mission control from loadmc
This can already be done with resetmc and was only included here because
loadmc was used in a different context.
2025-05-06 10:59:41 +02:00
bitromortac
452022ee75
lncli: establish connection after mc parsing for loadmc
It can take some time to unmarshal large mission control data sets such
that the macaroon can expire during that phase. We postpone the
connection establishment to give the user more time to answer the
prompt.
2025-05-06 10:59:38 +02:00
bitromortac
56ccf60267
lncli: correct docs for loadmc 2025-05-06 10:53:37 +02:00
Olaoluwa Osuntokun
850f1edc5f
build: bump to version v0.19 rc4 2025-05-05 12:58:35 -07:00
Olaoluwa Osuntokun
7b6c1cf840
Merge pull request #9781 from bitromortac/2505-loadmc
lncli: add loadmc command
2025-05-05 12:00:32 -07:00
bitromortac
aaccc2bcaa
docs: update release notes 2025-05-05 15:02:55 +02:00
bitromortac
8351018f8b
lncli: add loadmc command
This command lets one import data exported by `lncli querymc > mc.json`
via `lncli loadmc --mcdatapath mc.json`.
2025-05-05 15:02:55 +02:00
bitromortac
180f61b9eb
lncli: change importmc category 2025-05-05 15:02:55 +02:00
Oliver Gugger
334a7d1123
Merge pull request #9770 from bitromortac/2504-mc-migration-fix
routing+migration: fix mission control migration with nil failure msg
2025-05-02 14:22:59 +02:00
bitromortac
07f863a2a5
routing: refine amount scaling
Mission control may have outdated success/failure amounts for node pairs
that have channels with differing capacities. In that case we assume to
still find the liquidity as before and rescale the amounts to the
according range.
2025-05-02 10:30:28 +02:00
bitromortac
5afb0de8a8
routing: forget info for contradictions
If we encounter invalid mission control data, we fall back to no
knowledge about the node pair.
2025-05-02 10:30:28 +02:00
bitromortac
5ba9619d22
routing: don't compute prob for success amounts
We skip the evaluation of probabilities when the amount is lower than
the last success amount, as the probability would be evaluated to 1 in
that case.
2025-05-02 10:30:28 +02:00
bitromortac
3819941c12
routing: regularize bimodal model
If the success and fail amounts indicate that a channel doesn't obey a
bimodal distribution, we fall back to a uniform/linear success
probability model. This also helps to avoid numerical normalization
issues with the bimodal model.

This is achieved by adding a very small summand to the balance
distribution P(x) ~ exp(-x/s) + exp((x-c)/s), 1/c that helps to
regularize the probability distribution. The distribution becomes finite
for intermediate balances where the exponentials would be evaluated to
an exact zero (float) otherwise. This regularization is effective in
edge cases and leads to falling back to a uniform model should the
bimodal model fail.

This affects the normalization to be s * (-2 * exp(-c/s) + 2 + 1/s) and
the primitive function to receive an extra term x/(cs).

The previously added fuzz seed is expected to be resolved with this.
2025-05-02 10:30:12 +02:00
bitromortac
fe32105b3e
routing: add test for small scale
This test demonstrates that the current bimodal model leads to numerical
inaccuracies in a certain regime of successes and failures.
2025-05-02 10:26:13 +02:00
bitromortac
615b617cda
routing: add test for fuzz test special case
This test demonstrates an error found in a fuzz test by adding a
previously found seed, which will be fixed in an upcoming commit.

The following fuzz test is expected to fail:
go test -v -fuzz=Prob ./routing/
2025-05-02 10:25:19 +02:00
bitromortac
04eec71d54
routing: use default tolerance for bimodal testing 2025-05-02 10:24:22 +02:00
bitromortac
55e7343b14
routing: update to realistic test values
The bimodal model doesn't depend on the unit, which is why updating to
more realistic values doesn't require changes in tests.

We pin the scale in the fuzz test to not invalidate the corpus.
2025-05-02 10:23:56 +02:00
bitromortac
5020e1f98b
docs: update release notes 2025-05-02 08:43:04 +02:00
bitromortac
15f0888fa8
routing: fix mission control migration
This commit is temporary and demonstrates a panic. To be squashed with
the following commit.
2025-05-02 08:42:25 +02:00
bitromortac
aa9abb3d96
routing: make sure failure message is valid 2025-04-30 17:43:58 +02:00
bitromortac
a450d85309
routing: remove paymentFailure pointer
This introduces an option instead to signal whether there was a failure
for result interpretation.
2025-04-30 17:43:58 +02:00
bitromortac
4498a78cb0
routing: remove paymentFailureInfo
We can remove paymentFailureInfo since we can gate the result
interpretation on the source index, meaning that if we don't have a
source index, we deal with an unknown payment outcome because we
couldn't pinpoint the failing hop.
2025-04-30 17:43:57 +02:00
bitromortac
e5c541407f
routing: make msg and index optional
This is later used to handle their nil values.
2025-04-30 17:42:53 +02:00
Oliver Gugger
b068d79dfb
Merge pull request #9776 from saubyk/19.0-releasenotes-update
docs: update release notes with more detail on the RBF coop close section
2025-04-30 08:14:07 +02:00
saubyk
52e1764e0a
docs: update release notes with more detail on the RBF coop close section 2025-04-29 21:07:51 -07:00
Yong
f21e3f3ee5
Merge pull request #9772 from yyforyongyu/show-all-inputs
sweep: return all inputs in `PendingSweeps`
2025-04-30 02:24:52 +08:00
yyforyongyu
b610678703
docs: update release notes 2025-04-29 14:15:36 +08:00
yyforyongyu
8f936aa9eb
itest: update num of sweeps in AssertNumPendingSweeps
Since we now return all sweeps, we need to update the call to include
immature sweeps.
2025-04-29 14:15:31 +08:00
yyforyongyu
d991f5659a
commands: add new field MaturityHeight 2025-04-29 13:49:22 +08:00
yyforyongyu
94463c6844
walletrpc: add new field MaturityHeight 2025-04-28 21:44:10 +08:00
yyforyongyu
0807ce61a1
sweep: return all inputs from PendingSweeps
Previously we'd skip returning immature inputs to stay backwards
compatible. This has the downside as the node operator cannot take
actions on this input, like some customized batching strategy via
`bumpfee` RPC.

After this change, it means when calling `bumpfee` RPC, it will now
update the params for the immature input instead of returning an error
saying `output does not belong to wallet`.

We also add a `MaturityHeight` field so the user knows when this input
will be swept.
2025-04-28 21:44:10 +08:00
Oliver Gugger
b34afa33f6
Merge pull request #9764 from ZZiigguurraatt/RBF-release-notes-cleanup
docs/release-notes: fix indentation in RBF close section
2025-04-28 12:38:23 +02:00
ZZiigguurraatt
cc3615c0a8 docs/release-notes: fix indentation in RBF close section 2025-04-25 15:06:57 -04:00
Oliver Gugger
7e50b8438e
Merge pull request #9759 from guggero/fix-release
GitHub: fix order of operations for release build
2025-04-24 12:15:07 +02:00
Oliver Gugger
d9089b11ac
GitHub: fix order of operations 2025-04-24 08:36:17 +02:00
Olaoluwa Osuntokun
bc4229b32e
Merge pull request #9736 from Roasbeef/0-19-0-rc3
build: bump version to v0.19.0 rc3
2025-04-23 15:12:15 -07:00
Oliver Gugger
4862251833
Merge pull request #9750 from starius/fix-InternalKeyForAddr-for-imported-addresses
lnwallet: fix InternalKeyForAddr for imported addr
2025-04-23 17:53:13 +02:00
Boris Nagaev
dc2cad9b0f
docs: update release-notes for 0.19.0 2025-04-23 11:53:30 -03:00
Boris Nagaev
c9b5974341
itest: test imported address in coop close
Make sure that an address imported to LND via ImportTapscript or ImportPublicKey
can be used as a delivery address in coop close.

New test cases:
 - P2TR address imported with ImportTapscript
 - P2TR address imported with ImportPublicKey
 - P2WPKH address imported with ImportPublicKey

Safeguard against https://github.com/lightninglabs/loop/issues/923
2025-04-23 11:53:29 -03:00
Boris Nagaev
6451ce495a
itest: use prefixed sub-tests
More sub-tests are needed in "coop close with external delivery" itest, which
would break the limit for 50 blocks mined. Turning it into prefixed itest.

Also used new exclusion approach in test "remote signer" where all the prefixed
tests are excluded.
2025-04-23 11:52:39 -03:00
Boris Nagaev
429000e360
lnwallet: fix InternalKeyForAddr for imported addr
An address imported using ImportTapscript doesn't provide a private key
so it can't satisfy waddrmgr.ManagedPubKeyAddress interface. So we don't
return an error for imported addresses.
Fix https://github.com/lightninglabs/loop/issues/923
2025-04-23 11:17:00 -03:00
Boris Nagaev
b5c9df81f0
lnwallet: fix error message
Include the variable of interest (walletAddr), not the outcome of the check
(pubKeyAddr) which is always nil.
2025-04-23 11:17:00 -03:00
Oliver Gugger
a35ace7371
Merge pull request #9739 from ellemouton/rpcInterceptorMD
lnrpc+rpcperms: add ctx metadata pairs to RPCMiddlewareRequest
2025-04-23 11:21:32 +02:00
Oliver Gugger
1aad61c2b6
Merge pull request #9755 from guggero/payment-bandwidth-funding-blob
htlcswitch+routing: add funding blob to PaymentBandwidth
2025-04-22 22:23:45 +02:00
Oliver Gugger
56d2381f51
htlcswitch+routing: add funding blob to PaymentBandwidth
For certain auxiliary bandwidth checks we need to know the funding blob
that was present when the channel was created.
2025-04-22 21:21:11 +02:00
Oliver Gugger
987302923d
Merge pull request #9746 from ziggie1984/make-resolution-more-efficient
lnwallet: no need to consult the aux unit for legacy channels
2025-04-22 18:56:28 +02:00
Elle Mouton
0417877038
docs: update release notes 2025-04-22 15:03:40 +02:00
Oliver Gugger
fba64c812b
itest: test RPCMiddleware gRPC metadata propagation
Expand the rpc middleware test to assert gRPC metadata pair propagation
for rpc middleware requests.
2025-04-22 15:03:17 +02:00
Elle Mouton
e9fc852390
lnrpc+rpcperms: add ctx metadata pairs to RPCMiddlewareRequest
The MW interceptor works via a stream & sends requests to the
interceptor via this stream. Sometimes, the interceptor might want to
work with grpc metadata pairs that were sent via the context of the
original request though and so in this commit, we ensure that for each
request sent via the stream to the interceptr, the grpc metadata is sent
along explicitly as well. This is done by adding a new `metadata_pairs`
field to the RPCMiddlewareRequest proto message.
2025-04-22 15:03:17 +02:00
Olaoluwa Osuntokun
c9fe051b2f
Merge pull request #9749 from ziggie1984/fix-logging-invoices-payments
mulit: increase logprogress time
2025-04-21 16:40:46 -07:00
ziggie
b9620a7dc3
mulit: increase logprogress time
Switched the batchsize to 30 seconds instead of number of payments
and invoices.
2025-04-22 00:01:16 +02:00
ziggie
98384b6f04
docs: add release-notes 2025-04-21 11:09:42 +02:00
ziggie
b2087a8a2e
lnwallet: skip aux resolution call for non-anchor channels. 2025-04-21 11:09:00 +02:00
Olaoluwa Osuntokun
d2f9e97c57
build: bump version to v0.19.0 rc3 2025-04-18 16:18:24 -07:00
Olaoluwa Osuntokun
49faa6f543
build: bump kvdb version 2025-04-18 16:18:23 -07:00
Olaoluwa Osuntokun
cb481df816
Merge pull request #9719 from Roasbeef/accessman-log
accessman: add logging to new sub-system
2025-04-18 16:11:11 -07:00
Olaoluwa Osuntokun
9d53617586
Merge pull request #9734 from ziggie1984/improve-logging-for-invoices-payments
Improve logging when fetching invoices and payments
2025-04-18 16:10:45 -07:00
Olaoluwa Osuntokun
825ee3d40b
Merge pull request #9723 from ziggie1984/add-global-lock-walletdb
Add the global lock for the wallet db back for postgres
2025-04-18 14:17:44 -07:00
ziggie
440ed31419
multi: add more logging when fetching invoices and payments. 2025-04-18 16:33:39 +02:00
ziggie
729c84bee3
lncli: return more clear error msg for deletepayments 2025-04-18 15:30:39 +02:00
Olaoluwa Osuntokun
21ca1e650b
acessman: improve access manager logging and error handling
This commit adds logs to the new access manager. This'll help us monitor
the new system behavior, and may make debugging easier in the future.
2025-04-17 17:20:11 -07:00
Olaoluwa Osuntokun
6517104da6
lnutil: add LogPubKey helper function
This captures a common pattern where we want to log a peer's public key
along side each logging statement.
2025-04-17 17:20:09 -07:00
Oliver Gugger
51add8a701
Merge pull request #9722 from ziggie1984/fix-notifier-itest-flake
Change RPC call order for the btcd notifier
2025-04-17 22:50:07 +02:00
András Bánki-Horváth
579f6f0f68
Merge pull request #9724 from bhandras/fundpsbt-custom-input-lock
walletrpc: allow custom lock ID and duration in `FundPsbt`
2025-04-17 17:31:48 +02:00
Oliver Gugger
337d9a9b23
Merge pull request #9628 from guggero/bitcoind-29
scripts+GitHub: use bitcoind v29.0
2025-04-17 16:23:00 +02:00
Andras Banki-Horvath
e86bea3625
docs: update release notes for 0.19 2025-04-17 15:25:12 +02:00
Andras Banki-Horvath
fbe645f96a
itests: add FundPsbt custom lock ID and duration test 2025-04-17 15:25:12 +02:00
Andras Banki-Horvath
8dab512981
walletrpc: make use of custom lock ID and lock duration in FundPsbt 2025-04-17 15:25:12 +02:00
Andras Banki-Horvath
a62e410ebf
walletrpc: add custom lock ID and lock duration to FundPsbtRequest 2025-04-17 15:25:11 +02:00
Oliver Gugger
c25f98f5b9
Merge pull request #9727 from guggero/strict-forwarding
Aux bandwidth manager: also pass HTLC blob to `ShouldHandleTraffic`
2025-04-17 14:00:46 +02:00
Oliver Gugger
a304be6bad
Merge pull request #9715 from ellemouton/removeChanClean
itest: remove manual channel closures from route blinding tests
2025-04-17 14:00:08 +02:00
Oliver Gugger
5fb0f43172
htlcswitch+routing: add htlc blob to ShouldHandleTraffic
Whether we should let the aux bandwidth manager decide what the
bandwidth of a channel is should also depend on whether the HTLC is a
custom HTLC, not just the channel.
2025-04-17 11:52:56 +02:00
Oliver Gugger
cf0e0ff32c
switch: add trace log for circular route detection
Helps with debugging of strict forwarding issues.
2025-04-17 10:53:37 +02:00
ziggie
cadc8d0fba
btcdnotify: change order of rpc calls.
We have to make sure we register the block notifier before we
fetch the best block for block notifications.
2025-04-17 09:51:44 +02:00
Oliver Gugger
121e6c4015
docs: update release notes 2025-04-17 09:11:54 +02:00
Oliver Gugger
f4a6c3487a
scripts+GitHub: use bitcoind v29.0 2025-04-17 09:07:01 +02:00
Oliver Gugger
24fdae7dff
Merge pull request #9693 from yyforyongyu/debug-listunspent
Fix inaccurate `locked_balance`
2025-04-17 08:46:36 +02:00
Oliver Gugger
7381f4b221
Merge pull request #9687 from GeorgeTsagk/aux-trff-shpr-htlcview
`AuxTrafficShaper.PaymentBandwidth` uses HTLC view
2025-04-17 07:59:00 +02:00
yyforyongyu
3d69d70eba
docs: update release notes 2025-04-17 09:36:32 +08:00
yyforyongyu
07583f94ff
gomod: update btcwallet 2025-04-17 09:36:28 +08:00
yyforyongyu
ab2361b8fb
lnd: log lockedBalance in WalletBalance 2025-04-17 09:32:29 +08:00
yyforyongyu
9d7b55ed4f
itest: add wallet-related itest 2025-04-17 09:32:28 +08:00
yyforyongyu
0daadb05bf
lntest: add SendAllCoins and remove standby nodes context
Standby nodes are no longer used so we update `AssertNumUTXOs` to remove
the confusing check.
2025-04-17 09:32:28 +08:00
ziggie
fee68593ab
docs: add release notes 2025-04-16 16:36:18 +02:00
ziggie
947702f6de
multi: add global log for wallet db postgres back 2025-04-16 16:31:24 +02:00
Oliver Gugger
06f1ef47fa
Merge pull request #9720 from ziggie1984/add-btcd-notify-logs
chainntnfs: increase logging of the subsystems
2025-04-15 18:56:44 +02:00
ziggie
8dde8cfc59
chainntnfs: increase logging of the subsystems 2025-04-15 15:20:22 +02:00
Oliver Gugger
014706cc3e
Merge pull request #9713 from ellemouton/getInfoStarted
scripts: use LND status to determine when a node is ready for RPC calls
2025-04-15 12:40:32 +02:00
Elle Mouton
c70e1194a0
scripts/bw-compat: use status server to determine if node is ready
This commit adjusts the backwards compatability test in 2 ways:

1) we first set up the bitcoin backend before waiting for our network
   nodes to start. This is so that the nodes can sync to chain and
become fully active during the `wait_for_nodes` call.
2) Then, in the `wait_for_node` helper, we use the status server
   response to determine if a node is ready instead of just waiting for
`GetInfo` to return a response. This is because waiting for
SERVER_ACTIVE is a more accurate signal that the node is ready to
receive RPC calls.
2025-04-15 11:49:07 +02:00
Oliver Gugger
7d3b5a1bcf
Merge pull request #9714 from bhandras/invoice-migration-log-rate
invoices: reduce log spam when migrating invoices to SQL
2025-04-14 20:38:22 +02:00
Oliver Gugger
bec84e14a8
Merge pull request #9674 from ziggie1984/small-neutrino-fix
Move neutrino db also to postgres when using postgres as a backend
2025-04-14 18:46:44 +02:00
Oliver Gugger
905cf651ac
Merge pull request #9712 from guggero/bump-kvdb
mod: bump kvdb to v1.4.15
2025-04-14 18:37:39 +02:00
Elle Mouton
b13f22be6b
itest: remove manual channel closures from route blinding tests
The itest framework now handles the closure of any channels opened
during the test and so we dont need to manually close them. The manual
closure code in the route blinding tests were resulting in the
occasional test flake.
2025-04-14 17:11:24 +02:00
Andras Banki-Horvath
7020edb60d
invoices: reduce log spam when migrating invoices to SQL 2025-04-14 16:57:30 +02:00
ziggie
417d2de43e
docs: add release-notes 2025-04-14 16:28:54 +02:00
ziggie
5c402472a4
mulit: move neutrino.db also to postgres 2025-04-14 16:28:54 +02:00
Oliver Gugger
62b0c4cc20
mod: bump kvdb to v1.4.15 2025-04-14 15:59:01 +02:00
Oliver Gugger
6d648ad90a
Merge pull request #9684 from ziggie1984/master
multi: update new walletdb version in main go.mod and kvdb go.mod
2025-04-14 15:56:16 +02:00
George Tsagkarelis
d0ef248ae2
htlcswitch: add HtlcView as PaymentBandwidth argument
In order to get more precise bandwidth reports, we also need to provide
this method with the latest htlc view. Since aux data is committed to in
the channel commitment, some uncommited HTLCs may not be accounted for,
so we need to manually provide them via the HTLC view.
2025-04-14 15:42:28 +02:00
George Tsagkarelis
53254c79b4
lnwallet: add FetchLatestHTLCView to LightningChannel
We add a public method for the lightning channel to expose the latest
HtlcView. This is used in a follow up commit by the channel link.
2025-04-14 15:34:09 +02:00
George Tsagkarelis
a279058408
lnwallet: replace HtlcView with AuxHtlcView in existing code 2025-04-14 15:34:09 +02:00
George Tsagkarelis
b96d57e88f
lnwallet: add AuxHtlcView 2025-04-14 15:34:09 +02:00
Yong
4cf18ee45c
Merge pull request #9703 from yyforyongyu/fix-attempt-hash
Patch htlc attempt hash for legacy payments
2025-04-14 21:12:26 +08:00
Oliver Gugger
ff6f98a818
Merge pull request #9696 from yyforyongyu/add-aider
Add `development_guidelines.md` for both human and machine
2025-04-14 11:19:58 +02:00
yyforyongyu
45029d9b6c
docs: add development_guidelines.md
This file focuses on the requirements and steps needed to make an
acceptable PR.
2025-04-14 17:08:56 +08:00
Oliver Gugger
f7bd2a7228
Merge pull request #9705 from ffranr/assert-log-shutdown-only-with-handle
lntest: assert shutdown via logs only if log file handle is present
2025-04-11 17:48:19 +02:00
ffranr
24ba098798
lntest: assert shutdown via logs only if log handle is present
Ensure shutdown assertions through log inspection are only performed
when the harness node has an active log file handle. This avoids
errors during shutdown when log file output is disabled.
2025-04-11 15:30:13 +01:00
yyforyongyu
79ab2b2daf
docs: update release notes 2025-04-11 21:52:15 +08:00
yyforyongyu
106e1e91f0
routing: patch the hash field for legacy payments
For legacy payments, the hash field will be nil, and we need to use the
payment identifier instead. We have multiple ways to fix this:

A trivial solution is we can simply call `sharder.GetHash` in
`collectResult`, and pass this hash to `attempt.Circuit()`, which ends
up multiple methods taking the hash. This is bad as it's confusing why
the methods of `HTLCAttempt` need to take another hash value, while
itself already has the info via `HTLCAttempt.Hash`. We don't want an
exceptional case to influence our main flow.

We can then patch the field `HTLCAttempt.Hash`, and set it to the
payment hash if it's nil, which can be done in `collectResult`. This is
also less optimal as it means every htlc attempts, either legacy or not,
now need to bear this context.

The best way to do this is to patch the field in
`reloadInflightAttempts`. As we are sure any new payments made won't be
legacy, and the only source of legacy payments comes from reloading
existing payments.
2025-04-11 21:52:15 +08:00
yyforyongyu
8689eeede4
routing: add regression test to catch the panic 2025-04-11 21:52:14 +08:00
Oliver Gugger
b732525a98
Merge pull request #9603 from NishantBansal2003/validate-mpp
routerrpc: add validation to MPP params
2025-04-10 19:28:02 +02:00
Nishant Bansal
e747ead933
docs: add release notes
Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
2025-04-10 18:17:35 +02:00
Nishant Bansal
cfab97c8be
routerrpc: add validation to MPP params
Adds validation to ensure that MPP parameters are compatible
with the payment amount before attempting the payment. This
prevents payments from entering a path finding loop that
would eventually timeout.
Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
2025-04-10 18:17:34 +02:00
ziggie
140dd339c1
kvdb: add make specific error available in kvdb package 2025-04-09 18:47:54 +02:00
ziggie
490347b056
multi: update walletdb package 2025-04-09 18:47:53 +02:00
Oliver Gugger
867d27d68a
Merge pull request #9689 from starius/fix-typos
chainrpc: fix description of several methods
2025-04-09 11:17:13 +02:00
Boris Nagaev
acd296e760
chainrpc: fix description of several methods
They pointed to a non-existing type ChainNotifierService. The actual name
of the interface is ChainNotifierServer.
2025-04-08 17:43:23 -03:00
Oliver Gugger
aaf0a19b06
Merge pull request #9682 from guggero/make-space-for-build
GitHub: disable cache, remove hosted tools cache
2025-04-07 16:06:37 +02:00
Oliver Gugger
5ac72ed282
GitHub: disable cache, remove hosted tools cache
In an attempt to fix the "out of disk space" build error during release
builds, we first disable using Golang caches (which can be quite large)
and then remove a bunch of pre-installed tools and their caches to
provide some additional disk storage.
2025-04-07 12:23:16 +02:00
Oliver Gugger
a7c2c47397
Merge pull request #9670 from Roasbeef/19-rc2
build: bump version to v0.19.0 rc2
2025-04-07 10:08:25 +02:00
Olaoluwa Osuntokun
a7e89c130d
build: bump version to v0.19.0 rc2 2025-04-04 18:43:13 -07:00
Olaoluwa Osuntokun
676414b9d7
build: update to latest versions of sqldb+kvdb 2025-04-04 18:43:09 -07:00
Olaoluwa Osuntokun
f0ea5bf3b0
Merge pull request #9672 from Roasbeef/kvdb-sqlite-incremental-vacuum
kvdb/sqlite: enable incremental auto_vacuum on DB creation
2025-04-04 17:57:51 -07:00
Olaoluwa Osuntokun
9eef2a0d6c
build: temp replace for sqldb+kvdb 2025-04-04 15:49:29 -07:00
Olaoluwa Osuntokun
9f5bf49ac7
sqldb/sqlite: enable incremental auto_vacuum on DB creation 2025-04-04 15:49:29 -07:00
Olaoluwa Osuntokun
a0d6a5591f
kvdb/sqlite: enable incremental auto_vacuum on DB creation
In this commit, we make a change that enables the `auto_vacuum =
incremental` pragma for SQLite databases, but only when the database
file is first created. Incremental auto-vacuum allows SQLite to reclaim
unused space within the database file over time, preventing indefinite
growth.
2025-04-04 15:49:29 -07:00
Olaoluwa Osuntokun
580935a4a4
Merge pull request #9669 from Roasbeef/rbf-taproot-downgrade
multi: downgrade to legacy coop close for taproot channels
2025-04-04 14:38:36 -07:00
Olaoluwa Osuntokun
96662ad43a
lncfg: mention that taproot chans aren't supported for rbf close 2025-04-04 14:38:12 -07:00
Olaoluwa Osuntokun
1fc2c64770
itest: test all combos of rbf close and taproot chans
In this commit, we test all the combinations of rbf close and taproot
chans. This ensures that the downgrade logic works properly.

Along the way we refactor the tests slightly, and also split them up, as
running all the combos back to back mines more than 50 blocks in a test,
which triggers an error in the itest sanity checks.
2025-04-03 16:23:36 -07:00
Olaoluwa Osuntokun
c5d3d76c33
peer+server: downgrade to legacy coop close for taproot channels
In this commit, we implement logic to downgrade to the legacy coop close
for taproot channels. Before this commit, we wouldn't allow nodes to
start up with both the taproot flag and the rbf flag activated.

In the future, once we implement the spec updates, we'll add support for
this combo, and can revert parts of this commit.
2025-04-01 16:41:26 -07:00
Oliver Gugger
6a3845b79d
Merge pull request #9667 from guggero/kvdb-update
mod: bump kvdb to latest tagged version v1.4.13
2025-04-01 21:30:22 +02:00
Oliver Gugger
973db1b305
mod: bump kvdb to latest tagged version v1.4.13
After merging #9665, we can now bump to the tagged version.
2025-04-01 18:42:28 +02:00
András Bánki-Horváth
2f8a2510d0
Merge pull request #9665 from bhandras/kvdb-etcd-bump
kvdb: bump etcd libs to v3.5.12
2025-04-01 18:37:07 +02:00
Oliver Gugger
11d3a68561
Merge pull request #9666 from bhandras/invoice-bucket-tombstone-log-removal
lnd: remove unnecessary debug log to avoid misunderstanding
2025-04-01 18:33:25 +02:00
András Bánki-Horváth
54978afab5
Merge pull request #9655 from bhandras/bw-compat-test-sqlite
scripts+bw-compatibility-test: update Dave and make it use sqlite
2025-04-01 17:35:48 +02:00
Andras Banki-Horvath
0c8b0351d6
lnd: remove unnecessary debug log to avoid misunderstanding 2025-04-01 13:03:56 +02:00
Andras Banki-Horvath
e8825f2098
build: add temporary kvdb mod replace 2025-04-01 12:40:32 +02:00
Andras Banki-Horvath
377397e8bc
kvdb: bump etcd libs to v3.5.12 2025-04-01 12:40:31 +02:00
Olaoluwa Osuntokun
b01f4e5148
Merge pull request #9657 from bartoli/bartoli-patch-1
Fix release build command
2025-03-31 11:14:17 -07:00
Andras Banki-Horvath
a427a872a0
scripts+bw-compatibility-test: run and update Dave with sqlite 2025-03-31 16:02:55 +02:00
Olivier BARTHELEMY
336799ad48 Fix release build command
vendor folder needs to be inside lnd-source folder for the following comands to work, but the two tar commands extract them in separate folder
2025-03-31 13:01:09 +01:00
Oliver Gugger
856dc2db79
Merge pull request #9636 from guggero/review-checklist
README+docs: add code review checklist
2025-03-28 16:18:10 -06:00
Andras Banki-Horvath
be2bab90ce
build: add sqlite support to the dev.Dockerfile build 2025-03-28 19:07:33 +01:00
Oliver Gugger
b6d8ecc747
Merge pull request #9654 from bhandras/use-sqldb-v1.0.8
mod: use sqldb v1.0.8
2025-03-28 10:32:44 -06:00
Oliver Gugger
d0032b1251
README+docs: add code review checklist
We want to encourage new contributors to review code instead of creating
their own PRs as a first contribution.
2025-03-28 09:53:15 -05:00
Andras Banki-Horvath
be71d75a6c
mod: use sqldb v1.0.8 2025-03-28 15:02:17 +01:00
Oliver Gugger
b85571b94b
Merge pull request #9647 from bhandras/sqldb-migration-base-version
sqldb: establish a base DB version even if it's not yet tracked
2025-03-28 07:59:22 -06:00
Andras Banki-Horvath
83d4b7b6b8
docs: update release notes 2025-03-28 11:00:58 +01:00
Andras Banki-Horvath
6291fbcf70
mod: add temp sqldb replace 2025-03-28 11:00:58 +01:00
Andras Banki-Horvath
4c4aeddeb0
sqldb: add unit test for the v0.19.0-rc1 migration bug 2025-03-28 11:00:58 +01:00
Andras Banki-Horvath
83d62308e9
sqldb: test schema migration idempotency 2025-03-28 11:00:57 +01:00
Andras Banki-Horvath
7e54682493
sqldb: fix dirty migration in v0.19.0-rc1 2025-03-28 11:00:57 +01:00
Andras Banki-Horvath
dae212697d
sqldb: establish a base DB version even if it's not yet tracked
Previously, if a DB version wasn't available, we re-ran all schema
migrations without verifying the schema version. However, setting a
base schema version is essential because some earlier migrations were
not idempotent. This commit addresses the issue by using the current
schema version provided by sqlc as the base.
2025-03-28 11:00:57 +01:00
Andras Banki-Horvath
65b2bac81c
sqldb: make migration 1 and 3 idempotent 2025-03-28 11:00:56 +01:00
András Bánki-Horváth
b6cf1bcaa0
Merge pull request #9630 from xinhangzhou/master
refactor: use maps.Copy for cleaner map handling
2025-03-28 07:54:34 +01:00
Oliver Gugger
eb822a5e11
Merge pull request #9504 from guggero/closedchannels
lnrpc+rpcserver: add custom channel data for closed channels
2025-03-27 12:33:48 -06:00
Oliver Gugger
177bbd2721
docs: add release notes 2025-03-27 12:38:49 -05:00
Oliver Gugger
82b7891e2d
rpcserver: add custom channel data to pending channels
The pending force close and pending waiting close channels didn't have
their custom channel data populated yet.
2025-03-27 12:38:49 -05:00
Oliver Gugger
bab5cabd90
lnrpc+rpcserver: add custom channel data for closed channels
This commit adds the custom channel data for closed channels which
represents the initial funding state as well as the final balances at
closing time.
2025-03-27 12:38:47 -05:00
Oliver Gugger
a53c6dda64
Merge pull request #9650 from Roasbeef/early-offer-chan-flushing-flake
lnwallet/chancloser: fix flake in TestRbfChannelFlushingTransitions/early_offer
2025-03-27 08:18:07 -06:00
Yong
15dbc43f51
Merge pull request #9627 from yyforyongyu/sweep-under-budget
Sweep inputs even the budget cannot be covered
2025-03-27 13:31:25 +08:00
Olaoluwa Osuntokun
f08e7fe0d6
lnwallet/chancloser: fix flake in TestRbfChannelFlushingTransitions/early_offer
In this commit, we fix a flake in the
`TestRbfChannelFlushingTransitions/early_offer` test. The fix is simple:
this is actually an "iteration", as we have a self transition to the
ChannelNegotiation state first. We also don't need to send the
remoteOffer, so we set `sendInit` to false. The offer still needs to be
passed in to ensure that the assertions work however.
2025-03-26 16:36:13 -07:00
Olaoluwa Osuntokun
a307280c40
protofsm: reduce log spam during state transitions
In this commit, we reduce log spam a bit during state transitions. We
only log the type of the event when sending the event, as we'll print
the same event when creating the queue "applying", and later when
processing". Next we remove the "applying" log as that first event will
always be logged twice.

The combo of these to changes makes the logs much easier to follow.
2025-03-26 16:36:11 -07:00
Oliver Gugger
193d2a6581
Merge pull request #9649 from guggero/cmd-fix
cmd: fix incorrect error code
2025-03-26 13:19:43 -06:00
Oliver Gugger
04533e924c
cmd: fix incorrect error code
Fixes a bug introduced by #9605, fixes #9648.
We return a specific error in the RPC permission interceptor for the
case where the wallet is already unlocked or is still locked.
We need to catch those errors correctly to give the user a bit more
context on what to do.
2025-03-26 11:28:59 -05:00
Oliver Gugger
f48e5098b1
Merge pull request #9643 from yyforyongyu/fix-startup
lnd: make sure startup flow is not aborted
2025-03-26 07:56:46 -06:00
yyforyongyu
ec2f3add6e
sweep: remove dead code 2025-03-26 18:24:48 +08:00
yyforyongyu
c7bea07d58
sweep: start the sweeping if there are normal inputs
We now start the sweeping process if there are normal inputs to
partially cover the budget.
2025-03-26 18:24:47 +08:00
yyforyongyu
b6daa3bad4
docs: update release notes 2025-03-26 18:24:47 +08:00
yyforyongyu
43409c7840
rpcserver: use HtlcIndex as the unique key 2025-03-26 18:24:47 +08:00
yyforyongyu
70dec8e169
lnd: log sync status in GetInfo
This is added to fix a flake found in starting the node.
2025-03-26 18:24:47 +08:00
yyforyongyu
8ad122bd03
itest: add testBumpFeeLowBudget 2025-03-26 18:24:47 +08:00
yyforyongyu
64f7a7f3d0
lntest+itest: update block height in MineBlockWithTx
Make sure we update the harness's current height and assert nodes have
been synced. Also fixes some typo found.
2025-03-26 18:24:47 +08:00
yyforyongyu
883381d266
lntest+itest: return the tx from FundCoins
This is used is a following test.
2025-03-26 18:24:46 +08:00
yyforyongyu
4abc1461ad
itest: refactor runBumpFee to fix a flake
Make sure we assertPendingSweepResp in a wait call to wait for the
updated resp.
2025-03-26 18:24:46 +08:00
yyforyongyu
eea3561eea
sweep+itest: return next retry fee rate in TxFailed event
We now return the next retry fee rate in `TxFailed` event in
`TxPublisher`. When handling the event, `UtxoSweeper` will update the
inputs to make sure the starting fee rate is set before attempting the
next sweep.
2025-03-26 18:24:44 +08:00
yyforyongyu
6dbf4ce470
sweep: add method calculateRetryFeeRate
A minor refactor to prepare the upcoming changes.
2025-03-26 15:26:11 +08:00
yyforyongyu
861dc145bf
sweep: create sweep tx even the budget cannot be met
We now always create the sweeping tx even though the budget cannot be
covered so we don't miss the deadline. Note that the fee bump will fail
once the provided wallet input cannot cover the increase fees, which is
fine as these inputs will be marked as failed and be retried again in
the next block. When that happens, if there are new wallet UTXOs, a new
batch will be created to perform the fee bump.
2025-03-26 15:26:08 +08:00
yyforyongyu
3c4fd1b484
sweep: refactor AddWalletInputs by adding addWalletInput
A minor refactor to prepare for upcoming changes.
2025-03-26 14:25:29 +08:00
yyforyongyu
1e0ddf3a16
docs: update release notes 2025-03-26 14:15:52 +08:00
yyforyongyu
b1e55f5fe5
lnd: don't abort the startup on subsystem errors
We want to make sure the node can start if non-fatal error is returned.
2025-03-26 14:15:52 +08:00
Oliver Gugger
0a2b33abe5
Merge pull request #9645 from guggero/update-contributors
docs: update release notes with contributors
2025-03-25 11:42:15 -06:00
Oliver Gugger
300fbbc528
docs: update release notes with contributors
Adds all missing contributors since v0.18.0-beta, extracted from the git
log.
2025-03-25 12:07:37 -05:00
Oliver Gugger
2998f5b3c5
Merge pull request #9644 from ziggie1984/payment-unit-test
Add unit test to record pkg
2025-03-25 10:34:38 -06:00
yyforyongyu
799ce9752a
lnd: skip ErrEdgeNotFound when iterating channels
We now skip another error when iterating the channels for our persistent
peers to unblock connecting to other peers.
2025-03-26 00:25:30 +08:00
yyforyongyu
dd8f2888a0
lnd: log errors during the startup flow
To aid us debugging startup issues.
2025-03-26 00:25:30 +08:00
ziggie
3952cf39c3
record: add unit test 2025-03-25 10:36:16 -05:00
Oliver Gugger
d1093cd3c3
Merge pull request #9642 from yyforyongyu/skip-blockbeat
lnd: skip setting `blockbeat` for `nochainbackend` mode
2025-03-25 08:20:34 -06:00
Oliver Gugger
cf35be847c
Merge pull request #9626 from ziggie1984/payment-lifecycle-small-fix
payment lifecycle small fix
2025-03-25 08:15:48 -06:00
Oliver Gugger
44161a3643
Merge pull request #9631 from hieblmi/degrade-err
rpcserver: warn if sendcoins default conf target is used
2025-03-25 08:09:11 -06:00
Oliver Gugger
af2f11edff
Merge pull request #9544 from lightningnetwork/elle-graphCacheBase
graph: move graph cache out of CRUD layer
2025-03-25 07:43:06 -06:00
Oliver Gugger
813f26cbc1
Merge pull request #9621 from jjjike2021/exp/maps
multi: remove x/exp/maps dependency
2025-03-25 07:08:02 -06:00
yyforyongyu
ab9004492e
lnd: skip setting blockbeat for nochainbackend mode
If we are in `nochainbackend` mode, we need to skip fetching the best
block during startup, otherwise it will be blocked.
2025-03-25 20:57:51 +08:00
ziggie
0eca55fe94
docs: add release-notes 2025-03-25 04:42:03 -05:00
ziggie
013e408abe
multi: make validation for keysend stricter 2025-03-25 04:42:03 -05:00
Elle Mouton
947ca937c7
docs: update release notes 2025-03-25 08:04:42 +02:00
Yong
3351a1745e
Merge pull request #9543 from ziggie1984/fix-payment-inconsitency
multi: fix payment failure overwrite
2025-03-25 11:36:24 +08:00
jike
e72caf7b45 multi: remove x/exp/maps dependency 2025-03-25 10:34:19 +08:00
Olaoluwa Osuntokun
0053fda6da
Merge pull request #9624 from Roasbeef/0-19-branch
build: bump version to v0.19 rc1
2025-03-24 19:24:26 -07:00
Olaoluwa Osuntokun
1cebfedbae
Merge pull request #9607 from Roasbeef/unified-gossip-limiter
discovery: unify rate.Limiter across all gossip peers
2025-03-24 19:22:16 -07:00
Olaoluwa Osuntokun
8c3c53f63b
docs/release-notes: add release notes for gossip rate limit 2025-03-24 19:21:46 -07:00
Olaoluwa Osuntokun
c7ed5d65c6
multi: add new config options to tune gossip msg allocated bandwidth
We go with the defaults of if no values are set.
2025-03-24 19:21:45 -07:00
Olaoluwa Osuntokun
05702d48b2
discovery: switch to bytes based rate limiting for outbound msgs
In this commit, we revamp the old message based rate limiting. First, we
move to meter by bytes/s instead of messages/s. The old logic had an
error in that it limited groups of message replies, instead of each
message. With this new approach, we'll use the newly added
SerializedSize method to implement fine grained bandwidth metering.

We need to pick two values, the burst rate, and the msg bytes rate. The
burst rate is the max amt that can be sent in a given period of time. We
need to set this above 65 KB, or the max msg limit, otherwise no
messages can be sent. The bucket starts with this many tokens (bytes).
As those are depleted, the amount of tokens is refilled at the msg
bytes rate.

As conservative values, we've chosen 200 KB as the burst rate, and 100
KB/s as the limit.
2025-03-24 19:21:45 -07:00
Olaoluwa Osuntokun
5b93319aaa
build: bump version to v0.19 rc1 2025-03-24 15:49:23 -07:00
Olaoluwa Osuntokun
66077b1722
chainio: demote per-block logging to Debugf 2025-03-24 15:49:21 -07:00
Olaoluwa Osuntokun
67d2eac437
Merge pull request #9620 from guggero/testnet4
chain: add testnet4 support
2025-03-24 15:48:26 -07:00
Oliver Gugger
f7b3177483
Merge pull request #9558 from kornpow/fix-listchaintxns-lncli-inputs
Fix input sanitation for listchaintxns lncli cmd
2025-03-24 13:53:29 -06:00
Oliver Gugger
e979538cf9
Merge pull request #9634 from hieblmi/fix-nil-pointer
routerrpc: add nil check for MilliSat
2025-03-24 11:45:30 -06:00
xinhangzhou
b7e3c20383 refactor: use maps.Copy for cleaner map handling
Signed-off-by: xinhangzhou <shuangcui@aliyun.com>
2025-03-25 01:19:55 +08:00
Sam Korn
2d829560a6
docs: update release notes 2025-03-24 10:53:38 -06:00
Sam Korn
bb398456b5
cmd: more input parameters checks for listchaintxns cli command
add a parsing block height function and error when block heights would produce invalid results
2025-03-24 10:53:38 -06:00
Oliver Gugger
d757bb51ee
docs: add release notes 2025-03-24 11:53:23 -05:00
Oliver Gugger
0aea482b51
multi: add testnet4 support 2025-03-24 11:53:23 -05:00
Oliver Gugger
e5b9d9684a
mod: bump btcwallet and neutrino to latest version 2025-03-24 11:53:23 -05:00
Oliver Gugger
6a9ec1065b
Merge pull request #9612 from GustavoStingelin/tests/multimutex
multimutex: add unit tests
2025-03-24 10:14:58 -06:00
Slyghtning
d38ad17478
docs: update release notes 2025-03-24 16:57:42 +01:00
Slyghtning
35c9eecf4e
routerrpc: add nil check for MilliSat 2025-03-24 16:54:05 +01:00
ziggie
b010e95445
itest: add itest to test duplicate failure notification 2025-03-24 09:17:09 -05:00
ziggie
9e683a38ef
docs: add release-notes 2025-03-24 09:17:09 -05:00
ziggie
4bdd37534e
routing: Ensure to not fail a payment twice 2025-03-24 09:17:09 -05:00
Elle Mouton
878746c9c9
graph/db: refactor to group all topology notification fields
A clean-up commit just to separate out all topology related fields in
ChannelGraph into a dedicated struct that then gets mounted to the
ChannelGraph.
2025-03-24 15:05:48 +02:00
Elle Mouton
2221aaa889
graph/db: move Topology client management to ChannelGraph
We plan to later on add an option for a remote graph source which will
be managed from the ChannelGraph. In such a set-up, a node would rely on
the remote graph source for graph updates instead of from gossip sync.
In this scenario, however, our topology subscription logic should still
notify clients of all updates and so it makes more sense to have the
logic as part of the ChannelGraph so that we can send updates we receive
from the remote graph.
2025-03-24 15:05:48 +02:00
Elle Mouton
4131b3fc7e
graph/db: adjust TestPartialNode
The test as it stands today does not make sense as it adds a
Partial/Shell node to the graph via AddLightningNode which will never
happen since this is only ever triggered by the gossiper which only
calls the method with a full node announcement. Shell/Partial nodes are
only ever added via AddChannelEdge which will insert a partial node if
we are adding a channel edge which has node pub keys that we dont have a
node entry for. So we adjust the test to use this more accurate flow.
2025-03-24 15:05:48 +02:00
Elle Mouton
caf69cc4f9
docs: update release notes 2025-03-24 15:05:47 +02:00
Elle Mouton
8c11ca97e1
itest: rename closure for clarity 2025-03-24 15:05:47 +02:00
Elle Mouton
45450886d7
graph/db: populate the graph cache in Start instead of during construction
In this commit, we move the graph cache population logic out of the
ChannelGraph constructor and into its Start method instead.
2025-03-24 15:05:47 +02:00
Elle Mouton
b497c4694e
multi: add Start and Stop methods for ChannelGraph
We do this in preparation for moving channel cache population logic out
of the constructor and into the Start method. We also will later on
(when topology subscription is moved to the ChannelGraph), have a
goroutine that will need to be kicked off and stopped.
2025-03-24 15:05:47 +02:00
Elle Mouton
bb3839e422
graph/db: completely remove cache from KVStore 2025-03-24 15:05:47 +02:00
Elle Mouton
ba1d21d5c7
graph/db: move cache write for UpdateEdgePolicy
To the ChannelGraph.
2025-03-24 15:05:47 +02:00
Elle Mouton
9d0b9f9ace
graph/db: move cache write for MarkEdgeZombie
From the KVStore to the ChannelGraph.
2025-03-24 15:05:47 +02:00
Elle Mouton
4d00eb2aa4
graph/db: move FilterKnownChanIDs zombie logic up one layer
Here, we move the business logic in FilterKnownChanIDs from the CRUD
layer to the ChannelGraph layer. We also add a test for the logic.
2025-03-24 15:05:47 +02:00
Elle Mouton
cc4fcbf838
graph/db: move cache writes for Prune methods
This commit moves the cache writes for PruneGraphNodes and PruneGraph
from the KVStore to the ChannelGraph.
2025-03-24 15:05:47 +02:00
Elle Mouton
941e7bf6b3
graph/db: move cache update out of pruneGraphNodes
In preparation for moving the cache write completely out of KVStore, we
move the cache write up one layer.
2025-03-24 15:05:46 +02:00
Elle Mouton
71e5ab6200
graph/db: move some cache writes to ChannelGraph.
Here we move the cache writes for DisconnectBlockAtHeight and
DeleteChannelEdges to the ChannelGraph.
2025-03-24 15:05:46 +02:00
Elle Mouton
081c9dc082
graph/db: refactor delChannelEdgeUnsafe to return edge info
And update cache outside the method rather. This will make it easier to
completely move the cache write out to the ChannelGraph layer.
2025-03-24 15:05:46 +02:00
Elle Mouton
f75e6a1c10
graph/db: move various cache write calls to ChannelGraph
Here, we move the graph cache writes for AddLightningNode,
DeleteLightningNode, AddChannelEdge and MarkEdgeLive to the
ChannelGraph. Since these are writes, the cache is only updated if the
DB write is successful.
2025-03-24 15:05:46 +02:00
Elle Mouton
9fe9e32c6e
graph/db: move cache read checks to ChannelGraph.
This commit moves the graph cache checks for FetchNodeFeatures,
ForEachNodeDirectedChannel, GraphSession and ForEachNodeCached from the
KVStore to the ChannelGraph. Since the ChannelGraph is currently just a
pass-through for any of the KVStore methods, all that needs to be done
for calls to go via the ChannelGraph instead directly to the KVStore is
for the ChannelGraph to go and implement those methods.
2025-03-24 15:05:46 +02:00
Elle Mouton
88398e3dd9
graph/db: let ChannelGraph init the graphCache
In this commit, we let the ChannelGraph be responsible for populating
the graphCache and then passing it to the KVStore. This is a first step
in moving the graphCache completely out of the KVStore layer.
2025-03-24 15:05:46 +02:00
Elle Mouton
00432e46f3
multi: add ChannelGraph Config struct
And use this struct to pass NewChannelGraph anything it needs to be able
to init the KVStore that it houses. This will allow us to add
ChannelGraph specific options.
2025-03-24 15:05:46 +02:00
Elle Mouton
81e0608c10
graph/db: rename Options to KVStoreOptions
Namespace these options so that we can introduce separate options for
the new ChannelGraph.
2025-03-24 15:05:46 +02:00
Elle Mouton
ae3961b47f
graph/db: fix linter issues of old code
Since we have renamed a file housing some very old code, the linter has
now run on all this code for the first time. So we gotta do some
clean-up work here to make it happy.
2025-03-24 15:05:46 +02:00
Elle Mouton
1ee4bb8c51
graph/db: rename ChannelGraph and introduce the new ChannelGraph layer
In this commit, we rename the existing ChannelGraph struct to KVStore to
better reflect its responsibilities as the CRUD layer. We then introduce
a new ChannelGraph struct which will eventually be the layer above the
CRUD layer in which we will handle cacheing and topology subscriptions.
For now, however, it houses only the KVStore. This means that all calls
to the KVStore will now go through this layer of indirection first. This
will allow us to slowly move the graph Cache management out of the
KVStore and into the new ChannelGraph layer.

We introduce the new ChannelGraph and rename the old one in the same
commit so that all existing call-sites don't need to change at all :)
2025-03-24 15:05:46 +02:00
Elle Mouton
ed8e10e4b9
graph/db: rename graph.go file
Rename it to kv_store.go so that we can re-use the graph.go file name
later on. We will use it to house the _new_ ChannelGraph when the
existing ChannelGraph is renamed to more clearly reflect its
responsibilities as the CRUD layer.
2025-03-24 15:05:46 +02:00
Slyghtning
06c7cd82e9
rpcserver: warn if sendcoins default conf target is used 2025-03-24 13:20:47 +01:00
Gustavo Stingelin
a404f34646
multimutex: add unit tests 2025-03-24 00:40:35 -03:00
Olaoluwa Osuntokun
5235f3b24f
Merge pull request #9623 from Roasbeef/size-msg-test-msg
Size msg test msg
2025-03-21 12:18:36 -07:00
Olaoluwa Osuntokun
05a6b6838f
lnwire: add new TestSerializedSize method
This uses all the interfaces and implementations added in the prior test.
2025-03-20 18:28:53 -07:00
Olaoluwa Osuntokun
b2f24789dc
lnwire: revamp TestLightningWireProtocol using new rapid test gen
With what we added in the prior commit, we can significantly shrink the
size of this test. We also make it easier to extend in the future, as
this will fail if a new message is added, that doesn't have the needed
methods, as long as MsgEnd is updated.
2025-03-20 18:28:23 -07:00
Olaoluwa Osuntokun
eb877db2ff
lnwire: add new TestMessage interface for property tests
In this commit, we add a new `TestMessage` interface for use in property
tests. With this, we'll be able to generate a random instance of a given
message, using the rapid byte stream. This can also eventually be useful
for fuzzing.
2025-03-20 18:28:07 -07:00
Olaoluwa Osuntokun
56a100123b
lnwire: add new SerializedSize method to all wire messages
This'll be useful for the bandwidth based rate limiting we'll implement
in the next commit.
2025-03-20 18:27:52 -07:00
Yong
5d921723b1
Merge pull request #9609 from yyforyongyu/fix-listunspent
Fix inaccurate `listunspent` result
2025-03-21 09:22:11 +08:00
yyforyongyu
bdcd980868
docs: update release notes 2025-03-21 08:06:19 +08:00
yyforyongyu
ea66dacc07
itest: skip force close restart in windows 2025-03-21 08:06:19 +08:00
yyforyongyu
feeb9acaa7
itest: document flakeRaceInBitcoinClientNotifications 2025-03-21 08:06:19 +08:00
yyforyongyu
f0300762c0
lnwallet: move btcwallet log under BTWL
So we can focus on debugging `BTWL` without concerning `lnwallet`, which
has a lot of channel-specific loggings.
2025-03-21 08:06:18 +08:00
yyforyongyu
07f60bd910
lnd: enable logging for sqldb 2025-03-21 08:06:18 +08:00
yyforyongyu
9a25d7172e
sweep: improve loggings 2025-03-21 08:06:18 +08:00
yyforyongyu
533aeb8a25
itest: remove flakeFundExtraUTXO 2025-03-21 08:06:18 +08:00
yyforyongyu
07c60c85f9
lnwallet: remove direct walletdb reference
To make sure the db layer is not exposed and is only managed by the
`btcwallet` package.
2025-03-21 08:06:18 +08:00
yyforyongyu
6e1c098923
gomod: update btcwallet 2025-03-21 08:06:18 +08:00
yyforyongyu
16348e61db
sqldb: add trace logs in ExecuteSQLTransactionWithRetry 2025-03-21 08:06:17 +08:00
Olaoluwa Osuntokun
e8875e06fe
Merge pull request #9602 from lightningnetwork/yy-pending-remote-commit
multi: make sure HTLCs are locked in the itest
2025-03-20 18:11:02 -05:00
yyforyongyu
3b7f9e1975
docs: update release notes 2025-03-21 03:25:24 +08:00
yyforyongyu
652d39dcc7
itest: fix flakeSkipPendingSweepsCheckDarwin
Now that we have the new RPC to assert the HTLC state, this flake should
be fixed.
2025-03-21 03:25:24 +08:00
yyforyongyu
5a72d5258f
htlcswitch+itest: catch link quit signal when processing hodlQueue
This commit makes sure when processing resolutions, e.g, settling
invoices, when the link is already broken, the process would exit with
an error. This fixes the issue we found in the itest, where an
unexpected empty remote pending commitment was created although the
remote peer is already offline.
2025-03-21 03:25:24 +08:00
yyforyongyu
0892b595dd
lnrpc+rpcserver: add new field LockedIn for HTLCs
In this commit, we add a new field `LockedIn` on HTLCs so it can be used
to decide whether an HTLC found on the local commitment has been
committed on the remote commitment.
2025-03-21 03:25:23 +08:00
yyforyongyu
be4c4cc8ff
multi: improve logging 2025-03-21 03:24:26 +08:00
yyforyongyu
66c94cf51f
contractcourt: add String to CommitSet for logging 2025-03-21 03:24:26 +08:00
yyforyongyu
c5f17180bc
netann: remove unused param in newMockGraph 2025-03-21 03:24:26 +08:00
Yong
09b6745086
Merge pull request #9600 from lightningnetwork/yy-more-flakes
lntest+itest: document and fix more flakes
2025-03-21 03:23:02 +08:00
Oliver Gugger
b031002aed
Merge pull request #9604 from jjjike2021/fix-dep
fn: remove x/exp/slices dependency
2025-03-20 11:10:18 -06:00
Olaoluwa Osuntokun
ea050d06f0
Merge pull request #9610 from lightningnetwork/rbf-staging
multi: integrate rbf changes from staging branch
2025-03-19 15:01:23 -05:00
Oliver Gugger
de1ed93f52
Merge pull request #9601 from bitromortac/2503-neutrino-maxpeers
lnd: pass through neutrino MaxPeers config
2025-03-18 16:41:39 -06:00
Olaoluwa Osuntokun
42fa83700a docs/release-notes: update release notes for RBF close 2025-03-18 12:48:01 -05:00
bitromortac
a93ec3b01a
lnd: pass through neutrino MaxPeers config
This enables users to set the config. We check that the currently set
value of MaxPeers is non-zero.
2025-03-18 17:55:20 +01:00
Olaoluwa Osuntokun
3681ba6d8b peer: for RBF state machine block req if RBF iterating is outstanding
This fixes an issue in the itests in the restart case. We'd see an error
like:
```
2025-03-12 23:41:10.754 [ERR] PFSM state_machine.go:661: FSM(rbf_chan_closer(2f20725d9004f7fda7ef280f77dd8d419fd6669bda1a5231dd58d6f6597066e0:0)): Unable to apply event err="invalid state transition: received *chancloser.SendOfferEvent while in ClosingNegotiation(local=LocalOfferSent(proposed_fee=0.00000193 BTC), remote=ClosePending(txid=07229915459cb439bdb8ad4f5bf112dc6f42fca0192ea16a7d6dd05e607b92ae, party=Remote, fee_rate=1 sat/vb))"
```

We resolve this by waiting to send in the new request unil the old one
has been completed.
2025-03-18 11:44:59 -05:00
Olaoluwa Osuntokun
8df58a984c lnwallet/chancloser: add docs for new rbf chan closer 2025-03-18 11:44:59 -05:00
Olaoluwa Osuntokun
720d98cc15 msgmux: fix arg expectation for mock in unit test 2025-03-18 11:44:59 -05:00
Olaoluwa Osuntokun
8d9ed0ca73 docs/release-notes: add rbf coop close section 2025-03-18 11:44:59 -05:00
Olaoluwa Osuntokun
fccd984ac1 multi: extract new DeriveHeightHint() function, use for new rbf closer
With this commit, we make sure we set the right height hint, even if the
channel is a zero conf channel.
2025-03-18 11:44:59 -05:00
Olaoluwa Osuntokun
971ac5a14d itest: add new RBF coop close itest
The itest has both sides try to close multiple times, each time with
increasing fee rates. We also test the reconnection case, bad RBF
updates, and instances where the local party can't actually pay for
fees.
2025-03-18 11:44:57 -05:00
Olaoluwa Osuntokun
3a18bf088c multi: enable RBF co-op bumps after reconnection
In this commit, we alter the existing co-op close flow to enable RBF
bumps after re connection. With the new RBF close flow, it's possible
that after a success round _and_ a re connection, either side wants to do
another fee bump. Typically we route these requests through the switch,
but in this case, the link no longer exists in the switch, so any
requests to fee bump again would find that the link doesn't exist.

In this commit, we implement a work around wherein if we have an RBF
chan closer active, and the link isn't in the switch, then we just route
the request directly to the chan closer via the peer. Once we have the
chan closer, we can use the exact same flow as prior.
2025-03-18 11:44:21 -05:00
Olaoluwa Osuntokun
80b48fb49b peer: update rbf close client logic w/ error and iteration awareness
We'll properly handle a protocol error due to user input by halting, and
sending the error back to the user.

When a user goes to issue a new update, based on which state we're in,
we'll either kick off the shutdown, or attempt a new offer. This matches
the new spec update where we'll only send `Shutdown` once per
connection.
2025-03-18 11:44:21 -05:00
Olaoluwa Osuntokun
fbc67f7610 lntest+itest: extend CloseChannelAssertPending
In this commit, we extend `CloseChannelAssertPending` with new args that
returns the raw close status update (as we have more things we'd like to
assert), and also allows us to pass in a custom fee rate.
2025-03-18 11:44:21 -05:00
Olaoluwa Osuntokun
2899a757c4 lntest: fix error message in WaitForChannelCloseEvent
Resp is always nil, so we actually need to log event.Update here.
2025-03-18 11:44:21 -05:00
Olaoluwa Osuntokun
81d34c4ac2 peer+rpc: set new rbf coop close rbf update fields 2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
b32f576aeb lnrpc: add fee rate and local close bool to PendingUpdate
This'll allow us to notify the caller each time a new coop close
transaction with a higher fee rate is signed.
2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
282c50e444 peer: attempt to unregister endpoint before registering
If we hit an error, we want to wipe the state machine state, which also
includes removing the old endpoint.
2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
029fbd22fd peer: make activeChanCloses a SyncMap
This fixes some existing race conditions, as the `finalizeChanClosure`
function was being called from outside the main event loop.
2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
413cc3da35 itest: update async coop close itests to also use new rbf flow 2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
429bbf33ef server: thread through new NoRbfCoopClose option
For now, we disallow the option to be used with the taproot chans
option, as the new flow hasn't yet been updated for nonce usage.
2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
47af913b45 lncfg: add new protocol option - RbfCoopClose 2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
a9e538e52d feature: add new NoRbfCoopClose option 2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
6364e98f0e peer: conditionally create rbf coop close fsm based on feature bits
In this commit, we fully integrate the new RBF close state machine into
the peer.

For the restart case after shutdown, we can short circuit the existing
logic as the new FSM will handle retransmitting the shutdown message
itself, and doesn't need to delegate that duty to the link.

Unlike the existing state machine, we're able to restart the flow to
sign a coop close with a new higher fee rate. In this case, we can now
send multiple updates to the RPC caller, one for each newly singed coop
close transaction.

To implement the async flush case, we'll launch a new goroutine to wait
until the state machine reaches the `ChannelFlushing` state, then we'll
register the hook. We don't do this at start up, as otherwise the
channel may _already_ be flushed, triggering an invalid state
transition.
2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
f22cba9de1 peer: conditionally create new RBF chan closer 2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
4fff0d8401 peer: create ErrorReporter implementation for rbf-fsm 2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
2dae0ba96e peer: add new composite chanCloserFsm type
In this commit, we add a new composite chanCloserFsm type. This'll allow
us to store a single value that might be a negotiator or and rbf-er.

In a follow up commit, we'll use this to conditionally create the new
rbf closer.
2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
23a9a761e3 peer: add initial awareness of new rbf coop closer
In this commit, we use the interfaces we created in the prior commit to
make a new method capable of spinning up the new rbf coop closer.
2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
047d3532f9 peer: move existing chan closer init logic to new method
In the next commit, we'll start checking feature bits to decide how to
init the chan closer.

In the future, we can move the current chan closer handling logic into
an `MsgEndpoint`, which'll allow us to get rid of the explicit chan
closer map and direct handling.
2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
fd05199ff2 protofsm: add an upfront check for SendWhen predicates
In this commit, we add an upfront check for `SendWhen` predicates before
deciding to launch a goroutine. This ensures that when a message comes
along that is already ready to send, we do the send in a synchronous
manner.
2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
84b6d95b91 lnwallet/chancloser: increase test coverage of state machine 2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
7370617aaa lnwallet/chancloser: update RBF state machine to handle early offer case
In this commit, we update the RBF state machine to handle early offer
cases. This can happen if after we send out shutdown (to kick things
off), the remote party sends their offer early. This can also happen if
their outgoing shutdown (to ACK ours) was delayed for w/e reason, and we
get their offer first.

The alternative was to modify the state machine itself, but we feel that
handling this early case is better in line with the Robustness principle.
2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
333492e657 lnwallet: implement special case for OP_RETURN in rbf-coop
In this commit, we implement a special case for OP_RETURN scripts
outlined in the spec. If a party decides that its output will be too
small even after the dust check, then they can opt to set it to zero by sending an `OP_RETURN` as their script.
2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
e6d7a1a2ec protofsm: update state machine w/ new spec flow
In this commit, we implement the latest version of the RBF loop as
described in the spec. We remove the self loop back based on sending or
receiving shutdown. Instead, from the ClosePending state, we can trigger
a new loop by sending SendOfferEvent (we bump), or OfferReceivedEvent
(they bump).

We also update the rbf state machine w/ the new close addr logic. This
log ensures that the remote party always sends our current address, and
that if they send a new address, we'll update our view of it, and
counter sign the correct transaction.

We also add a CloseErr state. With this new state, we can ensure that
we're able to properly report errors back to the RPC client, and also
optionally force a reconnection or send a warning to the remote party.
2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
14eca4406e lnwire: update closing_complete and closing_sig to latest spec draft
Both these messages now carry the address of both parties, so you can
update an address without needing to send shutdown again.
2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
835d7623d4 lnwallet/chancloser: ignore spurious channel flushed events
If we go to close while the channel is already flushed, we might get an extra event, so we can safely ignore it and do a self state transition.
2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
27b24ab86a lnwallet/chancloser: add fee rate to ClosePending
This'll be useful to communicate what the new fee rate is to an RPC caller.
2025-03-18 11:44:20 -05:00
yyforyongyu
8b2ade7ec9 lnwallet+protofsm: fix logging 2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
baf0d83c37 peer: update chooseDeliveryScript to gen script if needed
In this commit, we update `chooseDeliveryScript` to generate a new
script if needed. This allows us to fold in a few other lines that
always followed this function into this expanded function.

The tests have been updated accordingly.
2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
8432e706d3 multi: extract new DeriveHeightHint() function, use for new rbf closer
With this commit, we make sure we set the right height hint, even if the
channel is a zero conf channel.
2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
b2794b07cb lnwallet/chancloser: enforce pubkey binding for msg mapper 2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
ed8a672bd3 protofsm: update StateMachine to meet new msgmux.Endpoint interface 2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
bf3007a2ce multi: thread thru RPC caller context from CloseChannel 2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
127d3bed66 input: add new ScriptIsOpReturn helper func 2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
29afeb42fb lnd: register protofsm logger 2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
a4064c70dc protofsm: don't return error on broadcast fail
We don't return an error on broadcast fail as the broadcast might have
failed due to insufficient fees, or inability to be replaced, which may
happen when one side attempts to unnecessarily bump their coop close
fee.
2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
daed552bf1 peer: make a DaemonAdapter impl for lnd
Similar to the last commit, in this commit, we make a daemon adapter
impl for lnd. We'll use this for any future protofsm based state
machine.
2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
c32d4f9695 peer: add an implementation of chancloser.ChanObserver
In this commit, we add an implementation of a new interface the rbf coop
state machine needs. We take care to accept interfaces everywhere, to
make this easier to test, and decouple from the concrete types we'll end
up using elsewhere.
2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
1d2c22d140 protofsm: add String() method to ProtocolState 2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
01382f4c2a htlcswitch: change LinkDirection to be a type alias
In this commit, we change LinkDirection to be a type alias. This makes
creating wrapper structs/interfaces easier, as we don't need to leak
htlcswitch.LinkDirection, instead we can accept a bool.
2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
82ac5fa912 feature: set optional bit for rbf-coop close by default 2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
752fc86c7d lnwire: add feature bits for new rbf coop close 2025-03-18 11:44:20 -05:00
Olaoluwa Osuntokun
c292acfe74
Merge pull request #9605 from yyforyongyu/fix-unlock-wallet
cmd: fix error parsed from status
2025-03-18 11:37:35 -05:00
András Bánki-Horváth
3e8339cebc
Merge pull request #9606 from bhandras/skip-migration-rename
config: rename skip-sql-invoice-migration to skip-native-sql-migration
2025-03-18 13:56:45 +01:00
yyforyongyu
49382a8f67
gomod: update tor to v1.1.6 2025-03-18 20:24:34 +08:00
yyforyongyu
1db110f4cb
itest: fix flake in testFundingExpiryBlocksOnPending
We need to make sure to wait for the RPC to show the updated results.
2025-03-18 20:24:34 +08:00
yyforyongyu
d122a2ecc6
itest+lntest: add flakePaymentStreamReturnEarly
Fix the flake found in the `testRelayingBlindedError` and documents the
flake found in other tests in one place.
2025-03-18 20:24:34 +08:00
yyforyongyu
e0296083ca
itest+lntest: fix flake in coopCloseWithHTLCsWithRestart
Previously we'd restart Alice and then restart Bob, which means once
Alice is shut down and started again before we shut down Bob, Bob will
attempt to connect Alice since the connection is permanent, which could
put the node in a weird state. We now make sure both nodes are shut down
first, then bring them back online to avoid the above case. We may,
however, create another test in the future to check the above case if needed.
2025-03-18 20:24:34 +08:00
yyforyongyu
a067aa45ef
itest: add flakeInconsistentHTLCView 2025-03-18 20:24:33 +08:00
Andras Banki-Horvath
55595954ad
config: rename skip-sql-invoice-migration to skip-native-sql-migration
In future releases we will use this flag to make potentially failing kv
to native SQL migrations optional. For this reason it is better to
rename it to avoid confusion.
2025-03-17 16:45:18 +01:00
yyforyongyu
e62aa7d43a
docs: update release notes 2025-03-17 22:59:56 +08:00
yyforyongyu
2d6148291f
cmd: fix error parsed from status
The `codes.Unimplemented` is only returned when the RPCs are not built.
When the wallet is in an unexpected state, `codes.Unknown` is returned
instead, so we need to catch it properly to make sure we return the
right error msg.
2025-03-17 22:59:56 +08:00
jike
796110f7af
fn: remove x/exp dependency 2025-03-17 16:43:38 +08:00
Oliver Gugger
053d63e110
Merge pull request #9546 from hieblmi/macaroon-ip-cidr-constraint
macaroons: ip range constraint
2025-03-14 10:22:43 -06:00
Oliver Gugger
c4a77d1b1c
Merge pull request #9502 from guggero/bandwidth-manager-fix
routing: don't set custom amount if manager isn't handling
2025-03-13 13:42:51 -06:00
Oliver Gugger
0044975293
docs: add release notes 2025-03-13 12:27:07 -05:00
Oliver Gugger
dac5de5ce9
htlcswitch+routing: return IsHandled from AuxBandwidth
To make it more clear whether the external traffic shaper is handling a
channel or not, we return an explicit boolean.
2025-03-13 12:26:50 -05:00
Slyghtning
bef0268e07
docs: update release notes 2025-03-13 09:57:41 -05:00
Slyghtning
1681be6d65
lncli: ip range caveat for macaroons 2025-03-13 09:57:41 -05:00
Slyghtning
26a4562263
itest: test for ip range checks for macaroons 2025-03-13 09:57:41 -05:00
Slyghtning
ea9a5a2a71
macaroons: add ip range checker 2025-03-13 09:57:39 -05:00
Oliver Gugger
6531d45050
Merge pull request #9458 from Crypt-iQ/banning_010072025
multi+server.go: add initial permissions for some peers
2025-03-12 08:14:31 -06:00
Eugene Siegel
6309b8a0f4
release-notes: update for 0.19.0 2025-03-11 20:42:35 -04:00
Eugene Siegel
a4acfcb0ef
sample-lnd.conf: update for num-restricted-slots 2025-03-11 20:42:35 -04:00
Eugene Siegel
d5ecad3834
itest: new test to check server access perms 2025-03-11 20:42:35 -04:00
Eugene Siegel
68ec766b61
funding+server.go: modify notifications to pass through server
This modifies the various channelnotifier notification functions
to instead hit the server and then call the notification routine.
This allows us to accurately modify the server's maps.
2025-03-11 20:42:35 -04:00
Eugene Siegel
6eb746fbba
server.go+accessman.go: introduce caches for access permissions
Here we introduce the access manager which has caches that will
determine the access control status of our peers. Peers that have
had their funding transaction confirm with us are protected. Peers
that only have pending-open channels with us are temporary access
and can have their access revoked. The rest of the peers are granted
restricted access.
2025-03-11 20:42:34 -04:00
Eugene Siegel
4cfc92f420
channelnotifier: add FundingTimeout and NotifyFundingTimeout
This signal will be used in the server.go code to potentially
demote temporary-access peers to restricted-access peers.
2025-03-11 20:42:34 -04:00
Eugene Siegel
15f17633aa
channeldb: FetchPermAndTempPeers to load access perms on startup
We introduce a new func FetchPermAndTempPeers that returns two maps.
The first map indicates the nodes that will have "protected" access
to the server. The second map indicates the nodes that have
"temporary" access to the server. This will be used in a future
commit in the server.go code.
2025-03-11 20:42:34 -04:00
Oliver Gugger
5d8309ea6b
Merge pull request #9596 from JoeGruffins/testingbtcwalletchange
deps: Update btcwallet to v0.16.10.
2025-03-11 11:31:25 -06:00
Oliver Gugger
04c76101dd
Merge pull request #9595 from yyforyongyu/fix-gossip-syncer
multi: fix flakes and gossip syncer
2025-03-11 09:33:05 -06:00
JoeGruff
c8d032afa9 deps: Update btcwallet to v0.16.10. 2025-03-11 11:11:29 +09:00
Oliver Gugger
a673826dee
Merge pull request #9563 from yyforyongyu/fix-unit-test
chanbackup: fix test flake in `TestUpdateAndSwap`
2025-03-10 16:01:57 -06:00
Oliver Gugger
97bba57520
Merge pull request #9597 from guggero/rebase-fix
contractcourt: fix rebase issue with removed variadic option
2025-03-10 15:46:51 -06:00
Oliver Gugger
0e9b7c5fa2
contractcourt: fix rebase issue with removed variadic option
The optional variadic functional parameters probably got lost during a
rebase.
2025-03-10 21:52:02 +01:00
yyforyongyu
faf8ce161a
docs: update release notes 2025-03-10 17:03:00 +08:00
yyforyongyu
d0abfbbaff
itest: remove redundant resume action
Removed a redundant resume action found in
`testForwardInterceptorRestart`, which was put there likely due to a
mistake.
2025-03-10 16:58:16 +08:00
yyforyongyu
51daa13cd7
routerrpc: improve logging in forwardInterceptor 2025-03-10 16:58:16 +08:00
yyforyongyu
37799b95b7
discovery: fix state transition in GossipSyncer
Previously, we would set the state of the syncer after sending the msg,
which has the following flow,

1. In state `queryNewChannels`, we send the msg `QueryShortChanIDs`.
2. Once the msg is sent, we change to state `waitingQueryChanReply`.

But there's no guarantee the remote won't reply back inbetween the two
step. When that happens, our syncer would still be in state
`queryNewChannels`, causing the following error,
```
[ERR] DISC gossiper.go:873: Process query msg from peer [Alice] got unexpected msg *lnwire.ReplyShortChanIDsEnd received in state queryNewChannels
```

To fix it, we now make sure the state is updated before sending the msg.
2025-03-10 16:58:16 +08:00
yyforyongyu
4d05730c79
tor: fix msg order in TestReconnectSucceed 2025-03-10 16:58:16 +08:00
yyforyongyu
76ade177af
chanbackup: fix test flake in TestUpdateAndSwap
To make it easier to debug, we break the old test into smaller ones and
fix a flake caused by uncleaned test dir.
2025-03-09 13:41:22 +08:00
Olaoluwa Osuntokun
b21b1e3acb
Merge pull request #9592 from guggero/tor-update
mod: bump tor submodule to v1.1.5 to fix flake
2025-03-07 16:21:03 -08:00
Oliver Gugger
342a75891c
mod: bump tor submodule to v1.1.5 to fix flake
After merging #9581, the flake in the coverage unit test should be gone.
All we have to do is update the submodule version to the fixed one
(since during unit tests the module is used not the physical directory
on disk).
2025-03-07 19:39:00 +01:00
Oliver Gugger
d5a7e8957f
Merge pull request #9581 from yyforyongyu/fix-TestReconnectSucceed
tor: fix `TestReconnectSucceed`
2025-03-07 12:37:19 -06:00
Oliver Gugger
115b452abe
Merge pull request #9562 from NishantBansal2003/test-num-block-fund
Make MaxWaitNumBlocksFundingConf Configurable for itest
2025-03-07 12:35:30 -06:00
Oliver Gugger
ab2dc09eb7
Merge pull request #9582 from yyforyongyu/flake-doc
itest: properly document the known flakes
2025-03-07 10:37:14 -06:00
Nishant Bansal
9a9086b26f
docs: add release notes.
Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
2025-03-07 21:33:15 +05:30
Nishant Bansal
4569c07e08
multi: Add itest for funding timeout
This commit adds an integration test that
verifies the funding timeout behavior in the
funding manager, in dev/integration test.
Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
2025-03-07 21:32:38 +05:30
Oliver Gugger
a5f54d1d6b
Merge pull request #9573 from ellemouton/checkUpdateStalenessBeforeRateLimit
discovery: obtain channelMtx before doing any DB calls in `handleChannelUpdate`
2025-03-07 04:17:00 -06:00
Oliver Gugger
76808a81a0
Merge pull request #9587 from guggero/mining-block-limit-configurable
lntest: make mining block limit configurable
2025-03-07 02:57:29 -06:00
Oliver Gugger
25c83104b7
lntest: make mining block limit configurable
Nudging test authors towards not mining too many blocks makes sense,
especially in lnd where we have a lot of integration tests.
But the lntest package is also used in other projects where this
restriction might lead to large refactors.
To be able to stage those refactors we also want to allow this limit to
be configurable if lntest is used as a library.
2025-03-07 09:22:51 +01:00
Oliver Gugger
72b570320a
Merge pull request #9586 from ellemouton/logConfig
config: only create a sub-log manager if one doesnt already exist
2025-03-07 01:30:02 -06:00
Elle Mouton
090e687144
config: only create a sub-log manager if one doesnt already exist
The ValidateConfig method needs to account for the caller already having
an initialised build.SubLoggerManager and then should not override it.
2025-03-06 18:02:25 +02:00
Oliver Gugger
7d7e1872c8
Merge pull request #9574 from ellemouton/fixWatcherPanic
lntest: wait for ChanUpdate req to be fully processed before sending another
2025-03-06 09:21:35 -06:00
yyforyongyu
95569df92e
itest: document a flake found on macOS 2025-03-06 22:46:31 +08:00
yyforyongyu
0f8f092ddd
itest: document a flake from TxNotifier 2025-03-06 09:11:30 +08:00
yyforyongyu
2f545717c9
itest: remove old TODOs
As they are fixed now.
2025-03-06 09:11:30 +08:00
yyforyongyu
f349323923
itest: move test testDisconnectingTargetPeer
Hence finish an old TODO.
2025-03-06 09:11:30 +08:00
yyforyongyu
4755eff0a8
itest: remove time.Sleep before closing channels
Remove an old TODO, which has been fixed with the `NoWait` flag for coop
close.
2025-03-06 09:11:13 +08:00
Elle Mouton
95277bbc35
docs: update release notes 2025-03-05 14:12:56 +02:00
Elle Mouton
2e85e08556
discovery: grab channel mutex before any DB calls
In `handleChanUpdate`, make sure to grab the `channelMtx` lock before
making any DB calls so that the logic remains consistent.
2025-03-05 14:12:55 +02:00
Elle Mouton
5e35bd8328
discovery: demonstrate channel update rate limiting bug
This commit adds a test to demonstrate that if we receive two identical
updates (which can happen if we get the same update from two peers in
quick succession), then our rate limiting logic will be hit early as
both updates might be counted towards the rate limit. This will be fixed
in an upcoming commit.
2025-03-05 14:12:23 +02:00
yyforyongyu
a116eef5eb
tor: fix TestReconnectSucceed
We now make sure the proxy server is running on a unique port. In
addition, we close the old conn before making a new conn.
2025-03-05 19:09:15 +08:00
yyforyongyu
ec7c36fd6a
itest: document missing wallet UTXO 2025-03-05 18:54:13 +08:00
yyforyongyu
061b7abf76
itest: document the flake found in preimage extraction
We now use dedicated methods to properly document the flakes in *one*
place.
2025-03-05 18:54:13 +08:00
Oliver Gugger
9feb761b4e
Merge pull request #9576 from ellemouton/logConfYamlTags
build: add yaml tags to some LogConfig fields
2025-03-04 13:27:11 -06:00
Elle Mouton
1dec8ab985
build: add yaml tags to embedded LogConfig structs
For any embedded struct, the `yaml:",inline"` tag is required.
2025-03-04 13:32:22 +02:00
Elle Mouton
57c6c236d8
lntest: wait for ChanUpdate req to be fully processed before sending another
Before this commit, it was possible for a request to be sent on the
`chanWatchRequests` channel in `WaitForChannelPolicyUpdate` and then for
the `ticker.C` case to select _before_ the `eventChan` select gets
triggered when the `topologyWatcher` closes the `eventChan` in its call
to `handlePolicyUpdateWatchRequest`. This could lead to a "close of a
closed channel" panic.

To fix this, this commit ensures that we only move on to the next
iteration of the select statement in `WaitForChannelPolicyUpdate` once
the request sent on `chanWatchRequests` has been fully handled.
2025-03-03 17:53:15 +02:00
Olaoluwa Osuntokun
f744a5477f
Merge pull request #9565 from guggero/bot-typo-fix-spam
GitHub+docs: no longer accept typo fixes to fight PR spam
2025-02-28 16:18:17 -08:00
Oliver Gugger
f1182e4338
Merge pull request #9521 from guggero/coverage-fixes
unit: remove GOACC, use Go 1.20 native coverage functionality
2025-02-28 09:54:12 -06:00
Oliver Gugger
7761e37522
GitHub: remove generated files from coverage 2025-02-28 14:55:34 +01:00
Oliver Gugger
124137e31a
GitHub+make: debug failing test, use official coveralls action 2025-02-28 14:55:34 +01:00
Oliver Gugger
576da75a07
multi: remove unneeded env variables
With Go 1.23 we don't need to set any of these variables anymore, as
they're the default values now.
2025-02-28 14:55:34 +01:00
Oliver Gugger
70ac201cb8
make+tools: remove goacc, use Go 1.20 builtin functionality
Starting with Go 1.20 the -coverprofile flag does the same that GOACC
did before.
2025-02-28 14:55:33 +01:00
Oliver Gugger
dc0ba72271
Merge pull request #9566 from yyforyongyu/improve-itest
lntest+itest: change the method signature of `AssertPaymentStatus`
2025-02-28 07:52:49 -06:00
Oliver Gugger
a78f9f6c0a
GitHub+docs: no longer accept typo fixes to fight PR spam 2025-02-28 13:02:49 +01:00
yyforyongyu
2d5a2ce78a
lntest+itest: change the method signature of AssertPaymentStatus
So this can be used in other tests when we only care about the payment
hash.
2025-02-28 19:07:38 +08:00
Oliver Gugger
dfd43c972c
Merge pull request #9519 from fuyangpengqi/master
refactor: use a more straightforward return value
2025-02-28 04:41:18 -06:00
fuyangpengqi
150f72414a refactor: use a more straightforward return value
Signed-off-by: fuyangpengqi <995764973@qq.com>
2025-02-28 17:09:56 +08:00
Oliver Gugger
8532955b35
Merge pull request #9540 from ellemouton/backwardsCompat
scripts+make+GH: Add simple backwards compatibility test to the CI
2025-02-27 05:05:03 -06:00
Elle Mouton
b3133b99d4
docs: add release note entry 2025-02-27 11:33:15 +02:00
Elle Mouton
343bdff26b
make+gh: add make helper and GH action
Add a makefile helper to run the neww backwards compatability test and
then add a new GH actions job to call it.
2025-02-27 11:33:15 +02:00
Elle Mouton
f0d4ea10a2
scripts/bw-compatibility-test: add backwards compat test
In this commit, a new backwards compatibility test is added. See the
added README.md file in this commit for all the info.
2025-02-27 11:33:15 +02:00
Oliver Gugger
e3d9fcb5ac
Merge pull request #9549 from yyforyongyu/fix-bitcond-test
Fix unit test flake `TestHistoricalConfDetailsTxIndex`
2025-02-26 07:23:30 -06:00
yyforyongyu
cfa4341740
routing: fix flake in TestFilteredChainView/bitcoind_polling 2025-02-26 19:51:53 +08:00
yyforyongyu
56fa3aae34
routing/chainview: refactor TestFilteredChainView
So each test has its own miner and chainView.
2025-02-26 19:51:53 +08:00
yyforyongyu
4bfcfea2ee
lntest+chainntnfs: make sure bitcoind node is synced
This commit makes sure the bitcoind node is synced to the miner when
initialized and returned from `NewBitcoindBackend`.
2025-02-26 19:51:52 +08:00
yyforyongyu
99d49dec6a
multi: change NewBitcoindBackend to take a miner as its param
Prepares for the following commit where we assert the chain backend is
synced to the miner.
2025-02-26 19:51:52 +08:00
yyforyongyu
c725ba9f25
docs: update release notes re flake fix 2025-02-26 19:51:52 +08:00
yyforyongyu
1618d2c789
chainntnfs+lntest: fix sync to miner block flake
In this commit we document an unexpected behavior found when connecting
a bitcoind node to a btcd node. We mitigate this in our test by
reconnecting the nodes when the connection is broken. We also limit the
connection made from `bitcoind` to be v1 only.
2025-02-26 19:51:52 +08:00
yyforyongyu
175301628f
lntest/unittest: make sure miner is connected to bitcoind
We change how the `bitcoind` node connects to the miner by creating a
temp RPC client and use it to connect to the miner. In addition we also
assert the connection is made.
2025-02-25 21:11:26 +08:00
yyforyongyu
245ea85894
lntest/unittest: assert bitcoind is shut down
Make sure the shutdown of `bitcoind` is finished without any errors.
2025-02-25 21:11:25 +08:00
yyforyongyu
7666d62a43
lntest/unittest: update config for miner and bitcoind
The config used for the miner is updated to skip banning and debug. For
bitcoind, the config is updated to use a unique port for P2P conn.
2025-02-25 21:10:40 +08:00
yyforyongyu
fa8527af09
Makefile+scripts: add unit test flake hunter
This commit adds a script to hunt flakes for a specific unit test with
trace logs. Also rename the make commands to make them more clear on
whether it's a unit test, itest, or paralleled itest.
2025-02-25 21:10:40 +08:00
Oliver Gugger
5d3680a6f6
Merge pull request #9484 from Abdulkbk/refactor-makedir
lnutils: add CreateDir util function
2025-02-25 01:00:45 -06:00
Oliver Gugger
b8c5e85821
Merge pull request #8900 from guggero/go-cc
Makefile: add GOCC variable
2025-02-25 00:59:14 -06:00
Yong
fca9fae2d8
Merge pull request #9433 from hieblmi/estimate-route-fee-fix
routerrpc: fix estimateroutefee for public route hints
2025-02-25 13:58:43 +08:00
Slyghtning
f867954a68
docs: update release notes 2025-02-24 09:56:24 +01:00
Slyghtning
6399d77c18
routerrpc: fix estimateroutefee for public route hints 2025-02-24 09:56:22 +01:00
Oliver Gugger
ad021a290d
Makefile: add GOCC variable 2025-02-23 09:48:54 +01:00
Oliver Gugger
5fe900d18d
Merge pull request #9534 from ellemouton/graph13
graph: refactor Builder network message handling
2025-02-21 08:38:35 -06:00
Elle Mouton
c89b616e7d
graph: refactor Builder network message handling
The exposed AddNode, AddEdge and UpdateEdge methods of the Builder are
currently synchronous since even though they pass messages to the
network handler which spins off the handling in a goroutine, the public
methods still wait for a response from the handling before returning.
The only part that is actually done asynchronously is the topology
notifications.

We previously tried to simplify things in [this
commit](d757b3bcfc)
but we soon realised that there was a reason for sending the messages to
the central/synchronous network handler first: it was to ensure
consistency for topology clients: ie, the ordering between when there is
a new topology client or if it is cancelled needs to be consistent and
handled synchronously with new network updates. So for example, if a new
update comes in right after a topology client cancels its subscription,
then it should _not_ be notified. Similariy for new subscriptions. So
this commit was reverted soon after.

We can, however, still simplify things as is done in this commit by
noting that _only topology subscriptions and notifications_ need to be
handled separately. The actual network updates do not need to. So that
is what is done here.

This refactor will make moving the topology subscription logic to a new
subsystem later on much easier.
2025-02-21 10:39:00 -03:00
Oliver Gugger
1227eb1cce
Merge pull request #9491 from ziggie1984/closechannel-rpc
Allow coop closing a channel with HTLCs on it via lncli
2025-02-21 05:05:53 -06:00
Olaoluwa Osuntokun
27440e8957
Merge pull request #9535 from guggero/remove-caching
GitHub: remove duplicate caching
2025-02-20 16:57:55 -08:00
Olaoluwa Osuntokun
553899bffb
Merge pull request #9447 from yyforyongyu/yy-sweeper-fix
sweep: start tracking input spending status in the fee bumper
2025-02-20 16:56:45 -08:00
Oliver Gugger
dc64ea97a2
GitHub: remove duplicate caching
Turns out that actions/setup-go starting with @v4 also adds caching.
With that, our cache size on disk has almost doubled, leading to the
GitHub runner running out of space in certain situation.
We fix that by disabling the automated caching since we already have our
own, custom-tailored version.
2025-02-20 18:14:29 +01:00
ziggie
8017139df5
docs: add release-notes 2025-02-20 17:43:18 +01:00
ziggie
f994c2cb9f
htlcswitch: fix log output 2025-02-20 17:43:18 +01:00
ziggie
f458844412
lnd: add max fee rate check to closechannel rpc 2025-02-20 17:43:18 +01:00
ziggie
59443faa36
multi: coop close with active HTLCs on the channel
For the lncli cmd we now always initiate the coop close even if
there are active HTLCs on the channel. In case HTLCs are on the
channel and the coop close is initiated LND handles the closing
flow in the background and the lncli cmd will block until the
transaction is broadcasted to the mempool. In the background LND
disallows any new HTLCs and waits until all HTLCs are resolved
before kicking of the negotiation process.
Moreover if active HTLCs are present and the no_wait param is not
set the error msg is now highlightning it so the user can react
accordingly.
2025-02-20 17:43:18 +01:00
Abdullahi Yunus
45a913ee91
lnutils: add createdir util function
This utility function replaces repetitive logic patterns
throughout LND.
2025-02-20 17:08:21 +01:00
Oliver Gugger
09a4d7e224
Merge pull request #9530 from ziggie1984/fix-debug-log
multi: fix debug log
2025-02-20 09:28:14 -06:00
yyforyongyu
9f7e2bfd96
contractcourt: fix errorlint 2025-02-20 23:14:12 +08:00
ziggie
9382fcb801
multi: fix debug log 2025-02-20 10:44:19 +01:00
yyforyongyu
7ab0e15937
sweep: fix error logging 2025-02-20 14:41:52 +08:00
yyforyongyu
353f208031
sweep: refactor IsOurTx to not return an error
Before this commit, the only error returned from `IsOurTx` is when the
root bucket was not created. In that case, we should consider the tx to
be not found in our db, since technically our db is empty.

A future PR may consider treating our wallet as the single source of
truth and query the wallet instead to check for past sweeping txns.
2025-02-20 14:41:52 +08:00
yyforyongyu
8d49246a54
docs: add release notes 2025-02-20 14:41:50 +08:00
yyforyongyu
c61f781be7
itest: split up force close tests
So we can focus on testing normal flow vs persistence flow.
2025-02-20 14:40:54 +08:00
yyforyongyu
74161f0d57
sweep: make sure recovered inputs are retried
Previously, when a given input is found spent in the mempool, we'd mark
it as Published and never offer it to the fee bumper. This is dangerous
as the input will never be fee bumped. We now fix it by always
initializing the input with state Init, and only use mempool to check
for fee and fee rate.

This changes the current restart behavior - as previously when a
sweeping tx is broadcast, the node shuts down, when it starts again, the
input will be offered to the sweeper again, but not to the fee bumper,
which means the sweeping tx will stay in the mempool with the last-tried
fee rate. After this change, after a restart, the input will be swept
again, and the fee bumper will monitor its status. The restart will also
behave like a fee bump if there's already an existing sweeping tx in the
mempool.
2025-02-20 14:40:54 +08:00
yyforyongyu
4bd1a344b9
sweep: signal tx in markInputFatal
This commit adds the failed tx to the result when marking the input as
fatal, which is used in the commit resolver when handling revoked
outputs.
2025-02-20 14:40:54 +08:00
yyforyongyu
b184afe227
sweep: handle missing inputs during fee bumping
This commit handles the case when the input is missing during the RBF
process, which could happen when the bumped tx has inputs being spent by
a third party. Normally we should be able to catch the spend early via
the spending notification and never attempt to fee bump the record.
However, due to the possible race between block notification and spend
notification, this cannot be guaranteed. Thus, we need to handle the
case during the RBF when seeing a `ErrMissingInputs`, which can only
happen when the inputs are spent by others.
2025-02-20 14:40:53 +08:00
yyforyongyu
4f469de18e
sweep: refactor handleInitialTxError and createAndCheckTx
This commit refactors `handleInitialTxError` and `createAndCheckTx` to
take a `monitorRecord` param, which prepares for the following commit
where we start handling missing inputs.
2025-02-20 14:40:53 +08:00
yyforyongyu
f614e7aed9
sweep: add createUnknownSpentBumpResult
A minor refactor to break the method `handleUnknownSpent` into two
steps, which prepares the following commit where we start handling
missing inputs.
2025-02-20 14:40:53 +08:00
yyforyongyu
db8319d70b
sweep: add method handleReplacementTxError
This is a minor refactor so the `createAndPublishTx` flow becomes more
clear, also prepares for the following commit where we start to handle
missing inputs.
2025-02-20 14:40:53 +08:00
yyforyongyu
42818949dc
sweep: retry sweeping inputs upon TxUnknownSpend
We now start handling `TxUnknownSpend` in our sweeper to make sure the
failed inputs are retried when possible.
2025-02-20 14:40:53 +08:00
yyforyongyu
2f1205a394
sweep: start tracking inputs spent by unknown tx
This commit adds a new field `InputsSpent` to the `BumpResult` so they
can be used to track inputs spent by txns not recoginized by the fee
bumper.
2025-02-20 14:40:53 +08:00
yyforyongyu
388183e173
itest: add fee replacement test 2025-02-20 14:40:52 +08:00
yyforyongyu
db351e1908
sweep: rename methods for clarity
We now rename "third party" to "unknown" as the inputs can be spent via
an older sweeping tx, a third party (anchor), or a remote party (pin).
In fee bumper we don't have the info to distinguish the above cases, and
leave them to be further handled by the sweeper as it has more context.
2025-02-20 14:40:52 +08:00
yyforyongyu
121116cff7
sweep: remove dead code and add better logging 2025-02-20 14:40:52 +08:00
yyforyongyu
50bc191feb
sweep: handle unknown spent in processRecords
This commit refactors the `processRecords` to always handle the inputs
spent when processing the records. We now make sure to handle unknown
spends for all backends (previously only neutrino), and rely solely on
the spending notification to give us the onchain status of inputs.
2025-02-20 14:40:52 +08:00
yyforyongyu
61cec43951
sweep: add a new event TxUnknownSpend 2025-02-20 14:40:52 +08:00
yyforyongyu
8c9ba327cc
sweep: add method getSpentInputs
To track the input and its spending tx, which will be used later to
detect unknown spends.
2025-02-20 14:40:52 +08:00
Oliver Gugger
0e87863481
Merge pull request #9523 from ellemouton/graph9
graph/db: use View directly instead of manual DB tx creation and commit
2025-02-19 01:55:18 -06:00
Oliver Gugger
31418e4b85
Merge pull request #9525 from ellemouton/graph10
graph: Restrict interface to update channel proof instead of entire channel
2025-02-18 11:34:38 -06:00
Elle
f9d29f90cd
Merge pull request #9513 from ellemouton/graph5
graph+routing: refactor to remove `graphsession`
2025-02-18 11:54:24 -03:00
Elle Mouton
9df8773163
graph: update proof by ID
This commit restricts the graph CRUD interface such that one can only
add a proof to a channel announcement and not update any other fields.
This pattern is better suited for SQL land too.
2025-02-18 11:05:28 -03:00
Elle Mouton
e58abbf0e5
graph/db: fix edges bucket check
This commit fixes a bug that would check that the passed `edge` argument
of UpdateChannelEdge is nil but it should actually be checking if the
`edges` bucket is nil.
2025-02-18 11:04:27 -03:00
Elle Mouton
03899ab1f9
graph/db: use View directly instead of manual Tx handling
In this commit, we use the available kvdb `View` method directly for
starting a graph session instead of manually creating and commiting the
transaction. Note this changes behaviour since failed tx create/commit
will now error instead of just log.
2025-02-18 10:18:55 -03:00
Elle Mouton
f3805002ff
graph/db: unexport methods that take a transaction
Unexport and rename the methods that were previously used by the
graphsession package.
2025-02-18 10:15:55 -03:00
Elle Mouton
e004447da6
multi: remove the need for the graphsession package
In this commit, we add a `GraphSession` method to the `ChannelGraph`.
This method provides a caller with access to a `NodeTraverser`. This is
used by pathfinding to create a graph "session" overwhich to perform a
set of queries for a pathfinding attempt. With this refactor, we hide
details such as DB transaction creation and transaction commits from the
caller. So with this, pathfinding does not need to remember to "close
the graph session". With this commit, the `graphsession` package may be
completely removed.
2025-02-18 10:15:41 -03:00
Elle Mouton
dfe2314a2a
routing: refactor pathfinding loop
In preparation for the next commit where we will start hiding underlying
graph details such as that a graph session needs to be "closed" after
pathfinding is done with it, we refactor things here so that the main
pathfinding logic is done in a call-back.
2025-02-18 10:11:34 -03:00
Elle Mouton
99c9440520
graph/db: define a NodeTraverser interface
Which describes methods that will use the graph cache if it is available
for fast read-only calls.
2025-02-18 10:10:04 -03:00
Elle Mouton
8ec08fbfa4
multi: remove the need for NewRoutingGraph
The `graphsession.NewRoutingGraph` method was used to create a
RoutingGraph instance with no consistent read transaction across calls.
But now that the ChannelGraph directly implements this, we can remove
The NewRoutingGraph method.
2025-02-18 07:59:57 -03:00
Elle Mouton
5d5cfe36c7
routing: rename routing Graph method
In preparation for having the ChannelGraph directly implement the
`routing.Graph` interface, we rename the `ForEachNodeChannel` method to
`ForEachNodeDirectedChannel` since the ChannelGraph already uses the
`ForEachNodeChannel` name and the new name is more appropriate since the
ChannelGraph currently has a `ForEachNodeDirectedChannelTx` method which
passes the same DirectedChannel type to the given call-back.
2025-02-18 07:59:57 -03:00
Elle Mouton
971832c792
graph: temporarily rename some ChannelGraph methods
Add the `Tx` suffix to both ForEachNodeDirectedChannelTx and
FetchNodeFeatures temporarily so that we free up the original names for
other use. The renamed methods will be removed or unexported in an
upcoming commit. The aim is to have no exported methods on the
ChannelGraph that accept a kvdb.RTx as a parameter.
2025-02-18 07:59:57 -03:00
Elle Mouton
9068ffcd8b
graph: let FetchNodeFeatures take an optional read tx
For consistency in the graphsessoin.graph interface, we let the
FetchNodeFeatures method take a read transaction just like the
ForEachNodeDirectedChannel. This is nice because then all calls in the
same pathfinding transaction use the same read transaction.
2025-02-18 07:59:57 -03:00
Oliver Gugger
b08bc99945
Merge pull request #9518 from starius/bumpfee-immediate-doc-fix
walletrpc: fix description of bumpfee.immediate
2025-02-18 02:02:36 -06:00
Olaoluwa Osuntokun
5ee81e1876
Merge pull request #9512 from Roasbeef/go-1-23
multi: update build system to Go 1.23
2025-02-17 16:45:12 -08:00
Boris Nagaev
0916f3e9b3
walletrpc: fix description of bumpfee.immediate
It waits for the next block and sends CPFP even if there are no other
inputs to form a batch.
2025-02-17 13:33:16 -03:00
Oliver Gugger
01819f0d42
Merge pull request #9516 from ellemouton/graph6
invoicesrpc: remove direct access to ChannelGraph pointer
2025-02-17 07:40:56 -06:00
Yong
65a18c5b35
Merge pull request #9515 from ellemouton/graphFixFlake
graph: ensure topology subscriber handling and network msg handling is synchronous
2025-02-17 20:48:05 +08:00
Oliver Gugger
fa10991545
multi: fix linter by updating to latest version 2025-02-17 10:40:55 +01:00
Olaoluwa Osuntokun
c40ea0cefc
invoices: fix new linter error w/ Go 1.23 2025-02-13 17:09:28 -08:00
Olaoluwa Osuntokun
bf192d292f
docs: update INSTALL.md with Go 1.23 instructions 2025-02-13 16:57:07 -08:00
Olaoluwa Osuntokun
7bc88e8360
build: update go.mod to use Go 1.23
This enables us to use the new language features that are a part of this
release.
2025-02-13 16:57:06 -08:00
Olaoluwa Osuntokun
61852fbe95
multi: update build system to Go 1.23 2025-02-13 16:57:06 -08:00
Oliver Gugger
68105be1eb
Merge pull request #9507 from saubyk/18.5-releasenotes-patch
Update release-notes-0.18.5.md
2025-02-13 13:03:23 -06:00
Suheb
5b93ab6a8f
Update release-notes-0.18.5.md and release-notes-0.19.0.md 2025-02-13 10:45:38 -08:00
Oliver Gugger
319a0ee470
Merge pull request #9493 from ziggie1984/lncli-no-replacement
For some lncli cmds we should not replace the content with other data
2025-02-13 09:52:52 -06:00
Oliver Gugger
2ec972032e
Merge pull request #9517 from yyforyongyu/fix-coverage
workflows: skip coverage error in job `finish`
2025-02-13 09:11:57 -06:00
yyforyongyu
2b0c25e31d
workflows: skip coverage error in job finish
Also updates the coverage action used.
2025-02-13 22:05:38 +08:00
Oliver Gugger
d5ac05ce87
Merge pull request #9501 from yyforyongyu/getinfo-blockheight
rpcserver: check `blockbeatDispatcher` when deciding `isSynced`
2025-02-13 04:17:45 -06:00
Elle Mouton
da43541c63
invoicesrpc: remove direct access to ChannelGraph pointer 2025-02-13 11:45:09 +02:00
Elle Mouton
f39a004662
Revert "graph: refactor Builder network message handling"
This reverts commit d757b3bcfc.
2025-02-13 11:19:07 +02:00
Yong
9c2c95d46f
Merge pull request #9478 from ellemouton/graph3
discovery+graph:  move funding tx validation to the gossiper
2025-02-13 15:00:14 +08:00
ziggie
3562767b23
lncli: only add additional info to specific cmds.
We only append the chan_id and the human readable scid
for the commands `listchannels` and `closedchannels`. This
ensures that other commands like `buildroute` are not affected
by this change so their output can be piped into other cmds.

For some cmds it is not very practical to replace the json output
because we might pipe it into other commands. For example when
creating the route we want to pipe the result of buildroute into
sendtoRoute.
2025-02-12 23:29:56 +01:00
Elle Mouton
e5db0d6314
graph+discovery: move funding tx validation to gossiper
This commit is a pure refactor. We move the transaction validation
(existence, spentness, correctness) from the `graph.Builder` to the
gossiper since this is where all protocol level checks should happen.
All tests involved are also updated/moved.
2025-02-12 15:48:08 +02:00
Elle Mouton
39bb23ea5e
discovery: lock the channelMtx before making the funding script
As we move the funding transaction validation logic out of the builder
and into the gossiper, we want to ensure that the behaviour stays
consistent with what we have today. So we should aquire this lock before
performing any expensive checks such as building the funding tx or
valdating it.
2025-02-12 13:59:09 +02:00
Oliver Gugger
693b3991ee
Merge pull request #9508 from ellemouton/ignoreSendCoverageFail
.github: ignore Send coverage errors
2025-02-12 05:23:29 -06:00
Elle Mouton
7853e36488
graph+discovery: calculate funding tx script in gossiper
In preparation for an upcoming commit which will move all channel
funding tx validation to the gossiper, we first move the helper method
which helps build the expected funding transaction script based on the
fields in the channel announcement. We will still want this script later
on in the builder for updating the ChainView though, and so we pass this
field along with the ChannelEdgeInfo. With this change, we can remove
the TapscriptRoot field from the ChannelEdgeInfo since the only reason
it was there was so that the builder could reconstruct the full funding
script.
2025-02-12 13:15:54 +02:00
Elle Mouton
8a07bb0950
discovery: prepare tests for preparing the mock chain
Here, we add a new fundingTxOption modifier which will configure how we
set-up expected calls to the mock Chain once we have moved funding tx
logic to the gossiper. Note that in this commit, these modifiers don't
yet do anything.
2025-02-12 13:15:54 +02:00
Elle Mouton
22e391f055
discovery: add AssumeChannelValid config option
in preparation for later on when we need to know when to skip funding
transaction validation.
2025-02-12 13:15:54 +02:00
Elle Mouton
00f5fd9b7f
graph: add IsZombieEdge method
This is in preparation for the commit where we move across all the
funding tx validation so that we can test that we are correctly updating
the zombie index.
2025-02-12 13:15:54 +02:00
Elle Mouton
870c865763
graph: export addZombieEdge and rename to MarkZombieEdge
The `graph.Builder`'s `addZombieEdge` method is currently called during
funding transaction validation for the case where the funding tx is not
found. In preparation for moving this code to the gossiper, we export
the method and add it to the ChannelGraphSource interface so that the
gossiper will be able to call it later on.
2025-02-12 13:15:53 +02:00
Elle Mouton
a67df6815c
.github: ignore Send coverage errors
Sometimes only the "Send coverage" step of a CI job will fail. This
commit turns this step into a "best effort" step instead so that it
does not block a CI job from passing.

It can, for example, often happen that a single job is re-run from the
GH UI, it then passes but the "Send coverage" step fails due to the
"Can't add a job to a build that is already closed." error meaning that
the only way to get the CI step to pass is to re-push and retrigger a
full CI run.
2025-02-12 12:34:02 +02:00
Oliver Gugger
4dbbd837c0
Merge pull request #9496 from ellemouton/graph4
graph: remove redundant iteration through a node's persisted channels
2025-02-12 03:36:32 -06:00
Olaoluwa Osuntokun
7b294311bc
Merge pull request #9150 from yyforyongyu/fix-stuck-payment
routing+htlcswitch: fix stuck inflight payments
2025-02-11 18:02:02 -08:00
yyforyongyu
759dc2066e
docs: update release notes 2025-02-12 09:48:03 +08:00
yyforyongyu
59759f861f
rpcserver: check blockbeatDispatcher when deciding isSynced
This commit changes `GetInfo` to include `blockbeatDispatcher`'s current
state when deciding whether the system is synced to chain. Previously we
check the best height against the wallet and the channel graph, we
should also do this to the blockbeat dispatcher to make sure the
internal consumers are also synced to the best block.
2025-02-12 09:48:02 +08:00
yyforyongyu
89c4a8dfd7
chainio: add method CurrentHeight
Add a new method `CurrentHeight` to query the current best height of the
dispatcher.
2025-02-12 09:48:02 +08:00
Oliver Gugger
fbc668ca53
Merge pull request #9500 from MPins/release-note-0.20.0
doc: creating release-notes-0.20.0.md
2025-02-11 02:22:38 -06:00
Elle Mouton
5c2c00e414
graph/db: remove GraphCacheNode interface
With the previous commit, the AddNode method was removed and since that
was the only method making use of the ForEachChannel on the
GraphCacheNode interface, we can remove that method. Since the only two
methods left just expose the node's pub key and features, it really is
not required anymore and so the entire thing can be removed along with
the implementation of it.
2025-02-11 08:19:33 +02:00
MPins
097c262341 doc: creating file release-notes-0.20.0.md 2025-02-10 23:50:04 -03:00
Elle Mouton
90179b651e
graph/db: remove unnecessary AddNode method on GraphCache
The AddNode method on the GraphCache calls `AddNodeFeatures` underneath
and then iterates through all the node's persisted channels and adds
them to the cache too via `AddChannel`.

This is, however, not required since at the time the cache is populated
in `NewChannelGraph`, the cache is populated will all persisted nodes
and all persisted channels. Then, once any new channels come in, via
`AddChannelEdge`, they are added to the cache via AddChannel. If any new
nodes come in via `AddLightningNode`, then currently the cache's AddNode
method is called which the both adds the node and again iterates through
all persisted channels and re-adds them to the cache. This is definitely
redundent since the initial cache population and updates via
AddChannelEdge should keep the cache fresh in terms of channels.

So we remove this for 2 reasons: 1) to remove the redundent DB calls and
2) this requires a kvdb.RTx to be passed in to the GraphCache calls
   which will make it hard to extract the cache out of the CRUD layer
and be used more generally.

The AddNode method made sense when the cache was first added in the
code-base
[here](369c09be61 (diff-ae36bdb6670644d20c4e43f3a0ed47f71886c2bcdf3cc2937de24315da5dc072R213))
since then during graph cache population, nodes and channels would be
added to the cache in a single DB transaction. This was, however,
changed [later
on](352008a0c2)
to be done in 2 separate DB calls for efficiency reasons.
2025-02-10 17:10:53 +02:00
Oliver Gugger
d10ab03b75
Merge pull request #9480 from ellemouton/autopilotRefactor
graph+autopilot: remove `autopilot` access to raw `graphdb.ChannelGraph`
2025-02-10 09:07:47 -06:00
Oliver Gugger
2a0dca77a0
Merge pull request #9495 from ziggie1984/fix-graphbuilder-flake
fix graphbuilder flake
2025-02-10 08:43:59 -06:00
ziggie
6373d84baf
graph: fix flake in unit test 2025-02-10 14:07:04 +01:00
Oliver Gugger
6eb8f1f6e3
Merge pull request #9477 from ellemouton/graph2
discovery+graph: various preparations for moving funding tx validation to the gossiper
2025-02-10 05:58:05 -06:00
Oliver Gugger
6f312d457f
Merge pull request #9451 from Juneezee/minmax
refactor: replace min/max helpers with built-in min/max
2025-02-10 05:57:34 -06:00
Oliver Gugger
6bf6603fb8
Merge pull request #9492 from yyforyongyu/itest-flake-interceptor
itest: fix flake in `testForwardInterceptorRestart`
2025-02-10 05:50:13 -06:00
Elle Mouton
3d0ae966c8
docs: update release notes 2025-02-10 09:46:15 +02:00
Elle Mouton
e7988a2c2b
autopilot: remove access to *graphdb.ChannelGraph
Define a new GraphSource interface that describes the access required by
the autopilot server. Let its constructor take this interface instead of
a raw pointer to the graphdb.ChannelGraph.
2025-02-10 09:46:15 +02:00
Elle Mouton
9b86ee53db
graph+autopilot: let autopilot use new graph ForEachNode method
Which passes a NodeRTx to the call-back instead of a `kvdb.RTx`.
2025-02-10 09:46:15 +02:00
Elle Mouton
14cedef58e
graph/db: add NodeRTx interface and implement it
In this commit, a new NodeRTx interface is added which represents
consistent access to a persisted models.LightningNode. The
ForEachChannel method of the interface gives the caller access to the
node's channels under the same read transaction (if any) that was used
to fetch the node in the first place. The FetchNode method returns
another NodeRTx which again will have the same underlying read
transaction.

The main point of this interface is to provide this consistent access
without needing to expose the `kvdb.RTx` type as a method parameter.
This will then make it much easier in future to add new implementations
of this interface that are backed by other databases (or RPC
connections) where the `kvdb.RTx` type does not apply.

We will make use of the new interface in the `autopilot` package in
upcoming commits in order to remove the `autopilot`'s dependence on the
pointer to the `*graphdb.ChannelGraph` which it has today.
2025-02-10 08:23:58 +02:00
Elle Mouton
3e5d807773
autopilot: add testDBGraph type
Introduce a new type for testing code so the main databaseChannelGraph
type does not need to make various write calls to the
`graphdb.ChannelGraph` but the new testDBGraph type still can for tests.
2025-02-10 08:17:37 +02:00
Elle Mouton
1184c9eaf8
autopilot: move tests code to test files
This is a pure code move commit where we move any code that is only ever
used by tests to test files. Many of the calls to the
graphdb.ChannelGraph pointer are only coming from tests code.
2025-02-10 08:16:34 +02:00
Elle Mouton
7cf5b5be02
graph: remove unused ForEachNode method from Builder
And from various interfaces where it is not needed.
2025-02-10 08:16:34 +02:00
yyforyongyu
e45e1f2b0e
itest: fix flake in testForwardInterceptorRestart
We need to make sure the links are recreated before calling the
interceptor, otherwise we'd get the following error, causing the test to
fail.
```
2025-02-07 15:01:38.991 [ERR] RPCS interceptor.go:624: [/routerrpc.Router/HtlcInterceptor]: fwd (Chan ID=487:3:0, HTLC ID=0) not found
```
2025-02-10 12:24:23 +08:00
yyforyongyu
58e76b726e
routing: add docs and monior refactor decideNextStep
Add verbose docs and refactor the method to exit early when `allow` is
true.
2025-02-10 11:36:18 +08:00
yyforyongyu
ca10707b26
itest+lntest: assert payment is failed once the htlc times out 2025-02-10 11:31:51 +08:00
yyforyongyu
faa3110127
docs: update release notes 2025-02-10 11:31:50 +08:00
yyforyongyu
5a62528fd7
routing: improve loggings for attempt result handling 2025-02-10 11:31:50 +08:00
yyforyongyu
1acf4d7d4d
routing: always update payment in the same goroutine
This commit refactors `collectResultAsync` such that this method is now
only responsible for collecting results from the switch. The method
`decideNextStep` is expanded to process these results in the same
goroutine where we fetch the payment from db, to make sure the lifecycle
loop always have a consistent view of a given payment.
2025-02-10 11:29:29 +08:00
yyforyongyu
e1279aab20
routing: add new method reloadPayment
To further shorten the lifecycle loop.
2025-02-10 11:28:23 +08:00
yyforyongyu
1fe2cdb765
routing: move amendFirstHopData into requestRoute 2025-02-10 11:28:23 +08:00
yyforyongyu
966cfccb94
routing: add new method reloadInflightAttempts
To shorten the method `resumePayment` and make each step more clear.
2025-02-10 11:28:23 +08:00
yyforyongyu
f96eb50ca8
channeldb+routing: cache circuit and onion blob for htlc attempt
This commit caches the creation of sphinx circuit and onion blob to
avoid re-creating them again.
2025-02-10 11:28:23 +08:00
yyforyongyu
46eb811543
routing: remove redundant GetHash 2025-02-10 11:28:18 +08:00
Olaoluwa Osuntokun
ce8cde6911
Merge pull request #9470 from ziggie1984/fix-sweepInput-bug
Make BumpFee RPC user inputs more stricter.
2025-02-07 14:39:31 -08:00
Elle Mouton
d025587135
docs: update existing release notes 2025-02-07 16:29:22 +02:00
Elle Mouton
011d819315
discovery: update chanAnn creation methods to take modifier options
In preparation for adding more modifiers. We want to later add a
modifier that will tweak the errors returned by the mock chain once
funding transaction validation has been moved to the gossiper.
2025-02-07 16:29:19 +02:00
Elle Mouton
b6210632f2
discovery: prep testCtx with a mock Chain
This is in preparation for moving the funding transaction validation
code to the gossiper from the graph.Builder since then the gossiper will
start making GetBlockHash/GetBlock and GetUtxo calls.
2025-02-07 16:28:39 +02:00
Elle Mouton
c354fd8f70
lnmock: let MockChain also implement lnwallet.BlockChainIO
Note that a compile-time assertion was not added as this leads to an
import cycle.
2025-02-07 16:28:00 +02:00
ziggie
022b3583b4
docs: update release-notes 2025-02-07 15:14:05 +01:00
Elle Mouton
8f37699db3
discovery: prepare tests for shared chain state
Convert a bunch of the helper functions to instead be methods on the
testCtx type. This is in preparation for adding a mockChain to the
testCtx that these helpers can then use to add blocks and utxos to.

See `notifications_test.go` for an idea of what we are trying to emulate
here. Once the funding tx code has moved to the gossiper, then the logic
in `notifications_test.go` will be removed.
2025-02-07 15:26:34 +02:00
Elle Mouton
b117daaa3c
discovery+graph: convert errors from codes to variables
In preparation for moving funding transaction validiation from the
Builder to the Gossiper in later commit, we first convert these graph
Error Codes to normal error variables. This will help make the later
commit a pure code move.
2025-02-07 15:26:16 +02:00
Oliver Gugger
3c0350e481
Merge pull request #9476 from ellemouton/graph1
graph: refactor `graph.Builder` update handling
2025-02-07 07:23:41 -06:00
Elle Mouton
a86a5edbd5
docs: update release notes 2025-02-07 13:01:39 +02:00
Elle Mouton
6169b47d65
graph: rename routerStats to builderStats
This logic used to be handled by the router. Update to reflect new
owner.
2025-02-07 13:01:39 +02:00
Elle Mouton
d757b3bcfc
graph: refactor Builder network message handling
The point of the `graph.Builder`'s `networkHandler` goroutine is to
ensure that certain requests are handled in a synchronous fashion.
However, any requests received on the `networkUpdates` channel, are
currently immediately handled in a goroutine which calls
`handleNetworkUpdate` which calls `processUpdate` before doing topology
notifications. In other words, there is no reason for these
`networkUpdates` to be handled in the `networkHandler` since they are
always handled asynchronously anyways. This design is most likely due to
the fact that originally the gossiper and graph builder code lived in
the same system and so the pattern was copied across.

So in this commit, we just remove the complexity. The only part we need
to spin off in a goroutine is the topology notifications.
2025-02-07 13:01:35 +02:00
ziggie
34c4d12c71
walletrpc: add new deadline-delta param to bumpfee rpc
Add new parameter deadline-delta to the bumpfee request and only
allow it to be used when the budget value is used as well.
2025-02-07 11:28:41 +01:00
Yong
5b1eaf9978
Merge pull request #9474 from ellemouton/nodeAnnConversion
graph/db: correctly handle de(ser)ialisation of `models.LightningNode` opaque addresses
2025-02-07 18:06:10 +08:00
Elle Mouton
276b335cf5
graph: refactor announcement handling logic
In this commit, we remove the `processUpdate` method which handles each
announement type (node, channel, channel update) in a separate switch
case. Each of these cases currently has a non-trivial amount of code.
This commit creates separate methods for each message type we want to
handle instead. This removes a level of indentation and will make things
easier to review when we start editing the code for each handler.
2025-02-07 07:30:00 +02:00
Elle Mouton
1974903fb2
multi: move node ann validation code to netann pkg
The `netann` package is a more appropriate place for this code to live.
Also, once the funding transaction code is moved out of the
`graph.Builder`, then no `lnwire` validation will occur in the `graph`
package.
2025-02-07 07:30:00 +02:00
Yong
457a245a4e
Merge pull request #9482 from yyforyongyu/itest-log-ts
lntest: log timestamp when printing errors
2025-02-06 16:16:54 +08:00
Eng Zer Jun
82a221be13
docs: add release note for #9451
Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2025-02-06 09:46:41 +08:00
Eng Zer Jun
e56a7945be
refactor: replace math.Min and math.Max with built-in min/max
Reference: https://github.com/lightningnetwork/lnd/pull/9451#pullrequestreview-2580942691
Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2025-02-06 09:46:40 +08:00
Eng Zer Jun
0899cee987
refactor: replace min/max helpers with built-in min/max
We can use the built-in `min` and `max` functions since Go 1.21.

Reference: https://go.dev/ref/spec#Min_and_max
Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2025-02-06 09:45:10 +08:00
yyforyongyu
5f4716c699
lntest: log timestamp when printing errors 2025-02-05 22:51:46 +08:00
Yong
bac699df8f
Merge pull request #9446 from yyforyongyu/yy-prepare-fee-replace
sweeper: rename `Failed` to `Fatal` and minor refactor
2025-02-05 22:48:41 +08:00
yyforyongyu
b98542bd96
docs: update release notes 2025-02-05 19:49:09 +08:00
yyforyongyu
e5f39dd644
sweep: refactor storeRecord to updateRecord
To make it clear we are only updating fields, which will be handy for
the following commit where we start tracking for spending notifications.
2025-02-05 19:49:09 +08:00
yyforyongyu
7eea7a7e9a
sweep: add requestID to monitorRecord
This way we can greatly simplify the method signatures, also paving the
upcoming changes where we wanna make it clear when updating the
monitorRecord, we only touch a portion of it.
2025-02-05 19:49:04 +08:00
yyforyongyu
bde5124e1b
sweep: shorten storeRecord method signature
This commit shortens the function signature of `storeRecord`, also makes
sure we don't call `t.records.Store` directly but always using
`storeRecord` instead so it's easier to trace the record creation.
2025-02-05 19:48:18 +08:00
yyforyongyu
c68b8e8c1e
sweep: rename Failed to Fatal
This commit renames `Failed` to `Fatal` as it sounds too close to
`PublishFailed`. We also wanna emphasize that inputs in this state won't
be retried.
2025-02-05 19:48:18 +08:00
Elle Mouton
16e2a48d0f
docs: update release notes 2025-02-05 12:41:50 +02:00
Elle Mouton
71b2338d53
graph/db: de(ser)ialise opaque node addrs
In this commit, we fix the bug demonstrated in the prior commit. We
correctly handle the persistence of lnwire.OpaqueAddrs.
2025-02-05 12:41:50 +02:00
Elle Mouton
d68d24d97e
graph/db: demonstrate LightningNode serialisation bug 2025-02-05 08:24:45 +02:00
Elle Mouton
b7509897d5
models: create a helper to convert wire NodeAnn to models.LNNode type
And use it in the gossiper. This helps ensure that we do this conversion
consistently.
2025-02-05 08:20:10 +02:00
Elle Mouton
c5cff4052b
lnwire_test: fix test doc string
This test started out demonstrating a bug. But that bug has since been
fixed. Fix the comment to reflect.
2025-02-05 08:17:42 +02:00
András Bánki-Horváth
6bf895aeb9
Merge pull request #9469 from bhandras/use-sqldb-1.0.7 2025-02-03 10:31:56 +01:00
András Bánki-Horváth
327eb8d8ca
Merge pull request #9438 from bhandras/invoice-bucket-tombstone
channeldb+lnd: set invoice bucket tombstone after migration
2025-02-01 10:59:51 +01:00
Andras Banki-Horvath
c10b765fff
build: use the tagged 1.0.7 version of sqldb 2025-02-01 10:54:11 +01:00
Olaoluwa Osuntokun
e40324358a
Merge pull request #9459 from ziggie1984/amp-htlc-invoices
invoices: amp invoices bugfix.
2025-01-31 13:11:52 -06:00
Oliver Gugger
d2c0279647
Merge pull request #9456 from mohamedawnallah/deprecate-warning-sendpayment-and-sendtoroute
lnrpc+docs: deprecate warning `SendToRoute`, `SendToRouteSync`, `SendPayment`, and `SendPaymentSync` in Release 0.19
2025-01-31 11:05:27 -06:00
Mohamed Awnallah
d4044c2fb6 docs: update release-notes-0.19.0.md
In this commit, we warn users about the removal
of RPCs `SendToRoute`, `SendToRouteSync`, `SendPayment`,
and `SendPaymentSync` in the next release 0.20.
2025-01-31 16:23:43 +00:00
ziggie
715cafa59a
docs: add release-notes. 2025-01-31 13:10:03 +01:00
ziggie
118261aca4
invoices+channeldb: Fix AMP invoices behaviour.
We now cancel all HTLCs of an AMP invoice as soon as it expires.
Otherwise because we mark the invoice as cancelled we would not
allow accepted HTLCs to be resolved via the invoiceEventLoop.
2025-01-31 13:10:02 +01:00
Oliver Gugger
f4bf99b161
Merge pull request #9462 from guggero/go-1-22-11
Update to Go 1.22.11
2025-01-30 10:18:50 -06:00
Oliver Gugger
94efe06495
docs: add release notes 2025-01-30 16:14:56 +01:00
Oliver Gugger
191c838ad1
multi: bump Go version to v1.22.11 2025-01-30 16:13:26 +01:00
Olaoluwa Osuntokun
32cdbb43f6
Merge pull request #9454 from ziggie1984/add_custom_error_msg
Add Custom Error msg and Prioritise replayed HTLCs
2025-01-29 22:48:22 -06:00
ziggie
f4e2f2a396 docs: add release-notes. 2025-01-29 22:45:37 -06:00
ziggie
15e6e35cdb
invoices: fix log entries and add a TODO.
We need to make sure if we cancel an AMP invoice we also cancel
all remaining HTLCs back.
2025-01-29 18:21:41 +01:00
ziggie
46f3260924
invoices: make sure the db uses the same testTime. 2025-01-29 18:21:40 +01:00
ziggie
34e56b69e9
invoicerpc: add clarifying comment. 2025-01-29 18:21:40 +01:00
ziggie
c95d73c898
invoices: remove obsolete code for AMP invoices.
We always fetch the HTLCs for the specific setID, so there is no
need to keep this code. In earlier versions we would call the
UpdateInvoice method with `nil` for the setID therefore we had
to lookup the AMPState. However this was error prune because in
case one partial payment times-out the AMPState would change to
cancelled and that could lead to not resolve HTLCs.
2025-01-29 18:21:40 +01:00
ziggie
0532990a04
invoices: enhance the unit test suite.
The invoiceregistry test suite also includes unit tests for
multi part payment especially also including payments to AMP
invoices.
2025-01-29 18:21:40 +01:00
ziggie
17e37bd7c2
multi: introduce new traffic shaper method.
We introduce a new specific fail resolution error when the
external HTLC interceptor denies the incoming HTLC. Moreover
we introduce a new traffic shaper method which moves the
implementation of asset HTLC to the external layers.
Moreover itests are adopted to reflect this new change.
2025-01-29 09:59:02 +01:00
ziggie
9ee12ee029
invoices: treat replayed HTLCs beforehand.
We make sure that HTLCs which have already been decided upon
are resolved before before allowing the external interceptor to
potentially cancel them back. This makes the implementation for
the external HTLC interceptor more streamlined.
2025-01-29 09:59:02 +01:00
Mohamed Awnallah
8d3611a3bd lnrpc: deprecate legacy RPCs
In this commit, we deprecate `SendToRouteSync`
and `SendPaymentSync` RPC endpoints.
2025-01-29 02:48:22 +00:00
Oliver Gugger
f25e44712f
Merge pull request #9445 from yyforyongyu/itest-flake
itest: fix flake in `testAnchorThirdPartySpend`
2025-01-27 02:12:34 -06:00
Oliver Gugger
1ed76af179
Merge pull request #9442 from ellemouton/miscErrorFormats
misc: fix incorrect inclusion of nil err in various formatted strings
2025-01-26 06:49:31 -06:00
yyforyongyu
bc8c1643c6
itest: move channel force closes into one sub test var 2025-01-26 09:16:02 +08:00
yyforyongyu
e4b205cd90
itest: fix flake in testAnchorThirdPartySpend
When mining lots of blocks in the itest, the subsystems can be out of
sync in terms of the best block height. We now assert the num of pending
sweeps on Alice's node to give her more time to sync the blocks,
essentially behaving like a `time.Sleep` as in reality, the blocks would
never be generated this fast.
2025-01-26 09:15:36 +08:00
Olaoluwa Osuntokun
c3cbfd8fb2
Merge pull request #9241 from Crypt-iQ/fix_vb
discovery+graph: track job set dependencies in vb
2025-01-24 11:21:35 -08:00
Oliver Gugger
baa34b06d3
Merge pull request #9232 from Abdulkbk/archive-channel-backups
chanbackup: archive old channel backup files
2025-01-24 05:42:13 -06:00
Elle Mouton
e3b94e4578
routerrpc: only log TrackPayment error if err is not nil 2025-01-24 12:49:45 +02:00
Elle Mouton
af8c8b4bb3
lntest: remove always-nil error from formatted error 2025-01-24 12:49:41 +02:00
Abdullahi Yunus
3bf15485ce
docs: add release note 2025-01-24 10:58:42 +01:00
Abdullahi Yunus
84f039db45
lnd+chanbackup: add lnd config flag
In this commit, we add the --no-backup-archive with a default
as false. When set to true then previous channel backup file will
not be archived but replaced. We also modify TestUpdateAndSwap
test to make sure the new behaviour works as expected.
2025-01-24 10:56:15 +01:00
Abdullahi Yunus
a51dfe9a7d
chanbackup: archive old channel backups
In this commit, we first check if a previous backup file exists,
if it does we copy it to archive folder before replacing it with
a new backup file. We also added a test for archiving chan backups.
2025-01-24 10:56:14 +01:00
Eugene Siegel
323b633895 graph -> discovery: move ValidationBarrier to discovery 2025-01-23 13:04:39 -05:00
Eugene Siegel
e0e4073bcd release-notes: update for 0.19.0 2025-01-23 11:43:07 -05:00
Eugene Siegel
6a47a501c3 discovery+graph: track job set dependencies in ValidationBarrier
This commit does two things:
- removes the concept of allow / deny. Having this in place was a
  minor optimization and removing it makes the solution simpler.
- changes the job dependency tracking to track sets of abstact
  parent jobs rather than individual parent jobs.

As a note, the purpose of the ValidationBarrier is that it allows us
to launch gossip validation jobs in goroutines while still ensuring
that the validation order of these goroutines is adhered to when it
comes to validating ChannelAnnouncement _before_ ChannelUpdate and
_before_ NodeAnnouncement.
2025-01-23 11:43:07 -05:00
Eugene Siegel
2731d09a0b graph: change ValidationBarrier usage in the builder code
This omits calls to InitJobDependencies, SignalDependants, and
WaitForDependants. These changes have been made here because
the router / builder code does not actually need job dependency
management. Calls to the builder code (i.e. AddNode, AddEdge,
UpdateEdge) are all blocking in the gossiper. This, combined
with the fact that child jobs are run after parent jobs in the
gossiper, means that the calls to the router will happen in the
proper dependency order.
2025-01-23 11:43:07 -05:00
Andras Banki-Horvath
3f6f6c19c1
docs: update release-notes-0.19.0.md 2025-01-23 15:14:26 +01:00
Andras Banki-Horvath
444524a762
channeldb+lnd: set invoice bucket tombstone after migration
This commit introduces the functionality to set a tombstone key
in the invoice bucket after the migration to the native SQL
database. The tombstone prevents the user from switching back
to the KV invoice database, ensuring data consistency and
avoiding potential issues like lingering invoices or partial
state in KV tables. The tombstone is checked on startup to
block any manual overrides that attempt to revert the migration.
2025-01-23 15:06:09 +01:00
Oliver Gugger
6cabc74c20
Merge pull request #8831 from bhandras/sql-invoice-migration
invoices: migrate KV invoices to native SQL for users of KV SQL backends
2025-01-23 05:48:25 -06:00
Oliver Gugger
49affa2dc3
Merge pull request #9424 from yyforyongyu/fix-gossip-ann
multi: fix inconsistent state in gossip syncer
2025-01-23 05:25:01 -06:00
Andras Banki-Horvath
b1a462ddba
docs: update release notes for 0.19.0 2025-01-23 09:11:03 +01:00
Andras Banki-Horvath
97c025f289
invoices: raise the number of allowed clients for the Postgres fixture 2025-01-23 09:11:02 +01:00
Andras Banki-Horvath
84598b6dc1
sqldb: ensure schema definitions are fully SQLite compatible
Previously, we applied replacements to our schema definitions
to make them compatible with both SQLite and Postgres backends,
as the files were not fully compatible with either.

With this change, the only replacement required for SQLite has
been moved to the generator script. This adjustment ensures
compatibility by enabling auto-incrementing primary keys that
are treated as 64-bit integers by sqlc.
2025-01-23 09:11:02 +01:00
Andras Banki-Horvath
ea98933317
invoices: allow migration test to work on kv sqlite channeldb 2025-01-23 09:11:02 +01:00
Andras Banki-Horvath
5e3ef3ec0c
invoices+sql: use the stored AmtPaid value instead of recalculating
Previously we'd recalculate the paid amount by summing amounts of
settled HTLCs. This approach while correct would stop the SQL migration
process as some KV invoices may have incorrectly stored paid amounts.
2025-01-23 09:11:02 +01:00
Andras Banki-Horvath
0839d4ba7b
itest: remove obsolete itest 2025-01-23 09:11:02 +01:00
Andras Banki-Horvath
a29f2430c1
itest: add integration test for invoice migration 2025-01-23 09:11:01 +01:00
Andras Banki-Horvath
8d20e2a23b
lnd: run invoice migration on startup
This commit runs the invoice migration if the user has a KV SQL backend
configured.
2025-01-23 09:11:01 +01:00
Andras Banki-Horvath
94e2724a34
sqldb+invoices: Optimize invoice fetching when the reference is only a hash
The current sqlc GetInvoice query experiences incremental slowdowns during
the migration of large invoice databases, primarily due to its complex
predicate set. For this specific use case, a streamlined GetInvoiceByHash
function provides a more efficient solution, maintaining near-constant
lookup times even with extensive table sizes.
2025-01-23 09:11:01 +01:00
Andras Banki-Horvath
b92f57e0ae
invoices: add migration code that runs a full invoice DB SQL migration 2025-01-23 09:11:01 +01:00
Andras Banki-Horvath
708bed517d
invoices: add migration code for a single invoice 2025-01-23 09:11:01 +01:00
Andras Banki-Horvath
43797d6be7
invoices: add method to create payment hash index
Certain invoices may not have a deterministic payment hash. For such
invoices we still store the payment hashes in our KV database, but we do
not have a sufficient index to retrieve them. This PR adds such index to
the SQL database that will be used during migration to retrieve payment
hashes.
2025-01-23 09:11:00 +01:00
Andras Banki-Horvath
be18f55ca1
invoices: extract method to create invoice insertion params 2025-01-23 09:11:00 +01:00
Andras Banki-Horvath
d65b630568
sqldb: remove unused preimage query parameter 2025-01-23 09:11:00 +01:00
Andras Banki-Horvath
b7d743929d
sqldb: add a temporary index to store KV invoice hash to ID mapping 2025-01-23 09:11:00 +01:00
Andras Banki-Horvath
3820497d7f
sqldb: set settled_at and settle_index on invocie insertion is set
Previously we intentially did not set settled_at and settle_index when
inserting a new invoice as those fields are set when we settle an
invoice through the usual invoice update. As migration requires that we
set these nullable fields, we can safely add them.
2025-01-23 09:11:00 +01:00
Andras Banki-Horvath
115f96c29a
multi: add call to directly insert an AMP sub-invoice 2025-01-23 09:10:59 +01:00
Andras Banki-Horvath
91c3e1496f
sqldb: separate migration execution from construction
This commit separates the execution of SQL and in-code migrations
from their construction. This change is necessary because,
currently, the SQL schema is migrated during the construction
phase in the lncfg package. However, migrations are typically
executed when individual stores are constructed within the
configuration builder.
2025-01-23 09:10:59 +01:00
Andras Banki-Horvath
b789fb2db3
sqldb: add support for custom in-code migrations
This commit introduces support for custom, in-code migrations, allowing
a specific Go function to be executed at a designated database version
during sqlc migrations. If the current database version surpasses the
specified version, the migration will be skipped.
2025-01-23 09:10:59 +01:00
Andras Banki-Horvath
9acd06d296
sqldb: add table to track custom SQL migrations
This commit adds the migration_tracker table which we'll use to track if
a custom migration has already been done.
2025-01-23 09:10:59 +01:00
Andras Banki-Horvath
680394518f
mod: temporarily replace sqldb with local version 2025-01-23 09:10:58 +01:00
Oliver Gugger
1f20bd352f
Merge pull request #9429 from yyforyongyu/update-action
.github: update actions versions
2025-01-20 09:01:46 -06:00
yyforyongyu
29603954bd
.github: update actions versions 2025-01-20 21:55:20 +08:00
Oliver Gugger
baa3b0dea3
Merge pull request #9425 from yyforyongyu/flake-fix
itest: fix flake in `testSweepHTLCs`
2025-01-17 10:36:55 -06:00
Oliver Gugger
27df4af53e
Merge pull request #9359 from NishantBansal2003/fix-timeout
routerrpc: add a default value for timeout_seconds in SendPaymentV2
2025-01-17 09:43:38 -06:00
yyforyongyu
c24f839fbe
itest: fix flake in testSweepHTLCs
We need to make sure Caarol finishes settling her invoice with Bob
before shutting down, so we make sure `AssertHTLCNotActive` on Bob
happens before shutting node Carol.
2025-01-17 22:58:37 +08:00
Yong
fb91b04906
Merge pull request #9405 from yyforyongyu/fix-unused-params
discovery+lnd: make param `ProofMatureDelta` configurable
2025-01-17 22:57:26 +08:00
yyforyongyu
ae2bcfe3d8
docs: add release notes 2025-01-17 21:44:23 +08:00
yyforyongyu
27a05694cb
multi: make ProofMatureDelta configurable
We add a new config option to set the `ProofMatureDelta` so the users
can tune their graphs based on their own preference over the num of
confs found in the announcement signatures.
2025-01-17 21:44:23 +08:00
yyforyongyu
56ff6d1fe0
docs: update release notes 2025-01-17 18:58:20 +08:00
yyforyongyu
772a9d5f42
discovery: fix mocked peer in unit tests
The mocked peer used here blocks on `sendToPeer`, which is not the
behavior of the `SendMessageLazy` of `lnpeer.Peer`. To reflect the
reality, we now make sure the `sendToPeer` is non-blocking in the tests.
2025-01-17 17:59:06 +08:00
yyforyongyu
9fecfed3b5
discovery: fix race access to syncer's state
This commit fixes the following race,
1. syncer(state=syncingChans) sends QueryChannelRange
2. remote peer replies ReplyChannelRange
3. ProcessQueryMsg fails to process the remote peer's msg as its state
   is neither waitingQueryChanReply nor waitingQueryRangeReply.
4. syncer marks its new state waitingQueryChanReply, but too late.

The historical sync will now fail, and the syncer will be stuck at this
state. What's worse is it cannot forward channel announcements to other
connected peers now as it will skip the broadcasting during initial
graph sync.

This is now fixed to make sure the following two steps are atomic,
1. syncer(state=syncingChans) sends QueryChannelRange
2. syncer marks its new state waitingQueryChanReply.
2025-01-17 02:39:07 +08:00
yyforyongyu
4b30b09d1c
discovery: add new method handleSyncingChans
This is a pure refactor to add a dedicated handler when the gossiper is
in state syncingChans.
2025-01-17 00:22:22 +08:00
yyforyongyu
eb2b0c783f
graph: fix staticcheck suggestion
From staticcheck: QF1002 - Convert untagged switch to tagged switch.
2025-01-17 00:21:45 +08:00
yyforyongyu
001e5599b6
multi: add debug logs for edge policy flow
This commit adds more logs around the ChannelUpdate->edge policy process
flow.
2025-01-17 00:17:23 +08:00
Yong
e0a920af44
Merge pull request #9420 from yyforyongyu/assert-channel-graph
itest+lntest: make sure to assert edge in both graph db and cache
2025-01-17 00:13:02 +08:00
yyforyongyu
faa1f67480
lntest: assert channel edge in both graph db and cache
We need to make sure the channel edge has been updated in both the graph
DB and cache.
2025-01-16 23:00:22 +08:00
yyforyongyu
e576d661ef
itest: fix docs 2025-01-16 23:00:21 +08:00
yyforyongyu
848f42ea1d
itest: fix flake in graph_topology_notifications
We need to make sure to wait for the `ListPeers` to give us the latest
response.
2025-01-16 16:19:37 +08:00
Nishant Bansal
3a3002e281
docs: add release notes.
Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
2025-01-15 19:46:09 +05:30
Nishant Bansal
23efbef946
itest: update tests with timeout_seconds
Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
2025-01-15 19:46:09 +05:30
Nishant Bansal
b577ad4661
routerrpc: default timeout_seconds to 60 in SendPaymentV2
If timeout_seconds is not set or is 0, the default value
of 60 seconds will be used.

Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
2025-01-15 19:45:44 +05:30
Oliver Gugger
572784a6a1
Merge pull request #9390 from NishantBansal2003/append-channel
Enhance `lncli` listchannels command with the chan_id and short_chan_id (human readable format)
2025-01-15 07:19:27 -06:00
Nishant Bansal
c2897a4c78
docs: add release notes.
Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
2025-01-15 17:58:40 +05:30
Nishant Bansal
103a194e5c
cmd/lncli: update listchannels output fields
Added scid_str as a string representation of chan_id,
replacing chan_id with scid, and including chan_id(BOLT02)
in lncli listchannels output.

Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
2025-01-15 17:58:06 +05:30
Nishant Bansal
7f8e89f3b4
lnwire: add 'x' separator in ShortChannelID method
Add the `AltString` method for `ShortChannelID` to produce a
human-readable format with 'x' as a separator
(block x transaction x output).

Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
2025-01-15 15:25:40 +05:30
Yong
4b16c2902e
Merge pull request #9383 from ziggie1984/bugfix-createmissingedge
bugfix createmissingedge
2025-01-14 16:20:15 +08:00
ziggie
e008190a85
localchans: bugfix so that we always use the correct chanID 2025-01-14 08:14:02 +01:00
Elle
e9dd01b60d
Merge pull request #9274 from ziggie1984/remove-2x-value
The sweeper subsystem uses now also the configured budget values for HTLCs
2025-01-13 19:09:11 +02:00
Oliver Gugger
b958667811
Merge pull request #9355 from Roasbeef/rapid-fuzz-htlc-blobs
contractcourt: add rapid derived fuzz test for HtlcAuxBlob
2025-01-13 10:35:53 -06:00
Oliver Gugger
1b0f41da48
Merge pull request #9378 from yyforyongyu/fix-unit-test
chainntnfs: fix test `testSingleConfirmationNotification`
2025-01-13 03:13:02 -06:00
Oliver Gugger
67a8c7cf51
Merge pull request #9391 from mohamedawnallah/choreRegisterToRegisteredInStartingDebugLogs
chore: change 'register' to 'registered' in lnrpc starting debug logs [skip ci]
2025-01-13 03:11:03 -06:00
Elle
98033f876d
Merge pull request #9344 from ellemouton/useUpdatedContextGuard
htlcswitch+go.mod: use updated fn.ContextGuard
2025-01-11 12:28:45 +02:00
Elle Mouton
950194a2da
htlcswitch+go.mod: use updated fn.ContextGuard
This commit updates the fn dep to the version containing the updates to
the ContextGuard implementation. Only the htlcswitch/link uses the guard
at the moment so this is updated to make use of the new implementation.
2025-01-11 06:17:43 +02:00
Elle
77848c402d
Merge pull request #9342 from ellemouton/slogProtofsm
protofsm: update GR Manager usage and start using structured logging
2025-01-10 19:55:18 +02:00
Elle Mouton
42ce9d639f
docs: add release notes entry 2025-01-10 18:34:26 +02:00
Elle Mouton
65c4c2c4d0
protofsm: use pointer to GoroutineManager 2025-01-10 18:25:19 +02:00
Elle Mouton
dfddeec8d4
protofsm: use structured logging 2025-01-10 18:25:19 +02:00
Elle Mouton
575ea7af83
protofsm: use prefixed logger for StateMachine
So that we dont have to remember to add the `FSM(%v)` prefix each time
we write a log line.
2025-01-10 18:25:19 +02:00
Elle Mouton
b887c1cc5d
protofsm: use updated GoroutineManager API
Update to use the latest version of the GoroutineManager which takes a
context via the `Go` method instead of the constructor.
2025-01-10 18:23:28 +02:00
Elle Mouton
4e0498faa4
go.mod: update btclog dep
This bump includes a fix which prevents attribute value quoting if the
value string contains a newline character. This is so that if we call
spew.DumpS(), the output will stay nicely formatted.

The update also includes a couple more Hex helpers which we can make use
of now.
2025-01-10 18:23:28 +02:00
Oliver Gugger
dd25e6eb22
Merge pull request #9361 from starius/optimize-context-guard
fn: optimize context guard
2025-01-10 09:33:21 -06:00
Oliver Gugger
70e7b56713
Merge pull request #9388 from chloefeal/fix
Fix some typos
2025-01-10 09:30:50 -06:00
Oliver Gugger
e449adb03a
Merge pull request #9404 from peicuiping/master
chore: fix some typos
2025-01-10 09:28:37 -06:00
Oliver Gugger
83a2100810
Merge pull request #9386 from ellemouton/updateElleKey
scripts/keys: update pub key for ellemouton
2025-01-10 09:25:37 -06:00
Oliver Gugger
1d9b30f139
Merge pull request #9411 from ellemouton/bumpUploadArtifactAction
.github: bump upload-artifact action to v4
2025-01-10 09:17:31 -06:00
Elle Mouton
a7a01f684d
.github: bump upload-artifact action to v4 2025-01-10 08:31:57 +02:00
ziggie
f36f7c1d84
docs: add release-notes 2025-01-09 07:56:40 +01:00
ziggie
5cd88f7e48
contractcourt: remove 2xamount requirement for the sweeper. 2025-01-08 08:57:50 +01:00
chloefeal
852a8d8746
chore: fix typo 2025-01-05 20:45:35 +08:00
peicuiping
06fef749a7 chore: fix some typos
Signed-off-by: peicuiping <ezc5@sina.cn>
2025-01-03 21:48:29 +08:00
Boris Nagaev
07c46680e9
fn/ContextGuard: use context.AfterFunc to wait
Simplifies context cancellation handling by using context.AfterFunc instead of a
goroutine to wait for context cancellation. This approach avoids the overhead of
a goroutine during the waiting period.

For ctxQuitUnsafe, since g.quit is closed only in the Quit method (which also
cancels all associated contexts), waiting on context cancellation ensures the
same behavior without unnecessary dependency on g.quit.

Added a test to ensure that the Create method does not launch any goroutines.
2025-01-02 10:38:26 -03:00
Boris Nagaev
e9ab603735
fn/ContextGuard: clear store of cancel funcs
If ContextGuard lives for some time after Quit method is called, the map won't
be collected by GC. Optimization.
2025-01-02 10:38:26 -03:00
Boris Nagaev
1750aec13d
fn: remove uneeded argument of ctxBlocking
Removed 'cancel' argument, because it is called only in case the context has
already expired and the only action that cancel function did was cancelling the
context.
2025-01-02 10:38:26 -03:00
Boris Nagaev
865da9c525
fn/ContextGuard: test cancelling blocking context
Make sure WgWait() doesn't block.
2025-01-02 10:38:26 -03:00
Mohamed Awnallah
e636c76300 chore: change register to registered [skip ci] 2024-12-28 23:06:07 +02:00
Elle Mouton
b986f57206
scripts/keys: update pub key for ellemouton 2024-12-21 08:11:38 +02:00
yyforyongyu
bafe5d009f
chainntnfs: fix test testSingleConfirmationNotification 2024-12-20 22:08:32 +08:00
Oliver Gugger
a388c1f39d
Merge pull request #9368 from lightningnetwork/yy-waiting-on-merge
Fix itest re new behaviors introduced by `blockbeat`
2024-12-20 07:44:54 -06:00
yyforyongyu
2913f6e4c9
itest: fix flake in testCoopCloseWithExternalDeliveryImpl
The response from `ClosedChannels` may not be up-to-date, so we wrap it
inside a wait closure.
2024-12-20 19:38:15 +08:00
yyforyongyu
76eeae32d6
itest: document and fix wallet UTXO flake 2024-12-20 19:38:14 +08:00
yyforyongyu
7ab4081ffd
lntest: make sure chain backend is synced to miner
We sometimes see `timeout waiting for UTXOs` error from bitcoind-related
itests due to the chain backend not synced to the miner. We now assert
it's synced before continue.
2024-12-20 19:38:14 +08:00
yyforyongyu
1dec926165
workflows: increase num of tranches to 16
Keep the SQL, etcd, bitcoin rpcpolling builds and non-ubuntu builds at 8
since they are less stable.
2024-12-20 19:38:14 +08:00
yyforyongyu
31b66962d8
lntest: properly handle shutdown error
This commit removes the panic used in checking the shutdown log.
Instead, the error is returned and asserted in `shutdownAllNodes` so
it's easier to check which node failed in which test. We also catch all
the errors returned from `StopDaemon` call to properly access the
shutdown behavior.
2024-12-20 19:38:14 +08:00
yyforyongyu
73574d919d
lntest: add timeouts for windows
For Windows the tests run much slower so we create customized timeouts
for them.
2024-12-20 19:38:14 +08:00
yyforyongyu
d7f8fa6ab6
lntest: increase port timeout 2024-12-20 19:38:14 +08:00
yyforyongyu
33b07be8c3
itest: even out num of tests per tranche
Previous splitting logic simply put all the remainder in the last
tranche, which could make the last tranche run significantly more test
cases. We now change it so the remainder is evened out across tranches.
2024-12-20 19:38:14 +08:00
yyforyongyu
c536bc220f
itest: add a prefix before appending a subtest case 2024-12-20 19:38:13 +08:00
yyforyongyu
686a7dd31c
docs: update release notes 2024-12-20 19:38:13 +08:00
yyforyongyu
becbdce64c
lntest: limit the num of blocks mined in each test 2024-12-20 19:38:13 +08:00
yyforyongyu
5236c05dc6
itest+lntest: add new method FundNumCoins
Most of the time we only need to fund the node with given number of
UTXOs without concerning the amount, so we add the more efficient
funding method as it mines a single block in the end.
2024-12-20 19:38:13 +08:00
yyforyongyu
691a6267be
workflows: use btcd for macOS
To increase the speed from 40m per run to roughly 20m per run.
2024-12-20 19:38:13 +08:00
yyforyongyu
77b2fa0271
lntest: make sure policies are populated in AssertChannelInGraph 2024-12-20 19:38:13 +08:00
yyforyongyu
c97c31a70b
lntest: increase node start timeout and payment benchmark timeout 2024-12-20 19:38:12 +08:00
yyforyongyu
efe81f2d3c
itest: track and skip flaky tests for windows
To make the CI indicative, we now starting tracking the flaky tests
found when running on Windows. As a starting point, rather than ignore
the windows CI entirely, we now identify there are cases where lnd can
be buggy when running in windows.

We should fix the tests in the future, otherwise the windows build
should be deleted.
2024-12-20 19:38:12 +08:00
yyforyongyu
e79ad6e5aa
itest: further reduce block mined in tests 2024-12-20 19:38:12 +08:00
yyforyongyu
6f2e7feb94
itest: breakdown testSendDirectPayment
Also fixes a wrong usage of `ht.Subtest`.
2024-12-20 19:38:12 +08:00
yyforyongyu
c7b8379602
itest: break down channel fundmax tests 2024-12-20 19:38:12 +08:00
yyforyongyu
37b8210f37
itest: break down taproot tests 2024-12-20 19:38:12 +08:00
yyforyongyu
efae8ea56f
itest: break down single hop send to route
Also removed the duplicate test cases.
2024-12-20 19:38:11 +08:00
yyforyongyu
c029f0a84f
itest: break down basic funding flow tests 2024-12-20 19:38:11 +08:00
yyforyongyu
c58fa01a66
itest: break down wallet import account tests 2024-12-20 19:38:11 +08:00
yyforyongyu
31aada65a4
itest: break down channel backup restore tests 2024-12-20 19:38:11 +08:00
yyforyongyu
7b1427a565
itest: break down payment failed tests 2024-12-20 19:38:11 +08:00
yyforyongyu
3319d0d983
itest: break down open channel fee policy 2024-12-20 19:38:11 +08:00
yyforyongyu
21c5d36812
itest: break down scid alias channel update tests 2024-12-20 19:38:10 +08:00
yyforyongyu
04a15039d7
itest: break all multihop test cases 2024-12-20 19:38:10 +08:00
yyforyongyu
a76ff79adc
itest: break down utxo selection funding tests 2024-12-20 19:38:10 +08:00
yyforyongyu
b1cb819f07
itest: break down channel restore commit types cases 2024-12-20 19:38:10 +08:00
yyforyongyu
5663edfc2f
itest: break remote signer into independent cases
So the test can run faster.
2024-12-20 19:38:10 +08:00
yyforyongyu
cca2364097
itest: optimize blocks mined in testGarbageCollectLinkNodes
There's no need to mine 80ish blocks here.
2024-12-20 19:38:10 +08:00
yyforyongyu
1950d89925
itest: document a flake found in SendToRoute 2024-12-20 19:38:10 +08:00
yyforyongyu
7e80b77535
itest: document a rare flake found in macOS 2024-12-20 19:38:09 +08:00
yyforyongyu
39104c53d4
lntest: fix flakeness in openChannelsForNodes
We now make sure the channel participants have heard their private
channel when opening channels.
2024-12-20 19:38:09 +08:00
yyforyongyu
cfb5713cda
itest+lntest: fix flake in MPP-related tests 2024-12-20 19:38:09 +08:00
yyforyongyu
fb59669ae8
itest: document details about MPP-related tests
This is needed so we can have one place to fix the flakes found in the
MPP-related tests, which is fixed in the following commit.
2024-12-20 19:38:09 +08:00
yyforyongyu
f912f407c8
lntest: increase rpcmaxwebsockets for btcd
This has been seen in the itest which can lead to the node startup
failure,
```
2024-11-20 18:55:15.727 [INF] RPCS: Max websocket clients exceeded [25] - disconnecting client 127.0.0.1:57224
```
2024-12-20 19:38:09 +08:00
yyforyongyu
4e85d86a2e
itest: fix flake in update_pending_open_channels 2024-12-20 19:38:09 +08:00
yyforyongyu
23edca8d19
itest: fix flake in testPrivateUpdateAlias 2024-12-20 19:38:08 +08:00
yyforyongyu
7c3564eeb6
itest: fix flake in runPsbtChanFundingWithNodes 2024-12-20 19:38:08 +08:00
yyforyongyu
7ceb9a4af5
lntest+itest: remove AssertNumActiveEdges
This is no longer needed since we don't have standby nodes, plus it's
causing panic in windows build due to `edge.Policy` being nil.
2024-12-20 19:38:08 +08:00
yyforyongyu
8f3100c984
itest: put mpp tests in one file 2024-12-20 19:38:08 +08:00
yyforyongyu
fee6b70519
itest: use ht.CreateSimpleNetwork whenever applicable
So we won't forget to assert the topology after opening a chain of
channels.
2024-12-20 19:38:08 +08:00
yyforyongyu
4eea2078fb
itest+routing: fix flake in runFeeEstimationTestCase
The test used 10s as the timeout value, which can easily cause a timeout
in a slow build so we increase it to 60s.
2024-12-20 19:38:08 +08:00
yyforyongyu
8b8f0c4eb4
itest: fix flake in testSwitchOfflineDelivery
The reconnection will happen automatically when the nodes have a
channel, so we just ensure the connection instead of reconnecting
directly.
2024-12-20 19:38:07 +08:00
yyforyongyu
66b35018b8
itest: fix flake in testRevokedCloseRetributionZeroValueRemoteOutput
We need to mine an empty block as the tx may already have entered the
mempool. This should be fixed once we start using the sweeper to handle
the justice tx.
2024-12-20 19:38:07 +08:00
yyforyongyu
c07162603d
itest: remove loop in wsTestCaseBiDirectionalSubscription
So we know which open channel operation failed.
2024-12-20 19:38:07 +08:00
yyforyongyu
782edde213
itest: fix and document flake in sweeping tests
We previously didn't see this issue because we always have nodes being
over-funded.
2024-12-20 19:38:07 +08:00
yyforyongyu
9f764c25f9
itest: flatten PSBT funding test cases
So it's easier to get the logs and debug.
2024-12-20 19:38:07 +08:00
yyforyongyu
762e59d78c
itest: fix flake for neutrino backend 2024-12-20 19:38:07 +08:00
yyforyongyu
3a45492398
itest: fix spawning temp miner 2024-12-20 19:38:07 +08:00
yyforyongyu
2a9b7ec536
itest: fix flake in testSendDirectPayment
This bug was hidden because we used standby nodes before, which always
have more-than-necessary wallet utxos.
2024-12-20 19:38:06 +08:00
yyforyongyu
e7310ff1b6
itest: fix testOpenChannelUpdateFeePolicy
This commit fixes a misuse of `ht.Subtest`, where the nodes should
always be created inside the subtest.
2024-12-20 19:38:06 +08:00
yyforyongyu
010a4f1571
lntest: add human-readble names and check num of nodes 2024-12-20 19:38:06 +08:00
yyforyongyu
f64b2ce8f2
lntest: make sure node is properly shut down
Soemtimes the node may be hanging for a while without being noticed,
causing failures in its following tests, thus making the debugging
extrememly difficult. We now assert the node has been shut down from the
logs to assert the shutdown process behaves as expected.
2024-12-20 19:38:06 +08:00
yyforyongyu
72f3f41d41
itest: remove unnecessary channel close and node shutdown
Since we don't have standby nodes anymore, we don't need to close the
channels when the test finishes. Previously we would do so to make sure
the standby nodes have a clean state for the next test case, which is no
longer relevant.
2024-12-20 19:38:06 +08:00
yyforyongyu
00772ae281
itest+lntest: remove standby nodes
This commit removes the standby nodes Alice and Bob.
2024-12-20 19:38:06 +08:00
yyforyongyu
11c9dd5ff2
itest: remove unused method setupFourHopNetwork 2024-12-20 19:38:05 +08:00
yyforyongyu
de8f14bed2
itest: remove the use of standby nodes
This commit removes the usage of the standby nodes and uses
`CreateSimpleNetwork` when applicable. Also introduces a helper method
`NewNodeWithCoins` to quickly start a node with funds.
2024-12-20 19:38:05 +08:00
yyforyongyu
3eda87fff9
itest: remove direct reference to stanby nodes
Prepares the upcoming refactor. We now never call `ht.Alice` directly,
instead, we always init `alice := ht.Alice` so it's easier to see how
they are removed in a following commit.
2024-12-20 19:38:05 +08:00
yyforyongyu
ef167835dd
workflows: pass action ID as the shuffle seed
To make sure each run is shuffled, we use the action ID as the seed.
2024-12-20 19:38:05 +08:00
yyforyongyu
88bd0cb806
itest: shuffle test cases to even out blocks mined in tranches
This commit adds a new flag to shuffle all the test cases before running
them so tests which require lots of blocks to be mined are less likely
to be run in the same tranch.

The other benefit is this approach provides a more efficient way to
figure which tests are broken since all the differnet backends are
running different tranches in their builds, we can identify more failed
tests in one push.
2024-12-20 19:38:05 +08:00
yyforyongyu
2c27df6c30
itest: print num of blocks for debugging 2024-12-20 19:38:05 +08:00
Oliver Gugger
fe48e65f42
Merge pull request #9381 from yyforyongyu/fix-no-space-left
workflows: fix no space left error
2024-12-20 12:21:53 +01:00
Oliver Gugger
8859dbc288
Merge pull request #9315 from lightningnetwork/yy-feature-blockbeat
Implement `blockbeat`
2024-12-20 12:20:32 +01:00
yyforyongyu
ecd82a3bcb
contractcourt: include custom records on replayed htlc
Add another case in addition to #9357.
2024-12-20 17:54:13 +08:00
yyforyongyu
a1bd8943db
lntest+itest: export DeriveFundingShim 2024-12-20 17:54:12 +08:00
yyforyongyu
c4a6abb14d
lntest+itest: remove the usage of ht.AssertActiveHtlcs
The method `AssertActiveHtlcs` is now removed due to it's easy to be
misused. To assert a given htlc, use `AssertOutgoingHTLCActive` and
`AssertIncomingHTLCActive` instead for ensuring the HTLC exists in the
right direction. Although often the case `AssertNumActiveHtlcs` would be
enough as it implicitly checks the forwarding behavior for an
intermediate node by asserting there are always num_payment*2 HTLCs.
2024-12-20 17:54:12 +08:00
yyforyongyu
36a87ad5f4
itest: assert payment status after sending 2024-12-20 17:54:12 +08:00
yyforyongyu
425877e745
itest: remove redundant block in multiple tests 2024-12-20 17:54:12 +08:00
yyforyongyu
9c0c373b7e
itest: flatten and fix testWatchtower 2024-12-20 17:54:12 +08:00
yyforyongyu
b7feeba008
itest+lntest: fix channel force close test
Also flatten the tests to make them easier to be maintained.
2024-12-20 17:54:12 +08:00
yyforyongyu
9d4a60d613
itest: remove redundant blocks in channel backup tests 2024-12-20 17:54:12 +08:00
yyforyongyu
a55408d4a1
itest: remove redudant block in testPsbtChanFundingWithUnstableUtxos 2024-12-20 17:54:11 +08:00
yyforyongyu
22b9350320
itest: remove redunant block mining in testChannelFundingWithUnstableUtxos 2024-12-20 17:54:11 +08:00
yyforyongyu
2494f1ed32
itest: remove redundant block mining in testFailingChannel 2024-12-20 17:54:11 +08:00
yyforyongyu
e1d5bbf171
itest: remove unnecessary force close 2024-12-20 17:54:11 +08:00
yyforyongyu
2adb356668
itest: rename file to reflect the tests 2024-12-20 17:54:11 +08:00
yyforyongyu
2f0256775e
itest: flatten testHtlcTimeoutResolverExtractPreimageRemote
Also remove unused code.
2024-12-20 17:54:11 +08:00
yyforyongyu
34951a6153
itest: flatten testHtlcTimeoutResolverExtractPreimageLocal
This commit simplfies the test since we only test the preimage
extraction logic in the htlc timeout resolver, there's no need to test
it for all different channel types as the resolver is made to be
oblivious about them.
2024-12-20 17:54:10 +08:00
yyforyongyu
f95e64f084
itest: flatten testMultiHopHtlcAggregation 2024-12-20 17:54:10 +08:00
yyforyongyu
52e6fb1161
itest: flatten testMultiHopHtlcRemoteChainClaim 2024-12-20 17:54:10 +08:00
yyforyongyu
8dd73a08a9
itest: flatten testMultiHopHtlcLocalChainClaim 2024-12-20 17:54:10 +08:00
yyforyongyu
d7b2025248
lntest+itest: flatten testMultiHopRemoteForceCloseOnChainHtlcTimeout 2024-12-20 17:54:10 +08:00
yyforyongyu
bef17f16cf
lntest+itest: flatten testMultiHopLocalForceCloseOnChainHtlcTimeout 2024-12-20 17:54:10 +08:00
yyforyongyu
bc31979f7b
itest: simplify and flatten testMultiHopReceiverChainClaim 2024-12-20 17:54:10 +08:00
yyforyongyu
9ab9cd5f99
lntest+itest: start flattening the multi-hop tests
Starting from this commit, we begin the process of flattening the
multi-hop itests to make them easier to be maintained. The tests are
refactored into their own test cases, with each test focusing on testing
one channel type. This is necessary to save effort for future
development.

These tests are also updated to reflect the new `blockbeat` behavior.
2024-12-20 17:54:09 +08:00
yyforyongyu
e45005b310
itest: fix testPaymentSucceededHTLCRemoteSwept 2024-12-20 17:54:09 +08:00
yyforyongyu
0778009ac2
itest: fix testBumpForceCloseFee 2024-12-20 17:54:09 +08:00
yyforyongyu
1aeea8a90f
itest: fix testSweepCommitOutputAndAnchor 2024-12-20 17:54:09 +08:00
yyforyongyu
d260a87f3b
itest: fix testSweepHTLCs 2024-12-20 17:54:09 +08:00
yyforyongyu
cacf222e11
itest: fix testSweepCPFPAnchorIncomingTimeout 2024-12-20 17:54:09 +08:00
yyforyongyu
40ac04a254
lntest+itest: fix testSweepCPFPAnchorOutgoingTimeout 2024-12-20 17:54:08 +08:00
yyforyongyu
4806b2fda7
multi: optimize loggings around changes from blockbeat 2024-12-20 17:54:08 +08:00
yyforyongyu
fecd5ac735
contractcourt: make sure launchResolvers is called on new blockbeat
This is an oversight from addressing this commment:
https://github.com/lightningnetwork/lnd/pull/9277#discussion_r1882410396

where we should focus on skipping the close events but not the
resolvers.
2024-12-20 17:54:08 +08:00
yyforyongyu
bd88948264
docs: add release notes for blockbeat series 2024-12-20 17:54:08 +08:00
yyforyongyu
cc60d2b41c
chainntnfs: skip dispatched conf details
We need to check `dispatched` before sending conf details, otherwise the
channel `ntfn.Event.Confirmed` will be blocking, which is the leftover
from #9275.
2024-12-20 17:54:08 +08:00
yyforyongyu
ea7d6a509b
contractcourt: register conf notification once and cancel when confirmed 2024-12-20 17:54:08 +08:00
yyforyongyu
a6d3a0fa99
contractcourt: process channel close event on new beat 2024-12-20 17:54:07 +08:00
yyforyongyu
c5b3033427
contractcourt: add close event handlers in ChannelArbitrator
To prepare the next commit where we would handle the event upon
receiving a blockbeat.
2024-12-20 17:54:07 +08:00
yyforyongyu
6eb9bb1ed6
multi: add new method ChainArbitrator.RedispatchBlockbeat
This commit adds a new method to enable us resending the blockbeat in
`ChainArbitrator`, which is needed for the channel restore as the chain
watcher and channel arbitrator are added after the start of the chain
arbitrator.
2024-12-20 17:54:07 +08:00
yyforyongyu
4d765668cc
contractcourt: use close height instead of best height
This commit adds the closing height to the logging and fixes a wrong
height used in handling the breach event.
2024-12-20 17:54:07 +08:00
yyforyongyu
3822c23833
contractcourt: notify blockbeat for chainWatcher
We now start notifying the blockbeat from the ChainArbitrator to the
chainWatcher.
2024-12-20 17:54:07 +08:00
yyforyongyu
8237598ed1
contractcourt: handle blockbeat in chainWatcher 2024-12-20 17:54:07 +08:00
yyforyongyu
4e30598263
contractcourt: add method handleCommitSpend
To prepare for the blockbeat handler.
2024-12-20 17:54:07 +08:00
yyforyongyu
c1a9390c36
contractcourt: register spend notification during init
This commit moves the creation of the spending notification from `Start`
to `newChainWatcher` so we subscribe the spending event before handling
the block, which is needed to properly handle the blockbeat.
2024-12-20 17:54:06 +08:00
yyforyongyu
07cb3aef00
contractcourt: implement Consumer on chainWatcher 2024-12-20 17:54:06 +08:00
yyforyongyu
63aa5aa6e9
contractcourt: offer outgoing htlc one block earlier before its expiry
We need to offer the outgoing htlc one block earlier to make sure when
the expiry height hits, the sweeper will not miss sweeping it in the
same block. This also means the outgoing contest resolver now only does
one thing - watch for preimage spend till height expiry-1, which can
easily be moved into the timeout resolver instead in the future.
2024-12-20 17:54:06 +08:00
yyforyongyu
819c15fa0b
contractcourt: break launchResolvers into two steps
In this commit, we break the old `launchResolvers` into two steps - step
one is to launch the resolvers synchronously, and step two is to
actually waiting for the resolvers to be resolved. This is critical as
in the following commit we will require the resolvers to be launched at
the same blockbeat when a force close event is sent by the chain watcher.
2024-12-20 17:54:06 +08:00
yyforyongyu
d2e81a19fd
contractcourt: fix concurrent access to launched 2024-12-20 17:54:06 +08:00
yyforyongyu
4f5ccb8650
contractcourt: fix concurrent access to resolved
This commit makes `resolved` an atomic bool to avoid data race. This
field is now defined in `contractResolverKit` to avoid code duplication.
2024-12-20 17:54:06 +08:00
yyforyongyu
47722292c5
contractcourt: add Launch method to outgoing contest resolver 2024-12-20 17:54:05 +08:00
yyforyongyu
ef98c52d10
contractcourt: add Launch method to incoming contest resolver
A minor refactor is done to support implementing `Launch`.
2024-12-20 17:54:05 +08:00
yyforyongyu
025d787fd2
invoices: exit early when the subscriber chan is nil
When calling `NotifyExitHopHtlc` it is allowed to pass a chan to
subscribe to the HTLC's resolution when it's settled. However, this
method will also return immediately if there's already a resolution,
which means it behaves like a notifier and a getter. If the caller
decides to only use the getter to do a non-blocking lookup, it can pass
a nil subscriber chan to bypass the notification.
2024-12-20 17:54:05 +08:00
yyforyongyu
71aec7bd94
contractcourt: add Launch method to htlc timeout resolver
This commit breaks the `Resolve` into two parts - the first part is
moved into a `Launch` method that handles sending sweep requests, and
the second part remains in `Resolve` which handles waiting for the
spend. Since we are using both utxo nursery and sweeper at the same
time, to make sure this change doesn't break the existing behavior, we
implement the `Launch` as following,
- zero-fee htlc - handled by the sweeper
- direct output from the remote commit - handled by the sweeper
- legacy htlc - handled by the utxo nursery
2024-12-20 17:54:05 +08:00
yyforyongyu
cf105e67f4
contractcourt: add Launch method to htlc success resolver
This commit breaks the `Resolve` into two parts - the first part is
moved into a `Launch` method that handles sending sweep requests, and
the second part remains in `Resolve` which handles waiting for the
spend. Since we are using both utxo nursery and sweeper at the same
time, to make sure this change doesn't break the existing behavior, we
implement the `Launch` as following,
- zero-fee htlc - handled by the sweeper
- direct output from the remote commit - handled by the sweeper
- legacy htlc - handled by the utxo nursery
2024-12-20 17:54:05 +08:00
yyforyongyu
913f5d4657
contractcourt: add Launch method to commit resolver 2024-12-20 17:54:05 +08:00
yyforyongyu
a98763494f
contractcourt: add Launch method to anchor/breach resolver
We will use this and its following commits to break the original
`Resolve` methods into two parts - the first part is moved to a new
method `Launch`, which handles sending a sweep request to the sweeper.
The second part remains in `Resolve`, which is mainly waiting for a
spending tx.

Breach resolver currently doesn't do anything in its `Launch` since the
sweeping of justice outputs are not handled by the sweeper yet.
2024-12-20 17:54:04 +08:00
yyforyongyu
730b605ed4
contractcourt: add resolve handlers in htlcTimeoutResolver
This commit adds more methods to handle resolving the spending of the
output based on different spending paths.
2024-12-20 17:54:04 +08:00
yyforyongyu
7083302fa0
contractcourt: add methods to checkpoint states
This commit adds checkpoint methods in `htlcTimeoutResolver`, which are
similar to those used in `htlcSuccessResolver`.
2024-12-20 17:54:04 +08:00
yyforyongyu
bfc95b8b2c
contractcourt: add sweep senders in htlcTimeoutResolver
This commit adds new methods to handle making sweep requests based on
the spending path used by the outgoing htlc output.
2024-12-20 17:54:04 +08:00
yyforyongyu
cb18940e75
contractcourt: remove redundant return value in claimCleanUp 2024-12-20 17:54:04 +08:00
yyforyongyu
c92d7f0fd0
contractcourt: add resolver handlers in htlcSuccessResolver
This commit refactors the `Resolve` method by adding two resolver
handlers to handle waiting for spending confirmations.
2024-12-20 17:54:04 +08:00
yyforyongyu
fb499bc4cc
contractcourt: add sweep senders in htlcSuccessResolver
This commit is a pure refactor in which moves the sweep handling logic
into the new methods.
2024-12-20 17:54:04 +08:00
yyforyongyu
10e5a43e46
contractcourt: add spend path helpers in timeout/success resolver
This commit adds a few helper methods to decide how the htlc output
should be spent.
2024-12-20 17:54:03 +08:00
yyforyongyu
1f2cfc6a60
contractcourt: add verbose logging in resolvers
We now put the outpoint in the resolvers's logging so it's easier to
debug.
2024-12-20 17:54:03 +08:00
yyforyongyu
0bab6b3419
chainio: use errgroup to limit num of goroutines 2024-12-20 17:54:03 +08:00
yyforyongyu
1d53e7d081
multi: improve loggings 2024-12-20 17:54:03 +08:00
yyforyongyu
45b243c91c
contractcourt: fix linter funlen
Refactor the `Start` method to fix the linter error:
```
contractcourt/chain_arbitrator.go:568: Function 'Start' is too long (242 > 200) (funlen)
```
2024-12-20 17:54:03 +08:00
yyforyongyu
8fc9154506
lnd: start blockbeatDispatcher and register consumers 2024-12-20 17:54:03 +08:00
yyforyongyu
16a8b623b3
lnd: add new method startLowLevelServices
In this commit we start to break up the starting process into smaller
pieces, which is needed in the following commit to initialize blockbeat
consumers.
2024-12-20 17:54:02 +08:00
yyforyongyu
545cea0546
multi: start consumers with a starting blockbeat
This is needed so the consumers have an initial state about the current
block.
2024-12-20 17:54:02 +08:00
yyforyongyu
802353036e
contractcourt: start channel arbitrator with blockbeat
To avoid calling GetBestBlock again.
2024-12-20 17:54:02 +08:00
yyforyongyu
e2e59bd90c
contractcourt: remove the immediate param used in Resolve
This `immediate` flag was added as a hack so during a restart, the
pending resolvers would offer the inputs to the sweeper and ask it to
sweep them immediately. This is no longer need due to `blockbeat`, as
now during restart, a block is always sent to all subsystems via the
flow `ChainArb` -> `ChannelArb` -> resolvers -> sweeper. Thus, when
there are pending inputs offered, they will be processed by the sweeper
immediately.
2024-12-20 17:54:02 +08:00
yyforyongyu
71295534bb
contractcourt: remove block subscription in channel arbitrator
This commit removes the block subscriptions used in `ChannelArbitrator`,
replaced them with the blockbeat managed by `BlockbeatDispatcher`.
2024-12-20 17:54:02 +08:00
yyforyongyu
045f8432b7
contractcourt: remove block subscription in chain arbitrator
This commit removes the block subscriptions used in `ChainArbitrator`
and replaced them with the blockbeat managed by `BlockbeatDispatcher`.
2024-12-20 17:54:02 +08:00
yyforyongyu
5f9d473702
contractcourt: remove waitForHeight in resolvers
The sweeper can handle the waiting so there's no need to wait for blocks
inside the resolvers. By offering the inputs prior to their mature
heights also guarantees the inputs with the same deadline are
aggregated.
2024-12-20 17:54:02 +08:00
yyforyongyu
3ac6752a77
sweep: remove redundant notifications during shutdown
This commit removes the hack introduced in #4851. Previously we had this
issue because the chain notifier was stopped before the sweeper, which
was changed a while back and we now always stop the chain notifier last.
In addition, since we no longer subscribe to the block epoch chan
directly, this issue can no longer happen.
2024-12-20 17:54:01 +08:00
yyforyongyu
e113f39d26
sweep: remove block subscription in UtxoSweeper and TxPublisher
This commit removes the independent block subscriptions in `UtxoSweeper`
and `TxPublisher`. These subsystems now listen to the `BlockbeatChan`
for new blocks.
2024-12-20 17:54:01 +08:00
yyforyongyu
801fd6b85b
multi: implement Consumer on subsystems
This commit implements `Consumer` on `TxPublisher`, `UtxoSweeper`,
`ChainArbitrator` and `ChannelArbitrator`.
2024-12-20 17:54:01 +08:00
yyforyongyu
b5a3a27c77
chainio: add partial implementation of Consumer interface 2024-12-20 17:54:01 +08:00
yyforyongyu
4b83d87baa
chainio: add BlockbeatDispatcher to dispatch blockbeats
This commit adds a blockbeat dispatcher which handles sending new blocks
to all subscribed consumers.
2024-12-20 17:54:01 +08:00
yyforyongyu
a1eb87e280
chainio: add helper methods to dispatch beats
This commit adds two methods to handle dispatching beats. These are
exported methods so other systems can send beats to their managed
subinstances.
2024-12-20 17:54:01 +08:00
yyforyongyu
01ac713aec
chainio: implement Blockbeat
In this commit, a minimal implementation of `Blockbeat` is added to
synchronize block heights, which will be used in `ChainArb`, `Sweeper`,
and `TxPublisher` so blocks are processed sequentially among them.
2024-12-20 17:54:00 +08:00
yyforyongyu
060ff013c1
chainio: introduce chainio to handle block synchronization
This commit inits the package `chainio` and defines the interface
`Blockbeat` and `Consumer`. The `Consumer` must be implemented by other
subsystems if it requires block epoch subscription.
2024-12-20 17:54:00 +08:00
yyforyongyu
30ee450091
sweep: make sure nil tx is handled
After previous commit, it should be clear that the tx may be failed to
created in a `TxFailed` event. We now make sure to catch it to avoid
panic.
2024-12-20 17:54:00 +08:00
yyforyongyu
78ce757e7b
sweep: break initialBroadcast into two steps
With the combination of the following commit we can have a more granular
control over the bump result when handling it in the sweeper.
2024-12-20 17:54:00 +08:00
yyforyongyu
f0c4e6dba1
sweep: remove redundant loopvar assign 2024-12-20 17:54:00 +08:00
yyforyongyu
7545bbfa92
sweep: make sure defaultDeadline is derived from the mature height 2024-12-20 17:54:00 +08:00
yyforyongyu
afc08c6623
sweep: add method isMature on SweeperInput
Also updated `handlePendingSweepsReq` to skip immature inputs so the
returned results are the same as those in pre-0.18.0.
2024-12-20 17:53:59 +08:00
yyforyongyu
ba238962d6
sweep: add method handleBumpEventError and fix markInputFailed
Previously in `markInputFailed`, we'd remove all inputs under the same
group via `removeExclusiveGroup`. This is wrong as when the current
sweep fails for this input, it shouldn't affect other inputs.
2024-12-20 17:53:59 +08:00
yyforyongyu
719ca5b229
sweep: remove redundant error from Broadcast 2024-12-20 17:53:59 +08:00
yyforyongyu
77ff2c0585
sweep: add handleInitialBroadcast to handle initial broadcast
This commit adds a new method `handleInitialBroadcast` to handle the
initial broadcast. Previously we'd broadcast immediately inside
`Broadcast`, which soon will not work after the `blockbeat` is
implemented as the action to publish is now always triggered by a new
block. Meanwhile, we still keep the option to bypass the block trigger
so users can broadcast immediately by setting `Immediate` to true.
2024-12-20 17:53:59 +08:00
yyforyongyu
2479dc7f2e
sweep: handle inputs locally instead of relying on the tx
This commit changes how inputs are handled upon receiving a bump result.
Previously the inputs are taken from the `BumpResult.Tx`, which is now
instead being handled locally as we will remember the input set when
sending the bump request, and handle this input set when a result is
received.
2024-12-20 17:53:59 +08:00
yyforyongyu
d0c7fd8aac
sweep: add new interface method Immediate
This prepares the following commit where we now let the fee bumpr
decides whether to broadcast immediately or not.
2024-12-20 17:53:59 +08:00
yyforyongyu
5f64280df4
sweep: add new error ErrZeroFeeRateDelta 2024-12-20 17:53:59 +08:00
yyforyongyu
6c2e8b9a00
sweep: add new state TxFatal for erroneous sweepings
Also updated the loggings. This new state will be used in the following
commit.
2024-12-20 17:53:58 +08:00
Oliver Gugger
1dfb5a0c20
Merge pull request #9382 from guggero/linter-update
lint: deprecate old linters, use new ref commit
2024-12-20 10:51:08 +01:00
Oliver Gugger
f3ddf4d8ea
.golangci.yml: speed up linter by updating start commit
With this we allow the linter to only look at recent changes, since
everything between that old commit and this most recent one has been
linted correctly anyway.
2024-12-20 10:02:24 +01:00
Oliver Gugger
ad29096aa1
.golangci.yml: turn off deprecated linters
All these linters produced a deprecation warning. They've all been
replaced by new linters, so we can safely turn them off.
2024-12-20 10:01:42 +01:00
Oliver Gugger
03eab4db64
Merge pull request #9377 from alingse/fix-nilnesserr
fix check node1Err !=nil but return a nil value error err
2024-12-20 09:26:38 +01:00
yyforyongyu
1167181d06
workflows: fix no space left error 2024-12-20 14:00:30 +08:00
Oliver Gugger
fd4531c751
Merge pull request #9379 from ziggie1984/release-doc-fix
Correct release notes for 18.4 and 19.0
2024-12-19 20:14:16 +01:00
ziggie
90db546b64
docs: update release-notes
Correct 18.4 release-notes for a change which will only be part
of 19.0.
2024-12-19 18:58:13 +01:00
alingse
a79fd08294 fix check node1Err !=nil but return a nil value error err
Signed-off-by: alingse <alingse@foxmail.com>
2024-12-19 14:57:28 +00:00
Oliver Gugger
2d629174e7
Merge pull request #9376 from yyforyongyu/remove-replace
gomod: remove replaces of `kvdb` and `sqldb`
2024-12-19 13:27:27 +01:00
yyforyongyu
785cef2a96
gomod: remove replace of sqldb and kvdb 2024-12-19 19:02:46 +08:00
Oliver Gugger
29a37d34d1
Merge pull request #9242 from aakselrod/reapply-8644
Reapply #8644
2024-12-19 08:31:59 +01:00
Oliver Gugger
7544a2b232
Merge pull request #9319 from thirdkeyword/master
chore: fix some problematic method name in comment
2024-12-19 08:30:39 +01:00
Oliver Gugger
dac4c1f2d3
Merge pull request #9374 from Crypt-iQ/fn_set_copy
fn: add set copy method Copy
2024-12-19 08:29:02 +01:00
thirdkeyword
4c844bfb77 chore: fix some problematic method name in comment
Signed-off-by: thirdkeyword <fliterdashen@gmail.com>
2024-12-19 13:57:07 +08:00
Eugene Siegel
6fb8269c84 fn: add set copy method Copy 2024-12-18 15:17:23 -05:00
Alex Akselrod
c9d217b144
docs: update release-notes for 0.19.0 2024-12-18 10:13:21 -08:00
Olaoluwa Osuntokun
8ecef03315
Merge pull request #9303 from yyforyongyu/fix-circuit-closed
htlcswitch+routing: handle nil pointer dereference properly
2024-12-17 12:19:32 +01:00
Oliver Gugger
729cd22bf6
Merge pull request #8914 from z017/master
fn: add synchronous write file
2024-12-16 18:39:52 +01:00
z017
0652cbd4a1
fn: add synchronous write file
The fn.WriteFile writes data like os.WriteFile but in sync mode.
Also adds a behaviour flag that enables removal of file on error.

Co-authored-by: Maurice Poirrier Chuden <mauricepoirrier@gmail.com>
Co-authored-by: Greg Weber <1183+gregwebs@users.noreply.github.com>
2024-12-16 11:16:00 -03:00
Oliver Gugger
522bb82755
Merge pull request #9357 from GeorgeTsagk/onchain-htlc-replay-wire-records
contractcourt: include custom records on replayed htlc
2024-12-16 12:50:04 +01:00
George Tsagkarelis
231dd6b464
contractcourt: include custom records on replayed htlc
When notifying the invoice registry for an exit hop htlc we also want to
include its custom records. The channelLink, the other caller of this
method, already populates this field. So we make sure the contest
resolver does so too.
2024-12-16 10:29:27 +01:00
yyforyongyu
7882e1ccff
lnwallet: add debug logs 2024-12-16 16:07:22 +08:00
yyforyongyu
39584be7e1
routing: fix nil pointer dereference in exitWithErr
In a rare case when the critical log is triggered when using postgres as
db backend, the `payment` could be nil cause the server is shutting
down, causing the payment fetching to return an error. We now cache its
state before fetching it from the db.
2024-12-16 16:07:22 +08:00
yyforyongyu
944f16255a
htlcswitch: handle nil circuit properly when settling
We have two sources which can call `handlePacketSettle`, either through
the link's `<-s.htlcPlex`, or the `<-s.resolutionMsgs`, which means the
`closeCircuit` could be call twice. Previously we already caught this
case inside `closeCircuit`, in that we would return a nil circuit upon
seeing `ErrUnknownCircuit`, indicating the circuit was removed. However,
we still need to account the case when the circuit is the process of
being closed, which is now fixed as we will ignore when seeing
`ErrCircuitClosing`.
2024-12-16 15:58:23 +08:00
Oliver Gugger
bb9c680a48
Merge pull request #9352 from guggero/update-roasbeef-key
scripts: update Roasbeef's key ID in verify-install.sh
2024-12-13 11:05:39 +01:00
Oliver Gugger
ff14847f30
Merge pull request #9343 from ellemouton/contextGuard
fn: expand the ContextGuard and add tests
2024-12-13 10:25:57 +01:00
Elle Mouton
f99cabf7b4
fn: rework the ContextGuard and add tests
In this commit, the ContextGuard struct is re-worked such that the
context that its new main WithCtx method provides is cancelled in sync
with a parent context being cancelled or with it's quit channel being
cancelled. Tests are added to assert the behaviour. In order for the
close of the quit channel to be consistent with the cancelling of the
derived context, the quit channel _must_ be contained internal to the
ContextGuard so that callers are only able to close the channel via the
exposed Quit method which will then take care to first cancel any
derived context that depend on the quit channel before returning.
2024-12-13 10:44:03 +02:00
Elle Mouton
c2165c4ede
fn: add Guard test helper
Copied from lightninglabs/loop
2024-12-13 10:44:03 +02:00
Alex Akselrod
53d34d2c60
github workflow: save postgres log to zip file 2024-12-12 10:40:40 -08:00
Alex Akselrod
c5d0976375
Makefile: log to file instead of console 2024-12-12 10:40:39 -08:00
Alex Akselrod
68e9aea40d
Makefile: tune params for db-instance for postgres itests 2024-12-12 10:40:38 -08:00
Alex Akselrod
780c271b80
sqldb: improve serialization error handling 2024-12-12 10:40:37 -08:00
Alex Akselrod
c01dcc2be7
graph/db: handle previously-unhandled errors 2024-12-12 10:40:36 -08:00
Alex Akselrod
c29fb81d1b
batch: handle serialization errors correctly 2024-12-12 10:40:36 -08:00
Alex Akselrod
211dd21082
itest: fix flake in multi-hop payments
To make this itest work reliably with multiple parallel SQL
transactions, we need to count both the settle and final HTLC
events. Otherwise, sometimes the final events from earlier
forwards are counted before the forward events from later
forwards, causing a miscount of the settle events. If we
expect both the settle and final event for each forward,
we don't miscount.
2024-12-12 10:40:35 -08:00
Alex Akselrod
a0e5a14574
log: add sub-logger for kvdb/sqlbase 2024-12-12 10:40:34 -08:00
Alex Akselrod
f3bb7095b2
Reapply "kvdb/postgres: remove global application level lock"
This reverts commit 67419a7c0c.
2024-12-12 10:40:33 -08:00
Alex Akselrod
3cebd370de
go.mod: use local kvdb to reapply removal of global postgres lock 2024-12-12 10:40:33 -08:00
Alex Akselrod
bf16b54f87
go.mod: update btcwallet to latest to eliminate waddrmgr deadlock 2024-12-12 10:40:32 -08:00
Oliver Gugger
6298f76aca
Merge pull request #9309 from yyforyongyu/fix-unit-test
chainntnfs: fix `TestHistoricalConfDetailsTxIndex`
2024-12-12 18:01:54 +01:00
Oliver Gugger
55b685dbd7
Merge pull request #9347 from lightningnetwork/cross-compile-fix
build/ci: attempt to fix failing cross compile step
2024-12-12 17:47:50 +01:00
Olaoluwa Osuntokun
78cbed985f
contractcourt: add rapid derived fuzz test for HtlcAuxBlob
In this commit, we add a rapid derived fuzz test for the HtlcAuxBlob
test. This uses the rapid (randomized property testing) into Go's built
in fuzzer. This wrapper will use the fuzz stream, and pass that into
rapid where the stream is used to make structured test inputs which are
tested against the existing properties.

This can be done more widely in the codebase, we pick a simple example
to port first before tackling others.
2024-12-12 16:57:58 +01:00
Oliver Gugger
fdb43c0b7b
Merge pull request #9348 from ziggie1984/update-goveralls
github: update goveralls tool
2024-12-12 12:36:13 +01:00
ziggie
c6bdbbe222
github: update goveralls tool
The goverall tool had a bug regarding the module versioning of
golang packages see also
https://github.com/mattn/goveralls/pull/222 for more background.
Goveralls is wrapped by another library to make it available for
github actions. So the relevant PR which is referenced here in
LND is:
https://github.com/shogo82148/actions-goveralls/pull/521.
2024-12-12 10:38:01 +01:00
Oliver Gugger
c022284f37
GitHub+make: run cross compile in matrix
This is just a test, perhaps this doesn't make any sense at all as it
will clog up too many build runners.
2024-12-12 10:09:30 +01:00
Oliver Gugger
66c3a46e4c
GitHub: allow not caching the Golang build cache
This is a tradeoff of disk space (and with that cache size) and
compilation speed. Because we're still running into disk full errors
with the full build cache, we remove it for the cross compile step.
Which means we'll do more work each time.
2024-12-12 10:09:30 +01:00
Oliver Gugger
e68b2ad015
Merge pull request #9341 from ellemouton/fnContext
fn: Remove ctx from GoroutineManager constructor
2024-12-12 10:03:01 +01:00
Oliver Gugger
4ee36e3762
scripts: update Roasbeef's key ID in verify-install.sh
This was overlooked in #9206.
We're updating to the parent key ID
A5B61896952D9FDA83BC054CDC42612E89237182.

cat scripts/keys/roasbeef.asc| gpg --with-colons --import-options show-only --import                                                                   ─╯
pub:-:255:22:DC42612E89237182:1729552568:::-:::scESCA:::::ed25519:::0:
fpr:::::::::A5B61896952D9FDA83BC054CDC42612E89237182:
uid:-::::1729552794::8D805FB762825ECBDA00EB0AF5FEDF76DE288546::Olaoluwa Osuntokun <laolu32@gmail.com>::::::::::0:
sub:-:255:18:19E88A946BEF6D65:1729552568:1761088568:::::e:::::cv25519::
fpr:::::::::70206BBE2C0A81EC381A212C19E88A946BEF6D65:
sub:-:255:22:966072F65DF1D016:1729552672:1761088672::::🅰️::::ed25519::
fpr:::::::::98E3632DC026406855001E22966072F65DF1D016:
sub:-:255:22:90525F7DEEE0AD86:1729552758:1761088758:::::s:::::ed25519::
fpr:::::::::296212681AADF05656A2CDEE90525F7DEEE0AD86:
2024-12-12 09:50:25 +01:00
Elle Mouton
37bb082f20
docs: add release notes entry 2024-12-12 09:32:57 +02:00
Elle Mouton
51eeb9ece3
fn: Remove ctx from GoroutineManager constructor 2024-12-12 09:32:57 +02:00
Olaoluwa Osuntokun
d6eeaec246
Merge pull request #8512 from lightningnetwork/rbf-coop-fsm
[3/4] - lnwallet/chancloser: add new protofsm based RBF chan closer
2024-12-11 00:57:46 +01:00
Olaoluwa Osuntokun
3c5f96de7c
lnwallet/chancloser: enable custom payer for rbf coop close
In this commit, we enable a custom payer for the rbf coop close. This
allows us to ensure that the party that started one side of the close
flow pays the fees.
2024-12-10 23:07:03 +01:00
Olaoluwa Osuntokun
d38c5e6222
lnwallet: update core coop close logic with custom payer
In this commit, we update the core coop close logic with the new custom
payer param. We also expand the existing unit tests to ensure that the
fee is deducted from the proper party.
2024-12-10 23:07:02 +01:00
Olaoluwa Osuntokun
d1b2bff2c8
lnwallet: update CoopCloseBalance to allow a paying party
This preps us for an upcoming change to the rbf coop state machine where
either party can pay for the channel fees. We also add a new test to
make sure the new function adheres to some key properties.
2024-12-10 23:07:02 +01:00
Olaoluwa Osuntokun
b8cf5ae98f
lnwallet: for rbf coop close, log the close tx 2024-12-10 23:07:02 +01:00
Olaoluwa Osuntokun
b94ce6fa08
lnwallet: use custom LockTime for rbf coop close 2024-12-10 23:07:02 +01:00
Olaoluwa Osuntokun
ab4297e127
lnwallet/chancloser: use block height as lock time for rbf-coop 2024-12-10 23:07:02 +01:00
Olaoluwa Osuntokun
540d3c0fc7
multi: switch to lock time from sequence for coop close v2 2024-12-10 23:07:01 +01:00
Olaoluwa Osuntokun
2decea86d8
lnwallet/chancloser: add unit tests for new rbf coop close 2024-12-10 23:07:01 +01:00
Olaoluwa Osuntokun
6b90254fd3
lnwallet/chancloser: create a MsgMapper for the protofsm rbf close
This'll allow us to treat the state machine as a MsgEndpoint, and have
the readHandler in the peer automatically send new messages to it.
2024-12-10 23:07:01 +01:00
Olaoluwa Osuntokun
e47a632745
lnwallet/chancloser: add state transitions for new protofsm rbf closer
In this commit, we add the state transitions for the new protofsm based
RBF chan closer. The underlying protocol is a new asymmetric co-op close
process, wherein either side can initiate a chan closer, and use their
settled funds to pay for fees within the channel.
2024-12-10 23:07:01 +01:00
Olaoluwa Osuntokun
f6525c9e7d
lnwallet: add ability to specify custom sequence for co-op close tx
In this commit, we add the ability to specify a custom sequence for a
co-op close tx. This'll come in handy later as the new co-op close
process allows a party to set a custom sequence.
2024-12-10 23:07:01 +01:00
Olaoluwa Osuntokun
62a3db1cc2
lnwallet/chancloser: add states for new protofsm rbf closer
In this commit, we add the initial set of states for the new protofsm
based rbf chan closer. A diagram outlining the new states and their
transitions can be found here:
https://gist.github.com/Roasbeef/acc4ff51b9dff127230228a05553cdfe.

Unlike the existing co-op close process, this co-op close can be
restarted at anytime if either side sends a shutdown message. From
there, we'll each obtain a new RBF'd version that can be re-broadcasted.

This commit creates the set of states, along with the environment that
our state machine will use to drive itself forward.
2024-12-10 23:07:01 +01:00
Olaoluwa Osuntokun
4791fc6082
protofsm: eliminate outer option layer in EmmittedEvent
We'll have the empty slice tuple represent the None case instead.
2024-12-10 23:06:59 +01:00
Oliver Gugger
7a3401555c
Merge pull request #9316 from ziggie1984/fix-blindedpath-mc
routing: fix mc blinded path behaviour.
2024-12-10 20:22:15 +01:00
ziggie
101311debb
docs: fix typos in release-notes 19.0 2024-12-10 19:05:49 +01:00
ziggie
2610663658
docs: add release-notes for 18.4 2024-12-10 19:05:49 +01:00
ziggie
decfdb68ac
routing: bugfix for mc reporting of blinded paths
When reporting an error  or a success case of a payment to a
blinded paths, the amounts to forward for intermediate hops
are set to 0 so we need to use the receiver amount instead.
2024-12-10 19:05:49 +01:00
ziggie
9327940ac4
routing: add pathfinding test
We add a test where we add duplicate hops in a route and verify
that the pathfinding engine can handle this edge case.
2024-12-10 19:05:49 +01:00
ziggie
3cec72ae9c
routing: improve lasthoppaylaod size calculation
Fixes a bug and makes the function more robust. Before
we would always return the encrypted data size of last hop
of the last path. Now we return the greatest last hop payload
not always the one of the last path.
2024-12-10 19:05:49 +01:00
ziggie
e47024b790
routing: Use NUMS point for blinded paths
To be able to do MPP payment to multiple blinded routes we need
to add a constant dummy hop as a final hop to every blined path.
This is used when sending or querying a blinded path, to let the
pathfinder be able to send MPP payments over different blinded
routes. For this dummy final hop we use a NUMS key so that we
are sure no other node can use this blinded pubkey either in a
normal or blinded route.
Moreover this helps us handling the mission control data for
blinded paths correctly because we always consider the blinded
pubkey pairs which are registered with mission control when
a payment to a blinded path fails.
2024-12-10 19:05:49 +01:00
Oliver Gugger
8c9de4d605
Merge pull request #9345 from ziggie1984/add-copy-method
Add a deep copy generic harness to the internal fn package
2024-12-10 17:25:46 +01:00
ziggie
e8ee087cbc
fn: add deep copy interface. 2024-12-10 14:10:33 +01:00
yyforyongyu
5867f0040f
gomod: update btcd for shutdown fix 2024-12-10 19:56:03 +08:00
yyforyongyu
f03e242515
chainntnfs: ensure previous test succeeded before running 2024-12-10 19:56:01 +08:00
ziggie
df30b481c6
input: export NUMS key parser. 2024-12-09 23:08:50 +01:00
Olaoluwa Osuntokun
fb429d658b
Merge pull request #9330 from ProofOfKeags/update/fn2
multi: update to fn v2
2024-12-09 12:56:23 +01:00
Oliver Gugger
c9ae63a5c8
Merge pull request #9338 from yyforyongyu/fix-invoice-htlcs-order
lnrpc: sort `Invoice.HTLCs` based on `HtlcIndex`
2024-12-09 08:52:54 +01:00
yyforyongyu
7374392abe
lnrpc: sort Invoice.HTLCs based on HtlcIndex
So the returned HTLCs are ordered.
2024-12-07 09:54:46 +08:00
Oliver Gugger
5659c0184d
Merge pull request #9337 from Guayaba221/patch-1
chore: fix typo in ruby.md
2024-12-06 14:10:51 +01:00
planetBoy
ac59b06f59
Update ruby.md 2024-12-06 13:48:22 +01:00
Olaoluwa Osuntokun
366e48bced
Merge pull request #9333 from guggero/aux-traffic-shaper-refactor
[custom channels]: refactor AuxTrafficManager to be used for forwarding as well
2024-12-05 21:04:11 +01:00
Olaoluwa Osuntokun
8d7f0853c7
Merge pull request #9324 from ziggie1984/non-blocking-startup-chan-arbitrator
Don't block on channel arbitator startup
2024-12-05 18:28:20 +01:00
ziggie
0004e31997
docs: add release-notes 2024-12-05 15:11:12 +01:00
ziggie
17bc8827c5
contractcourt: refactor start up of arbitrators
We decouple the state machine of the channel arbitrator from
the start-up process so that we can startup the whole daemon
reliably.
2024-12-05 15:11:12 +01:00
Oliver Gugger
86b3be71fe
multi: thread through and use AuxTrafficShaper 2024-12-05 12:39:34 +01:00
Oliver Gugger
a2e78c3984
htlcswitch: thread through packet's inbound wire records
For calculating the available auxiliary bandwidth of a channel, we need
access to the inbound custom wire records of the HTLC packet, which
might contain auxiliary information about the worth of the HTLC packet
apart from the BTC value being transported.
2024-12-05 12:39:33 +01:00
Oliver Gugger
117c6bc781
multi: move routing.TlvTrafficShaper => htlcswitch.AuxTrafficShaper
With this commit we move the traffic shaper definition from the routing
package to the HTLC switch package as a preparation for being able to
use it there as well.
At the same time we rename it to AuxTrafficShaper to be more in line
with the other auxiliary components.
2024-12-05 12:39:33 +01:00
Keagan McClelland
ed2989ae33
multi: update to fn v2 2024-12-04 13:19:00 -07:00
Oliver Gugger
fa309c9a0e
Merge pull request #9329 from gijswijs/docs-skip-ci
clarify skipping the CI checks in the docs
2024-12-04 16:02:02 +01:00
Oliver Gugger
e30e43e644
Merge pull request #9331 from yyforyongyu/fix-lll
multi: rename `lll` to `ll` and remove unused `nolint`
2024-12-04 08:48:21 +01:00
yyforyongyu
d108e14c5d
multi: rename lll to ll and remove unused nolint 2024-12-04 07:20:59 +08:00
Oliver Gugger
48fba10562
Merge pull request #9257 from starius/estimatefeerate-regtest2
chainreg: use feerate estimator in regtest and simnet
2024-12-03 18:56:50 +01:00
Gijs van Dam
42a1d5e22b
docs: clarify skipping the CI checks
[skip ci]
2024-12-03 18:16:13 +01:00
Olaoluwa Osuntokun
ff2a1a4bbb
Merge pull request #9318 from guggero/cross-compile-fix
make: remove exotic build targets from release list
2024-12-03 04:47:03 -06:00
Oliver Gugger
f4d5d4ed8a
Merge pull request #9305 from ProofOfKeags/update/tlv-fn2
tlv: update to fn v2
2024-12-03 09:12:14 +01:00
Oliver Gugger
0c9b65578f
Merge pull request #9328 from guggero/golang-min-version
docker+docs: update documentation around Golang minimum version
2024-12-02 11:22:57 +01:00
Oliver Gugger
0474b4ff20
Merge pull request #9314 from ellemouton/slog1
build+lnd+docs: start using slog and add commit_hash to log lines
2024-12-02 09:53:31 +01:00
Oliver Gugger
2fdfa26268
docs: update Golang minimum version in docs/INSTALL.md 2024-12-02 09:49:25 +01:00
Oliver Gugger
7692ced5f5
multi: update comment on changing Go version
Since we now have a scripted check that makes sure the Golang version is
synced throughout all files, it is important that the main version in
the Makefile is changed, then all other versions can be detected by that
script.
2024-12-02 09:48:26 +01:00
Elle Mouton
afbb9aa41c
docs: add release note entry 2024-12-02 09:23:07 +02:00
Elle Mouton
8862ce99d5
lnd: use errors.Is for error comparison 2024-12-02 09:23:07 +02:00
Elle Mouton
ed4279b6f3
lnd: update Main logs to use structured logging 2024-12-02 09:23:07 +02:00
Elle Mouton
32fbea2f85
lnd: use structured logs in mkErr helper 2024-12-02 09:23:06 +02:00
Elle Mouton
755ad49440
build: Append commit hash to the main LND context
It can be disabled via the new `logging.no-commit-hash` config option.
2024-12-02 09:23:06 +02:00
Elle Mouton
efe08b836e
docs: add structured logging docs in code formatting rules doc 2024-12-02 09:23:06 +02:00
Elle Mouton
a04f8dfd4f
lnd: pass ctx to rest proxy 2024-12-02 09:23:06 +02:00
Elle Mouton
c5d849e044
.golangci: Replace lll with custom ll 2024-12-02 09:23:06 +02:00
Elle Mouton
ab7aae0708
multi: rename nolint:lll to nolint:ll
Find and replace all nolint instances refering to the `lll` linter and
replace with `ll` which is the name of our custom version of the `lll`
linter which can be used to ignore log lines during linting.

The next commit will do the configuration of the custom linter and
disable the default one.
2024-12-02 09:14:21 +02:00
Elle Mouton
88f5369066
tools+.: add custom linter configuration file
Add this file both to the main LND directory so that devs can use it for
local linter runs and also add it to the `tools` directory so that the
docker environment used to run the linter in CI has access to it. A
custom linter binary can be built via `golangci-lint custom`. This will
pull in and register all the plugins listed in the new config file when
building the new binary. The new binary can then be run using
`custom-gcl run`.
2024-12-02 09:14:21 +02:00
Elle Mouton
bd55b2795b
tools/linters: ignore log lines
This commit adds a feature to our custom lll linter which will ignore
log lines (both single and multi-lined log lines) for the lll linter.
2024-12-02 09:14:21 +02:00
Oliver Gugger
f6aff58a6d
Merge pull request #9322 from hieblmi/fix-estimate-route-fee
cmd: fix default timeout for estimateroutefee
2024-11-29 11:21:25 +01:00
Slyghtning
440cdb3172
docs: update release notes 2024-11-29 10:23:10 +01:00
Slyghtning
85d89a5b41
cmd: fix default timeout for estimateroutefee 2024-11-29 10:20:03 +01:00
Elle Mouton
9637a8132e
tools/linters: convert to LinterPlugin implementation
This commit introduces the `LLPlugin` type and converts the existing lll
code such that the LLPlugin implements the register.LinterPlugin
interface. This will allow us to plug it into golangci-linter as a
plugin.
2024-11-29 11:03:27 +02:00
Elle Mouton
acbcb771af
tools/linters: Add copied golangci-lint lll linter code
In this commit, we copy the implementation of the golangci-lint lll
linter which we currently use in our CI flow during the `make lint`
check. This commit copies the code mostly as is (the only exception
being the line which trims white space before checking if a line starts
with "import"), and formats it to fit our codebase guidelines. A test is
also added so that we can be sure that the following commits which
adjust the implementation have the intended results.

The custom linter is put into its own module as this is requied by the
`golangci-lint custom` when building the custom linter binary which
includes the plugin.
2024-11-29 10:44:22 +02:00
Oliver Gugger
f2d9868906
Merge pull request #9287 from Abhinav-Bansal751/master
Update docs/grpc/python.md and ruby.md
2024-11-29 09:30:07 +01:00
Oliver Gugger
506586a37e
Merge pull request #9236 from ellemouton/moveGraphDBCode
[1/3] Graph RIP: refactor+graph: move all graph related DB code to the graph package
2024-11-28 13:47:16 +01:00
Elle Mouton
54dbaa60c9
docs: add release notes entry for 9236 2024-11-28 13:51:15 +02:00
Elle Mouton
439a6c7d6c
multi: rename chan DB Open method to OpenForTesting 2024-11-28 13:51:15 +02:00
Elle Mouton
4089fbcb44
multi: fix linter errors 2024-11-28 13:51:15 +02:00
Elle Mouton
fd2ea411be
itest: assert no failed updates in test
So that this fails earlier on if the actual call to UpdateChannelPolicy
fails.
2024-11-28 13:51:14 +02:00
Elle Mouton
adcaa8802f
multi: remove kvdb.Tx from ChannelGraphSource.ForAllOutgoingChannels
and the same for ChannelStateDB.FetchChannel. Most of the calls to these
methods provide a `nil` Tx anyways. The only place that currently
provides a non-nil tx is in the `localchans.Manager`. It takes the
transaction provided to the `ForAllOutgoingChannels` callback and passes
it to it's `updateEdge` method. Note, however, that the
`ForAllOutgoingChannels` call is a call to the graph db and the call to
`updateEdge` is a call to the `ChannelStateDB`. There is no reason that
these two calls need to happen under the same transaction as they are
reading from two completely disjoint databases. And so in the effort to
completely split untangle the relationship between the two databases, we
now dont use the same transaction for these two calls.
2024-11-28 13:49:41 +02:00
Elle Mouton
6e13898981
multi: move LightningNode struct to models package 2024-11-28 13:36:32 +02:00
Elle Mouton
ccb8f0eeb8
refactor: move graphsession pkg to graph package 2024-11-28 13:36:15 +02:00
Elle Mouton
b86980ec40
channeldb: remove graph db from channeldb
Now that the channel.DB no longer uses the ChannelGraph pointer, we can
completely remove it as a member of channeldb.DB.
2024-11-28 13:36:15 +02:00
Elle Mouton
2c083bc017
multi: let chan and graph db implement AddrSource
Then use both to construct a multiAddrSource AddrSource and use that
around the code-base.
2024-11-28 13:36:15 +02:00
Elle Mouton
51c2f709e1
channeldb: let AddrsForNode indicate if the node was found or not
Before this commit, the `(channeldb.DB).AddrsForNode` method treats the
results from the channel db and the graph db slightly differently. It
errors out if the channel db is unaware of the node in question but does
not error out if the graph is unaware of the node. So this commit
changes the logic slightly so that a "node-unknown" error from either
backing source is not seen as an error.
2024-11-28 13:36:15 +02:00
Elle Mouton
9537026df9
channeldb: implement a multi-source AddrSource
In this commit, we implement a version of the AddrSource interface which
merges the results of a set of AddrSource implementations. This will
later be used to merge the results of the channel db and graph db.
2024-11-28 13:36:15 +02:00
Elle Mouton
083d3c9d7c
channeldb: define a single AddrSource interface
Our aim is to completely remove the `channeldb.DB`'s direct dependence
on the `graphdb.ChannelGraph` pointer. The only place where it still
depends on this pointer is in the `(DB).AddrsForNode(..)` method where
it queries both the channel DB and the graph db for the known addresses
for the node in question and then combines the results. So, to separate
these out, we will define an AddrsForNodes interface in this commit
which we will later let both the ChannelGraph and channeldb.DB both
implement and we will merge these results outside of the channeldb
package.

All this commit does is to unify the `AddrSource` interface since this
has been defined separately in a couple of places.
2024-11-28 13:36:15 +02:00
Elle Mouton
1859993734
channeldb+graphdb: init graph DB outside of channel db
We also now use the graph DB's own optional functions. An instance of
the graph is currently still passed to the channeldb's
`CreateWithBackend` function. This will be removed in a later commit
once the two have been completely disjoint.
2024-11-28 13:36:15 +02:00
Elle Mouton
74a4b1922b
refactor: move graph related DB code to graph/db from channeldb
This is a pure refactor commit. It moves over all the graph related CRUD
code from `channeldb` to `graph/db`.
2024-11-28 13:36:13 +02:00
Elle Mouton
9f54ec90aa
multi+refactor: move models package to graph/db
All the structs defined in the `channeldb/models` package are graph
related. So once we move all the graph CRUD code to the graph package,
it makes sense to have the schema structs there too. So this just moves
the `models` package over to `graph/db/models`.
2024-11-28 13:34:33 +02:00
Elle Mouton
b707fd55b2
contractcourt: use graphdb outpoint helpers
Start using the single set of exported write/read functions for
wire.Outpoint.
2024-11-28 13:34:08 +02:00
Elle Mouton
382539a6eb
channeldb/graphdb: move outpoint ser/deser funcs to graphdb
We have the same helpers for writing and reading a wire.Outpoint type
defined separately in a couple places. We will want to use these from
the graph db package soon though so instead of defining them again
there, this commit unifies things and creates a single exported set of
helpers. The next commit will make use of these.
2024-11-28 13:34:08 +02:00
Elle Mouton
3365461500
channeldb+graph/db: move net.Addr encode/decode to graph db package
In preparation for moving the graph related schema structs to the graph
package in an upcoming commit, we move these methods to the graph
package. The structs we will move make use of these methods but we still
import them from channeldb so as to keep the ReadElement and
WriteElement helpers working as they do today.
2024-11-28 13:34:08 +02:00
Elle Mouton
1e81d83f78
channeldb: export net.Addr encode/decode methods
We'll move these to the new graphdb package later and import them from
there.
2024-11-28 13:34:08 +02:00
Oliver Gugger
4681b52c5a
make: remove exotic build targets from release list
With this commit we remove build targets from our list of release build
OS/architecture pairs to reduce the overhead of building for them with
every pull request and every release.

According to https://tooomm.github.io/github-release-stats/?username=lightningnetwork&repository=lnd
those targets are rarely used, compared to the most popular ones:

| Release File                          | Absolute DL | Relative DL |
|---------------------------------------|-------------|-------------|
| lnd-darwin-amd64-v0.18.3-beta.tar.gz  | 1,930       | 72          |
| lnd-darwin-arm64-v0.18.3-beta.tar.gz  | 2,020       | 162         |
| lnd-freebsd-386-v0.18.3-beta.tar.gz   | 1,864       | 6           |
| lnd-freebsd-amd64-v0.18.3-beta.tar.gz | 1,874       | 16          |
| lnd-freebsd-arm-v0.18.3-beta.tar.gz   | 1,865       | 7           |
| lnd-linux-386-v0.18.3-beta.tar.gz     | 1,873       | 15          |
| lnd-linux-amd64-v0.18.3-beta.tar.gz   | 3,355       | 1,497       |
| lnd-linux-arm64-v0.18.3-beta.tar.gz   | 3,053       | 1,195       |
| lnd-linux-armv6-v0.18.3-beta.tar.gz   | 1,868       | 10          |
| lnd-linux-armv7-v0.18.3-beta.tar.gz   | 1,893       | 35          |
| lnd-linux-mips-v0.18.3-beta.tar.gz    | 1,859       | 1           |
| lnd-linux-mips64-v0.18.3-beta.tar.gz  | 1,860       | 2           |
| lnd-linux-mipsle-v0.18.3-beta.tar.gz  | 1,859       | 1           |
| lnd-linux-ppc64-v0.18.3-beta.tar.gz   | 1,858       | 0           |
| lnd-linux-ppc64le-v0.18.3-beta.tar.gz | 1,859       | 1           |
| lnd-linux-s390x-v0.18.3-beta.tar.gz   | 1,859       | 1           |
| lnd-netbsd-amd64-v0.18.3-beta.tar.gz  | 1,859       | 1           |
| lnd-openbsd-amd64-v0.18.3-beta.tar.gz | 1,861       | 3           |
| lnd-source-v0.18.3-beta.tar.gz        | 1,863       | 5           |
| lnd-windows-386-v0.18.3-beta.zip      | 1,895       | 37          |
| lnd-windows-amd64-v0.18.3-beta.zip    | 1,997       | 139         |
| lnd-windows-arm-v0.18.3-beta.zip      | 1,868       | 10          |
2024-11-28 09:42:05 +01:00
Oliver Gugger
1ebdefb943
Merge pull request #9311 from Roasbeef/protofsm-race-fix
protofsm: fix race in state machine executor tests
2024-11-28 09:28:04 +01:00
Boris Nagaev
23b025cff6
docs: add release notes (feerate estimator) 2024-11-27 10:30:52 -03:00
Boris Nagaev
b89387a6da
chainreg: use feerate estimator in regtest, simnet
If feeurl is not provided and LND is running in bitcoind or btcd mode, use
fee estimator provided by bitcoind or btcd instead of using static fee estimator
(which always returns 50 sats/vbyte).

This enables simulating high feerate environment in regtest and simnet not only
via feeurl, but also by filling mempool with high feerate transactions, which is
closer to what happens in mainnet.

Also skip creating bitcoind or btcd fee estimator, if feeurl is provided. Before
LND logged both "Initializing bitcoind backed fee estimator" and "Using external
fee estimator" if running in bitcoind mode with feeurl provided, but actually
feeurl was used, so this is not a change of behavior.
2024-11-27 10:24:26 -03:00
Oliver Gugger
ae32c29deb
Merge pull request #9295 from ellemouton/logPerformanceFix
go.mod: bump btclog dep
2024-11-27 11:43:43 +01:00
Elle Mouton
c2923e2214
multi: remove PrefixLog
And instead use the new btclog Logger `WithPrefix` method.
2024-11-27 10:44:32 +02:00
Elle Mouton
b98fc168ec
go.mod+build: update btclog dep 2024-11-27 10:44:01 +02:00
Oliver Gugger
c8cfa59316
Merge pull request #8270 from ProofOfKeags/feature/stfu
DynComms [1/n]: Implement Quiescence Protocol
2024-11-27 09:27:34 +01:00
Olaoluwa Osuntokun
0ff0ac84f6
protofsm: fix race in state machine executor tests
In this commit, we fix an existing race in the new `protofsm` state
machine executor tests.

The race would appear as such:
```
--- FAIL: TestStateMachineMsgMapper (0.00s)
    state_machine_test.go:165:
        Error Trace:/home/runner/work/lnd/lnd/protofsm/state_machine_test.go:165
                    /home/runner/work/lnd/lnd/protofsm/state_machine_test.go:451
        Error:      Object expected to be of type *protofsm.dummyStateStart, but was *protofsm.dummyStateFin
        Test:       TestStateMachineMsgMapper
FAIL
FAILgithub.com/lightningnetwork/lnd/protofsm0.116s
FAIL
```

This race condition was triggered as before we would start the state
machine _then_ register for notifications. In `Start` we emit the
starting event, then enter the main loop. If that event gets emitted
before our subscription, then we'll miss the event, as the terminal
event will be the only one received.

We fix this by registering for the events before the daemon has started.
2024-11-26 18:58:53 -06:00
Keagan McClelland
debc43daf2
docs: add quiescence to release notes 2024-11-26 14:13:44 -07:00
Keagan McClelland
127e4fff28
htlcswitch: add logging to quiescer 2024-11-26 14:13:44 -07:00
Keagan McClelland
ac0c24aa7b
htlcswitch: don't pass pending update counts into quiescer
This change simplifies some of the quiescer responsibilities in
favor of making the link check whether or not it has a clean state
to be able to send or receive an stfu. This change was made on the
basis that the only use the quiescer makes of this information is
to assess that it is or is not zero. Further the difficulty of
checking this condition in the link is barely more burdensome than
selecting the proper information to pass to the quiescer anyway.
2024-11-26 14:13:43 -07:00
Keagan McClelland
a4c49a88f1
htlcswitch: add quiescence timeout that is aborted by Resume 2024-11-26 14:13:43 -07:00
Keagan McClelland
111c9b05f3
htlcswitch+peer: allow the disabling of quiescence
Here we add a flag where we can disable quiescence. This will be used
in the case where the feature is not negotiated with our peer.
2024-11-26 14:13:39 -07:00
Keagan McClelland
48ee643c0d
htlcswitch: implement noop quiescer
In this commit we implement a noop quiescer that we will use when
the feature hasn't been negotiated. This will make it far easier to
manage quiescence operations without having a number of if statements
in the link logic.
2024-11-26 13:52:54 -07:00
Keagan McClelland
5906ca2537
htlcswitch: add test for deferred processing remote adds when quiescent 2024-11-26 13:52:54 -07:00
Keagan McClelland
4fbab45a5f
htlcswitch: defer processRemoteAdds when quiescent
In this commit we defer processRemoteAdds using a new mechanism on
the quiescer where we capture a closure that needs to be run. We
do this because we need to avoid the scenario where we send back
immediate resolutions to the newly added HTLCs when quiescent as
it is a protocol violation. It is not enough for us to simply defer
sending the messages since the purpose of quiescence itself is to
have well-defined and agreed upon channel state. If, for whatever
reason, the node (or connection) is restarted between when these
hooks are captured and when they are ultimately run, they will
be resolved by the resolveFwdPkgs logic when the link comes back
up.

In a future commit we will explicitly call the quiescer's resume
method when it is OK for htlc traffic to commence.
2024-11-26 13:52:53 -07:00
Keagan McClelland
77fd8c6a21
itest+lntest: add itest for Quiesce RPC method 2024-11-26 13:52:51 -07:00
Keagan McClelland
7255b7357c
htlcswitch: implement InitStfu link operation 2024-11-26 13:51:57 -07:00
Keagan McClelland
bca1516429
lnd: finish Quiesce implementation using new link op 2024-11-26 13:51:57 -07:00
Keagan McClelland
70e3804121
htlcswitch: add link operation for initiating quiescence 2024-11-26 13:51:57 -07:00
Keagan McClelland
a085b59814
lnd: implement new Quiesce RPC with link operation stub 2024-11-26 13:51:57 -07:00
Keagan McClelland
99f5ca4018
lnrpc add new RPC 'Quiesce' to protobuf definitions 2024-11-26 13:51:57 -07:00
Keagan McClelland
7a5b55a473
lnwire: signal that we support quiescence 2024-11-26 13:51:56 -07:00
Keagan McClelland
6d30ab6c4f
htlcswitch: drop connection if link updates after stfu 2024-11-26 13:51:56 -07:00
Keagan McClelland
44c87ef1d7
htlcswitch: bounce packets when quiescent 2024-11-26 13:51:56 -07:00
Keagan McClelland
c9debea408
lnwire: add IsChannelUpdate function to distinguish channel updates 2024-11-26 13:51:56 -07:00
Keagan McClelland
2ece1fdc54
htlcswitch: implement stfu response
htlcswitch: use quiescer SendOwedStfu method in link stfu implementation
2024-11-26 13:51:56 -07:00
Keagan McClelland
f5b7866287
htlcswitch: define state machine for quiescence
htlcswitch: add sendOwedStfu method to quiescer
2024-11-26 13:51:46 -07:00
Elle
fbeab726e1
Merge pull request #8390 from carlaKC/7883-experimental-endorsement
Add Experimental Endorsement Signalling
2024-11-26 09:43:22 +02:00
Keagan McClelland
2a1384174b
tlv: update to fn v2 2024-11-25 13:25:52 -07:00
Oliver Gugger
66940024ac
Merge pull request #9290 from w3irdrobot/broadcast-misspelling
docs: fix broadcast misspelling
2024-11-25 12:25:51 +01:00
Oliver Gugger
7e8dc2d389
Merge pull request #9301 from chuangjinglu/master
multi: fix some function names in interface comment
2024-11-25 11:59:01 +01:00
chuangjinglu
bcfd8d5b47 multi: fix some function names in interface comment
Signed-off-by: chuangjinglu <chuangjinglu@outlook.com>
2024-11-25 10:49:00 +08:00
w3irdrobot
d3bff47167
docs: fix broadcast misspelling 2024-11-22 16:20:02 -05:00
Carla Kirk-Cohen
a8c159b1c7
docs: add experimental endorsement 2024-11-22 09:31:47 -05:00
Carla Kirk-Cohen
7a876e8898
itest: add coverage for experimental endorsement 2024-11-22 09:31:46 -05:00
Carla Kirk-Cohen
f02bb58486
multi: add experimental endorsement feature bit and disable option 2024-11-22 09:16:58 -05:00
Carla Kirk-Cohen
4bb5b0c27c
lnrpc: set a zero value endorsement signal on sender outgoing htlc
Before we have sufficient signaling in the network to relay this
signal, set a zero value experimental endorsement value on the sender's
outgoing htlc. Once the network is relaying this signal and a flag day
has been set, we'll be able to set a non-zero value here.
2024-11-22 09:16:57 -05:00
Carla Kirk-Cohen
774bfa740a
htlcswitch: relay experimental endorsement signal with update_add_htlc 2024-11-22 09:16:56 -05:00
Oliver Gugger
94f7ed46de
Merge pull request #8998 from Abdulkbk/trx-pagination
pagination: add pagination to wallet transactions
2024-11-22 09:30:11 +01:00
Oliver Gugger
38ec1a1c64
Merge pull request #8805 from breez/jssdwt-insert-edge-when-not-found
localchans: recreate missing edge if not found
2024-11-22 08:42:51 +01:00
Abdullahi Yunus
1c6790bd9b add release note 2024-11-22 08:23:58 +01:00
Abdullahi Yunus
6ee8d2c9b9 lnwallettest: test for tranasctions pagination
In this commit, we test for different values of index_offset and
max_transactions settings when getting transactions to make sure
the right number of transactions are returned along with the right
first and last indices.
2024-11-22 08:23:58 +01:00
Abdullahi Yunus
762d01536b multi: return txns first and last indices
In this commit we introduce first and last indices for the
tranasctions returned which can be used to seek for further
transactions in a pagination style.

Signed-off-by: Abdullahi Yunus <abdoollahikbk@gmail.com>
2024-11-22 08:22:55 +01:00
Abdullahi Yunus
cd1df4ac34 multi: modify listtxn definition
This commit modifies listtransactiondetails method definition to
take in additional params: index_offset and maxTxn
2024-11-22 08:21:27 +01:00
Oliver Gugger
41c2521e8a
Merge pull request #9291 from yyforyongyu/fix-log-unit
lnwallet: log the amounts in the same unit
2024-11-21 14:46:46 +01:00
yyforyongyu
b243394049
lnwallet: log the amounts in the same unit 2024-11-21 20:42:47 +08:00
Jesse de Wit
266531271b
localchans: do error if an edge policy is missing 2024-11-21 10:57:54 +01:00
Oliver Gugger
9fc6c998ba
Merge pull request #9292 from yyforyongyu/fix-graphbuiler-shutdown
lnd: stop `graphBuilder` during shutdown
2024-11-21 10:33:52 +01:00
Oliver Gugger
8b99d71f93
Merge pull request #9284 from ProofOfKeags/update/fn2
fn: update go.mod to v2
2024-11-21 09:53:05 +01:00
yyforyongyu
57084804d4
lnd: stop graphBuilder during shutdown 2024-11-21 15:37:49 +08:00
Keagan McClelland
a3e87cfebd
fn: update fn/go.mod to v2 2024-11-20 10:28:55 -07:00
Abdullahi Yunus
037db4278a lnrpc: modify gettransaction parameters
This commit adds index_offset and max_transactions to the list
of parameters passed during gettransactions call. index_offset
specify transactions to skip and max_transactions the total
transactions returned
2024-11-20 18:02:00 +01:00
Oliver Gugger
a4195fa2ce
Merge pull request #9288 from lightningnetwork/channel-acceptor-commitment-type
[custom channels]: add taproot overlay channel type to channel acceptor
2024-11-20 15:29:30 +01:00
Oliver Gugger
4b563e6f49
Merge pull request #9253 from ziggie1984/fix-chanArb-deadlock
fix chanArb deadlock
2024-11-20 10:41:03 +01:00
Oliver Gugger
1c3caf47bb
docs: add release notes 2024-11-20 10:37:55 +01:00
Oliver Gugger
c809340095
chanacceptor: add custom channel commitment type 2024-11-20 10:37:55 +01:00
Bansal
c74d970b77
Update ruby.md
removed googleapis refernces
2024-11-20 13:59:00 +05:30
Bansal
11942cfd5e
Update python.md 2024-11-20 13:57:31 +05:30
ziggie
879041b75e
docs: add release notes 2024-11-20 09:19:09 +01:00
ziggie
ed6a246439
rpcserver: add robustness check 2024-11-20 09:18:25 +01:00
ziggie
5486f32444
multi: introduce an option for resolutions
We don't always need the resolutions in the local force close
summary so we make it an option.
2024-11-20 09:18:24 +01:00
Oliver Gugger
a10195042e
Merge pull request #9283 from guggero/fix-release-notes
docs: fix release notes
2024-11-19 21:20:43 +01:00
Oliver Gugger
ea72db711c
docs: fix release notes 2024-11-19 21:09:15 +01:00
Olaoluwa Osuntokun
ab3b3c848c
Merge pull request #9258 from yyforyongyu/fix-notification
chainntnfs: fix missing notifications
2024-11-19 10:43:25 -08:00
Olaoluwa Osuntokun
c3fac0eb92
Merge pull request #8337 from lightningnetwork/protofsm
[1/4] - protofsm: add new package for driving generic protocol FSMs
2024-11-19 10:41:49 -08:00
Oliver Gugger
e1df8e9161
Merge pull request #9281 from ziggie1984/export-towerclientdb-version
wtdb: export versions of wtclient.db
2024-11-19 17:53:14 +01:00
Oliver Gugger
9519a23a97
Merge pull request #9278 from starius/debuglevel-show-no-checks
config: fix "lnd --debuglevel show" command
2024-11-19 17:53:00 +01:00
Boris Nagaev
64c660c19e
itest: test "lnd --debuglevel=show" command
Tests that "lnd --debuglevel=show" command works and prints the list of
supported subsystems.
2024-11-19 12:44:57 -03:00
Boris Nagaev
3360d285fb
config: improve error message
Previously, the error message had one missing space:
"... either --bitcoin.mainnet, or bitcoin.testnet,bitcoin.simnet, ..."

I added a space after "bitcoin.testnet,".
2024-11-19 12:44:57 -03:00
Boris Nagaev
1bc25c1136
config: fix "lnd --debuglevel show" command
Previously, "lnd --debuglevel show" complained that bitcoin backend is not
configured:

$ lnd --debuglevel show
failed to load config: ValidateConfig: either --bitcoin.mainnet, or ...

To make it working, the user had to specify --bitcoin.mainnet and configure one
of the backends, e.g. "--bitcoin.node neutrino".

With this fix, the command works without any additional flags.
2024-11-19 12:44:55 -03:00
ziggie
aaa0abc8a9
wtdb: export versions of wtclient.db
We now make it possible to get the current db version of the
wtclient.db. Moreover we can now fetch the latest available
migration version for the client db. This allows us to compare
whether the client.db has all the expected migrations applied.
2024-11-19 16:42:52 +01:00
Oliver Gugger
411ad1d9cd
Merge pull request #9271 from morehouse/simplify_fuzz_wtwire
wtwire: simply fuzz targets
2024-11-19 12:57:34 +01:00
Oliver Gugger
c136901f24
Merge pull request #9270 from starius/goroutine-manager-bool
fn: improvements for GoroutineManager
2024-11-19 12:56:04 +01:00
Oliver Gugger
f4a1299d37
Merge pull request #9272 from lightningnetwork/sweeper-output-index
sweep: update storeRecord to include utxo index
2024-11-19 12:42:26 +01:00
Elle
d14f4c7e1f
Merge pull request #9279 from ellemouton/misc
misc: nil checks and remove deprecated default values
2024-11-19 10:37:01 +02:00
yyforyongyu
db6901c9f3
chainntnfs: skip duplicate numConfsLeft notifications
This commit adds a new state to the `ConfNtfn` struct to start tracking
the number of confs left to be notified to avoid sending duplicate
notifications.
2024-11-19 16:27:26 +08:00
yyforyongyu
7695880c13
chainntnfs: add new method notifyNumConfsLeft
So it's easier to handle the following commit where we start skipping
duplicate notifications.
2024-11-19 16:02:19 +08:00
yyforyongyu
0c3b2b5e06
docs: update release notes 2024-11-19 15:56:27 +08:00
yyforyongyu
4ff1f19fbf
chainntnfs: make sure notification is sent to the whole set
This commit fixes a bug where the confirmation details may be missed.
When the same tx is subscribed via `RegisterConfirmationsNtfn`, we will
put them into the same set and notify the whole set. However, this logic
is missing when performing the rescan - once the confirmation detail is
found, we only notify the current subscriber. Later we will skip
notifying other subscribers in `UpdateConfDetails` due to the
`confSet.details != nil` check. We now fix it by immediately notify all
the subscribers when the confirmation detail is found during the rescan.
2024-11-19 15:56:08 +08:00
yyforyongyu
1200b75546
chainntnfs: always notify txns before block
This commit changes the order of notifications when a relevant tx is
found in a block and now we will always notify the tx subscribers before
notifying the block, which has implications in the upcoming blockbeat.

When a block notification is subscribed via `RegisterBlockEpochNtfn` and
a confirm or spend is subscribed via `RegisterConfirmationsNtfn` or
`RegisterSpendNtfn`, we would always notify the block first before the
tx, causing the subsystem to think there's no relevant txns found in
this block, while the notifications are sent later. We now fix it by
always sending the txns notifications first, so the subsystem can
receive the txns, process them, then attempt to advance its state based
on the block received.
2024-11-19 15:55:22 +08:00
Olaoluwa Osuntokun
2e3c0b2a7d
protofsm: use new fn.GoroutineManager to manage goroutines
This fixes an isuse that can occur when we have concurrent calls to
`Stop` while the state machine is driving forward.
2024-11-18 20:49:01 -08:00
Olaoluwa Osuntokun
6de0615cd5
protofsm: allow multiple internal events to be emitted
In this commit, we update the execution logic to allow multiple internal
events to be emitted. This is useful to handle potential out of order
state transitions, as they can be cached, then emitted once the relevant
pre-conditions have been met.
2024-11-18 20:49:00 -08:00
Olaoluwa Osuntokun
847c1a789d
protofsm: add SpendMapper to craft custom spend events
In this commit, we add the SpendMapper which allows callers to create
custom spent events. Before this commit, the caller would be able to
have an event sent to them in the case a spend happens, but that event
wouldn't have any of the relevant spend details.

With this new addition, the caller can specify how to take a generic
spend event, and transform it into the state machine specific spend
event.
2024-11-18 20:49:00 -08:00
Olaoluwa Osuntokun
d805c0fc3c
protofsm: add CustomPollInterval for mocking purposes
Adding this makes a state machine easier to unit test, as the caller can
specify a custom polling interval.
2024-11-18 20:49:00 -08:00
Olaoluwa Osuntokun
35ea05d5dc
protofsm: add ErrorReporter interface
We'll use this to be able to signal to a caller that a critical error
occurred during the state transition.
2024-11-18 20:49:00 -08:00
Olaoluwa Osuntokun
96a98bc071
protofsm: add logging 2024-11-18 20:49:00 -08:00
Olaoluwa Osuntokun
44f035330b
protofsm: add a Name() method to the env
This'll be used later to uniquely identify state machines for
routing/dispatch purposes.
2024-11-18 20:49:00 -08:00
Olaoluwa Osuntokun
424ae09631
protofsm: add ability for state machine to consume wire msgs
In this commit, we add the ability for the state machine to consume wire
messages. This'll allow the creation of a new generic message router
that takes the place of the current peer `readHandler` in an upcoming
commit.
2024-11-18 20:48:59 -08:00
Olaoluwa Osuntokun
bf10e31167
protofsm: convert state machine args into config 2024-11-18 20:48:59 -08:00
Olaoluwa Osuntokun
d17e737558
protofsm: add optional daemon event on init
In this commit, we add an optional daemon event that can be specified to
dispatch during init. This is useful for instances where before we
start, we want to make sure we have a registered spend/conf notification
before normal operation starts.

We also add new unit tests to cover this, and the prior spend/conf event
additions.
2024-11-18 20:48:59 -08:00
Olaoluwa Osuntokun
7f69ceb2d4
protofsm: add daemon events for spend+conf registration 2024-11-18 20:48:59 -08:00
Olaoluwa Osuntokun
3bae7f32cd
protofsm: add new package for driving generic protocol FSMs
In this PR, we create a new package, `protofsm` which is intended to
abstract away from something we've done dozens of time in the daemon:
create a new event-drive protocol FSM. One example of this is the co-op
close state machine, and also the channel state machine itself.

This packages picks out the common themes of:

  * clear states and transitions between them
  * calling out to special daemon adapters for I/O such as transaction
    broadcast or sending a message to a peer
  * cleaning up after state machine execution
  * notifying relevant callers of updates to the state machine

The goal of this PR, is that devs can now implement a state machine
based off of this primary interface:
```go
// State defines an abstract state along, namely its state transition function
// that takes as input an event and an environment, and returns a state
// transition (next state, and set of events to emit). As state can also either
// be terminal, or not, a terminal event causes state execution to halt.
type State[Event any, Env Environment] interface {
	// ProcessEvent takes an event and an environment, and returns a new
	// state transition. This will be iteratively called until either a
	// terminal state is reached, or no further internal events are
	// emitted.
	ProcessEvent(event Event, env Env) (*StateTransition[Event, Env], error)

	// IsTerminal returns true if this state is terminal, and false otherwise.
	IsTerminal() bool
}
```

With their focus being _only_ on each state transition, rather than all
the boiler plate involved (processing new events, advancing to
completion, doing I/O, etc, etc).

Instead, they just make their states, then create the state machine
given the starting state and env. The only other custom component needed
is something capable of mapping wire messages or other events from the
"outside world" into the domain of the state machine.

The set of types is based on a pseudo sum type system wherein you
declare an interface, make the sole method private, then create other
instances based on that interface. This restricts call sites (must pass
in that interface) type, and with some tooling, exhaustive matching can
also be enforced via a linter.

The best way to get a hang of the pattern proposed here is to check out
the tests. They make a mock state machine, and then use the new executor
to drive it to completion. You'll also get a view of how the code will
actually look, with the focus being on the: input event, current state,
and output transition (can also emit events to drive itself forward).
2024-11-18 20:48:57 -08:00
Olaoluwa Osuntokun
0e999ed9f5
Merge pull request #9275 from yyforyongyu/fix-peer-shutdown
peer+lnd: fix peer blocking on node shutdown
2024-11-18 20:15:12 -08:00
Elle Mouton
9f72ce7dd4
config: remove default values for deprecated fields
These values have been deprecated but since we still set the default
values, the "please remove it" error still shows up.
2024-11-19 05:49:30 +02:00
Olaoluwa Osuntokun
7ed2c10671
sweep: update storeRecord to include utxo index
In this commit, we complete a recently added feature by ensuring that
even if we go through the RBF loop to create a txn, that we still
populate the `outpointToIndex` map. Unit tests have been updated to
ensure this is always set as expected.
2024-11-18 18:12:41 -08:00
Elle Mouton
90ed0fe54c
lntest: nil check edges 2024-11-18 21:09:16 +02:00
yyforyongyu
3ab5669ff2
docs: update release notes 2024-11-18 18:49:34 +08:00
yyforyongyu
fe03aa0201
peer+lnd: fix peer blocking on node shutdown
This commit fixes a case where the peer blocks the shutdown process.
During the shutdown, the server will call `s.DisconnectPeer`, which
calls `peer.Disconnect`. Because the peer never enters `peer.Start` via
`s.peerInitializer`, the `startReady` chan will not be closed, causing
`peer.Disconnect` to hang forever. We now fix it by only block on
`startReady` when the peer is started.
2024-11-18 18:49:34 +08:00
Oliver Gugger
bd36f76530
Merge pull request #9273 from Roasbeef/itests-pid
lntest: print node PID when launching in itests
2024-11-18 09:38:48 +01:00
Olaoluwa Osuntokun
b9105c35e5 lntest: print node PID when launching in itests
In this commit, we start to print the PID of node's we created within an
itest. This is useful when debugging itests with `dlv` by attaching to a
given node. By printing out the PID, we facilitate such debugging
attempts.
2024-11-15 17:21:43 -08:00
Oliver Gugger
95b248a1ef
Merge pull request #9194 from lightningnetwork/aux-channel-htlc
multi: generate and pass along HTLC resolution blobs for aux channels
2024-11-15 13:07:35 +01:00
Oliver Gugger
9a1adbeeaa
docs: move 0.18.4 items, add full list of custom chan PRs 2024-11-15 09:50:01 +01:00
Oliver Gugger
e6efa2e438
Merge pull request #9269 from hieblmi/sendpayment-fix-negative-feelimit
routerrpc: fix sendpayment_v2 negative fee limit
2024-11-15 09:40:43 +01:00
Olaoluwa Osuntokun
414894348a
sweep: update BudgetInputSet.Budget() to factor in extra budget
In this commit, we update the `Budget()` call to factor in the
`extraBudget` value. Otherwise, when we go to intialize the fee
function, we won't factor in the extra budget, and will determine that
we can't broadcast/bump.
2024-11-14 16:09:59 -08:00
Olaoluwa Osuntokun
87b4991bb6
lnwallet: add whoseCommit to FetchLeavesFromCommit
This is useful for additional context to know which commit the
AuxLeafStore is fetching the leaves for.
2024-11-14 16:09:58 -08:00
Olaoluwa Osuntokun
ba16a74491
sweep: expand NotifyBroadcast to include an outpoint index
In this commit, we expand the `NotifyBroadcast` to include an outpoint
index. This is useful as it indicates the index of a given required tx
out input.
2024-11-14 16:09:58 -08:00
Olaoluwa Osuntokun
ab41f28a8f
contractcourt: pass in new aux resolution blob to sweeper in resolvers
With this commit, we update all the resolvers to pass in the new htlc
resolution blobs. Along the way, we remove the old blocking guard on
this resolution logic for HTLCs with blobs.
2024-11-14 16:09:58 -08:00
Olaoluwa Osuntokun
7ef2683586
contractcourt: update encode/decode for taproot aux data
When we read/write the aux data, we need to make sure we always set the
new fields for aux HTLCs.
2024-11-14 16:09:58 -08:00
Olaoluwa Osuntokun
9b8adf5f5c
contractcourt: add HtlcBlobs to taprootBriefcase
In this commit, we add the set of HtlcBlobs to the taprootBriefcase
struct. This new field will store all the resolution blobs for a given
HTLC. We also add some new property based tests along the way for
adequate test coverage.
2024-11-14 16:09:58 -08:00
Olaoluwa Osuntokun
1e7c5415ae
input: add new Preimage method to input.Input
In this commit, we add a new method to obtain an option of a preimage to
the input.Input struct. This is useful for callers that have an Input,
and want to optionally obtain the preimage.
2024-11-14 16:09:58 -08:00
Olaoluwa Osuntokun
af60fa0c3a
lnwallet: populate resolution blob for incoming+outgoing HTLC resolutions
In this commit, we populate the resolution blobs for the incoming and
outgoing HTLCs. We take care to populate the AuxSigDesc with the correct
information, as we need to pass along the second-level aux signature and
also sign desc along with it.
2024-11-14 16:09:57 -08:00
Olaoluwa Osuntokun
08d0aa2368
channel: add ResolutionBlob to Incoming+Outgoing HtlcResolution
Similar to the other blobs we have for the commitment output force close
resolution, these blobs will be used to ensure that we have everything
needed to sweep aux HTLCs.
2024-11-14 16:09:57 -08:00
Olaoluwa Osuntokun
98b41c6576
channel: always specify ChanType in ResolutionReq 2024-11-14 16:09:57 -08:00
Olaoluwa Osuntokun
bf350c6ede
lnwallet: expand attributes in ResolutionReq
In this commit, we add some additional attributes to the ResolutionReq
struct. These will be used to make sure that we can properly handle all
the HTLC variants, on chain.

The `AuxSigDesc` will be used to communicate if an HTLC needs to go to
the second level or not. It contains the second-level sig information
needed to finalize a broadcast to the second level.
2024-11-14 16:09:57 -08:00
Boris Nagaev
891e9621d7
fn: GoroutineManager.Go returns bool, not error
This was requested in https://github.com/lightningnetwork/lnd/pull/9140#discussion_r1821181787
2024-11-14 15:03:25 -03:00
Slyghtning
de451d72c7
docs: update release notes 2024-11-14 14:31:48 +01:00
Slyghtning
8f8942cda9
routerrpc: fix sendpayment_v2 negative fee limit 2024-11-14 14:31:46 +01:00
Jesse de Wit
20f7c73801
docs: update release notes 2024-11-14 11:42:22 +01:00
Jesse de Wit
c84126309d
channeldb: skip nil scheduler options
This is a robustness option to ensure LND doesn't crash when this
function is accidentally called with `AddChannelEdge(edge, nil)`.
2024-11-14 11:42:22 +01:00
Jesse de Wit
7d9d100e94
localchans: add policy when missing 2024-11-14 11:42:22 +01:00
Boris Nagaev
669ebf49d4
fn: stress test GoroutineManager.Stop calls
Make sure there is no race condition between Done() and Wait() methods in the
GoroutineManager implementation.

See https://github.com/lightningnetwork/lnd/pull/9141#issuecomment-2467726384
2024-11-14 01:13:02 -03:00
Matt Morehouse
9dd921243d
wtwire: explain emptyMsg parameter in function comment
This makes it unnecessary to explain the argument at every call site, so
we can simplify each fuzz target.
2024-11-13 15:53:29 -06:00
Matt Morehouse
567ff5d751
wtwire: remove incorrect function comment
The harness doesn't return anything, so don't say that it does.
2024-11-13 15:53:29 -06:00
Matt Morehouse
e2f7eb963f
wtwire: move message prefixing to the harness
The prefixing is done every time the harness is used, so it may as well
reside in the harness itself.

Since we already pass an empty message of the correct type, we can use
that message to get the required prefix bytes.
2024-11-13 15:53:29 -06:00
Matt Morehouse
38f9f795e4
wtwire: s/harness/wireMsgHarness
This slightly more descriptive name obviates the repetitive comments at
every call site.
2024-11-13 15:53:29 -06:00
Yong
0876173c45
Merge pull request #9261 from yyforyongyu/fix-rpcclient-shutdown
multi: fix rpcclient shutdown
2024-11-14 03:18:07 +08:00
Oliver Gugger
60caa04a4d
Merge pull request #9263 from morehouse/simplify_fuzz_lnwire
lnwire: simplify fuzz targets
2024-11-13 18:32:15 +01:00
yyforyongyu
1c6d89446d
docs: update release notes 2024-11-14 01:22:32 +08:00
yyforyongyu
fc5f8e32f5
chanfitness: exit early when there are no updates 2024-11-14 01:22:32 +08:00
yyforyongyu
523ecc0653
multi: wait for rpcclients shutdown to complete
We need to call `WaitForShutdown` after stopping the rpc clients of the
chain backends.
2024-11-14 01:22:31 +08:00
Matt Morehouse
75bdf2d252
lnwire: use assertEqualFunc in onion failure harness
Simplifies the code slightly and improves the error message printed if
the original and deserialized messages do not match.
2024-11-13 10:25:16 -06:00
Matt Morehouse
d0e6a7a37b
lnwire: adapt harness for custom equality funcs
There are several fuzz targets that can't use the standard require.Equal
check for various reasons. By adapting the harness to accept a custom
equality function, we can reduce code duplication in these targets.
2024-11-13 10:25:16 -06:00
Matt Morehouse
b9381acb2d
lnwire: move message prefixing to the harness
The prefixing is done every time the harness is used, so it may as well
reside in the harness itself.
2024-11-13 10:25:16 -06:00
Matt Morehouse
b7a1a1e9b8
lnwire: s/harness/wireMsgHarness
This slightly more descriptive name distinguishes the wire message
harness from the onion failure harness while also obviating the
repetitive comments at every call site.
2024-11-13 10:25:16 -06:00
Matt Morehouse
4097527efc
lnwire: remove superfluous "Prefix with..." comments
These comments add nothing of value since the following line is always
self-documenting:

  data = prefixWithMsgType(data, MsgTypeToBePrefixed)
2024-11-13 10:25:12 -06:00
Oliver Gugger
8918b62943
Merge pull request #9266 from famouswizard/patch-1
Typo Update ruby.md
2024-11-13 16:46:03 +01:00
Oliver Gugger
304c8df7b9
Merge pull request #9251 from morehouse/fuzz_lnwire
lnwire: add new fuzz targets
2024-11-13 14:23:16 +01:00
Oliver Gugger
4d184dffd0
Merge pull request #9259 from yyforyongyu/prepare-itest-for-blockbeat
trivial: prepare itest for `blockbeat`
2024-11-13 12:57:42 +01:00
Jesse de Wit
5e5291d044
localchans: add test for createEdge and manager 2024-11-13 12:13:11 +01:00
Jesse de Wit
bb4d3db8bc
localchans: recreate missing edge if not found
If a node contains a channel, but doesn't have a corresponding edge in
the graph database, updating the channel policy would fail. In this
commit the edge is recreated if the channel exists. This ensures a node
can recover from a missing edge in the graph database by calling
updatechanpolicy.
2024-11-13 12:13:11 +01:00
Jesse de Wit
aa2ddf77d0
lncli: add create_missing_edge 2024-11-13 12:13:11 +01:00
Jesse de Wit
f1299fdd57
lnrpc: add create_missing_edge flag 2024-11-13 12:13:11 +01:00
wizard
e0f27a68ea
Typo Update ruby.md
Fix typo: "handhsake" to "handshake"
2024-11-13 13:59:58 +03:00
yyforyongyu
653a8ac55e
lntest+itest: add new method AssertChannelInGraph
This commit replaces `AssertTopologyChannelOpen` with
`AssertChannelInGraph`, which asserts a given channel edge is found.
`AssertTopologyChannelOpen` only asserts a given edge has been received
via the topology subscription, while we need to make sure the channel is
in the graph before continuing our tests.
2024-11-12 23:55:40 +08:00
yyforyongyu
7dda2960a5
lntest: update the block height of HarnessTest during start 2024-11-12 23:55:40 +08:00
yyforyongyu
2905e7b3df
lntest+itest: kill node and wait its process
Fix a flake found in `testRPCMiddlewareInterceptor` when running in
macOS.
2024-11-12 23:55:40 +08:00
yyforyongyu
90bca1022b
lntest: retry finding the payment from ListPayments 2024-11-12 23:55:40 +08:00
yyforyongyu
4506fd30cb
lntest: change the nodeCounter to be atomic 2024-11-12 23:55:13 +08:00
yyforyongyu
cc6ff150b9
lntest: remove deprecated config --profile 2024-11-12 14:46:31 +08:00
yyforyongyu
6ac588e655
lntest: remove an invalid check
`openChannelsForNodes` is called inside `openZeroConfChannelsForNodes`
so this value can be true.
2024-11-12 14:46:30 +08:00
Yong
4f6b510869
Merge pull request #9248 from yyforyongyu/fix-itest
lntest: fix edge assertion and reset min relay fee
2024-11-11 22:16:03 +08:00
yyforyongyu
7dd9a17625
lntest+itest: fix testUpdateChanStatus 2024-11-11 20:57:50 +08:00
yyforyongyu
d27ff34b04
lntest+itest: rename AssertNumEdges to AssertNumActiveEdges
To properly reflect what the assertion is.
2024-11-11 20:57:49 +08:00
yyforyongyu
35992e1503
lntest+itest: expore method createSimpleNetwork
So it can be used via `ht.CreateSimpleNetwork`.
2024-11-11 20:57:49 +08:00
yyforyongyu
9682aa7a78
itest: fix testZeroConfChannelOpen and testOptionScidAlias
Use a new node instead of standby node to avoid closing the channels in
the end.
2024-11-11 20:54:10 +08:00
yyforyongyu
a745d74e7c
lntest: make sure standby nodes' edges are cleaned 2024-11-11 20:54:10 +08:00
yyforyongyu
e7d0754615
lntest: make sure minRelayFeerate is reset
So every test starts with the default minRelayFeerate.
2024-11-11 20:54:10 +08:00
Matt Morehouse
4f7267ecea
lnwire: add fuzz target for Fee TLV
The new Fee TLV is not included in any other messages within the lnwire
package, so it currently has no fuzzing coverage.  This fuzz target
directly tests the encoding/decoding of the TLV to get some coverage.
2024-11-08 15:03:30 -06:00
Matt Morehouse
f1b7d52308
lnwire: add fuzz target for Schnorr sig conversion
Analogous to FuzzConvertFixedSignature but for Schnorr signatures.
2024-11-08 15:01:48 -06:00
Matt Morehouse
b82ae51a0b
lnwire: add fuzz target for route blinding message
Add a simple decode/encode target for the FailInvalidBlinding message.
2024-11-08 15:00:06 -06:00
Matt Morehouse
2784da13f8
lnwire: add fuzz targets for gossip 1.75 messages
Add simple decode/encode targets for AnnouncementSignatures2,
ChannelAnnouncement2, and ChannelUpdate2.
2024-11-08 14:57:13 -06:00
Oliver Gugger
5a8026aba9
Merge pull request #9249 from yyforyongyu/fix-shutdown
routing: fix missionControlStore blocks on shutting down
2024-11-08 11:09:45 +01:00
Elle
4430687076
Merge pull request #9240 from carlaKC/9166-mergeonly
htlcswitch: merge htlc custom records on modify
2024-11-08 08:38:00 +02:00
Olaoluwa Osuntokun
faa4f24806
Merge pull request #9182 from ziggie1984/fix-feebuffer
Deprecate dust-threshold config value
2024-11-07 19:41:38 -08:00
Olaoluwa Osuntokun
3a14382720
Merge pull request #9068 from ziggie1984/cancel-back-dust-htlc
Cancel back outgoing dust htlcs before commitment is confirmed.
2024-11-07 19:40:33 -08:00
ziggie
59f32682a5
contractcourt: introduce option for commitKey. 2024-11-07 22:40:53 +01:00
ziggie
d5e8df7067
docs: add release-notes for LND 19 2024-11-07 22:40:53 +01:00
ziggie
50d6864137
itest: adopt itests for the new behavior
Now outgoing dust-htlcs are canceled back before the commitment
is confirmed onchain.
2024-11-07 22:40:53 +01:00
ziggie
7aa9ade4c2
contratcourt: dont consider dust htlc for anchor sweep
Now that we cancel dust htlcs prematurely even before the
commitment tx is confirmed we don't consider dust htlcs when
creating the cpfp transaction.
2024-11-07 22:40:52 +01:00
ziggie
8ed8665d50
contractcourt: Cancel dust htlcs prematurely
We will now cancel dust htlcs on the local/remote commits after
we decided to go onchain. This can be done because dust cannot
be enforced onchain and therefore there is no way to also reveil
the preimage onchain.
2024-11-07 22:40:52 +01:00
Carla Kirk-Cohen
151068c5e9
release-notes: note that modify custom records are merged 2024-11-07 13:48:04 -05:00
Carla Kirk-Cohen
7896bef2a0
htlcswitch: merge copy htlc custom records 2024-11-07 13:46:54 -05:00
Oliver Gugger
0899077fb5
Merge pull request #7762 from guggero/empty-resp
lnrpc: return meaningful response instead of empty one
2024-11-07 19:14:44 +01:00
yyforyongyu
4fe36f1887
docs: add release notes 2024-11-08 01:53:07 +08:00
Oliver Gugger
f42636fec9
Merge pull request #8985 from ProofOfKeags/fn/collect-results
fn: more fn goodies
2024-11-07 18:50:57 +01:00
ziggie
20dc7f29f8
multi: fix typo and add comment. 2024-11-07 18:32:45 +01:00
ziggie
328a711dbe
contractcourt: enhance chainAction type
We distinguish between dangling and dust htlcs. This does not
change any logic but only introduces new types to later act on them
differently when we begin to fail dust htlcs earlier in a later
commit.
2024-11-07 18:32:45 +01:00
ziggie
8890f83bde
contratcourt: refactor resolving htlc logic
Refactor the part where we are failing back the incoming htlc
when the channel of the corresponding outgoing htlc is force
closed. We do this because in furture commits we separate the
logic when we fail back the incoming htlc (abandonForward).
Right now we fail abandon dust forwards and non-dust forwards
only when the commitment transaction is confirmed. Later we will
move the canceling of the upstream htlc when the commitment
transaction is broadcasted instead of waiting until the commitment
tx is confirmed. The reason for that is that dust cannot be enforced
onchain anyways so there is no reason to wait.
2024-11-07 18:32:45 +01:00
ziggie
98a270bd30
contractcourt: improve robustness 2024-11-07 18:32:44 +01:00
yyforyongyu
227cc3a45c
routing: fix missionControlStore blocks on shutting down 2024-11-08 01:23:33 +08:00
Keagan McClelland
9f35664a12
fn: breaking - make or else functions accept error argument 2024-11-06 17:32:59 -07:00
Keagan McClelland
dd7f1569c7
fn: breaking - rename ChainOption to FlatMapOption for consistency 2024-11-06 17:32:59 -07:00
Keagan McClelland
5e947046a3
fn: breaking - give Result's FlatMap proper functor annotation 2024-11-06 17:32:59 -07:00
Keagan McClelland
4d16d5ff15
fn: breaking - replace AndThen2 with more principled LiftA2Result 2024-11-06 17:32:59 -07:00
Keagan McClelland
1480287cfc
fn: breaking - improve naming of option api functions 2024-11-06 17:32:59 -07:00
Keagan McClelland
be50dd9acb
fn: breaking - remove deprecated Result functions 2024-11-06 17:32:59 -07:00
Keagan McClelland
1805fd6f19
fn: breaking - polish Either API 2024-11-06 17:32:58 -07:00
Keagan McClelland
c6734ea013
fn: breaking - reverse argument order in slice funcs 2024-11-06 17:32:50 -07:00
Keagan McClelland
a026d64c1b
fn: breaking - fix type variables for better inference 2024-11-06 16:50:06 -07:00
Keagan McClelland
9bbd327a10
fn: add Sink to Result 2024-11-06 16:50:02 -07:00
Keagan McClelland
5dec35426c
fn: add transpositions for Option and Result 2024-11-06 16:40:36 -07:00
Keagan McClelland
8971c4c3ae
fn: add operations to Result API 2024-11-06 16:40:15 -07:00
Keagan McClelland
5faad77f33
fn: add conversions between Result and Option 2024-11-06 16:39:33 -07:00
Keagan McClelland
a741c54175
fn: add compound slice functions 2024-11-06 16:29:41 -07:00
Keagan McClelland
7a7f3bdb2c
fn: fix UnwrapOrFail semantics 2024-11-06 15:55:18 -07:00
ziggie
aebf03aca4
docs: add release-notes for 19. 2024-11-06 18:52:07 +01:00
ziggie
643e415448
scripts: dont check new config value.
Because we need to remain backwards compatible with the old
`dust-threshold` value we set the default value for
`channel-max-fee-exposure` later in the program flow. Given
the restrictions of the sample config check we need to exclude
this value from the check.
2024-11-06 18:52:07 +01:00
ziggie
7b2da94750
multi: deprecate dust-treshold config value
Replace ambigious config value "dust-treshold" with a more clear
"channel-max-fee-exposure" exposure value. The old value is
deprecated and will be removed in the near future.
2024-11-06 18:16:06 +01:00
ziggie
a040f8fafa
htlcswitch: increase log 2024-11-06 18:12:43 +01:00
Oliver Gugger
5673dab596
docs: add release notes 2024-11-06 15:30:31 +01:00
Oliver Gugger
4bf24695d7
cmd/commands: fix custom message commands
The sendcustom and subscribecustom sub commands weren't correctly
categorized and didn't have any usage text set.
2024-11-06 15:30:30 +01:00
Oliver Gugger
0e5c724b64
rpcserver: fix formatting and grammar issues 2024-11-06 15:30:29 +01:00
Oliver Gugger
870e845a1e
cmd/lncli: print new status responses to CLI 2024-11-06 15:30:27 +01:00
Oliver Gugger
d89ea2d335
walletrpc: return meaningful responses instead of empty 2024-11-06 15:30:26 +01:00
Oliver Gugger
26216f749a
itest: add tests for new backup responses 2024-11-06 15:30:25 +01:00
Oliver Gugger
45e13cead1
lnrpc+rpcserver: return meaningful responses instead of empty 2024-11-06 15:30:24 +01:00
Oliver Gugger
d8c920fa7a
chanbackup+rpcserver+server: return number of recovered channels 2024-11-06 15:30:23 +01:00
Oliver Gugger
15a5fe7704
channeldb+rpcserver: return number of deleted payments 2024-11-06 15:30:21 +01:00
Oliver Gugger
e3cc4d70c0
Merge pull request #8600 from GeorgeTsagk/chanfunding-coinselect-nofeecheck
Add `maxFeeRatio` parameter to sanityCheckFee in psbt coin selection
2024-11-05 20:56:02 +01:00
George Tsagkarelis
c1f5908d9d
docs: update release notes for fundpsbt max_fee_ratio 2024-11-05 19:56:39 +01:00
George Tsagkarelis
b51dde7af8
cmd: expose max_fee_ratio to fundpsbt command 2024-11-05 19:56:38 +01:00
George Tsagkarelis
55c3da3d13
lnwallet+lnrpc: use maxFeeRatio across coin selection tests 2024-11-05 19:56:37 +01:00
George Tsagkarelis
f2b76325d4
lnrpc: add maxFeeRatio to FundPsbtRequest 2024-11-05 19:56:36 +01:00
George Tsagkarelis
a72dae314c
lnwallet: use new maxFeeRatio for sanityCheckFee func 2024-11-05 19:55:07 +01:00
Oliver Gugger
85175814a4
Merge pull request #9013 from guggero/fundpsbt-fee-rate-sat-per-kwu
[walletrpc]: add `sat_per_kw` fee option to `FundPsbt` RPC
2024-11-05 09:24:49 +01:00
Oliver Gugger
8849dd21d4
docs: add release notes 2024-11-04 14:12:50 +01:00
Oliver Gugger
837ff3427c
cmd/lncli: add new --sat_per_kw flag to lncli wallet fundpsbt 2024-11-04 14:12:26 +01:00
Oliver Gugger
fb8bdfefd3
walletrpc: add sat_per_kw fee rate option
To allow more precise fee rates, we add a sat_per_kw option to the fee
oneof in the gRPC definition.
2024-11-04 14:12:26 +01:00
Oliver Gugger
22ae3e5ddd
Merge pull request #9167 from ellemouton/mcEncodingToTLV
routing+migration32: update migration 32 to use pure TLV encoding for mission control results
2024-11-01 13:16:04 +01:00
Elle Mouton
30e0d6ca9c
docs: update release notes 2024-11-01 12:28:06 +02:00
Elle Mouton
5370c90906
routing+migration32: update MC encoding to use pure TLV
In this commit, we update an existing migration which at the time of
writing has not been included in a release. We update it so that it
converts the format used for MissionControl result encoding to use pure
TLV instead. The 3 structs that have been updated are: `mcHop`,
`mcRoute` and `paymentResult`.
2024-11-01 12:28:06 +02:00
Oliver Gugger
006026f617
Merge pull request #9215 from carlaKC/9166-docs
routerrpc/trivial: add docs to forward interceptor
2024-11-01 09:46:16 +01:00
Oliver Gugger
891108d2ea
Merge pull request #9221 from yyforyongyu/fix-TestChannelLinkCancelFullCommitment
htlcswitch: fix flake in `TestChannelLinkCancelFullCommitment`
2024-11-01 09:45:49 +01:00
Elle Mouton
d9a073ad7e
routing+channeldb: let Vertex implement tlv.RecordProducer
So that we can use it in TLV encoding.
Also add this to the codec for channeldb migration 32 since we will be
using it there in an upcoming adjustment commit.
2024-11-01 08:59:54 +02:00
Elle Mouton
a5f3fa17e7
lnwire21: update Msat and TrueBoolean
Add the TrueBoolean type along with its Record method. Also update the
Millisatoshi type with a Record method. Both of these will be used in an
upcoming commit which adjusts a mission control migration to use pure
TLV types.
2024-11-01 08:59:54 +02:00
ziggie
5fe9bb2cff
htlcswitch: adopt test for dust threshold 2024-10-31 17:35:53 +01:00
ziggie
cfa073e6ec
htlcswitch: increase logs 2024-10-31 17:35:27 +01:00
yyforyongyu
8311bc56e5
htlcswitch: fix flake in TestChannelLinkCancelFullCommitment
We bring down the max number of inflight HTLCs in the link's unit tests
to speed up the tests.
2024-10-31 20:23:31 +08:00
Oliver Gugger
c0e85d36ae
Merge pull request #8381 from anibilthare/issue_8205_num_retries_2
watchtower: Add retry logic for fetching blocks
2024-10-31 10:22:19 +01:00
Oliver Gugger
684041bbfe
Merge pull request #9233 from ellemouton/moveLoggingCompressorOption
build+config: move file logger specific options to `logging.file`
2024-10-30 14:10:59 +01:00
Oliver Gugger
22c3cc51ed
Merge pull request #9223 from starius/close-to-external-p2tr
lnwallet: fix closechannel for P2TR external addr
2024-10-30 14:10:21 +01:00
Oliver Gugger
5da2e8dec2
Merge pull request #9237 from yyforyongyu/fix-btcdnotify-config
chainntnfs/btcdnotify: remove redundant config params re-assignment
2024-10-30 14:09:10 +01:00
Elle Mouton
e547d21920
multi: update log file max num and size defaults 2024-10-30 13:30:04 +02:00
Elle Mouton
8106810d9e
docs: add release notes 2024-10-30 13:30:04 +02:00
Elle Mouton
22c13790df
build+config: move maxlogfiles and maxfilesize to logger config
Add deprecation notices but continue to read both.
2024-10-30 13:30:04 +02:00
Elle Mouton
3adbdbb39a
multi: move logcompressor to logging.file.compressor
This is not a breaking change since the logcompressor config option has
not been released yet.
2024-10-30 13:30:04 +02:00
Elle Mouton
1886d3865b
build: extract File logging options into own struct
In preparation for extending LoggerConfig with file specific config.
2024-10-30 13:16:39 +02:00
Elle Mouton
0512357c17
build: dedup logging config
Move all shared code to config.go which has no build tags.
2024-10-30 13:15:34 +02:00
Elle Mouton
4c5e5653e4
build: rename config.go file
to config_prod.go in preparation for adding a shared config.go file.
2024-10-30 13:15:33 +02:00
Oliver Gugger
4c48402868
Merge pull request #9222 from ffranr/add-maybesome-opt
Improve Option documentation and add MaybeSome function
2024-10-30 09:31:38 +01:00
Elle
7ceb3d61b5
Merge pull request #9213 from ellemouton/updateSubmodules
go.mod: update LND sub-module deps
2024-10-30 09:13:07 +02:00
ffranr
71298f7614
fn: fix documentation comment for Option
Remove the type generic from the `Option` type struct in the doc
comment.
2024-10-29 20:16:20 +00:00
ffranr
c49d038540
fn: add OptionFromPtr function
Add a function `OptionFromPtr` that constructs an `Option` from a
pointer. The function signature is: `OptionFromPtr : *A -> Option[A]`.

This utility has proven useful in the taproot-assets project.
2024-10-29 20:16:20 +00:00
yyforyongyu
611405f59c
chainntnfs/btcdnotify: remove redundant config params re-assignment 2024-10-30 02:01:14 +08:00
Elle Mouton
b4d098fb29
go.mod: update LND sub-module deps 2024-10-29 15:01:00 +02:00
Oliver Gugger
4778b146cc
Merge pull request #9219 from myxmaster/improve-sanitycheckfee-error-msg
chanfunding: improve sanityCheckFee error message for clarity
2024-10-29 13:07:06 +01:00
Oliver Gugger
4e54db4ac8
Merge pull request #9228 from yyforyongyu/fix-itest-flakes
itest: fix flake in `payment_failure_reason_canceled`
2024-10-29 13:05:32 +01:00
yyforyongyu
785e058b77
itest: fix flake in testPaymentFailureReasonCanceled
Fix the case,
```
--- FAIL: TestLightningNetworkDaemon/tranche01/46-of-191/bitcoind/payment_failure_reason_canceled (20.86s)
        harness.go:2113:
            	Error Trace:	/home/runner/work/lnd/lnd/lntest/harness.go:2113
            	            				/home/runner/work/lnd/lnd/itest/lnd_payment_test.go:1183
            	            				/home/runner/work/lnd/lnd/itest/lnd_payment_test.go:1138
            	            				/home/runner/work/lnd/lnd/lntest/harness.go:396
            	            				/home/runner/work/lnd/lnd/itest/lnd_test.go:139
            	Error:      	err from HTLC interceptor stream
            	Test:       	TestLightningNetworkDaemon/tranche01/46-of-191/bitcoind/payment_failure_reason_canceled
            	Messages:   	received err from HTLC interceptor stream: rpc error: code = Unknown desc = interceptor already exists
```
2024-10-29 15:32:17 +08:00
yyforyongyu
21535826ba
lntest: remove redundant MineBlocks 2024-10-29 15:31:21 +08:00
myxmaster
7b5b92ff02 chanfunding: improve sanityCheckFee error message for clarity and update coin_select_test accordingly 2024-10-28 20:04:34 +01:00
Oliver Gugger
5cec466fad
Merge pull request #9212 from ellemouton/slogFollowUps
multi: update log dep for sub-modules
2024-10-28 16:32:46 +01:00
Boris Nagaev
4b87072514
itest: coop_close_with_external_delivery with p2tr
Customize the itest with the type of external delivery address. Test with P2TR
address type in addition to P2WKPH.
2024-10-28 11:10:41 -03:00
Boris Nagaev
9fec11eeb5
lnwallet: fix closechannel for P2TR external addr
If the delivery address is P2TR, function InternalKeyForAddr checks its
existance in the wallet to return internal key for it in case it is a custom
taproot channel. It used to return the error returned by wallet.AddressInfo.
The error is now ignored if it is ErrAddressNotFound error. This fixes
"lncli closechannel --delivery_addr <external p2tr address" case.
2024-10-28 11:08:15 -03:00
Oliver Gugger
c37baa68d8
Merge pull request #9226 from sputn1ck/sendcoins_selectutxo_fix_amt
cmd/sendcoins: fix display amount if select utxo and sweepall is set
2024-10-28 11:34:12 +01:00
sputn1ck
6be3e5b609
cmd/sendcoins: fix amt if select utxo
This commit fixes the display of the amount when
selecting utxos for the sendcoins command and
combining it with the `-sweepall` flag. Prior this
would show the full balance of the wallet. Now it
shows the total amount of the selected utxos.
2024-10-28 10:03:52 +01:00
Elle Mouton
67419a7c0c
Revert "kvdb/postgres: remove global application level lock"
This reverts commit 43a1ca4f3d.
2024-10-28 09:35:18 +02:00
Elle Mouton
a03d5fcb9e
tor: update btclog dep 2024-10-26 14:18:32 +02:00
Elle Mouton
83751ee208
sqldb: update btclog dep 2024-10-26 14:18:00 +02:00
Elle Mouton
a6e68cb6b8
kvdb: update btclog dep 2024-10-26 14:16:31 +02:00
Elle Mouton
de358667f3
healthcheck: bump btclog dep 2024-10-26 14:16:25 +02:00
Elle Mouton
1e39e3758e
build: apply log type flags in NewDefaultLogHandlers
So that the logging config "Disable" options and log type flags are all
handled in one place. Other repo's can then re-use this nicely.
2024-10-26 14:15:14 +02:00
Animesh Bilthare
ecd4480331
watchtower: Add retry logic for fetching blocks
By default, try to fetch the blocks 3 more times
in case of error.
2024-10-26 16:59:13 +05:30
Oliver Gugger
acbb33bb7b
Merge pull request #9220 from oren-z0/issue-9169-code-comments-fix
Fix comments regarding breach hint and key calculations
2024-10-25 11:14:22 +02:00
Oliver Gugger
582468c0f6
Merge pull request #9199 from GeorgeTsagk/htlc-modify-cancel-htlc
Add `cancelSet` flag HtlcModify interface
2024-10-25 10:57:09 +02:00
Oren
54b30423a3 watchtower: fix comments on breach hint and key 2024-10-25 00:07:04 +03:00
George Tsagkarelis
4b1bb102bf
invoices: cancel htlc on HtlcModify signal 2024-10-24 12:59:29 +02:00
George Tsagkarelis
1c17356fdc
lnrpc+invoices: add cancelSet to HtlcModifier interface 2024-10-24 12:59:28 +02:00
Carla Kirk-Cohen
38bfe1bb86
routerrpc: add docs to forward interceptor 2024-10-23 10:28:07 -04:00
Oliver Gugger
e488002db5
Merge pull request #9214 from guggero/release-go-version-check
makefile+scripts: add Go version check to release command
2024-10-23 14:20:38 +02:00
ffranr
7919b3f204
makefile+scripts: add Go version check to release command
This commit updates `scripts/release.sh` to include a check for the
correct Go version before executing the release build. This ensures that
the release binaries are built with the specified Go version,
maintaining consistency and integrity for developer signatures.
2024-10-23 13:34:35 +02:00
Elle
b4ef22bf47
Merge pull request #9083 from ellemouton/slog
log: structured logging
2024-10-22 20:00:56 +02:00
Yong
6a5a79fb88
Merge pull request #9209 from guggero/lntest-shutdown
lntest: shutdown all nodes at end of test
2024-10-23 02:00:39 +08:00
Oliver Gugger
5f20fd4d9f
lntest: shutdown all nodes at end of test
Fixes an issue where the standby nodes would keep running even after the
test finished (successfully or with a failure).
2024-10-22 18:12:16 +02:00
Elle Mouton
30d39ac595
build+sample_conf: option to print log call-site 2024-10-22 17:07:50 +02:00
Elle Mouton
eddcdb2a5a
docs: add release note entry 2024-10-22 17:03:56 +02:00
Elle Mouton
9d0cd3ff4f
multi: update more loggers to the v2 type 2024-10-22 17:03:56 +02:00
Elle Mouton
49bfbecc4e
build: add CriticalS to ShutdownLogger
Ensure that the ShutdownLogger correctly calls shutdown for the new
CriticalS method added to the btclog.Logger.
2024-10-22 17:03:56 +02:00
Elle Mouton
ba1ce84b27
build: update prefixed logger
Such that it implements the new expanded interface of btclog v2.
2024-10-22 17:03:56 +02:00
Elle Mouton
23602e017e
multi: start updating various loggers to use the new v2 type 2024-10-22 17:03:55 +02:00
Elle Mouton
a8da3e525b
build: add styling option for console logger
This will only be available when the dev build tag is set
2024-10-22 17:03:55 +02:00
Elle Mouton
cfa7fceb0b
build+config: add default handler constructor
Since most of our projects will use the same handler duo and apply the
config options the same way, let's make our lives easier and add a
default handler constructor.
2024-10-22 17:03:55 +02:00
Elle Mouton
5ed7bf1b71
config: add logging config options
This commit adds config options so that users can for both the console
logger and the file logger set the following things:

- disable the logger
- omit timestamps from log lines.
2024-10-22 17:03:55 +02:00
Elle Mouton
cd697913ef
build: switch to slog Handlers
Start using the new slog handlers. With this commit we also remove the
need for the LogWriter since we let our LogRotator implement io.Writer
and pass that in to our log file handler.
2024-10-22 17:03:51 +02:00
Elle Mouton
9a9761008c
build: add handler set implementations
This commit then also adds an implementation of the btclog.Handler
interface called `handlerSets` which basically lets us have a Handler
backed by many Handlers (we need one for the stdout and one for our log
file).
2024-10-22 15:29:34 +02:00
Elle Mouton
387a1a8831
build: separate sublogger and rotator pipe management
These are two separate concerns. So this commit splits them up and just
passes a LogWriter from the one to the other. This will become cleaner
in an upcoming commit where the Rotator will implement io.Writer and
there will no longer be a need for LogWriter.
2024-10-22 15:19:58 +02:00
Elle Mouton
ec5b39c120
build: move LevelSubLogger to own file
To make the upcoming commits easier to review and for general code
organisation.
2024-10-22 15:17:10 +02:00
Yong
caff5a00d1
Merge pull request #9138 from ziggie1984/detail_debug_process
Detail LND's debug procedures.
2024-10-22 20:26:11 +08:00
Oliver Gugger
91c82c1938
Merge pull request #9206 from Roasbeef/roasbeef-pgp-2024
scripts/key: add new gpg key for roasbeef
2024-10-22 09:23:57 +02:00
Olaoluwa Osuntokun
49275e1d46
Merge pull request #9197 from guggero/aux-signer-batching-fixes
[custom channels]: Aux signer batching fixes
2024-10-21 17:49:15 -07:00
Olaoluwa Osuntokun
cb0f0dd8a4
scripts/key: add new gpg key for roasbeef
In this commit, we add a new signing for key for roasbeef. The signing
key has been attested to here by myself in several locations on the
Internet:
  * https://gist.github.com/Roasbeef/58c5eb9d5243df73ea3b081f3848b5ce
  * https://x.com/roasbeef
  * https://keybase.io/roasbeef
2024-10-21 16:42:09 -07:00
Oliver Gugger
f34bf51d16
Merge pull request #9200 from starius/fix-logging
peer: fix logging (missing argument) [skip ci]
2024-10-21 10:23:48 +02:00
Boris Nagaev
04a2003f3b
peer: fix logging (missing argument) [skip ci]
Previously, LND log used to have such records:
[WRN] PEER: Peer(...): Link=%!v(MISSING) is not active

Now the short channel ID is printed there instead of "%!v(MISSING)".
2024-10-18 12:09:44 -03:00
ziggie
f73504a26d
docs: add release notes. 2024-10-18 13:11:09 +02:00
ziggie
8bb79d10e9
lnd: fix debuglevel bug.
We need to setup the loggers before we are able to show the
complete list of available subsystems.
2024-10-18 13:11:09 +02:00
ziggie
027de0a82c
invoices: add proper startup for the interceptor
During testing of this PR I figured out the the htlc interceptor
was not shutdown and started properly in terms of closing the
quit channel but also in terms of the relevant logs.
2024-10-18 13:03:30 +02:00
ziggie
448193b0fd
multi: separate profiler config 2024-10-18 13:03:30 +02:00
ziggie
64c20ca308
docs: Detail LND's debug possibilities
Describe the basics of the LND profiler so that node runners can
easily provide profile dumps to analyse faulty behaviour more
quickly.

Also update the description of LND's logging system.
2024-10-18 13:03:30 +02:00
ziggie
8d4d3e0323
rpc: return loglevels of the current subsystems 2024-10-18 13:03:29 +02:00
Jonathan Harvey-Buschel
ac719dc016
gitignore: ignore vscode workspace files 2024-10-17 17:35:29 +02:00
Jonathan Harvey-Buschel
dce80ac9a1
lnwallet: test link quit signal handling 2024-10-17 17:35:29 +02:00
Jonathan Harvey-Buschel
77ae7afe78
multi: link quit can interrupt commitment signing
In this commit, we make sig job handling when singing a next commitment
non-blocking by allowing the shutdown of a channel link to prevent
further waiting on sig jobs by the channel state machine. This addresses
possible cases where the aux signer may be shut down via a separate quit
signal, so the state machine could block indefinitely on receiving an
update on a sig job.
2024-10-17 17:35:29 +02:00
Oliver Gugger
6edd942c70
mod: bump fn to v1.2.3 2024-10-17 17:34:11 +02:00
Jonathan Harvey-Buschel
cd14c52ecd
htlcswitch: pass quit chans as unidirectional
This is a requirement for replacing the quit channel with a Context.
The Done() channel of a Context is always recv-only, so all users of
that channel must not expect a bidirectional channel.
2024-10-17 17:33:15 +02:00
Jonathan Harvey-Buschel
48567e8944
lnwallet: sort sig jobs before submission 2024-10-17 17:33:15 +02:00
Jonathan Harvey-Buschel
a0baa8fb41
lnwallet: test aux signer shutdown handling 2024-10-17 17:33:15 +02:00
Jonathan Harvey-Buschel
22deba3d26
lnwallet: refactor test code for HTLC add and recv 2024-10-17 17:33:14 +02:00
Jonathan Harvey-Buschel
b3f953b1c4
multi: allow mock aux signer to customize sig jobs 2024-10-17 17:33:14 +02:00
Oliver Gugger
395a761eb6
Merge pull request #9196 from lightningnetwork/fn-context-guard
fn: add ContextGuard from tapd repo
2024-10-17 17:31:48 +02:00
Oliver Gugger
deefa3a9f1
fn: add ContextGuard from tapd repo
This adds the ContextGuard struct from the taproot-assets repository
that has been in use there for a while.

The context guard allows for easy creation of contexts that depend on a
central wait group and quit channel.

A context can either be created with a timeout and quit, meaning it will
cancel either on reaching the timeout or when the central quit channel
is closed.

Or a context can be created to block and use a timeout, meaning it will
_not_ cancel on quit but rather block the shutdown until it is completed
(or times out).

The third way is to create a context that just cancels on quit with no
timeout.
2024-10-17 13:11:30 +02:00
Oliver Gugger
f46ae2f893
Merge pull request #9195 from yyforyongyu/optimize-itest-setup
itest+lntest: speed up test setup
2024-10-17 10:16:06 +02:00
Oliver Gugger
6992ecc8fc
Merge pull request #9137 from ViktorTigerstrom/2024-09-sync-lock-fix
lnd: allow shutdown signal during `IsSynced` check
2024-10-17 10:04:14 +02:00
yyforyongyu
7849e63e0f
docs: update release doc 2024-10-17 07:45:06 +08:00
yyforyongyu
bf8ae68eea
lntest: add method AssertTxnsNotInMempool
So we only need to do one `GetRawMempool` lookup when checking the
exclusion of multiple txns.
2024-10-17 07:34:40 +08:00
yyforyongyu
e560e00aeb
lntest: remove unused return value 2024-10-17 07:34:25 +08:00
yyforyongyu
ed3cb26188
itest+lntest: stop using pointer to chainhash.Hash
This commit fixes the methods used in `lntest` so they stop using
pointers to chainhash.
2024-10-17 07:14:51 +08:00
Oliver Gugger
af6b88a9f6
Merge pull request #9171 from ellemouton/genUnsignedTLVRanges
tlv: generate types for gossip unsigned range
2024-10-16 16:04:17 +02:00
Oliver Gugger
fb5e7f4b23
Merge pull request #9059 from guggero/bitcoind-28
CI: run integration tests against bitcoind v28
2024-10-16 15:55:44 +02:00
Oliver Gugger
f0b1015bae
Merge pull request #9187 from guggero/fix-custom-data
rpcserver+lncli: fix custom channel data encoding issue
2024-10-15 17:31:25 +02:00
Oliver Gugger
8c79940374
cmd/commands: don't error out on replacement failure 2024-10-15 16:40:35 +02:00
Oliver Gugger
5f86e2529b
Merge pull request #9172 from guggero/initwallet-mac-root-key
cmd: allow deterministic macaroon derivation with `lncli`
2024-10-15 12:09:02 +02:00
Oliver Gugger
5d60da54a3
rpcserver: don't write any custom channel data if empty 2024-10-15 09:56:32 +02:00
Oliver Gugger
fcb21dfc24
docs: document deterministic macaroons, add release notes 2024-10-15 09:48:47 +02:00
Oliver Gugger
882f25667e
cmd/commands: add root_key flag to bakemacaroon command 2024-10-15 09:48:46 +02:00
Oliver Gugger
6e932c6bc8
macaroons: add BakeFromRootKey function 2024-10-15 09:48:46 +02:00
Oliver Gugger
fd37c6f90a
cmd/commands: add mac_root_key flag to unlock commands
This commit adds a new --mac_root_key flag to both the lncli create and
lncli createwatchonly commands that allows the user to specify the
macaroon root key that should be used when creating the macaroon
database on wallet initialization.
This allows for deterministic wallet initialization and baking of
macaroons before the wallet is initialized.
2024-10-15 09:48:18 +02:00
Elle Mouton
63d5e92af1
tlv: generate types for gossip unsigned range
For pure TLV messages such as Gossip 1.75 and Bolt12 messages, there are
defined TLV ranges for unsigned data vs signed data. The signed ranges are
the inclusive ranges of 0 to 159 and 1000000000 to 2999999999.
What we do here in this commit is: 1) update the number of main types
generated so that they include type 160 which is the first type in the
un-signed range and we will make use of this type for signature fields
in gossip messages. Next, we add two marker types so that we can define
the rest of the ranges.
2024-10-15 06:21:55 +02:00
ProofOfKeags
7bf9b59816
Merge pull request #9097 from ProofOfKeags/refactor/evaluate-htlc-view
[KILO]: DynComms Prefactor: Refactor/evaluate htlc view
2024-10-14 13:10:00 -06:00
Keagan McClelland
5307e7a5e4
lnwallet: return balance changes rather than modifying references
Here we return the balance deltas from evaluateHTLCView rather than
passing in references to variables that will be modified. It is a
far cleaner and compositional approach which allows readers of this
code to more effectively reason about the code without having to
keep the whole codebase in their head.
2024-10-14 09:40:47 -06:00
Keagan McClelland
f0eecfa2cd
lnwallet: inline and remove process[Add|Remove]Entry
This commit observes that processAddEntry and processRemoveEntry
are only invoked at a single call-site. Here we inline them at their
call-sites, which will unlock further simplifications of the code
that will allow us to remove pointer mutations in favor of explicit
expression oriented programming.

We also delete the tests associated with these functions, the overall
functionality is implicitly tested by the TestEvaluateHTLCView tests.
2024-10-14 09:33:39 -06:00
Keagan McClelland
1222cb8b10
lnwallet: remove continue statements from evaluateHTLCView loops
This further reduces loop complexity in evaluateHTLCView by using
explicit filter steps rather than loop continue statements.
2024-10-14 09:33:39 -06:00
Keagan McClelland
d5aab4a8c1
lnwallet: consolidate loops in evaluateHTLCView
We had four for-loops in evaluateHTLCView that were exact mirror
images of each other. By making use of the new ChannelParty and
Dual facilities introduced in prior commits, we consolidate these
into two for-loops.
2024-10-14 09:33:39 -06:00
Keagan McClelland
51c28496a4
lnwallet: simplify fee calculation in evaluateHTLCView
This commit simplifies how we compute the commitment fee rate based
off of the live updates. Prior to this commit we processed all of
the FeeUpdate paymentDescriptors of both ChannelParty's. Now we only
process the last FeeUpdate of the OpeningParty
2024-10-14 09:33:38 -06:00
Keagan McClelland
4b2a4e36ad
lnwallet: pack htlcView.{OurUpdates|TheirUpdates} into Dual.
This commit moves the collection of updates behind a Dual structure.
This allows us in a later commit to index into it via a ChannelParty
parameter which will simplify the loops in evaluateHTLCView.
2024-10-14 09:33:38 -06:00
Keagan McClelland
1b2cb14254
lnwallet: change bool isIncoming to new lntypes.ChannelParty
This commit removes another raw boolean value and replaces it with
a more clear type/name. This will also assist us when we later try
and consolidate the logic of evaluateHTLCView into a single
coherent computation.
2024-10-14 09:33:38 -06:00
Keagan McClelland
b902d0825d
lnwallet: use fn.Set API directly instead of empty struct map. 2024-10-14 09:33:38 -06:00
Keagan McClelland
d82d02831d
lnwallet: remove mutateState from evaluateHTLCView
In line with previous commits we are progressively removing the
mutateState argument from this call stack for a more principled
software design approach.

NOTE FOR REVIEWERS:
We take a naive approach to updating the tests here and simply
take the functionality we are removing from evaluateHTLCView and
run it directly after the function in the test suite.

It's possible that we should instead remove this from the test
suite altogether but I opted to take a more conservative approach
with respect to reducing the scope of tests. If you have opinions
here, please make them known.
2024-10-14 09:33:16 -06:00
Oliver Gugger
4213ab8947
docs: add release notes 2024-10-14 17:26:59 +02:00
Oliver Gugger
a63542b618
mod: fix unit tests by updating error matching in btcwallet 2024-10-14 17:26:59 +02:00
Oliver Gugger
c156d17da5
lnwallet: turn off RBF detection in test 2024-10-14 17:26:59 +02:00
Oliver Gugger
10802305e5
Merge pull request #8183 from starius/close-tx-in-static-backup
chanbackup, server, rpcserver: put close unsigned tx, remote signature and commit height to SCB
2024-10-14 16:44:19 +02:00
Oliver Gugger
91ade89a78
lntest: avoid port collision on Tor listen port
bitcoind now seems to listen on the -bind port at all times. So we need
to make sure multiple instances don't collide by using a unique port.
2024-10-14 16:10:19 +02:00
Oliver Gugger
56a36acf63
lntest: move debug flag, disable spammy libevent 2024-10-14 16:10:18 +02:00
Oliver Gugger
6ac1ffd274
github+scripts: bump itests to bitcoind v28, allow testing with RCs 2024-10-14 16:10:18 +02:00
Boris Nagaev
65df996358
docs/recovery: add Last resort manual force close
Also updated release-notes.
2024-10-14 09:44:32 -03:00
Boris Nagaev
eda06b19c6
itest: test channel.backup changes upon shutdown 2024-10-14 09:44:32 -03:00
Boris Nagaev
25eecd7a87
lnwallet: fix godoc of TapscriptTweak 2024-10-14 09:44:32 -03:00
Boris Nagaev
3de94c11ae
lntest: fix typo 2024-10-14 09:44:32 -03:00
Boris Nagaev
ef8535356b
itest/lnd_channel_backup_test: fix typos 2024-10-14 09:44:32 -03:00
Boris Nagaev
29946df4e5
server: produces a channel backup upon shutdown
This is needed to keep channel.backup up-to-date if the node is stopped.
2024-10-14 09:44:32 -03:00
Boris Nagaev
fb397c11f1
chanbackup/pubsub: add method ManualUpdate
This method inserts channel updates and waits for them to be processed. It will
be used to update channel.backup upon LND shutdown.
2024-10-14 09:44:32 -03:00
Boris Nagaev
df84148ed2
chanbackup: add Single.CloseTxInputs field
The field is optional. It stores inputs needed to produce signed commit tx using
chantools scbforceclose, which calls function GetSignedCommitTx. New backups
have this field filled if commit tx is available (for all cases except when DLP
is active). If a backup has this data, the field is filled from it, otherwise it
is kept empty.

Modified test function genRandomOpenChannelShell to cover new types of channels
(simple taproot channel and custom channel) and to cover combinations of bits.
Make sure that TapscriptRoot field is properly packed and unpacked.
2024-10-14 09:44:17 -03:00
Boris Nagaev
f485e079b7
chanbackup: add backup version for TapscriptRoot
Previous to this change taproot assets channels and simple taproot channels were
considered the same in the context of chanbackup package, since they stored the
same data. In the following commits we are adding the data needed to produce a
signed commitment transaction from a SCB file and in order to do that we need to
add more fields and a custom channel gets one additional field (TapscriptRoot)
compared to a simple taproot channel. So now we have to distinguish these kinds
of channels in chanbackup package.

See PR https://github.com/lightningnetwork/lnd/pull/8183 for more details.
2024-10-11 10:00:42 -03:00
Boris Nagaev
90c45ddd8f
lnwallet: factor out func GetSignedCommitTx
This pure function creates signed commit transaction, using various
inputs passed as struct TaprootSignedCommitTxInputs and a signer.

This is needed to be able to store the inputs without a signature
in SCB and sign the transaction in chantools scbforceclose.

See https://github.com/lightningnetwork/lnd/pull/8183/files#r1423959791
2024-10-11 10:00:42 -03:00
Boris Nagaev
e7776a4c1e
contractcourt: fix doc of commitSweepResolver
It is used for sweeping time-locked outputs as well as non time-locked outputs.
2024-10-11 10:00:42 -03:00
Boris Nagaev
e04aaa0de0
chanbackup: test encoding of taproot channel 2024-10-11 10:00:42 -03:00
Boris Nagaev
a1aec88268
[lncli] exportchanbackup single channel in hex
It used to be base64, which is not compatible with verifychanbackup,
expecting hex.
2024-10-11 10:00:42 -03:00
Oliver Gugger
136cb42457
Merge pull request #9168 from feelancer21/fix-lncli-wallet-proto
lnrpc: fix lncli documentation tags in walletkit.proto
2024-10-11 06:58:54 -06:00
Keagan McClelland
819239c5c8
lnwallet: inline processUpdateFee and remove the function entirely
In this commit we observe that the previous commit reduced the role
of this function to a single assignment statement with numerous newly
irrelevant parameters. This commit makes the choice of inlining it at
the two call-sites within evaluateHTLCView and removing the funciton
definition entirely. This also allows us to drop a huge portion of
newly irrelevant test code.
2024-10-11 11:25:58 +02:00
Keagan McClelland
05347c8392
lnwallet: bring processFeeUpdate in line with process[Add|Remove]Entry
This commit redoes the API and semantics of processFeeUpdate to make
it consistent with the semantics of it's sister functions. This is
part of an ongoing series of commits to remove mutateState arguments
pervasively from the codebase.

As with the previous commit this makes state mutation the caller's
responsibility. This temporarily increases code duplication at the
call-sites, but this will unlock other refactor opportunities.
2024-10-11 11:25:58 +02:00
Keagan McClelland
49add0f57b
lnwallet: eliminate inner-most layer of evil mutateState nonsense
This commit begins the process of moving towards a more principled
means of state tracking. We eliminate the mutateState argument from
processAddEntry and processRemoveEntry and move the responsibility
of mutating said state to the call-sites.

The current call-sites of these functions still have their *own*
mutateState argument which will be eliminated during upcoming commits.
However, following the principle of micro-commits I opted to break
these changes up to make review simpler.
2024-10-11 11:25:52 +02:00
Keagan McClelland
71da6b5336
lnwallet: consolidate redundant cases using Dual.ForParty 2024-10-11 11:12:36 +02:00
Keagan McClelland
b457b40f80
lnwallet: pack paymentDescriptor add/remove heights into Duals
The purpose of this commit is to begin the process of packing
symmetric fields into the newly introduced Dual structure. The
reason for this is that the Dual structure has a handy indexing
method where we can supply a ChannelParty and get back a value.
This will cut down on the amount of branching code in the main
lines of the codebase logic, making it easier to follow what is
going on.
2024-10-11 11:10:37 +02:00
feelancer21
5290598f1b
lnrpc: fix lncli documentation tags in walletkit.proto
Fixes the tag for `wallet pendingsweeps` and adds the tag for
`wallet estimatefeerate`.
2024-10-08 18:19:36 +02:00
Oliver Gugger
0dd58ee529
Merge pull request #9033 from ziggie1984/non-anchor-channel-bump
error out when non-anchor channels are tried to bump the force close fee.
2024-10-07 23:30:17 -06:00
Olaoluwa Osuntokun
bdc5187749
Merge pull request #9159 from ziggie1984/fn-set-addon
fn: Add Size and IsEmpty methods to Set
2024-10-07 17:30:05 +02:00
Olaoluwa Osuntokun
8e451cb8be
Merge pull request #9144 from ziggie1984/add-rpcclient-logging
lnd: add logger for rpcclient
2024-10-07 11:05:00 +02:00
Olaoluwa Osuntokun
e9fbbe2528
Merge pull request #9143 from ellemouton/rb-set-bit-required
feature+rpcserver: add SetBit helper to set dependent bits
2024-10-04 19:16:34 -07:00
Olaoluwa Osuntokun
e2c97ede81
Merge pull request #9141 from starius/goroutines2
fn: add goroutine manager
2024-10-04 19:15:46 -07:00
Olaoluwa Osuntokun
35759dea1b
Merge pull request #9117 from Liongrass/patch-6
Update link in musig2.md
2024-10-04 19:09:16 -07:00
Olaoluwa Osuntokun
b8c828d730
Merge pull request #9135 from ProofOfKeags/fn/slice-uncons
[MICRO]: fn: add uncons, unsnoc and its component projections
2024-10-04 19:08:58 -07:00
Keagan McClelland
4e1579cf4c
fn: add uncons, unsnoc and its component projections 2024-10-04 14:47:11 -06:00
Elle
d90871bee7
Merge pull request #9161 from ellemouton/fixInvalidBlindingEncodeSig
channeldb/mig/lnwire21: fix FailInvalidBlinding Encode sig
2024-10-04 12:52:49 +02:00
Elle Mouton
8f6d336ab2
channeldb/mig/lnwire21: fix FailInvalidBlinding Encode sig
fix the signature of the Encode method so that it does in fact implement
the Serializable interface defined in the lnwire21 package. This differs
slightly from the interface defined in the main lnwire package.
2024-10-04 10:31:46 +02:00
ziggie
42e8a43375
fn: Add Size and IsEmpty methods to Set 2024-10-04 01:07:47 +02:00
Olaoluwa Osuntokun
42500b2129
Merge pull request #9154 from ziggie1984/master
multi: bump btcd version.
2024-10-03 15:41:59 -07:00
ziggie
4402137fb4
multi: bump btcd version.
The new SignCompact return values had to be adopted across the
code base.
2024-10-03 21:56:21 +02:00
Olaoluwa Osuntokun
2d333178c3
Merge pull request #8960 from lightningnetwork/0-19-staging-rebased
[custom channels 5/5]: merge custom channel staging branch into master
2024-10-03 10:07:16 -07:00
Olaoluwa Osuntokun
bdaa9f4146
sweep: ensure we factor in extra change addrs in MaxFeeRateAllowed 2024-10-02 18:10:14 -07:00
George Tsagkarelis
f2adabb989
docs: update release notes 2024-10-02 18:10:12 -07:00
Olaoluwa Osuntokun
8494887adc
lnd: signal taproot overlay chans based on config
We also add a sanity check to make sure they can't be signaled without
the aux interfaces.
2024-10-02 18:10:11 -07:00
Olaoluwa Osuntokun
c6ba5d11f1
lncfg: add new config option for taproot overlay chans 2024-10-02 18:10:10 -07:00
Olaoluwa Osuntokun
2395c4d0a1
rpc+funding: add taproot overlay as RPC chan type 2024-10-02 18:10:09 -07:00
Olaoluwa Osuntokun
d72de4cdbc
lnrpc: add SIMPLE_TAPROOT_OVERLAY feature bit 2024-10-02 18:10:08 -07:00
Olaoluwa Osuntokun
18521dbe0c
funding: add chan type awareness for new taproot chans overlay 2024-10-02 18:10:07 -07:00
Olaoluwa Osuntokun
0ee9b02094
lnwallet: add awareness of taproot overlay chan type to reservations 2024-10-02 18:10:06 -07:00
Olaoluwa Osuntokun
f21c1165a0
feature: add awareness of new taproot chans overlay feature bit
This bit will be false by default in current production deployments.
2024-10-02 18:10:05 -07:00
Olaoluwa Osuntokun
7f9268c38f
lnwire: add new taproot chans overlay feature bit 2024-10-02 18:10:04 -07:00
Olaoluwa Osuntokun
4920bf6e1a
contractcourt: integration aux sweeper to breach arb
Similar to the sweeper, when we're about to make a new breach
transaction, we ask the sweeper for a new change address, if it has one.
Then when we go to publish, we notify broadcast.
2024-10-02 18:10:03 -07:00
Olaoluwa Osuntokun
c59f11168b
contractcourt: update makeBreachedOutput to accept resolution blob 2024-10-02 18:10:02 -07:00
Olaoluwa Osuntokun
f81cd79bca
contractcourt: update GenSweepScript to return internal key
For the upcoming aux sweeper integration, the internal key is needed for
the call backs.
2024-10-02 18:10:01 -07:00
Olaoluwa Osuntokun
9a181105e2
multi: hook up new aux interfaces 2024-10-02 18:10:00 -07:00
Olaoluwa Osuntokun
47f728e548
contractcourt: pause resolution for HTLCs w/ custom records
This is a hold over until the aux resolution is finalized for HTLC
outputs.
2024-10-02 18:09:59 -07:00
Olaoluwa Osuntokun
eaea11e48f
sweep: update sweeper to use AuxSweeper to add extra change addr
In this commit, we start to use the AuxSweeper (if present) to obtain a
new extra change addr we should add to the sweeping transaction. With
this, we'll take the set of inputs and our change addr, and then maybe
gain a new change addr to add to the sweep transaction.

The extra change addr will be treated as an extra required tx out,
shared across all the relevant inputs. This'll also be used in
NeedWalletInput to make sure that we add an extra input if needed to be
able to pay for the change addr.
2024-10-02 18:09:58 -07:00
Olaoluwa Osuntokun
cb93f8c01a
sweep: add new AuxSweeper interface
In this commit, we add a new AuxSweeper interface. This'll take a set of
inputs, and a change addr for the sweep transaction, then optionally
return a new sweep output to be added to the sweep transaction.

We also add a new NotifyBroadcast method.  This'll be used to notify
that we're _about_ to broadcast a sweeping transaction. The set of
inputs is passed in, which allows the caller to prepare for the ultimate
broadcast of the sweeping transaction.

We also add ExtraTxOut to BumpRequest pass fees to NotifyBroadcast. This
allows the callee to know the total fee of the sweeping transaction.
2024-10-02 18:09:57 -07:00
Olaoluwa Osuntokun
d52d30d46e
server+sweep: convert GenSweepScript to use new addr type
We convert it to use lnwallet.AddrWithKey, as in the future, knowing the
internal key for an address will be useful.
2024-10-02 18:09:56 -07:00
Olaoluwa Osuntokun
88ae4cbb21
contractcourt: set resolution blob in commitSweepResolver 2024-10-02 18:09:55 -07:00
Olaoluwa Osuntokun
bf3cf9ef3c
input: add ResolutionBlob method to inputKit
We also update breachedOutput w/ the new API.
2024-10-02 18:09:54 -07:00
Olaoluwa Osuntokun
3f50339898
input: refactor all inputs to use MakeBaseInput, add opts
In this commit, we refactor all the other constructors for the input to
use MakeBaseInput. We also add a new set of functional options as well.
This'll be useful later on to ensure that new options are properly
applied to all the input types.
2024-10-02 18:09:53 -07:00
Olaoluwa Osuntokun
e0ced8e629
lnwallet+peer: move internalKeyForAddr to lnwallet package
This way we can re-use it. We also make it slightly more generalized.
2024-10-02 18:09:52 -07:00
Olaoluwa Osuntokun
f74d1ce53b
contractcourt: add CommitBlob to taprootBriefcase
This'll be used to store the extra resolution information for the
commitment outputs.
2024-10-02 18:09:51 -07:00
Olaoluwa Osuntokun
4619cefc8f
lnwallet: add new aux resolver interface
This will be used by external callers to modify the way we resolve
contracts on chain. For a given contract, we'll store an extra "blob",
that will later be presented during the sweeping phase.
2024-10-02 18:09:50 -07:00
Olaoluwa Osuntokun
0f2c16de99
contractcourt: convert taprootBriefcase to use new tlv record type
This commit doesn't yet go all the way to modify all the other records
quite yet.
2024-10-02 18:09:49 -07:00
Olaoluwa Osuntokun
bf0bd64023
lnwire: modify TestLightningWireProtocol to use sub-tests
This way, it's possible to run induvidual tests to target failures.
2024-10-02 18:09:48 -07:00
George Tsagkarelis
d1a2dd6bd0
routing: add htlcAmt to PaymentBandwidth method of TlvTrafficShaper
This commit was added to the 0-19-staging branch recently and therefore
didn't make it into a previous part yet. So it's unrelated to the
changes in this part but is required for the whole custom channel saga.
2024-10-02 18:09:46 -07:00
András Bánki-Horváth
9eb405e9c6
fn: add goroutine manager
The package provides type GoroutineManager which is used to launch goroutines
until context expires or the manager is stopped. Stop method blocks until all
started goroutines stop.

Original code by Andras https://go.dev/play/p/HhRpE-K2lA0

Adjustments and tests by Boris.
2024-10-02 20:29:59 -03:00
ziggie
2915936a39
docs: add release-notes. 2024-10-03 00:38:50 +02:00
ziggie
6387c0ab50
walletrpc: error out for non-anchor chans.
Return an error if a non-anchor channel is tried to be fee-bumped.
2024-10-03 00:37:49 +02:00
Leonhard Weese
2a15f932ac Update link in musig2.md
This document contained a link to a MuSig2 draft BIP, which is no longer available. I have updated the link with the link to BIP327 in the bitcoin/bips repository.

Update musig2.md

i've removed the extra spaces and standardized the formatting for this section
2024-10-02 10:17:16 -07:00
ziggie
7ea2080abe
lnd: add logger for rpcclient 2024-10-02 11:51:24 +02:00
Elle Mouton
4d44d11ece
rpcserver: use SetBit helper for setting Bolt11 blinded bit
This commit uses the new SetBit helper for setting the Bolt11 route
blinding required bit in a generated invoice. This helper will take care
of setting the dependent bits of the feature bit (namely the route
blinding and TLV feature bits and will use the required variant of
both).
2024-10-02 10:52:59 +02:00
Elle Mouton
49a87469db
feature: add SetBit helper to set dependent bits
This commit adds a new SetBit helper function in the features package
along with a test for it.  SetBit sets the given feature bit on the
given feature bit vector along with any of its dependencies. If the bit
is required, then all the dependencies are also set to required,
otherwise, the optional dependency bits are set. Existing bits are only
upgraded from optional to required but never downgraded from required to
optional.
2024-10-02 10:52:59 +02:00
Olaoluwa Osuntokun
9f0cc159ea
Merge pull request #8644 from Roasbeef/remove-sql-mutex-part-deux
kvdb/postgres: remove global application level lock
2024-10-01 18:06:45 -07:00
Elle
f1207ef740
Merge pull request #9001 from ellemouton/namespacedMC
routing: Namespaced Mission Control
2024-10-01 20:21:53 +02:00
Elle Mouton
83c1d731e7
docs: add release note entry 2024-10-01 18:02:26 +02:00
Elle Mouton
8126930d6c
routing: support multiple namespaced MissionControls 2024-10-01 14:11:05 +02:00
Elle Mouton
5eff056933
routing: prefix MC logs with namespace 2024-10-01 14:06:54 +02:00
Elle Mouton
f0f4f2df21
routing: separate MissionControl from MissionControlManager
This commit renames the previous MissionControl to MissionController and
the previous MissionController interface to MissionControlQuerier. This
is done because soon the (new) MissionController will back multiple
namespaced MissionControl instances.  For now, it just houses a single
MissionControl in the default namespace.

This commit also replaces the MissionControl's `now` function with a
`clock.Clock`.
2024-10-01 14:06:50 +02:00
Elle Mouton
28a828a11e
routing: unexport MissionControl mutex
Only the MissionControl instance should use this variable and it should
not be accessible to users of MissionControl.
2024-10-01 13:50:50 +02:00
Elle Mouton
6b449a6633
routing: start writing and reading from namespaced MC
and invoke the associated mission control migration.
2024-10-01 13:50:50 +02:00
Elle Mouton
bfe4a08341
channeldb/migration33: migrate MC store pairs to default namespace
In this commit, the mission control store is migrated such that all
existing pairs which are currently stored directly in the top level
results bucket are now instead moved to a "default" namespace bucket.

Note that this migration is not yet invoked in this commit. The
migration will be invoked in the same commit that starts writing and
reading the new format.
2024-10-01 13:50:50 +02:00
Elle Mouton
2dd9046622
routing: introduce missionControlDB abstraction
So that `missionControlStore` can be unaware of the backing DB structure
it is writing to. In an upcoming commit when we change mission control
to write to namespaced buckets instead, we then only need to update the
`namespacedDB` implementation.
2024-10-01 13:50:50 +02:00
Elle
75eaaf7c5c
Merge pull request #8911 from ellemouton/reduceMCRouteEncoding
routing+channeldb: use a more minimal encoding for MC routes
2024-10-01 13:49:11 +02:00
Viktor Tigerström
52b3a06733
lnd: allow shutdown signal during IsSynced check
Prior to this commit, lnd could become unresponsive to shutdown signals
during the `IsSynced` check. Since the `IsSynced` check can occasionally
take a long time to complete, this could result in lnd failing to shut
down promptly.
2024-10-01 11:42:47 +02:00
Elle Mouton
34303e77a1
docs: add release notes entry 2024-10-01 10:53:48 +02:00
Olaoluwa Osuntokun
43a1ca4f3d
kvdb/postgres: remove global application level lock
In this commit, we remove the global application level lock from the
postgres backend. This lock prevents multiple write transactions from
happening at the same time, and will also block a writer if a read is on
going. Since this lock was added, we know always open DB connections
with the strongest level of concurrency control available:
`LevelSerializable`. In concert with the new auto retry logic, we ensure
that if db transactions conflict (writing the same key/row in this
case), then the tx is retried automatically.

Removing this lock should increase perf for the postgres backend, as now
concurrent write transactions can proceed, being serialized as needed.
Rather then trying to handle concurrency at the application level, we'll
set postgres do its job, with the application only needing to retry as
necessary.
2024-09-30 16:58:46 -07:00
Elle Mouton
383a6d274f
routing+channeldb: migrate MC store to use minimal Route encoding
Add a new mcRoute type that houses the data about a route that MC
actually uses. Then add a migration (channeldb/migration32) that
migrates the existing store from its current serialisation to the new,
more minimal serialisation.
2024-09-30 11:40:25 +02:00
Elle Mouton
96445f99b4
channeldb: boiler plate code for migration32
In preparation for the commit which will add the main logic for
migration 32 (which will migrate the MC store to use a more minimal
encoding), this commit just adds some of the code that the migration
will need to the package.
2024-09-30 11:40:25 +02:00
Elle Mouton
82ae0220c8
lnwire21: add custom records parsing
We add the new custom records encoding/decoding logic to the "frozen"
lnwire21 package. We can do this because nothing uses this logic yet. If
the custom records logic changes, the changes should _not_ be added to
the lnwire21 version.
2024-09-30 08:59:02 +02:00
Elle Mouton
33ab4b9db9
lnwire21: add CodeInvalidBlinding
To prevent the need to copy the entire onion_error.go file for a new
Mission Control migration, this commit just updates the existing
lnwire21/onion_error.go file with the new CodeInvalidBlinding code. The
lnwire21 should not really ever be updated but adding a new code should
be fine as it does not affect old migrations since this is a new code.
2024-09-30 08:58:15 +02:00
Olaoluwa Osuntokun
1acc8393bc
Merge pull request #9087 from ProofOfKeags/fn/list-filter
fn: add Filter to List
2024-09-28 12:47:04 +09:00
Keagan McClelland
f7264e6a5b
fn: add Filter to List
This commit adds an immutable Filter method to the linked List API.
This is useful because there are several instances wherein we iterate
through the linked List and only process a subset of it in some way
or another.
2024-09-26 14:24:36 -06:00
Olaoluwa Osuntokun
6485a816d1
Merge pull request #9134 from ellemouton/checkPayAddrBeforeDeref
routerrpc: check payaddr before using for probing
2024-09-26 14:50:29 +09:00
Elle Mouton
8663950c68
feature: remove b11 feature bit from default invoice set 2024-09-25 14:32:52 +09:00
Elle Mouton
f1015cd58d
server: stop interceptable switch in Stop 2024-09-25 14:32:52 +09:00
Elle Mouton
0bd76ffe32
invoices: init quit channel of modifier
Also add atomic start and stop vars to prevent close of a closed
channel.
2024-09-25 14:32:29 +09:00
Elle Mouton
7dc86acb8c
multi: update PaymentAddr to use fn.Option
Since it is allowed to not be set and so can lead to nil deref panics if
it is a pointer.
2024-09-25 11:14:35 +09:00
Oliver Gugger
84c91f701c
Merge pull request #9062 from Roasbeef/htlc-resolution-sweeper
contractcourt: use the sweeper for HTLC offered remote timeout resolu…
2024-09-19 09:43:16 -06:00
Oliver Gugger
611852fd34
Merge pull request #9123 from guggero/bump-18-99
build: bump master to version v0.18.99-beta
2024-09-19 03:57:11 -06:00
Oliver Gugger
4ff7d77a79
Merge pull request #9095 from lightningnetwork/extract-part4-from-staging-branch
[custom channels 4/5]: Extract PART4 from mega staging branch
2024-09-19 03:45:01 -06:00
Oliver Gugger
e0b4601fb9
multi: add co-op close custom data to close update
With this commit we populate additional information about the close
outputs (including potential custom channel data) in the close update
RPC message.
This will allow custom channels to find out how the additional close
outputs look like on chain and what data they might commit to.

We also hook up the aux custom data formatter, so it can format the
custom channel data to JSON.
2024-09-19 10:18:41 +02:00
Olaoluwa Osuntokun
8dee76a1b8
peer: decorate delivery addr w/ internal key
In this commit, we move to add the internal key to the delivery addr. This way, we give the aux chan closer the extra information it may need to properly augment the normal co-op close process.
2024-09-19 10:18:41 +02:00
Olaoluwa Osuntokun
44ab7e6b10
server+peer: init peer struct w/ AuxChanCloser if present 2024-09-19 10:18:41 +02:00
Olaoluwa Osuntokun
625d426a56
lnwallet: modify CoopCloseBalance to not depend on chan commit 2024-09-19 10:18:40 +02:00
Olaoluwa Osuntokun
8d651b9370
lnwallet/chancloser: add aux chan closer, use in coop flow 2024-09-19 10:18:40 +02:00
Olaoluwa Osuntokun
7ff251ca44
lnwallet/chancloser: add new AuxChanCloser interface 2024-09-19 10:18:40 +02:00
Olaoluwa Osuntokun
117a144f4e
lnwallet: add ability to do custom sort for coop close txn 2024-09-19 10:18:40 +02:00
Olaoluwa Osuntokun
517608ca3b
lnwallet: add ability to add extra co-op close outputs 2024-09-19 10:18:40 +02:00
Olaoluwa Osuntokun
7b396f4969
lnwallet: add FundingBlob method to LightningChannel 2024-09-19 10:18:40 +02:00
Olaoluwa Osuntokun
099f5566bc
lnwire: add CustomRecords to shutdown message 2024-09-19 10:18:39 +02:00
Oliver Gugger
dc32a48246
build: bump master to version v0.18.99-beta
As is customary when preparing for the next major (or minor) release, we
bump the version to .99 to allow us to set the minimum required version
to something like v0.18.4-beta in lndclient and it would still accept
the master branch (even if that target version hasn't been
released/tagged yet).
2024-09-19 09:33:54 +02:00
ffranr
9a972e1b0c
itest: add basic invoice HTLC modifier integration test
This commit introduces a basic integration test for the invoice
HTLC modifier. The test covers scenarios where an invoice is settled with a
payment that is less than the invoice amount, facilitated by the invoice
HTLC modifier.
2024-09-19 09:21:38 +02:00
Oliver Gugger
d37df75bc0
lnrpc+rpcserver: encode custom records as custom channel data
With this commit we encode the custom records as a TLV stream into the
custom channel data field of the invoice HTLC.
This allows the custom data parser to parse those records and replace it
with human-readable JSON on the RPC interface.
2024-09-19 09:21:38 +02:00
ffranr
bbae7148aa
multi: pass UpdateAddHtlc message custom records to invoice modifier 2024-09-19 09:21:38 +02:00
Oliver Gugger
83923c7f33
lnwire: add MergedCopy method to CustomRecords 2024-09-19 09:21:38 +02:00
ffranr
0c6a1558d5
lntest: add HtlcModifier support to node RPC harness
This commit enhances the itest LND node harness to include support for
the new `HtlcModifier` RPC endpoint.
At the same time we move another method to the correct file.
2024-09-19 09:21:38 +02:00
ffranr
1975fa60e2
invoicesrpc: add HtlcModifier RPC endpoint and modifier RPC server
This commit introduces a singleton invoice HTLC modifier RPC server and
an endpoint to activate it. The server interfaces with the internal
invoice HTLC modifier interpreter, handling the marshalling between RPC
types and internal formats.
2024-09-19 09:21:37 +02:00
ffranr
008d265cdb
invoicesrpc: add HTLC modifier to invoices RPC server
This commit integrates the HTLC modifier service into the
invoices RPC server.
2024-09-19 09:21:37 +02:00
ffranr
481dfe21bc
lnd: initialize invoice settlement interceptor at server startup
This commit initiates the invoice settlement interceptor during the
main server startup, assigning it a handle within the server.
2024-09-19 09:21:37 +02:00
ffranr
c58b6a25a2
invoices: integrate settlement interceptor with invoice registry
This commit updates the invoice registry to utilize the settlement
interceptor during the invoice settlement routine. It allows the
interceptor to capture the invoice, providing interception clients an
opportunity to determine the settlement outcome.
2024-09-19 09:21:37 +02:00
ffranr
b8c8774b5d
invoices: add invoice htlc interceptor service
This commit introduces a new invoice htlc interceptor service
that intercepts invoice HTLCs during their settlement phase. It forwards
HTLCs to a subscribed client to determine their settlement outcomes.

This commit also introduces an interface to facilitate integrating the
interceptor with other packages.
2024-09-19 09:21:36 +02:00
Oliver Gugger
cdad5d988d
Merge pull request #9072 from lightningnetwork/extract-part3-from-staging-branch
[custom channels 3/5]: Extract PART3 from mega staging branch
2024-09-19 01:20:55 -06:00
Olaoluwa Osuntokun
13a7becb91
Merge pull request #8044 from ellemouton/g175Messages
[1/7] lnwire: add new Gossip 1.75 messages
2024-09-18 16:05:38 -07:00
Oliver Gugger
52e50d807d
htlcswitch: override amount check on custom records 2024-09-18 19:07:27 +02:00
Oliver Gugger
cdc3a4a6c6
channeldb: add NextHeight, fix formatting 2024-09-18 19:07:27 +02:00
Oliver Gugger
d49da574e3
lnd: add aux data parser
This commit adds an optional data parser that can inspect and in-place
format custom data of certain RPC messages.
We don't add an implementation of the interface itself, as that will be
provided by external components when packaging up lnd as a bundle with
other software.
2024-09-18 19:07:27 +02:00
Oliver Gugger
5e1a98cd43
lnrpc+rpcserver: add and populate custom channel data 2024-09-18 19:07:26 +02:00
Jonathan Harvey-Buschel
ea83300942
lnwallet: sort sig jobs before submission
To make sure we attempt to read the results of the sig batches in the
same order they're processed, we sort them _before_ submitting them to
the batch processor.
Otherwise it might happen that we try to read on a result channel that
was never sent on because we aborted due to an error.
We also use slices.SortFunc now which doesn't use reflection and might
be slightly faster.
2024-09-18 19:07:26 +02:00
Olaoluwa Osuntokun
83fdbda2fa
multi: obtain+verify aux sigs for all second level HTLCs
In this commit, we start to use the new AuxSigner to obtain+verify aux sigs for all second level HTLCs. This is similar to the existing SigPool, but we'll only attempt to do this if the AuxSigner is present (won't be for most channels).
2024-09-18 19:04:53 +02:00
Elle Mouton
077273e66e
docs: update release notes 2024-09-18 16:20:28 +02:00
Elle Mouton
a62201b61d
netann: add chan update 2 validate and verify funcs 2024-09-18 16:20:28 +02:00
Elle Mouton
580c10477f
lnwire: add ChannelUpdate2
Add the new ChannelUpdate2 message and ensure that it implements the
ChannelUpdate interface.
2024-09-18 16:20:28 +02:00
Elle Mouton
21c9ef8904
netann: validation and verification funcs for ChannelAnnouncement2 2024-09-18 16:14:59 +02:00
Elle Mouton
5fc1da3abe
netann: add MsgHash helper
This commit adds the MsgHash helper function which can be used to
calculate the digest of a message to be signed using schnorr signatures.
2024-09-18 16:14:59 +02:00
Elle Mouton
a438eb3af3
lnwire: add ChannelAnnouncement2 message
And ensure that it implements the ChannelAnnouncement interface.
2024-09-18 16:14:59 +02:00
Elle Mouton
66302ceb29
lnwire: make RawFeatureVector a Record producer 2024-09-18 16:14:58 +02:00
Elle Mouton
0b4e5a0d83
lnwire: add AnnounceSignatures2 message
And ensure that it implements the AnnounceSignatures interface.
2024-09-18 16:14:58 +02:00
Elle Mouton
ced88a9978
netann: let ChannelUpdate validate methods take in the new interface 2024-09-18 16:14:58 +02:00
Elle Mouton
35d0c61c12
netann: let ValidateChannelAnn take the new interface 2024-09-18 16:14:58 +02:00
Elle Mouton
34e9ee1ee5
lnwire: lnwire: add a ChannelUpdate interface
In this commit, a new ChannelUpdate interface is added and
ChannelUpdate1 is made to implement the new interface.
2024-09-18 16:14:58 +02:00
Elle Mouton
7bbf89625f
multi: move ChannelUpdate validate methods to netann 2024-09-18 16:13:18 +02:00
Elle Mouton
e07d23567c
lnwire: add a ChannelAnnouncement interface
Add a new ChannelAnnouncement interface and ensure that
ChannelAnnouncement1 implements it.
2024-09-18 16:13:17 +02:00
Elle Mouton
615db1fc2e
multi: move channel announcement validation to netann
from the graph package.
2024-09-18 16:13:17 +02:00
Elle Mouton
a6bf76a0b7
discovery+lnwallet: add fetchPKScript helper to gossiper
This commit makes an lnwallet.BlockChainIO available to the gossiper and
uses it to construct a helper that can be used to fetch the pk script
for a given SCID. This will be used for channel announcement
verification in an upcoming commit.
2024-09-18 16:13:17 +02:00
Elle Mouton
9be84c1bdc
graph+lnwallet: move FetchTx logic to lnwallet
So that it can be re-used elsewhere.
2024-09-18 16:13:17 +02:00
Elle Mouton
f230e2c574
lnwire: add AnnounceSignatures interface
Add a AnnounceSignatures interface and ensure that AnnounceSignatures1
implements it.
2024-09-18 16:13:17 +02:00
Elle Mouton
df65b7cad9
lnwire: add FirstBlock and BlockRange to GossipTimestampRange
Add new FirstBlockHeight and BlockRange TLV fields to the
GossipTimestampRange message. This will be used to query for Gossip 1.75
messages which are timestamped using block height instead of Unix
timestamps.
2024-09-18 16:13:17 +02:00
Elle Mouton
60b0e46c36
lnwire: add btc and node announcement nonces to channel_ready
In preparation for Gossip 1.75, we add new TLV's to the `ChannelReady`
message. Namely: `AnnouncementBitcoinNonce` and `AnnouncementNodeNonce`.
These will be used to exchange nones required for producing the partial
signature to be send in the `AnnouncementSignatures2` message.
The type numbers for these new fields are even because if they are set,
then a peer is expecting its peer to understand gossip 1.75 and the new
fields.
2024-09-18 16:13:17 +02:00
Elle Mouton
60f331edb1
multi: rename ChannelUpdate to ChannelUpdate1
In preparation for adding a new ChannelUpdate2 message and a
ChannelUpdate interface, we rename the existing message to
ChannelUpdate1.
2024-09-18 16:13:17 +02:00
Elle Mouton
bb44efa21f
multi: rename ChannelAnnouncement to ChannelAnnouncment1
In preparation for adding the new ChannelAnnouncement2 message along
with a ChannelAnnouncement interface, we rename the existing message to
ChannelAnnouncement1.
2024-09-18 16:13:17 +02:00
Elle Mouton
05d76b696d
multi: rename AnnounceSignatures to AnnounceSignatures1
In preparation for adding a new message, AnnounceSignatures2 along with
an AnnounceSignatures interface, we rename the existing message to
AnnounceSignatures1.
2024-09-18 16:13:16 +02:00
Oliver Gugger
838a32d13c
Merge pull request #9116 from xixishidibei/master
bug: fix incorrect parameters in test cases
2024-09-18 04:04:28 -06:00
Oliver Gugger
bd84fd256e
lnwire: add custom records field to type CommitSig 2024-09-18 10:25:42 +02:00
Olaoluwa Osuntokun
1e85c5054e
lnwallet: add WithAuxSigner option to channel 2024-09-18 10:25:42 +02:00
Oliver Gugger
f52a163334
lnwallet: clarify usage of cancel and response channels 2024-09-18 10:25:42 +02:00
Oliver Gugger
953fb073d4
lnwallet: allow read-only access to HtlcView's HTLCs
Due to a recent refactor, the HTLCs are no longer an exported type.
Custom channels need access to those updates, so we provide them in a
read-only manner.
2024-09-18 10:25:42 +02:00
Olaoluwa Osuntokun
aa0c680e18
lnwallet: add new AuxSigner interface to mirror SigPool
In this commit, we add a new aux signer interface that's meant to mirror the SigPool. If present, this'll be used to (maybe) obtain signatures for second level HTLCs for certain classes of custom channels.
2024-09-18 10:25:42 +02:00
Oliver Gugger
0b64b80642
funding: inform aux controller about channel ready/finalize 2024-09-18 10:25:41 +02:00
Oliver Gugger
5c854a2f53
multi: add tapscript root to gossip message 2024-09-18 10:25:41 +02:00
Olaoluwa Osuntokun
bcb66585d4
funding+lnwallet: finish hook up new aux funding flow
For the initiator, once we get the signal that the PSBT has been
finalized, we'll call into the aux funder to get the funding desc. For
the responder, once we receive the funding_created message, we'll do the
same.

We now also have local+remote aux leaves for the commitment transaction.

Some old TODO comments that in retrospect aren't required anymore are
removed as well.
2024-09-18 10:25:41 +02:00
Olaoluwa Osuntokun
7ec48a5054
funding+lnwallet: only blind tapscript root early in funding flow
In this commit, we modify the aux funding work flow slightly. We won't
be able to generate the full AuxFundingDesc until both sides has
sent+received funding params. So we'll now only attempt to bind the
tapscript root as soon as we send+recv the open_channel message.

We'll now also make sure that we pass the tapscript root all the way
down into the musig2 session creation.
2024-09-18 10:25:41 +02:00
Olaoluwa Osuntokun
bed4562584
lnwallet: for PsbtIntent return the internal key in the POutput
We also add a new assertion to the itests to ensure the field is being properly set.
2024-09-18 10:25:41 +02:00
Olaoluwa Osuntokun
7144a1c733
lnwallet: add TaprootInternalKey method to ShimIntent
If this is a taproot channel, then we'll return the internal key which'll be useful to callers.
2024-09-18 10:25:41 +02:00
Olaoluwa Osuntokun
65f54cb075
config+serer: add AuxFundingController as top level cfg option 2024-09-18 10:25:40 +02:00
Olaoluwa Osuntokun
84cc9a1f0b
funding: create new AuxFundingController interface
In this commit, we make a new `AuxFundingController` interface capable of processing messages off the wire. In addition, we can use it to abstract away details w.r.t how we obtain a `AuxFundingDesc` for a given channel.

We'll now use this whenever we get a channel funding request, to make sure we pass along the custom state that a channel may require.
2024-09-18 10:25:40 +02:00
Olaoluwa Osuntokun
72beb7955d
lnwallet: use AuxFundingDesc to populate all custom chan info
With this commit, we'll now populate all the custom channel information within the OpenChannel and ChannelCommitment structs.
2024-09-18 10:25:40 +02:00
Olaoluwa Osuntokun
116a6430f0
lnwallet: add new AuxFundingDesc struct
This struct will house all the information we'll need to do a class of custom channels that relies primarily on adding additional items to the tapscript root of the HTLC/commitment/funding outputs.
2024-09-18 10:25:40 +02:00
Oliver Gugger
08a7290b81
Merge pull request #9103 from ziggie1984/signing-release
Documentation how to sign a LND/LNCLI release by a developer
2024-09-18 01:49:12 -06:00
Oliver Gugger
191688f316
Merge pull request #9111 from Liongrass/patch-2
Fix links to resolve issue in Builder's Guide (https and redirect)
2024-09-18 01:45:43 -06:00
Oliver Gugger
cd6f72d2f2
Merge pull request #9113 from Liongrass/patch-4
Update http to https in c#.md
2024-09-18 01:45:31 -06:00
Oliver Gugger
375338d816
Merge pull request #9114 from Liongrass/patch-5
Update links in javascript.md
2024-09-18 01:45:16 -06:00
xixishidibei
c7505812c5 bug: fix incorrect parameters in test cases
Signed-off-by: xixishidibei <xixishidibei@outlook.com>
2024-09-18 14:58:41 +08:00
ziggie
eba481e790
docs: add instructions how to sign a LND release. 2024-09-18 01:31:12 +02:00
Leonhard Weese
923153d463
Update links in javascript.md
I updated two `gprc.io` resources.

Additionally the article on [javascript protocol buffers](https://developers.google.com/protocol-buffers/docs/reference/javascript-generated) is no longer available. I believe [this article](https://protobuf.dev/protobuf-javascript/) replaces it and that Google has moved the protobuf documentation to another domain.
2024-09-18 03:34:35 +08:00
Leonhard Weese
ab71c0cf40
Update http to https in c#.md
This pull request changes an outdated link as it causes issue [#670](https://github.com/lightninglabs/docs.lightning.engineering/pull/670). The document is mirrored on the Builder's Guide repo
2024-09-18 03:28:05 +08:00
Leonhard Weese
e494d7401a
Fix links to resolve issue in Builder's Guide (https and redirect)
There are some links in this document that cause issues for pull request [#670](https://github.com/lightninglabs/docs.lightning.engineering/pull/670) in the Builder's Guide Repo.
2024-09-18 03:18:45 +08:00
ziggie
f5f52a65c9
scripts: rename PGP key file.
The name in the verify-install.sh and the PGP key file need to be
the same.
2024-09-16 10:49:18 +02:00
Oliver Gugger
edd9ade7e5
Merge pull request #9017 from jharveyb/log_comress_zstd
log: bump logrotate dep, switch to zstd compressor
2024-09-15 01:36:11 -06:00
Yong
da7f8aeb61
Merge pull request #9099 from lightningnetwork/kvdb-wallet-db
kvdb: update to latest walletdb version
2024-09-15 15:22:15 +08:00
Olaoluwa Osuntokun
903c8fc076
contractcourt: use the sweeper for HTLC offered remote timeout resolution
In this commit, we bring the timeout resolver more in line with the
success resolver by using the sweeper to handle the HTLC offered remote
timeout outputs. These are outputs that we can sweep directly from the
remote party's commitment transaction when they broadcast their version
of the commitment transaction.

With this change, we slim down the scope slightly by only doing this for
anchor channels. Non-anchor channels will continue to use the
utxonursery for this output type for now.
2024-09-12 18:13:44 -07:00
Olaoluwa Osuntokun
c898b68bba
contractcourt: use t.Run in TestHtlcTimeoutResolver
Along the way we refactor the test to eliminate some unnecessary line
length.
2024-09-12 18:13:42 -07:00
Olaoluwa Osuntokun
06b3588512
kvdb: update to latest walletdb version 2024-09-12 16:41:43 -07:00
Jonathan Harvey-Buschel
0a451b6d36
docs: add release notes 2024-09-12 12:31:03 -04:00
Jonathan Harvey-Buschel
b3c25166ed
build+mod: add zstd compressor support 2024-09-12 12:31:03 -04:00
Jonathan Harvey-Buschel
2f71822fe0
mod: bump logrotate to v1.1.2 2024-09-12 12:31:02 -04:00
Jonathan Harvey-Buschel
f360532eb1
lnd+build: add logcompressor flag 2024-09-12 12:31:02 -04:00
Oliver Gugger
750770e190
Merge pull request #8981 from ProofOfKeags/refactor/payment-descriptor-quarantine
[KILO]: Quarantine paymentDescriptor to the lnwallet package
2024-09-12 09:09:02 -06:00
Oliver Gugger
4fc2f4ae87
Merge pull request #9090 from Roasbeef/bolt-update-sept-2024
kvdb: update bbolt to v1.3.11
2024-09-12 06:15:23 -06:00
Olaoluwa Osuntokun
f8962af444
kvdb: update bbolt to v1.3.11 2024-09-11 10:45:12 -07:00
Oliver Gugger
fd154dd726
Merge pull request #9082 from morehouse/fuzz_reply_channel_range_fixes
lnwire: manually compare Timestamps in fuzz test
2024-09-11 05:27:09 -06:00
Oliver Gugger
a518f7dfbc
Merge pull request #9084 from morehouse/detect_invalid_timestamp_field
lnwire: detect invalid timestamp field
2024-09-11 03:31:01 -06:00
Oliver Gugger
a6f0ec9f00
Merge pull request #9077 from rhg4d9ow35/fix-issue-8897
commands: update interactive input in create command description
2024-09-11 03:28:29 -06:00
Oliver Gugger
a6e65d49cc
Merge pull request #9086 from morehouse/fix_fuzz_probability
routing: skip fuzzing when capacity is 0
2024-09-11 02:34:11 -06:00
CharlieZKSmith
000e3381b1 docs: add release notes 2024-09-10 21:46:02 -07:00
Keagan McClelland
93d17a48a8
lnwallet: remove unnecessary chanID argument form unsignedLocalUpdates 2024-09-10 13:57:05 -07:00
Keagan McClelland
4fadbb09bd
htlcswitch+lnwallet: quarantine paymentDescriptor to lnwallet
The objective of this commit is to make paymentDescriptor a private
data structure so we can quarantine it to the lnwallet package.
To accomplish this we had to prevent it from leaking out via the
arguments or return values of the public functions in lnwallet.
This naturally had consequences for the htlcswitch package as we
choose other mechanisms for tracking the data that paymentDescriptor
was responsible for.

Astoundingly, this was highly successful and allowed us to remove
a ton of redundant code. The diff for this commit represents a
substantial reduction in total lines of code as well as extraneous
arguments and return values from key functions.

This also sets the stage for future commits where we actually will
be attempting to rid lnwallet of paymentDescriptor completely.
2024-09-10 13:56:56 -07:00
Matt Morehouse
8373cca43c
routing: skip fuzzing when capacity is 0
probabilityFormula() is expected to return an error if capacity is 0, so
we should exclude that case from fuzzing.

Previously it was attempted to avoid this case by seeding the corpus
with an input that had capacity 1. That is not an effective solution
since the fuzzer can still generate an input with capacity 0.
2024-09-10 15:17:39 -05:00
Matt Morehouse
532fe05b63
discovery: remove check for incorrect number of timestamps
The check is no longer required, as it is now done during decoding.
2024-09-10 14:52:16 -05:00
Matt Morehouse
c1a6d3ac31
lnwire: fail decoding on incorrect number of timestamps
Currently if an incorrect number of timestamps is given, we fail later
on in the GossipSyncer. It makes more sense to fail right away, since we
already do that for incorrect SCID formats (e.g., unsorted or duplicate
SCIDs). There is already a matching check in Encode for incorrect number
of timestamps, so adding this check to Decode makes things symmetric.
2024-09-10 14:36:30 -05:00
Oliver Gugger
779903af9d
Merge pull request #9080 from ziggie1984/ziggie-pgp
scripts: add gpg key for ziggie [skip ci]
2024-09-10 11:50:59 -06:00
ziggie
82d99f3ebf
scripts: add gpg key for ziggie [skip ci] 2024-09-10 19:40:13 +02:00
Matt Morehouse
475cd6e344
lnwire: manually compare Timestamps in fuzz test
We can't use require.Equal because it considers nil slices and empty
slices to be not equal.
2024-09-10 11:41:31 -05:00
Oliver Gugger
65046561c4
Merge pull request #9079 from ziggie1984/remove-unsupported-nolint-directive
chanfunding: remove unsupported linter directive.
2024-09-10 09:01:50 -06:00
ziggie
6f26f2a5b3
chanfunding: remove unsupported linter directive.
Remove unsupported linter and also change ifelse clause to
switch statement.
2024-09-10 16:32:59 +02:00
Oliver Gugger
556c664984
Merge pull request #9078 from yyforyongyu/itest-log
Makefile: add `-test.v` for `make itest`
2024-09-10 08:04:52 -06:00
yyforyongyu
db332db025
Makefile: add -test.v for make itest 2024-09-10 17:21:03 +08:00
Keagan McClelland
27ff5f085a
lnwallet: remove unused function PayDescsFromRemoteLogUpdates
This function is no longer used as of the last commit and it is the
last remaining leak of the PaymentDescriptor type through the public
API.
2024-09-09 20:59:41 -06:00
Keagan McClelland
dc60f78f7f
htlcswitch: remove PaymentDescriptor conversion from reforwardSettleFails
This is part of a systematic removal of PaymentDescriptor from the mechanics
of the htlcswitch package.
2024-09-09 20:59:41 -06:00
Keagan McClelland
957557a937
htlcswitch+lnwallet: remove PaymentDescriptor from ReceiveRevocation returns
This is part of a systematic removal of PaymentDescriptor from the public
API of the lnwallet package. This marks the last change needed before we
make the PaymentDescriptor structure private.
2024-09-09 20:59:22 -06:00
Keagan McClelland
1ae5705954
htlcswitch: remove PaymentDescriptor from processRemoteSettleFails call signature
This is part of a systematic removal of PaymentDescriptor from the mechanics
of the htlcswitch package.
2024-09-09 17:15:31 -06:00
Keagan McClelland
aa38041240
htlcswitch: remove PaymentDescriptor from processRemoteAdds call signature
This is part of a systematic removal of PaymentDescriptor from the mechanics
of the htlcswitch package.
2024-09-09 17:15:30 -06:00
Keagan McClelland
d881809a4d
htlcswitch: remove PaymentDescriptor from hodlHtlc
This is part of a systematic removal of PaymentDescriptor from the
mechanics of the htlcswitch package.
2024-09-09 16:53:11 -06:00
Keagan McClelland
a0818a0d16
htlcswitch: remove PaymentDescriptor from the processExitHop's call signature
This is part of a systematic removal of PaymentDescriptor from the mechanics
of the htlcswitch package.
2024-09-09 16:53:10 -06:00
Keagan McClelland
07647fe53a
htlcswitch: remove PaymentDescriptor from settleHTLC's call signature
This is done as part of a systematic removal of PaymentDescriptor from
the mechanics of the htlcswitch package.
2024-09-09 16:53:10 -06:00
Keagan McClelland
877d29c717
htlcswitch: remove PaymentDescriptor from sendHTLCError's call signature
This is done as part of a systematic removal of PaymentDescriptor from
the mechanics of the htlcswitch package.
2024-09-09 16:53:10 -06:00
Keagan McClelland
0fb29b2598
channeldb: add convenience functions for generating Source/Dest Refs 2024-09-09 16:53:10 -06:00
Keagan McClelland
1e2bf3e0b3
lnwallet: add function to convert paymentDescriptor to LogUpdate
Here we add a function that is capable of recovering LogUpdates from
paymentDescriptors and we refactor the lnwallet code to use this
rather than doing JIT inline construction of the LogUpdates.
2024-09-09 16:53:08 -06:00
Keagan McClelland
721a0c5edc
lnwire+htlcswitch: change NewInvalidBlinding to use array instead of slice 2024-09-09 16:26:41 -06:00
Keagan McClelland
5505b6daff
htlcswitch: change sendMalformedHTLCError to take array instead of slice 2024-09-09 16:26:41 -06:00
Keagan McClelland
5deb4c253a
htlcswitch+lnwallet: fix OnionBlob to 1366 bytes 2024-09-09 16:26:41 -06:00
Keagan McClelland
df3c6b72ab
lnwallet: track ChannelID on PaymentDescriptor
In this commit we track the ChannelID on the PaymentDescriptor.
This will be useful in upcoming commits that need to be able to
reconstruct lnwire.Message values from PaymentDescriptors as the
Messages that are exchanged to update channel state all include
the ChannelID.
2024-09-09 16:26:41 -06:00
Keagan McClelland
a89e24f487
lnwallet: add ChannelID method to LightningChannel
In this commit we introduce a convenience method to LightningChannel
to allow us to quickly grab the channel id. This will be important
in upcoming commits where we need to remember the ChannelID to
reconstruct update messages.
2024-09-09 16:26:41 -06:00
CharlieZKSmith
7af85d44fd commands: update interactive input in create command description 2024-09-09 09:03:18 -07:00
Oliver Gugger
a895b1c6ca
Merge pull request #9051 from calvinrzachman/export-routing-interface
routing: export dbMPPayment interface
2024-09-09 05:15:38 -06:00
Oliver Gugger
31a6f87c4d
Merge pull request #9049 from lightningnetwork/extract-part2-from-staging-branch
[custom channels 2/5]: Extract PART2 from mega staging branch
2024-09-05 12:08:36 -06:00
George Tsagkarelis
427d41dc1e
itest: add interceptor and first hop data tests 2024-09-05 18:00:53 +02:00
Oliver Gugger
81c8331f82
lnrpc: add first hop custom data to route 2024-09-05 18:00:53 +02:00
George Tsagkarelis
aa17543d23
routing: use first hop records on path finding 2024-09-05 18:00:53 +02:00
Oliver Gugger
4804cbf139
channeldb+routing: persist first hop custom data for route 2024-09-05 18:00:53 +02:00
George Tsagkarelis
c39143052e
lnd: use impl cfg TlvTrafficShaper 2024-09-05 18:00:52 +02:00
Olaoluwa Osuntokun
f04fa54622
routing: skip amtInRange for custom HTLCs
We might be trying to send an invoice amount that's greater than the size of the channel, but once you factor in the custom channel logic, an actual HTLC can be sent over the channel to pay that larger payment.

As a result, we'll skip over this check if a have a custom HTLC.
2024-09-05 18:00:52 +02:00
George Tsagkarelis
5b4de5f0d4
routing: add TlvTrafficShaper to bandwidth hints 2024-09-05 18:00:52 +02:00
George Tsagkarelis
5dcda25881
htlcswitch: expose custom channel blob from link 2024-09-05 18:00:49 +02:00
George Tsagkarelis
5cb68a59c5
lnwallet: expose commitment blob from channel 2024-09-05 11:18:17 +02:00
Oliver Gugger
afdceab400
lnrpc: add first hop custom records to RPC payment info 2024-09-05 11:18:16 +02:00
Oliver Gugger
1b31835230
channeldb+routing: persist first hop custom records
With this commit we make sure the first hop custom records aren't lost
on restart/resume of a payment, so we persist it as part of the
PaymentCreationInfo struct.
2024-09-05 11:18:16 +02:00
George Tsagkarelis
42e358e3d3
lnrpc: add wire records fields to payment+interceptor RPCs 2024-09-05 11:18:16 +02:00
George Tsagkarelis
878f964a33
multi: use wire records on payment and intercept flows 2024-09-05 11:18:16 +02:00
Oliver Gugger
aa86020b84
lnwallet: extract diskCommit, remove unused error return value 2024-09-05 11:18:16 +02:00
ffranr
ccea257c92
itest: add itest for field modification HTLC interception response
Implement an integration test where an HTLC is intercepted and the
interception response modifies fields in the resultant p2p message.
2024-09-05 11:18:16 +02:00
ffranr
fb14d8c96e
routerrpc: extend HTLC forward interceptor resp with modification fields
This commit extends the forward HTLC intercept response with fields that
can be used in conjunction with a `ResumeModified` action to modify the
intercepted HTLC p2p message.
2024-09-05 11:18:15 +02:00
ffranr
abca4b8234
htlcswitch: add resume modified HTLC action to switch
Introduce `ResumeModified` action to resume standard behavior of a p2p
message with optional modifications as specified by the client during
interception.
2024-09-05 11:18:15 +02:00
ffranr
8d1059f41c
lnwire: add custom records field to type UpdateFulfillHtlc
- Introduce the field `CustomRecords` to the type `UpdateFulfillHtlc`.
- Encode and decode the new field into the `ExtraData` field of the
`update_fulfill_htlc` wire message.
- Empty `ExtraData` field is set to `nil`.
2024-09-05 11:18:15 +02:00
ffranr
81f6a8060f
lnwire: add custom records field to type UpdateAddHtlc
- Introduce the field `CustomRecords` to the type `UpdateAddHtlc`.
- Encode and decode the new field into the `ExtraData` field of
  the `update_add_htlc` wire message.
2024-09-05 11:18:15 +02:00
ffranr
af50694643
lnwire: add ExtraOpaqueData helper functions and methods
Introduces a couple of new helper functions for both the ExtraOpaqueData
and CustomRecords types along with new methods on the ExtraOpaqueData.
2024-09-05 11:18:15 +02:00
ffranr
17c0a70b07
lnwire: add unit tests for ExtraOpaqueData.PackRecords 2024-09-05 10:29:42 +02:00
George Tsagkarelis
705e567357
itest: add dynamic scid alias routing test 2024-09-05 10:29:42 +02:00
George Tsagkarelis
70f82bc4ff
routerrpc: add XAddLocalChanAliases & XDeleteLocalChanAliases 2024-09-05 10:29:42 +02:00
Oliver Gugger
604bd81100
aliasmgr: export alias start and end ranges
Because we restrict custom SCID aliases to be in a specific range, we
export the range start and end values so a user of the RPCs we're going
to add in the next commits can adjust their values to fit within the
range.
2024-09-05 08:44:33 +02:00
Oliver Gugger
466f550ddb
aliasmgr: avoid collision when requesting alias
With the new RPC calls that we are going to add in the next commits, it
will be possible for users to add (local only, non-gossipped) SCID
aliases for channels. Since those will be in the same range as the ones
given out by RequestAlias, we need to make sure that when we generate a
new one that it doesn't collide with an already existing one.
2024-09-05 08:44:33 +02:00
Oliver Gugger
80dfaeb16d
aliasmgr: add map type alias 2024-09-05 08:44:33 +02:00
George Tsagkarelis
ea92d0aecc
multi: refresh htlcswitch aliases on aliasmgr update 2024-09-05 08:44:32 +02:00
George Tsagkarelis
e31cdc4db3
aliasmgr: add delete local alias method 2024-09-05 08:44:32 +02:00
Olaoluwa Osuntokun
6747fc13b4
Merge pull request #9063 from guggero/update-sqldb
mod: bump sqldb to latest version
2024-09-04 13:18:21 -07:00
Oliver Gugger
6900fb54e2
Merge pull request #9064 from longhutianjie/master
bug: fix json tag
2024-09-04 05:43:01 -06:00
longhutianjie
26dd72d93a
bug: fix json tag 2024-09-04 18:03:15 +08:00
Oliver Gugger
cb85e6f8ba
mod: bump sqldb to latest version 2024-09-04 09:33:27 +02:00
Oliver Gugger
258cf81240
Merge pull request #9050 from bhandras/native-sql-invoice-fixes
invoices+sqldb: small fixes to address some inconsistencies between KV and native SQL invoice DB implementations
2024-09-04 01:30:52 -06:00
Olaoluwa Osuntokun
ce27e4e62c
Merge pull request #9046 from Roasbeef/taproot-chan-sync-bug-fix
lnwallet: ensure we re-sign retransmitted commits for taproot channels
2024-09-03 17:25:19 -07:00
Olaoluwa Osuntokun
80b257991e
docs/release-notes: add release notes entry for taproot chans bug fix 2024-09-03 17:23:53 -07:00
Olaoluwa Osuntokun
b8e39c2311
lnwallet: expand chan sync tests to cover taproot channels
In this commit, we expand some of the existing chan sync tests to cover
taproot channels (the others already did). Along the way, we always
assert that the `PartialSig` is populated on retransmission. In
addition, we now send the new commit sig rather than the existing
in-memory one to test the new logic that re-signs the commitment.
2024-09-03 17:23:48 -07:00
Olaoluwa Osuntokun
528199839a
lnwallet: extract initMusigNonce from initRevocationWindows
This'll be useful later to make some enhancements to the existing unit
tests.
2024-09-03 17:23:46 -07:00
Olaoluwa Osuntokun
69a1cf4f23
lnwallet: ensure we re-sign retransmitted commits for taproot channels
In this commit, we fix an existing bug with the taproot channel type
that can cause force closes if a peer disconnects while attempting to
send the commitment signature.

Before this commit, since the `PartialSig` we send is never committed to
disk, the version read wouldn't contain the musig2 partial sig. We never
write these signatures to disk, as each time we make a new session, we
need to generate fresh nonces to avoid nonce-reuse.

Due to the above interaction, if we went to re-send a signature after a
disconnection, the `CommitSig` message we sent wouldn't actually contain
a `PartialSigWithNonce`, causing a protocol error.
2024-09-03 17:23:38 -07:00
Andras Banki-Horvath
1f7ac8e853
docs: update docs/release-notes/release-notes-0.18.3.md 2024-09-03 19:42:52 +02:00
Andras Banki-Horvath
06d4267a76
sqldb: fix end date filter when querying invoices
Previously, the SQL implementation of the invoice query simply
converted the start and end timestamps to time and used them
in SQL queries to check for inclusivity. However, this logic
failed when the start and end timestamps were equal.

This commit addresses and corrects this issue.
2024-09-03 19:40:47 +02:00
Andras Banki-Horvath
b57910ee3a
sqldb+invoices: synchronize SQL invoice updater behavior with KV version
Previously SQL invoice updater ignored the set ID hint when updating an
AMP invoice resulting in update subscriptions returning all of the AMP
state as well as all AMP HTLCs. This commit synchornizes behavior with
the KV implementation such that we now only return relevant AMP state
and HTLCs when updating an AMP invoice.
2024-09-03 19:40:46 +02:00
Andras Banki-Horvath
c8de7a1699
channeldb: filter AMP state to relevant set IDs
When fetching an AMP invoice we sometimes filter HTLCs to selected set
IDs, however we always kept the full AMP state which is irrelevant as it
contains state for all AMP payments. This was a side effect of
UpdateInvoice needing to serialize the whole invoice when storing after
an update but it is an unwanted "feature" as users will need to filter
to relevant set when listing an AMP payment or subsribing to an update.
2024-09-03 19:40:46 +02:00
Alex Akselrod
ffbdcc1d5d
invoices: ensure AMP subinvoices are correctly updated w/nativesql 2024-09-03 19:40:46 +02:00
Alex Akselrod
e3a939aa0d
itest: check that AMP subinvoices are correctly updated w/nativesql 2024-09-03 19:40:46 +02:00
Alex Akselrod
f1b7953465
invoices/sqldb: query by ChanID when updating AMP invoice preimage 2024-09-03 19:40:45 +02:00
Oliver Gugger
e8c5e7d5ce
Merge pull request #9044 from ProofOfKeags/refactor/stfu-prefactor
Refactor/stfu prefactor
2024-08-29 09:49:53 -06:00
Oliver Gugger
54eeb0e792
Merge pull request #9045 from ziggie1984/fix-blinded-payment-tc
fix blinded path itest.
2024-08-29 04:51:20 -06:00
ziggie
64fb4cb4ac
itest: fix blinded path itest. 2024-08-29 09:50:46 +02:00
Keagan McClelland
1422729f80
lnwallet+htlcswitch: define expanded NumPendingUpdates
This commit squashes the below operations for a net result where
we have an expanded capability of assessing pending updates. This
is made possible by packing the components into Duals in the prior
commits. We squash the operations to simplify review.

htlcswitch+lnwallet: rename PendingLocalUpdateCount

lnwallet: complete pending update queries API for LightningChannel

lnwallet+htlcswitch: consolidate NumPendingUpdates using ChannelParty

This commit makes the observation that we can cleanly define the
NumPendingUpdates function using a single expression by taking
advantage of the relevant fields being properly packed into Duals.
2024-08-28 14:03:00 -07:00
Keagan McClelland
ce7fcd30f8
lnwallet: pack commitment message indices into Dual
This is yet another commit that packs a symmetric structure into
a Dual. This is the last one needed for the time being to consolidate
Num{X}UpdatesPendingOn{Y} functions into a single one.
2024-08-28 14:02:59 -07:00
Keagan McClelland
2e7fbc446f
lnwallet: pack update logs into Dual
This commit, like the last one packs the update logs into a symmetric
Dual structure. This will allow us to index into them more concisely
in higher order logic.
2024-08-28 13:58:33 -07:00
Keagan McClelland
8077862225
lnwallet: pack commit chains into Dual
This commit packs the LightningChannel's localCommitmentChain and
remoteCommitmentChain into a Dual structure for better symmetric
access. This will be leveraged by an upcoming commit where we want
to more concisely express how we compute the number of pending
updates.
2024-08-28 13:51:23 -07:00
Keagan McClelland
a0515a16db
htlcswitch: extract error handling for syncChanStates 2024-08-28 13:46:52 -07:00
Oliver Gugger
5c3a8e949c
Merge pull request #9030 from lightningnetwork/extract-part1-from-staging-branch
[custom channels 1/5]: extract PART1 from mega staging branch
2024-08-28 07:06:53 -06:00
Oliver Gugger
9dfbde7013
lnwallet: thread thru input.AuxTapleaf to all relevant areas
In this commit, we start to thread thru the new aux tap leaf structures to all relevant areas. This includes: commitment outputs, resolution creation, breach handling, and also HTLC scripts.
2024-08-28 13:28:48 +02:00
Oliver Gugger
2ab22b0f0b
lnwallet: refactor commit keys to use lntypes.Dual 2024-08-28 13:26:18 +02:00
Oliver Gugger
b45d72fe59
multi: thread thru the AuxLeafStore everywhere 2024-08-28 13:26:14 +02:00
Olaoluwa Osuntokun
2510c19024
channeldb: add HtlcIndex to HTLCEntry
This may be useful for custom channel types that base everything off the index (a global value) rather than the output index (can change with each state).
2024-08-28 13:24:04 +02:00
Olaoluwa Osuntokun
669740c84e
channeldb: add custom blobs to RevocationLog+HTLCEntry
This'll be useful for custom channel types that want to store extra information that'll be useful to help handle channel revocation cases.
2024-08-28 13:23:30 +02:00
Olaoluwa Osuntokun
61f276856a
channeldb: convert RevocationLog to use RecordT 2024-08-28 13:22:48 +02:00
Olaoluwa Osuntokun
1b1e7a6168
channeldb: convert HTLCEntry to use tlv.RecordT 2024-08-28 13:21:46 +02:00
Olaoluwa Osuntokun
c1e641e9d9
lnwallet: add TLV blob to PaymentDescriptor + htlc add
In this commit, we add a TLV blob to the PaymentDescriptor struct. We also now thread through this value from the UpdateAddHTLC message to the PaymentDescriptor mapping, and the other way around.
2024-08-28 13:21:12 +02:00
Olaoluwa Osuntokun
d95c1f93f3
lnwallet+channeldb: add new AuxLeafStore for dynamic aux leaves
In this commit, we add a new AuxLeafStore which can be used to dynamically fetch the latest aux leaves for a given state. This is useful for custom channel types that will store some extra information in the form of a custom blob, then will use that information to derive the new leaf tapscript leaves that may be attached to reach state.
2024-08-28 13:21:08 +02:00
Olaoluwa Osuntokun
c36cd9298f
input: add some utility type definitions for aux leaves
In this commit, we add some useful type definitions for the aux leaf.
2024-08-28 11:52:32 +02:00
Olaoluwa Osuntokun
d25f881433
lnwallet: add custom tlv blob to internal commitment struct
In this commit, we also add the custom TLV blob to the internal commitment struct that we use within the in-memory commitment linked list.

This'll be useful to ensure that we're tracking the current blob for our in memory commitment for when we need to write it to disk.
2024-08-28 11:52:32 +02:00
Olaoluwa Osuntokun
12352d9e6e
lnwallet: export the HtlcView struct
We'll need this later on to ensure we can always interact with the new aux blobs at all stages of commitment transaction construction.
2024-08-28 11:52:32 +02:00
Olaoluwa Osuntokun
5c4428c3cf
channeldb: new custom blob nested TLV
In this commit, for each channel, we'll now start to store an optional custom blob. This can be used to store extra information for custom channels in an opauqe manner.
2024-08-28 11:52:32 +02:00
Olaoluwa Osuntokun
1aae47fd71
input+lnwallet: update taproot scripts to accept optional aux leaf
In this commit, we update all the taproot scripts to also accept an
optional aux leaf. This aux leaf can be used to add more redemption
paths for advanced channels, or just as an extra commitment space.
2024-08-28 11:52:31 +02:00
Olaoluwa Osuntokun
8588c9bfd7
lnwallet: add initial unit tests for musig2+tapscript root chans 2024-08-28 11:52:31 +02:00
Olaoluwa Osuntokun
82ba5bf0bf
lnwallet+peer: add tapscript root awareness to musig2 sessions
With this commit, the channel is now aware of if it's a musig2 channel, that also has a tapscript root. We'll need to always pass in the tapscript root each time we: make the funding output, sign a new state, and also verify a new state.
2024-08-28 11:52:31 +02:00
Olaoluwa Osuntokun
c8b7987a39
lnwallet: update internal funding flow w/ tapscript root
This isn't hooked up yet to the funding manager, but with this commit, we can now start to write internal unit tests that handle musig2 channels with a tapscript root.
2024-08-28 11:52:31 +02:00
Olaoluwa Osuntokun
142b408be7
multi: update GenTaprootFundingScript to pass tapscript root
In most cases, we won't yet be passing a root. The option usage helps us keep the control flow mostly unchanged.
2024-08-28 11:52:31 +02:00
Olaoluwa Osuntokun
eeee2979e5
lnwallet/chanfunding: add optional tapscript root 2024-08-28 11:52:31 +02:00
Olaoluwa Osuntokun
2c56b3120a
multi: add new tapscript root option to GenTaprootFundingScript
This'll allow us to create a funding output that uses musig2, but uses a tapscript tweak rather than a normal BIP 86 tweak.
2024-08-28 11:52:31 +02:00
Olaoluwa Osuntokun
edf959d39f
channeldb: add optional TapscriptRoot field + feature bit 2024-08-28 11:52:29 +02:00
Olaoluwa Osuntokun
ecca095a9b
channeldb: consolidate root bucket TLVs into new struct
In this commit, we consolidate the root bucket TLVs into a new struct.
This makes it easier to see all the new TLV fields at a glance. We also
convert TLV usage to use the new type param based APis.
2024-08-28 11:51:54 +02:00
Calvin Zachman
7547c234cc
routing: export dbMPPayment interface
This will assist external programs attempting to re-use
ChannelRouter code and leverage the ControlTower's FetchPayment
method.
2024-08-27 19:00:58 -05:00
Olaoluwa Osuntokun
1bf7ad9b43
Merge pull request #9009 from Crypt-iQ/gossip_ban_8132024
discovery: implement banning for invalid channel anns
2024-08-27 18:50:00 -05:00
Oliver Gugger
64b8c6299a
Merge pull request #9039 from ellemouton/htlcPersistenceAcrossRestart
lnwallet: correctly set UpdateAddHTLC.BlindingPoint on reload from disk
2024-08-27 12:45:27 -06:00
Eugene Siegel
95acc78013
release-notes: update for 0.18.3 2024-08-27 14:11:07 -04:00
Eugene Siegel
013452cff0
discovery: implement ChannelAnnouncement banning
This commit hooks up the banman to the gossiper:
- peers that are banned and don't have a channel with us will get
  disconnected until they are unbanned.
- peers that are banned and have a channel with us won't get
  disconnected, but we will ignore their channel announcements until
  they are no longer banned. Note that this only disables gossip of
  announcements to us and still allows us to open channels to them.
2024-08-27 14:11:06 -04:00
Eugene Siegel
9380292a5a
graph: export NewErrf and ErrorCode for upcoming gossiper unit tests 2024-08-27 14:11:06 -04:00
Eugene Siegel
8e0d7774b2
discovery: clean up scid variable usage 2024-08-27 14:11:06 -04:00
Eugene Siegel
99b86ba462
multi: extend lnpeer.Peer interface with Disconnect function
This will be used in the gossiper to disconnect from peers if their
ban score passes the ban threshold.
2024-08-27 14:11:06 -04:00
Eugene Siegel
0173e4c44d
discovery: add banman for channel announcements
This commit introduces a ban manager that marks peers as banned if
they send too many invalid channel announcements to us. Expired
entries are purged after a certain period of time (currently 48 hours).
2024-08-27 14:11:06 -04:00
Eugene Siegel
199e83d3f2
channeldb: add PutClosedScid and IsClosedScid
This commit adds the ability to store closed channels by scid in
the database. This will allow the gossiper to ignore channel
announcements for closed channels without having to do any
expensive validation.
2024-08-27 14:11:05 -04:00
Elle Mouton
6c7be5b933
docs: update release notes 2024-08-27 19:31:51 +02:00
Elle Mouton
b0852a22fd
lnwallet+itest: fix PaymentDescriptor creation for blinded path htlc
This commit fixes the instantiation of the BlindingPoint member of
PaymentDescriptor during the conversion from persisted LogUpdates.
Previously, the blinding point was not set correctly. The test from the
previous commit is also updated to now assert that this behaviour is now
correct.
2024-08-27 18:18:15 +02:00
Elle Mouton
9ac8e673b4
itest: demonstrate UpdateAddHTLC reloading bug
This commit adds a new route blinding itest that demonstrates that the
reloading and re-forwarding of an UpdateAddHTLC message on restart
currently is done incorrectly for a blinded path payment. This is due to
the fact that the blinding point member is not currently set correctly.
This is fixed in the next commit which will also change the test to
assert that the behaviour is now correct.
2024-08-27 18:18:10 +02:00
Oliver Gugger
8939a217c3
Merge pull request #9021 from aakselrod/run-nativesql-itests
itest: ensure LND gets correct CLI options when itest are parallel
2024-08-25 01:25:03 -06:00
Alex Akselrod
302c690136
docs: update release notes 2024-08-24 13:28:16 -07:00
Alex Akselrod
8d5f8d822d
itest: ensure LND gets correct CLI options when itest are parallel 2024-08-24 13:28:14 -07:00
Oliver Gugger
dcd921ad69
Merge pull request #9029 from tlindi/admin-macaroon-recreate-sample
update macaroons/README.md
2024-08-23 06:45:07 -06:00
tlindi
b59ced60c0 update macaroons/README.md
Provide sample of command to create macaroon with similar rights as admin.macaroon to be used with 3rd party applications.

Co-Authored-By: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2024-08-23 15:10:04 +03:00
Oliver Gugger
306695cd78
Merge pull request #9025 from lightningnetwork/extract-from-staging-branch
[custom channels]: extract some independent commits from mega staging branch
2024-08-23 05:04:46 -06:00
Yong
278cf03078
Merge pull request #9028 from yyforyongyu/improve-ci-log
Makefile: silence testing log in `make itest-parallel`
2024-08-23 17:55:04 +08:00
Oliver Gugger
d0d397545a
Merge pull request #9026 from ziggie1984/bugfix-minHTLC-blindedpath
blindedpath: Minor bug fixes and feature addition.
2024-08-23 03:07:46 -06:00
Oliver Gugger
61edb36283
routing+lnrpc: fix log statements 2024-08-23 10:58:14 +02:00
Oliver Gugger
1167e9b6dd
server: fix logging of pubkey 2024-08-23 10:58:14 +02:00
Oliver Gugger
c5973aa136
cmd/commands: export StripPrefix 2024-08-23 10:58:13 +02:00
Oliver Gugger
efbaf90caf
lnwallet: add Tree() method, fix formatting 2024-08-23 10:58:13 +02:00
Oliver Gugger
f0648e24a5
lnwallet: export GenTaprootHtlcScript 2024-08-23 10:58:13 +02:00
Oliver Gugger
cf2174fc2d
lnwallet: export AnchorSize 2024-08-23 10:58:13 +02:00
Oliver Gugger
22a3988222
cmd/lncli: move commands and export
We want to export some of our CLI code to re-use in other projects. But
in Golang you cannot import code from a `main` package.
So we need to move the actual code into its own package and only have
the `func main()` in the `main` package.
2024-08-23 10:57:59 +02:00
yyforyongyu
35287ee899
scripts: silence test outputs 2024-08-23 15:48:09 +08:00
ziggie
816b25e2ba
docs: add release-notes. 2024-08-22 19:09:43 +02:00
ziggie
25f7b1c362
blindedpath: minHTLC for blinded path change.
We will not add a buffer to the chan policy for blinded paths in case
the sender amount violates the minHTLC restriction in the first place.

Moreover we disgard a route fast if the payment amount is smaller than
the minHTLC along the route.
2024-08-22 19:09:43 +02:00
Olaoluwa Osuntokun
75477c8896
funding: use atomic.Uint64 for chanIDNonce
This lets us get rid of the mutex usage there. We also shift the algo slightly to increment by 1, then use that as the next value, which plays nicer with the atomics.
2024-08-22 18:33:24 +02:00
Olaoluwa Osuntokun
614711748c
funding: add new type alias for PendingChanID = [32]byte
This'll be useful for new interface definitions that use the contents of the package.
2024-08-22 18:33:24 +02:00
ffranr
1297e8f7c7
htlcswitch: add missing method doc 2024-08-22 18:33:24 +02:00
ffranr
7df093b3b1
multi: improve comment grammar 2024-08-22 18:33:24 +02:00
ffranr
9f3593a341
lnwire: add type CustomRecords
This commit introduces the `CustomRecords` type in the `lnwire` package,
designed to hold arbitrary byte slices. Each entry in this map can
associate with TLV type values that are greater than or equal to 65536.
2024-08-22 18:33:24 +02:00
ziggie
73984964a8
blindedpath: fix log output. 2024-08-22 17:48:27 +02:00
ziggie
5d7113ac1d
blindedpath: remove blockexpiry check.
Removes a check where we would NOT allow to create a blinded invoice
with an expiry (invoice expiry in seconds considered as block time)
lower than the min_final_ctlv_delta.
2024-08-22 17:45:45 +02:00
Oliver Gugger
9e926924f1
mod: bump tlv to v1.2.6 2024-08-22 12:21:52 +02:00
Olaoluwa Osuntokun
38441d24c9
lnwallet/chanfunding: rename assembler.go to interface.go
In this commit, we rename the files as assembler.go houses the primary
interfaces/abstractions of the package. In the rest of the codebase,
this file is near uniformly called interface.go, so we rename the file
to make the repo more digestible at a scan.
2024-08-22 12:21:47 +02:00
Olaoluwa Osuntokun
cc9e2b783e
Merge pull request #8961 from yyforyongyu/fix-leaseoutput
Improve the performace of `LeaseOutput`
2024-08-21 16:31:26 -07:00
Oliver Gugger
a028064fa7
Merge pull request #9007 from Roasbeef/go-1-22
build: set min build version to Go 1.22.6
2024-08-20 12:27:02 -06:00
Oliver Gugger
3ae6553eef
.golangci: fix config, update new-from-rev 2024-08-20 19:14:44 +02:00
Oliver Gugger
e99e6662cf
multi: update linter, fix new issues 2024-08-20 19:14:44 +02:00
Oliver Gugger
9eef428e77
.golangci: remove or rename old and deprecated linters 2024-08-20 19:13:23 +02:00
Olaoluwa Osuntokun
19b5a1fb05
build: set min build version to Go 1.22.6
Go 1.23 was released this week, so with this PR we update the build
system to officially support the last two releases.
2024-08-20 19:13:23 +02:00
Olaoluwa Osuntokun
b4693b2010
Merge pull request #9011 from ziggie1984/fix-gossip-syncer
Fix TimeStamp issue in the Gossip Syncer
2024-08-19 16:58:52 -07:00
ziggie
fc5b763ab6
docs: update release-notes. 2024-08-17 04:29:58 +02:00
ziggie
e5d7a7d371
discovery: add detailed comment.
Describe why it is ok to resurrect zombie channels based on the
timestamp of the `ReplyChannelRange` msg although its not verifiable
data.
2024-08-17 04:29:58 +02:00
ziggie
6fb1e0c17d
multi: fix time.Time initialization.
ChanUpdate timestamps are now restircted so that they cannot be
more than two weeks into the future. Moreover channels with both
timestamps in the ReplyChannelRange msg either too far in the past
or too far in the future are not queried.

Moreover fix unitests.
2024-08-17 04:29:57 +02:00
Olaoluwa Osuntokun
8ac184a911
Merge pull request #8520 from lightningnetwork/peer-msg-router
[2/4] - peer: add new abstract message router
2024-08-16 14:57:44 -07:00
Olaoluwa Osuntokun
7c24e33614
Merge pull request #8996 from ProofOfKeags/dual
[MICRO]: lntypes: Add Dual[A] primitive type
2024-08-16 14:47:39 -07:00
Keagan McClelland
34f7c31dc8
lntypes: Add Dual[A] primitive type
This commit introduces a new type Dual[A] to make it easier to
manage symmetric configurations or state for lightning channels.
2024-08-15 10:53:18 -07:00
ziggie
aecfe1c484
discovery: fix log line.
if we use %x here we would get the hex representation of the
String() method of the vertex, which is wrong.
2024-08-15 12:09:52 +02:00
Oliver Gugger
0aced5ce2d
Merge pull request #8857 from MPins/issue-8793
Fixing the current state update when lncli debuglevel and lncli setmccfg are called
2024-08-15 02:33:54 -06:00
Olaoluwa Osuntokun
f09c517bc6 peer: don't stop global msg router
In this commit, we fix a bug that would cause a global message router to
be stopped anytime a peer disconnected. The global msg router only
allows `Start` to be called once, so afterwards, no messages would
properly be routed.
2024-08-14 19:23:02 -07:00
Olaoluwa Osuntokun
b028af1836 multi: make MsgRouter available in the ImplementationCfg
With this commit, we allow the `MsgRouter` to be available in the
`ImplementationCfg`. With this, programs outside of lnd itself are able
to now hook into the message processing flow to direct handle custom
messages, and even normal wire messages.
2024-08-14 19:23:02 -07:00
Olaoluwa Osuntokun
927aa84b5f peer: update readHandler to dispatch to msgRouter if set
Over time with this, we should be able to significantly reduce the size
of the peer.Brontide struct as we only need all those deps as the peer
needs to recognize and handle each incoming wire message itself.
2024-08-14 19:23:02 -07:00
Olaoluwa Osuntokun
f124195ae9 msgmux: add new abstract message router
In this commit, we add a new abstract message router. Over time, the
goal is that this message router replaces the logic we currently have in
the readHandler (the giant switch for each message).

With this new abstraction, can reduce the responsibilities of the
readHandler to *just* reading messages off the wire and handing them off
to the msg router. The readHandler no longer needs to know *where* the
messages should go, or how they should be dispatched.

This will be used in tandem with the new `protofsm` module in an
upcoming PR implementing the new rbf-coop close.
2024-08-14 19:23:02 -07:00
MPins
4dda2e2c23 docs: release-notes-0.19.0.md 2024-08-14 14:44:17 -03:00
MPins
cde5c5b59d lnd: Update the DebugLevel on main cfg 2024-08-14 14:42:49 -03:00
MPins
b08e65ec85 lnd: Create a callback function (UpdatingRoutingConfig)
This callback function is called whenever the command `lncli setmccfg`
is used to change the routing settings.
2024-08-14 14:42:49 -03:00
MPins
de495534e4 routing: Call the function to update routing config on main cfg
NewMissionControl propagate the callback function defined on server.go and
SetConfig call it to update the infos on main cfg.
2024-08-14 14:42:49 -03:00
Yong
77c7f776d5
Merge pull request #9002 from Roasbeef/tlv-edge-info-bug-fix
discovery: fix bug that can lead to sending invalid chan_ann msgs
2024-08-14 23:29:36 +08:00
Olaoluwa Osuntokun
f7daa307dd
discovery: fix bug that can lead to sending invalid chan_ann msgs
Initially in lnd, we didn't store the extra TLV data that could be
dangling off of gossip messages. This was fixed initially in lnd v0.5
with this PR: https://github.com/lightningnetwork/lnd/pull/1825.

Within the PR, we incorrect set the `ExtraOpaqueData` (extra TLV blob)
of the `ChannelAnnouncement` to the value stored in `edge`, which is
actually our channel update. As 6-ish years ago we didn't yet have
anything that used the TLV gossip fields, this went unnoticed.

Fast forward to 2024, we shipped an experimental version of inbounbd
fees. This starts to store additional data in the `ExtraOpaqueData`
field, the TLV for the inbound fee. Initially, everything is valid when
the first `ChannelAnnouncement` is sent, but as soon as a user attempts
to set an inbound fee policy, we'd incorrectly swap in that new
serialized TLV for the _channel announcement_:
841e24399c (diff-1eda595bbebe495bd74a6a0431c46b66cb4e8b53beb311067c010feac2665dcbR2560).

Since we're just trying to generate a new `channel_update`, we don't
also regenerate the signature for the `channel_announcement` message. As
a result, we end up storing a `channel_announcement` with an invalid sig
on disk, continuing to broadcast that to peers.
2024-08-13 19:39:16 -07:00
Olaoluwa Osuntokun
c0420fe67d
Merge pull request #8943 from Roasbeef/alloy-linear-fee-model
docs/alloy-models: add new folder for Alloy models along w/ model for Linear Fee Function bug fix
2024-08-12 19:14:56 -07:00
Oliver Gugger
2f2efc7824
Merge pull request #8843 from ziggie1984/bumpforceclosefee-rpc
bumpforceclosefee rpc
2024-08-12 06:04:53 -06:00
ziggie
08aba8ce31
docs: add release notes. 2024-08-09 19:41:27 +02:00
ziggie
22b504db7c
sweep: add TODO to the sweeper behavior.
Make sure we only trigger the sweep of a specific input when
updating its fee parameters not the all the current pending
registered sweeps.
2024-08-09 19:41:27 +02:00
ziggie
2d04813dc3
itest: Add itest for bumpclosefeerate rpc.
Add an itest which will bump the close fee rate of an anchor
channel which is force closed without having any HTLCs at stake.
2024-08-09 19:41:27 +02:00
ziggie
ae28f75557
miner: bugfix
return the transaction when found in the mempool.
2024-08-09 19:41:26 +02:00
ziggie
75f6622ccf
itest: remove unused LEGACY channel code.
Legacy Channel types are not tested anymore via the multi-hop
test harness, therefore we can remove unused code.
2024-08-09 18:54:57 +02:00
ziggie
07b18c1c86
multi: add bumpforceclosefee rpc endpoint.
Add a new bumpforceclosefee rpc endpoint to the wallet server.
Move the logic from the lncli level to the wallet server rpc level.
This is more in line with a proper client-server design.

wallet lncli: use new bumpforceclosefee endpoint.

Besides using the new bumpforceclosefee rpc endpoint we also enable the
bumping of taproot anchor channels.
2024-08-09 18:54:56 +02:00
yyforyongyu
363e529f9b
docs: update release notes 2024-08-09 22:01:57 +08:00
yyforyongyu
8259e0fb5f
itest: remove redundant call to ht.WaitForBlockchainSync
This check has already been done when mining blocks.
2024-08-09 22:01:57 +08:00
yyforyongyu
3e36adf476
mulit: remove ListLeasedOutputs in LeaseOutput
This commit removes the call toe `ListLeasedOutputs` in `LeaseOutput` -
the returned info from `ListLeasedOutputs` can easily be accessed via
`FetchInputInfo` and this info is only used at one callsite.
`ListLeasedOutputs` then becomes unnecessary here, plus it's very slow
and needs to be refactored in `btcwallet` instead.
2024-08-09 22:01:57 +08:00
yyforyongyu
f70c919164
lnrpc: fetch utxo info in lockInputs
This commit prepares the following commit where we change the
`LeaseOutput` to be more efficient.
2024-08-09 22:01:57 +08:00
yyforyongyu
b17db4a32a
lntest+lnwallet: remove the method FetchInputInfo
This method is no longer used. In addition, the `Derivation` field on
the `Utxo` is also removed to avoid nil dereference.
2024-08-09 22:01:56 +08:00
yyforyongyu
d7381ce3fe
rpcserver: remove redundant call to FetchInputInfo 2024-08-09 22:01:56 +08:00
yyforyongyu
aba8507b2a
lnrpc+lnwallet: replace FetchInputInfo with new methods
This commit replaces the usage of `FetchInputInfo` with
`FetchOutpointInfo` and `FetchDerivationInfo` to remove unncessary
fetching of the derivation path.
2024-08-09 21:51:18 +08:00
yyforyongyu
9801ee036b
lnwallet+lntest: add FetchOutpointInfo and FetchDerivationInfo 2024-08-09 21:51:17 +08:00
yyforyongyu
8f35612364
mod: update btcwallet version 2024-08-09 21:51:15 +08:00
Olaoluwa Osuntokun
9aa7e7510c
docs/alloy-model: add linear fee function model
In this commit, we add a model for the linear fee function we use in lnd
for fee bumping. This models can be used to reproduce the issue reported
in https://github.com/lightningnetwork/lnd/issues/8741, and can also be
shown that that bug fix resolves a counter example found by the model
checker.
2024-08-01 17:42:31 -07:00
Olaoluwa Osuntokun
7c61dc6dc4
docs/alloy-models: add initial README.md for Alloy models 2024-08-01 17:42:22 -07:00
1052 changed files with 112081 additions and 39991 deletions

107
.circleci/config.yml Normal file
View File

@ -0,0 +1,107 @@
version: 2
jobs:
# publish jobs require $DOCKERHUB_REPO, $DOCKERHUB_USER, $DOCKERHUB_PASS defined
amd64:
machine:
enabled: true
steps:
- checkout
- run:
command: |
LATEST_TAG=${CIRCLE_TAG:8} #trim "basedon-" from tag
#
sudo docker build --pull -t $DOCKERHUB_REPO:$LATEST_TAG-amd64 -f linuxamd64.Dockerfile .
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-amd64
arm32:
machine:
enabled: true
steps:
- checkout
- run:
command: |
LATEST_TAG=${CIRCLE_TAG:8} #trim "basedon-" from tag
#
# Make sure the builder is copy the arm emulator
sudo docker run --rm --privileged multiarch/qemu-user-static:register --reset
sudo apt update
sudo apt install -y qemu qemu-user-static qemu-user binfmt-support
sudo cp /usr/bin/qemu-arm-static "qemu-arm-static"
sed -i -e 's/#EnableQEMU //g' "linuxarm32v7.Dockerfile"
sudo docker build --pull -t $DOCKERHUB_REPO:$LATEST_TAG-arm32v7 -f linuxarm32v7.Dockerfile .
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-arm32v7
arm64:
machine:
enabled: true
steps:
- checkout
- run:
command: |
LATEST_TAG=${CIRCLE_TAG:8} #trim "basedon-" from tag
#
# Make sure the builder is copy the arm emulator
sudo docker run --rm --privileged multiarch/qemu-user-static:register --reset
sudo apt update
sudo apt install -y qemu qemu-user-static qemu-user binfmt-support
sudo cp /usr/bin/qemu-aarch64-static "qemu-aarch64-static"
sed -i -e 's/#EnableQEMU //g' "linuxarm64v8.Dockerfile"
sudo docker build --pull -t $DOCKERHUB_REPO:$LATEST_TAG-arm64v8 -f linuxarm64v8.Dockerfile .
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-arm64v8
multiarch:
machine:
enabled: true
image: default
steps:
- run:
command: |
#
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
#
LATEST_TAG=${CIRCLE_TAG:8} #trim "basedon-" from tag
sudo docker manifest create --amend $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-amd64 $DOCKERHUB_REPO:$LATEST_TAG-arm32v7 $DOCKERHUB_REPO:$LATEST_TAG-arm64v8
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-amd64 --os linux --arch amd64
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-arm32v7 --os linux --arch arm --variant v7
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-arm64v8 --os linux --arch arm64 --variant v8
sudo docker manifest push $DOCKERHUB_REPO:$LATEST_TAG -p
workflows:
version: 2
publish:
jobs:
- amd64:
filters:
# ignore any commit on any branch by default
branches:
ignore: /.*/
# only act on version tags
tags:
only: /basedon-.+/
- arm32:
filters:
branches:
ignore: /.*/
tags:
only: /basedon-.+/
- arm64:
filters:
branches:
ignore: /.*/
tags:
only: /basedon-.+/
- multiarch:
requires:
- amd64
- arm32
- arm64
filters:
branches:
ignore: /.*/
tags:
only: /basedon-.+/

4
.custom-gcl.yml Normal file
View File

@ -0,0 +1,4 @@
version: v1.57.0
plugins:
- module: 'github.com/lightningnetwork/lnd/tools/linters'
path: ./tools/linters

4
.dockerignore Normal file
View File

@ -0,0 +1,4 @@
Dockerfile
linuxamd64.Dockerfile
linuxarm32v7.Dockerfile
.circleci/

4
.gitattributes vendored Normal file
View File

@ -0,0 +1,4 @@
# Declare files that will always have CRLF line endings on checkout.
*.sh text eol=lf
*.go text eol=lf
Makefile text eol=lf

View File

@ -7,18 +7,26 @@ inputs:
key-prefix:
description: "A prefix to use for the cache key, to separate cache entries from other workflows"
required: false
use-build-cache:
description: "Whether to use the build cache"
required: false
# Boolean values aren't supported in the workflow syntax, so we use a
# string. To not confuse the value with true/false, we use 'yes' and 'no'.
default: 'yes'
runs:
using: "composite"
steps:
- name: setup go ${{ inputs.go-version }}
uses: actions/setup-go@v3
uses: actions/setup-go@v5
with:
go-version: '${{ inputs.go-version }}'
cache: 'false'
- name: go cache
uses: actions/cache@v3
if: ${{ inputs.use-build-cache == 'yes' }}
uses: actions/cache@v4
with:
# In order:
# * Module download cache
@ -30,11 +38,39 @@ runs:
~/.cache/go-build
~/Library/Caches/go-build
~\AppData\Local\go-build
key: ${{ runner.os }}-go-${{ inputs.go-version }}-${{ inputs.key-prefix }}-${{ github.job }}-${{ hashFiles('**/go.sum') }}
# The key is used to create and later look up the cache. It's made of
# four parts:
# - The base part is made from the OS name, Go version and a
# job-specified key prefix. Example: `linux-go-1.23.12-unit-test-`.
# It ensures that a job running on Linux with Go 1.23 only looks for
# caches from the same environment.
# - The unique part is the `hashFiles('**/go.sum')`, which calculates a
# hash (a fingerprint) of the go.sum file.
key: ${{ runner.os }}-go-${{ inputs.go-version }}-${{ inputs.key-prefix }}-${{ hashFiles('**/go.sum') }}
# The restore-keys provides a list of fallback keys. If no cache
# matches the key exactly, the action will look for a cache where the
# key starts with one of the restore-keys. The action searches the
# restore-keys list in order and restores the most recently created
# cache that matches the prefix. Once the job is done, a new cache is
# created and saved using the new key.
restore-keys: |
${{ runner.os }}-go-${{ inputs.go-version }}-${{ inputs.key-prefix }}-${{ github.job }}-
${{ runner.os }}-go-${{ inputs.go-version }}-${{ inputs.key-prefix }}-
- name: go module cache
if: ${{ inputs.use-build-cache == 'no' }}
uses: actions/cache@v4
with:
# Just the module download cache.
path: |
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ inputs.go-version }}-${{ inputs.key-prefix }}-no-build-cache-${{ github.job }}-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-${{ inputs.go-version }}-${{ inputs.key-prefix }}-no-build-cache-${{ github.job }}-
${{ runner.os }}-go-${{ inputs.go-version }}-${{ inputs.key-prefix }}-no-build-cache-
- name: set GOPATH
shell: bash
run: |

View File

@ -11,8 +11,9 @@ Steps for reviewers to follow to test the change.
- [ ] Bug fixes contain tests triggering the bug to prevent regressions.
### Code Style and Documentation
- [ ] The change obeys the [Code Documentation and Commenting](https://github.com/lightningnetwork/lnd/blob/master/docs/code_contribution_guidelines.md#CodeDocumentation) guidelines, and lines wrap at 80.
- [ ] Commits follow the [Ideal Git Commit Structure](https://github.com/lightningnetwork/lnd/blob/master/docs/code_contribution_guidelines.md#IdealGitCommitStructure).
- [ ] The change is not [insubstantial](https://github.com/lightningnetwork/lnd/blob/master/docs/code_contribution_guidelines.md#substantial-contributions-only). Typo fixes are not accepted to fight bot spam.
- [ ] The change obeys the [Code Documentation and Commenting](https://github.com/lightningnetwork/lnd/blob/master/docs/development_guidelines.md#code-documentation-and-commenting) guidelines, and lines wrap at 80.
- [ ] Commits follow the [Ideal Git Commit Structure](https://github.com/lightningnetwork/lnd/blob/master/docs/development_guidelines.md#ideal-git-commit-structure).
- [ ] Any new logging statements use an appropriate subsystem and logging level.
- [ ] Any new lncli commands have appropriate tags in the comments for the rpc in the proto file.
- [ ] [There is a change description in the release notes](https://github.com/lightningnetwork/lnd/tree/master/docs/release-notes), or `[skip ci]` in the commit message for small changes.

View File

@ -21,17 +21,20 @@ defaults:
shell: bash
env:
BITCOIN_VERSION: "27"
BITCOIN_VERSION: "29"
TRANCHES: 8
# TRANCHES defines the number of tranches used in the itests.
TRANCHES: 16
# If you change this value, please change it in the following files as well:
# /.travis.yml
# /Dockerfile
# /dev.Dockerfile
# /make/builder.Dockerfile
# /.github/workflows/release.yml
GO_VERSION: 1.22.5
# SMALL_TRANCHES defines the number of tranches used in the less stable itest
# builds
#
# TODO(yy): remove this value and use TRANCHES.
SMALL_TRANCHES: 8
# If you change this please also update GO_VERSION in Makefile (then run
# `make lint` to see where else it needs to be updated as well).
GO_VERSION: 1.23.12
jobs:
########################
@ -41,8 +44,11 @@ jobs:
name: Sqlc check
runs-on: ubuntu-latest
steps:
- name: cleanup space
run: rm -rf /opt/hostedtoolcache
- name: git checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: setup go ${{ env.GO_VERSION }}
uses: ./.github/actions/setup-go
@ -64,8 +70,11 @@ jobs:
name: RPC and mobile compilation check
runs-on: ubuntu-latest
steps:
- name: cleanup space
run: rm -rf /opt/hostedtoolcache
- name: git checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: setup go ${{ env.GO_VERSION }}
uses: ./.github/actions/setup-go
@ -92,8 +101,11 @@ jobs:
name: check commits
runs-on: ubuntu-latest
steps:
- name: cleanup space
run: rm -rf /opt/hostedtoolcache
- name: git checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
@ -115,8 +127,11 @@ jobs:
name: lint code
runs-on: ubuntu-latest
steps:
- name: cleanup space
run: rm -rf /opt/hostedtoolcache
- name: git checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
@ -143,18 +158,33 @@ jobs:
cross-compile:
name: cross compilation
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
# Please keep this list in sync with make/release_flags.mk!
include:
- name: i386
sys: freebsd-386 linux-386 windows-386
- name: amd64
sys: darwin-amd64 freebsd-amd64 linux-amd64 netbsd-amd64 openbsd-amd64 windows-amd64
- name: arm
sys: darwin-arm64 freebsd-arm linux-armv6 linux-armv7 linux-arm64 windows-arm
steps:
- name: cleanup space
run: rm -rf /opt/hostedtoolcache
- name: git checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: setup go ${{ env.GO_VERSION }}
uses: ./.github/actions/setup-go
with:
go-version: '${{ env.GO_VERSION }}'
key-prefix: cross-compile
use-build-cache: 'no'
- name: build release for all architectures
run: make release
run: make release sys="${{ matrix.sys }}"
########################
# sample configuration check
@ -163,8 +193,11 @@ jobs:
name: sample configuration check
runs-on: ubuntu-latest
steps:
- name: cleanup space
run: rm -rf /opt/hostedtoolcache
- name: git checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: setup go ${{ env.GO_VERSION }}
uses: ./.github/actions/setup-go
@ -185,16 +218,19 @@ jobs:
fail-fast: false
matrix:
unit_type:
- btcd unit-cover
- unit-cover
- unit tags="kvdb_etcd"
- unit tags="kvdb_postgres"
- unit tags="kvdb_sqlite"
- btcd unit-race
- unit-race
- unit-module
steps:
- name: cleanup space
run: rm -rf /opt/hostedtoolcache
- name: git checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
@ -203,7 +239,7 @@ jobs:
uses: ./.github/actions/rebase
- name: git checkout fuzzing seeds
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
repository: lightninglabs/lnd-fuzz
path: lnd-fuzz
@ -223,20 +259,26 @@ jobs:
- name: run ${{ matrix.unit_type }}
run: make ${{ matrix.unit_type }}
- name: Clean coverage
run: grep -Ev '(\.pb\.go|\.pb\.json\.go|\.pb\.gw\.go)' coverage.txt > coverage-norpc.txt
if: matrix.unit_type == 'unit-cover'
- name: Send coverage
uses: shogo82148/actions-goveralls@v1
if: matrix.unit_type == 'btcd unit-cover'
uses: coverallsapp/github-action@v2
if: matrix.unit_type == 'unit-cover'
continue-on-error: true
with:
path-to-profile: coverage.txt
file: coverage-norpc.txt
flag-name: 'unit'
format: 'golang'
parallel: true
########################
# run ubuntu integration tests
# run integration tests with TRANCHES
########################
ubuntu-integration-test:
name: run ubuntu itests
basic-integration-test:
name: basic itests
runs-on: ubuntu-latest
if: '!contains(github.event.pull_request.labels.*.name, ''no-itest'')'
strategy:
@ -250,23 +292,14 @@ jobs:
args: backend=bitcoind cover=1
- name: bitcoind-notxindex
args: backend="bitcoind notxindex"
- name: bitcoind-rpcpolling
args: backend="bitcoind rpcpolling" cover=1
- name: bitcoind-etcd
args: backend=bitcoind dbbackend=etcd
- name: bitcoind-postgres
args: backend=bitcoind dbbackend=postgres
- name: bitcoind-sqlite
args: backend=bitcoind dbbackend=sqlite
- name: bitcoind-postgres-nativesql
args: backend=bitcoind dbbackend=postgres nativesql=true
- name: bitcoind-sqlite-nativesql
args: backend=bitcoind dbbackend=sqlite nativesql=true
- name: neutrino
args: backend=neutrino cover=1
steps:
- name: cleanup space
run: rm -rf /opt/hostedtoolcache
- name: git checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
@ -284,84 +317,62 @@ jobs:
run: ./scripts/install_bitcoind.sh $BITCOIN_VERSION
- name: run ${{ matrix.name }}
run: make itest-parallel tranches=${{ env.TRANCHES }} ${{ matrix.args }}
run: make itest-parallel tranches=${{ env.TRANCHES }} ${{ matrix.args }} shuffleseed=${{ github.run_id }}${{ strategy.job-index }}
- name: Clean coverage
run: grep -Ev '(\.pb\.go|\.pb\.json\.go|\.pb\.gw\.go)' coverage.txt > coverage-norpc.txt
if: ${{ contains(matrix.args, 'cover=1') }}
- name: Send coverage
if: ${{ contains(matrix.args, 'cover=1') }}
uses: shogo82148/actions-goveralls@v1
continue-on-error: true
uses: coverallsapp/github-action@v2
with:
path-to-profile: coverage.txt
file: coverage-norpc.txt
flag-name: 'itest-${{ matrix.name }}'
format: 'golang'
parallel: true
- name: Zip log files on failure
if: ${{ failure() }}
timeout-minutes: 5 # timeout after 5 minute
run: 7z a logs-itest-${{ matrix.name }}.zip itest/**/*.log
run: 7z a logs-itest-${{ matrix.name }}.zip itest/**/*.log itest/postgres.log
- name: Upload log files on failure
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: ${{ failure() }}
with:
name: logs-itest-${{ matrix.name }}
path: logs-itest-${{ matrix.name }}.zip
retention-days: 5
########################
# run windows integration test
# run integration tests with SMALL_TRANCHES
########################
windows-integration-test:
name: run windows itest
runs-on: windows-latest
integration-test:
name: itests
runs-on: ubuntu-latest
if: '!contains(github.event.pull_request.labels.*.name, ''no-itest'')'
strategy:
# Allow other tests in the matrix to continue if one fails.
fail-fast: false
matrix:
include:
- name: bitcoind-rpcpolling
args: backend="bitcoind rpcpolling"
- name: bitcoind-etcd
args: backend=bitcoind dbbackend=etcd
- name: bitcoind-sqlite
args: backend=bitcoind dbbackend=sqlite
- name: bitcoind-sqlite-nativesql
args: backend=bitcoind dbbackend=sqlite nativesql=true
- name: bitcoind-postgres
args: backend=bitcoind dbbackend=postgres
- name: bitcoind-postgres-nativesql
args: backend=bitcoind dbbackend=postgres nativesql=true
steps:
- name: git checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: fetch and rebase on ${{ github.base_ref }}
if: github.event_name == 'pull_request'
uses: ./.github/actions/rebase
- name: setup go ${{ env.GO_VERSION }}
uses: ./.github/actions/setup-go
with:
go-version: '${{ env.GO_VERSION }}'
key-prefix: integration-test
- name: run itest
run: make itest-parallel tranches=${{ env.TRANCHES }} windows=1
- name: kill any remaining lnd processes
if: ${{ failure() }}
shell: powershell
run: taskkill /IM lnd-itest.exe /T /F
- name: Zip log files on failure
if: ${{ failure() }}
timeout-minutes: 5 # timeout after 5 minute
run: 7z a logs-itest-windows.zip itest/**/*.log
- name: Upload log files on failure
uses: actions/upload-artifact@v3
if: ${{ failure() }}
with:
name: logs-itest-windows
path: logs-itest-windows.zip
retention-days: 5
########################
# run macOS integration test
########################
macos-integration-test:
name: run macOS itest
runs-on: macos-14
if: '!contains(github.event.pull_request.labels.*.name, ''no-itest'')'
steps:
- name: git checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
@ -376,13 +387,108 @@ jobs:
key-prefix: integration-test
- name: install bitcoind
run: |
wget https://bitcoincore.org/bin/bitcoin-core-${BITCOIN_VERSION}.0/bitcoin-${BITCOIN_VERSION}.0-arm64-apple-darwin.tar.gz
tar zxvf bitcoin-${BITCOIN_VERSION}.0-arm64-apple-darwin.tar.gz
mv bitcoin-${BITCOIN_VERSION}.0 /tmp/bitcoin
run: ./scripts/install_bitcoind.sh $BITCOIN_VERSION
- name: run ${{ matrix.name }}
run: make itest-parallel tranches=${{ env.SMALL_TRANCHES }} ${{ matrix.args }} shuffleseed=${{ github.run_id }}${{ strategy.job-index }}
- name: Clean coverage
run: grep -Ev '(\.pb\.go|\.pb\.json\.go|\.pb\.gw\.go)' coverage.txt > coverage-norpc.txt
if: ${{ contains(matrix.args, 'cover=1') }}
- name: Send coverage
if: ${{ contains(matrix.args, 'cover=1') }}
continue-on-error: true
uses: coverallsapp/github-action@v2
with:
file: coverage-norpc.txt
flag-name: 'itest-${{ matrix.name }}'
format: 'golang'
parallel: true
- name: Zip log files on failure
if: ${{ failure() }}
timeout-minutes: 5 # timeout after 5 minute
run: 7z a logs-itest-${{ matrix.name }}.zip itest/**/*.log
- name: Upload log files on failure
uses: actions/upload-artifact@v4
if: ${{ failure() }}
with:
name: logs-itest-${{ matrix.name }}
path: logs-itest-${{ matrix.name }}.zip
retention-days: 5
########################
# run windows integration test
########################
windows-integration-test:
name: windows itest
runs-on: windows-latest
if: '!contains(github.event.pull_request.labels.*.name, ''no-itest'')'
steps:
- name: git checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: fetch and rebase on ${{ github.base_ref }}
if: github.event_name == 'pull_request'
uses: ./.github/actions/rebase
- name: setup go ${{ env.GO_VERSION }}
uses: ./.github/actions/setup-go
with:
go-version: '${{ env.GO_VERSION }}'
key-prefix: integration-test
- name: run itest
run: PATH=$PATH:/tmp/bitcoin/bin make itest-parallel tranches=${{ env.TRANCHES }} backend=bitcoind
run: make itest-parallel tranches=${{ env.SMALL_TRANCHES }} windows=1 shuffleseed=${{ github.run_id }}
- name: kill any remaining lnd processes
if: ${{ failure() }}
shell: powershell
run: taskkill /IM lnd-itest.exe /T /F
- name: Zip log files on failure
if: ${{ failure() }}
timeout-minutes: 5 # timeout after 5 minute
run: 7z a logs-itest-windows.zip itest/**/*.log
- name: Upload log files on failure
uses: actions/upload-artifact@v4
if: ${{ failure() }}
with:
name: logs-itest-windows
path: logs-itest-windows.zip
retention-days: 5
########################
# run macOS integration test
########################
macos-integration-test:
name: macOS itest
runs-on: macos-14
if: '!contains(github.event.pull_request.labels.*.name, ''no-itest'')'
steps:
- name: git checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: fetch and rebase on ${{ github.base_ref }}
if: github.event_name == 'pull_request'
uses: ./.github/actions/rebase
- name: setup go ${{ env.GO_VERSION }}
uses: ./.github/actions/setup-go
with:
go-version: '${{ env.GO_VERSION }}'
key-prefix: integration-test
- name: run itest
run: make itest-parallel tranches=${{ env.SMALL_TRANCHES }} shuffleseed=${{ github.run_id }}
- name: Zip log files on failure
if: ${{ failure() }}
@ -390,7 +496,7 @@ jobs:
run: 7z a logs-itest-macos.zip itest/**/*.log
- name: Upload log files on failure
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: ${{ failure() }}
with:
name: logs-itest-macos
@ -412,8 +518,11 @@ jobs:
- github.com/golang/protobuf v1.5.3
steps:
- name: cleanup space
run: rm -rf /opt/hostedtoolcache
- name: git checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: ensure dependencies at correct version
run: if ! grep -q "${{ matrix.pinned_dep }}" go.mod; then echo dependency ${{ matrix.pinned_dep }} should not be altered ; exit 1 ; fi
@ -426,18 +535,39 @@ jobs:
runs-on: ubuntu-latest
if: '!contains(github.event.pull_request.labels.*.name, ''no-changelog'')'
steps:
- name: cleanup space
run: rm -rf /opt/hostedtoolcache
- name: git checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: release notes check
run: scripts/check-release-notes.sh
########################
# Backwards Compatibility Test
########################
backwards-compatability-test:
name: backwards compatability test
runs-on: ubuntu-latest
steps:
- name: git checkout
uses: actions/checkout@v4
- name: 🐳 Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: 🛡️ backwards compatibility test
run: make backwards-compat-test
# Notify about the completion of all coverage collecting jobs.
finish:
if: ${{ always() }}
needs: [unit-test, ubuntu-integration-test]
needs: [unit-test, basic-integration-test]
runs-on: ubuntu-latest
steps:
- uses: shogo82148/actions-goveralls@v1
- name: Send coverage
uses: coverallsapp/github-action@v2
continue-on-error: true
with:
parallel-finished: true

View File

@ -10,13 +10,9 @@ defaults:
shell: bash
env:
# If you change this value, please change it in the following files as well:
# /.travis.yml
# /Dockerfile
# /dev.Dockerfile
# /make/builder.Dockerfile
# /.github/workflows/main.yml
GO_VERSION: 1.22.5
# If you change this please also update GO_VERSION in Makefile (then run
# `make lint` to see where else it needs to be updated as well).
GO_VERSION: 1.23.12
jobs:
main:
@ -24,14 +20,18 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: git checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: cleanup space
run: rm -rf /opt/hostedtoolcache && mkdir -p /opt/hostedtoolcache/go
- name: setup go ${{ env.GO_VERSION }}
uses: actions/setup-go@v2
uses: actions/setup-go@v5
with:
go-version: '${{ env.GO_VERSION }}'
cache: 'false'
- name: Set env
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
@ -123,10 +123,12 @@ jobs:
that `vendor.tar.gz` and `lnd-source-${{ env.RELEASE_VERSION }}.tar.gz` are in the current directory, follow these steps:
```
tar -xvzf vendor.tar.gz
tar -xvzf lnd-source-${{ env.RELEASE_VERSION }}.tar.gz
GO111MODULE=on go install -v -mod=vendor -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=${{ env.RELEASE_VERSION }}" ./cmd/lnd
GO111MODULE=on go install -v -mod=vendor -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=${{ env.RELEASE_VERSION }}" ./cmd/lncli
mv vendor.tar.gz lnd-source/
cd lnd-source
tar -xvzf vendor.tar.gz
go install -v -mod=vendor -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=${{ env.RELEASE_VERSION }}" ./cmd/lnd
go install -v -mod=vendor -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=${{ env.RELEASE_VERSION }}" ./cmd/lncli
```
The `-mod=vendor` flag tells the `go build` command that it doesn't need to fetch the dependencies, and instead, they're all enclosed in the local vendor directory.

6
.gitignore vendored
View File

@ -44,6 +44,9 @@ cmd/cmd
*.key
*.hex
# Ignore the custom linter binary if it is built.
custom-gcl
cmd/lncli/lncli
# Files from mobile build.
@ -66,6 +69,7 @@ profile.tmp
.DS_Store
.vscode
*.code-workspace
# Coverage test
coverage.txt
@ -76,3 +80,5 @@ coverage.txt
# Release build directory (to avoid build.vcs.modified Golang build tag to be
# set to true by having untracked files in the working directory).
/lnd-*/
.aider*

View File

@ -1,18 +1,10 @@
run:
# timeout for analysis
deadline: 10m
# If you change this please also update GO_VERSION in Makefile (then run
# `make lint` to see where else it needs to be updated as well).
go: "1.23.12"
# Skip autogenerated files for mobile and gRPC as well as copied code for
# internal use.
skip-files:
- "mobile\\/.*generated\\.go"
- "\\.pb\\.go$"
- "\\.pb\\.gw\\.go$"
- "internal\\/musig2v040"
skip-dirs:
- channeldb/migration_01_to_11
- channeldb/migration/lnwire21
# Abort after 10 minutes.
timeout: 10m
build-tags:
- autopilotrpc
@ -30,14 +22,22 @@ run:
- integration
linters-settings:
custom:
ll:
type: "module"
description: "Custom lll linter with 'S' log line exclusion."
settings:
# Max line length, lines longer will be reported.
line-length: 80
# Tab width in spaces.
tab-width: 8
# The regex that we will use to detect the start of an `S` log line.
log-regex: "^\\s*.*(L|l)og\\.(Info|Debug|Trace|Warn|Error|Critical)S\\("
errorlint:
# Check for incorrect fmt.Errorf error wrapping.
errorf: true
govet:
# Don't report about shadowed variables
check-shadowing: false
gofmt:
# simplify code: gofmt with `-s` option, true by default
simplify: true
@ -55,17 +55,12 @@ linters-settings:
excludes:
- G402 # Look for bad TLS connection settings.
- G306 # Poor file permissions used when writing to a new file.
- G601 # Implicit memory aliasing in for loop.
- G115 # Integer overflow in conversion.
staticcheck:
go: "1.22.5"
checks: ["-SA1019"]
lll:
# Max line length, lines longer will be reported.
line-length: 80
# Tab width in spaces.
tab-width: 8
funlen:
# Checks the number of lines in a function.
# If lower than 0, disable the check.
@ -104,16 +99,22 @@ linters-settings:
- 'errors.Wrap'
gomoddirectives:
replace-local: true
replace-allow-list:
# See go.mod for the explanation why these are needed.
- github.com/ulikunitz/xz
- github.com/gogo/protobuf
- google.golang.org/protobuf
- github.com/lightningnetwork/lnd/sqldb
linters:
enable-all: true
disable:
# We instead use our own custom line length linter called `ll` since
# then we can ignore log lines.
- lll
# Global variables are used in many places throughout the code base.
- gochecknoglobals
@ -131,26 +132,15 @@ linters:
- gochecknoinits
# Deprecated linters. See https://golangci-lint.run/usage/linters/.
- interfacer
- golint
- maligned
- scopelint
- exhaustivestruct
- bodyclose
- contextcheck
- nilerr
- noctx
- rowserrcheck
- sqlclosecheck
- structcheck
- tparallel
- unparam
- wastedassign
- ifshort
- varcheck
- deadcode
- nosnakecase
# Disable gofumpt as it has weird behavior regarding formatting multiple
# lines for a function which is in conflict with our contribution
@ -158,9 +148,7 @@ linters:
- gofumpt
# Disable whitespace linter as it has conflict rules against our
# contribution guidelines. See https://github.com/bombsimon/wsl/issues/109.
#
# TODO(yy): bring it back when the above issue is fixed.
# contribution guidelines.
- wsl
# Allow using default empty values.
@ -189,7 +177,7 @@ linters:
- wrapcheck
# Allow dynamic errors.
- goerr113
- err113
# We use ErrXXX instead.
- errname
@ -204,16 +192,44 @@ linters:
# The linter is too aggressive and doesn't add much value since reviewers
# will also catch magic numbers that make sense to extract.
- gomnd
- mnd
# Some of the tests cannot be parallelized. On the other hand, we don't
# gain much performance with this check so we disable it for now until
# unit tests become our CI bottleneck.
# Some of the tests cannot be parallelized. On the other hand, we don't
# gain much performance with this check so we disable it for now until
# unit tests become our CI bottleneck.
- paralleltest
# New linters that we haven't had time to address yet.
- testifylint
- perfsprint
- inamedparam
- copyloopvar
- tagalign
- protogetter
- revive
- depguard
- gosmopolitan
- intrange
- goconst
# Deprecated linters that have been replaced by newer ones.
- tenv
issues:
# Only show newly introduced problems.
new-from-rev: 8c66353e4c02329abdacb5a8df29998035ec2e24
new-from-rev: 03eab4db64540aa5f789c617793e4459f4ba9e78
# Skip autogenerated files for mobile and gRPC as well as copied code for
# internal use.
skip-files:
- "mobile\\/.*generated\\.go"
- "\\.pb\\.go$"
- "\\.pb\\.gw\\.go$"
- "internal\\/musig2v040"
skip-dirs:
- channeldb/migration_01_to_11
- channeldb/migration/lnwire21
exclude-rules:
# Exclude gosec from running for tests so that tests with weak randomness
@ -254,8 +270,8 @@ issues:
- forbidigo
- godot
# Allow fmt.Printf() in lncli.
- path: cmd/lncli/*
# Allow fmt.Printf() in commands.
- path: cmd/commands/*
linters:
- forbidigo

View File

@ -1,9 +1,6 @@
# If you change this value, please change it in the following files as well:
# /dev.Dockerfile
# /make/builder.Dockerfile
# /.github/workflows/main.yml
# /.github/workflows/release.yml
FROM golang:1.22.5-alpine as builder
# If you change this please also update GO_VERSION in Makefile (then run
# `make lint` to see where else it needs to be updated as well).
FROM golang:1.24.11-alpine as builder
# Force Go to use the cgo based DNS resolver. This is required to ensure DNS
# queries required to connect to linked containers succeed.
@ -24,7 +21,7 @@ RUN apk add --no-cache --update alpine-sdk \
&& cd /go/src/github.com/lightningnetwork/lnd \
&& git checkout $checkout \
&& make release-install
# Start a new, final image.
FROM alpine as final

101
Makefile
View File

@ -1,19 +1,17 @@
PKG := github.com/lightningnetwork/lnd
ESCPKG := github.com\/lightningnetwork\/lnd
MOBILE_PKG := $(PKG)/mobile
TOOLS_DIR := tools
GOCC ?= go
PREFIX ?= /usr/local
BTCD_PKG := github.com/btcsuite/btcd
GOACC_PKG := github.com/ory/go-acc
GOIMPORTS_PKG := github.com/rinchsan/gosimports/cmd/gosimports
GO_BIN := ${GOPATH}/bin
BTCD_BIN := $(GO_BIN)/btcd
GOIMPORTS_BIN := $(GO_BIN)/gosimports
GOMOBILE_BIN := $(GO_BIN)/gomobile
GOACC_BIN := $(GO_BIN)/go-acc
MOBILE_BUILD_DIR :=${GOPATH}/src/$(MOBILE_PKG)/build
IOS_BUILD_DIR := $(MOBILE_BUILD_DIR)/ios
@ -23,23 +21,21 @@ ANDROID_BUILD := $(ANDROID_BUILD_DIR)/Lndmobile.aar
COMMIT := $(shell git describe --tags --dirty)
# Determine the minor version of the active Go installation.
ACTIVE_GO_VERSION := $(shell go version | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p')
ACTIVE_GO_VERSION_MINOR := $(shell echo $(ACTIVE_GO_VERSION) | cut -d. -f2)
COMMIT := $(subst -dirty,-fresh-btcpay,$(COMMIT))
LDFLAGS := -ldflags "-X $(PKG)/build.Commit=$(COMMIT)"
LOOPVARFIX :=
ifeq ($(shell expr $(ACTIVE_GO_VERSION_MINOR) \>= 21), 1)
LOOPVARFIX := GOEXPERIMENT=loopvar
endif
# Determine the minor version of the active Go installation.
ACTIVE_GO_VERSION := $(shell $(GOCC) version | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p')
ACTIVE_GO_VERSION_MINOR := $(shell echo $(ACTIVE_GO_VERSION) | cut -d. -f2)
# GO_VERSION is the Go version used for the release build, docker files, and
# GitHub Actions. This is the reference version for the project. All other Go
# versions are checked against this version.
GO_VERSION = 1.22.5
GO_VERSION = 1.23.12
GOBUILD := $(LOOPVARFIX) go build -v
GOINSTALL := $(LOOPVARFIX) go install -v
GOTEST := $(LOOPVARFIX) go test
GOBUILD := $(GOCC) build -v
GOINSTALL := $(GOCC) install -v
GOTEST := $(GOCC) test
GOFILES_NOVENDOR = $(shell find . -type f -name '*.go' -not -path "./vendor/*" -not -name "*pb.go" -not -name "*pb.gw.go" -not -name "*.pb.json.go")
@ -73,8 +69,8 @@ endif
DOCKER_TOOLS = docker run \
--rm \
-v $(shell bash -c "go env GOCACHE || (mkdir -p /tmp/go-cache; echo /tmp/go-cache)"):/tmp/build/.cache \
-v $(shell bash -c "go env GOMODCACHE || (mkdir -p /tmp/go-modcache; echo /tmp/go-modcache)"):/tmp/build/.modcache \
-v $(shell bash -c "$(GOCC) env GOCACHE || (mkdir -p /tmp/go-cache; echo /tmp/go-cache)"):/tmp/build/.cache \
-v $(shell bash -c "$(GOCC) env GOMODCACHE || (mkdir -p /tmp/go-modcache; echo /tmp/go-modcache)"):/tmp/build/.modcache \
-v $(shell bash -c "mkdir -p /tmp/go-lint-cache; echo /tmp/go-lint-cache"):/root/.cache/golangci-lint \
-v $$(pwd):/build lnd-tools
@ -91,17 +87,13 @@ all: scratch check install
# ============
# DEPENDENCIES
# ============
$(GOACC_BIN):
@$(call print, "Installing go-acc.")
cd $(TOOLS_DIR); go install -trimpath -tags=tools $(GOACC_PKG)
$(BTCD_BIN):
@$(call print, "Installing btcd.")
cd $(TOOLS_DIR); go install -trimpath $(BTCD_PKG)
cd $(TOOLS_DIR); $(GOCC) install -trimpath $(BTCD_PKG)
$(GOIMPORTS_BIN):
@$(call print, "Installing goimports.")
cd $(TOOLS_DIR); go install -trimpath $(GOIMPORTS_PKG)
cd $(TOOLS_DIR); $(GOCC) install -trimpath $(GOIMPORTS_PKG)
# ============
# INSTALLATION
@ -162,7 +154,7 @@ release-install:
release: clean-mobile
@$(call print, "Releasing lnd and lncli binaries.")
$(VERSION_CHECK)
./scripts/release.sh build-release "$(VERSION_TAG)" "$(BUILD_SYSTEM)" "$(RELEASE_TAGS)" "$(RELEASE_LDFLAGS)"
./scripts/release.sh build-release "$(VERSION_TAG)" "$(BUILD_SYSTEM)" "$(RELEASE_TAGS)" "$(RELEASE_LDFLAGS)" "$(GO_VERSION)"
#? docker-release: Same as release but within a docker container to support reproducible builds on BSD/MacOS platforms
docker-release:
@ -195,20 +187,32 @@ ifeq ($(dbbackend),postgres)
docker rm lnd-postgres --force || echo "Starting new postgres container"
# Start a fresh postgres instance. Allow a maximum of 500 connections so
# that multiple lnd instances with a maximum number of connections of 50
# each can run concurrently.
docker run --name lnd-postgres -e POSTGRES_PASSWORD=postgres -p 6432:5432 -d postgres:13-alpine -N 500
docker logs -f lnd-postgres &
# that multiple lnd instances with a maximum number of connections of 20
# each can run concurrently. Note that many of the settings here are
# specifically for integration testing and are not fit for running
# production nodes. The increase in max connections ensures that there
# are enough entries allocated for the RWConflictPool to allow multiple
# conflicting transactions to track serialization conflicts. The
# increase in predicate locks and locks per transaction is to allow the
# queries to lock individual rows instead of entire tables, helping
# reduce serialization conflicts. Disabling sequential scan for small
# tables also helps prevent serialization conflicts by ensuring lookups
# lock only relevant rows in the index rather than the entire table.
docker run --name lnd-postgres -e POSTGRES_PASSWORD=postgres -p 6432:5432 -d postgres:13-alpine -N 1500 -c max_pred_locks_per_transaction=1024 -c max_locks_per_transaction=128 -c enable_seqscan=off
docker logs -f lnd-postgres >itest/postgres.log 2>&1 &
# Wait for the instance to be started.
sleep $(POSTGRES_START_DELAY)
endif
clean-itest-logs:
rm -rf itest/*.log itest/.logs-*
#? itest-only: Only run integration tests without re-building binaries
itest-only: db-instance
itest-only: clean-itest-logs db-instance
@$(call print, "Running integration tests with ${backend} backend.")
rm -rf itest/*.log itest/.logs-*; date
EXEC_SUFFIX=$(EXEC_SUFFIX) scripts/itest_part.sh 0 1 $(TEST_FLAGS) $(ITEST_FLAGS)
date
EXEC_SUFFIX=$(EXEC_SUFFIX) scripts/itest_part.sh 0 1 $(SHUFFLE_SEED) $(TEST_FLAGS) $(ITEST_FLAGS) -test.v
$(COLLECT_ITEST_COVERAGE)
#? itest: Build and run integration tests
@ -218,10 +222,10 @@ itest: build-itest itest-only
itest-race: build-itest-race itest-only
#? itest-parallel: Build and run integration tests in parallel mode, running up to ITEST_PARALLELISM test tranches in parallel (default 4)
itest-parallel: build-itest db-instance
itest-parallel: clean-itest-logs build-itest db-instance
@$(call print, "Running tests")
rm -rf itest/*.log itest/.logs-*; date
EXEC_SUFFIX=$(EXEC_SUFFIX) scripts/itest_parallel.sh $(ITEST_PARALLELISM) $(NUM_ITEST_TRANCHES) $(TEST_FLAGS) $(ITEST_FLAGS)
date
EXEC_SUFFIX=$(EXEC_SUFFIX) scripts/itest_parallel.sh $(ITEST_PARALLELISM) $(NUM_ITEST_TRANCHES) $(SHUFFLE_SEED) $(TEST_FLAGS) $(ITEST_FLAGS)
$(COLLECT_ITEST_COVERAGE)
#? itest-clean: Kill all running itest processes
@ -245,12 +249,12 @@ unit-debug: $(BTCD_BIN)
$(UNIT_DEBUG)
#? unit-cover: Run unit tests in coverage mode
unit-cover: $(GOACC_BIN)
unit-cover: $(BTCD_BIN)
@$(call print, "Running unit coverage tests.")
$(GOACC)
$(UNIT_COVER)
#? unit-race: Run unit tests in race detector mode
unit-race:
unit-race: $(BTCD_BIN)
@$(call print, "Running unit race tests.")
env CGO_ENABLED=1 GORACE="history_size=7 halt_on_errors=1" $(UNIT_RACE)
@ -263,18 +267,18 @@ unit-bench: $(BTCD_BIN)
# FLAKE HUNTING
# =============
#? flakehunter: Run the integration tests continuously until one fails
flakehunter: build-itest
#? flakehunter-itest: Run the integration tests continuously until one fails
flakehunter-itest: build-itest
@$(call print, "Flake hunting ${backend} integration tests.")
while [ $$? -eq 0 ]; do make itest-only icase='${icase}' backend='${backend}'; done
#? flake-unit: Run the unit tests continuously until one fails
flake-unit:
@$(call print, "Flake hunting unit tests.")
while [ $$? -eq 0 ]; do GOTRACEBACK=all $(UNIT) -count=1; done
#? flakehunter-unit: Run the unit tests continuously until one fails
flakehunter-unit:
@$(call print, "Flake hunting unit test.")
scripts/unit-test-flake-hunter.sh ${pkg} ${case}
#? flakehunter-parallel: Run the integration tests continuously until one fails, running up to ITEST_PARALLELISM test tranches in parallel (default 4)
flakehunter-parallel:
#? flakehunter-itest-parallel: Run the integration tests continuously until one fails, running up to ITEST_PARALLELISM test tranches in parallel (default 4)
flakehunter-itest-parallel:
@$(call print, "Flake hunting ${backend} integration tests in parallel.")
while [ $$? -eq 0 ]; do make itest-parallel tranches=1 parallel=${ITEST_PARALLELISM} icase='${icase}' backend='${backend}'; done
@ -319,7 +323,7 @@ check-go-version: check-go-version-dockerfile check-go-version-yaml
#? lint-source: Run static code analysis
lint-source: docker-tools
@$(call print, "Linting source.")
$(DOCKER_TOOLS) golangci-lint run -v $(LINT_WORKERS)
$(DOCKER_TOOLS) custom-gcl run -v $(LINT_WORKERS)
#? lint: Run static code analysis
lint: check-go-version lint-source
@ -351,6 +355,11 @@ help: Makefile
@$(call print, "Listing commands:")
@sed -n 's/^#?//p' $< | column -t -s ':' | sort | sed -e 's/^/ /'
#? backwards-compat-test: Run basic backwards compatibility test
backwards-compat-test:
@$(call print, "Running backwards compatability test")
./scripts/bw-compatibility-test/test.sh
#? sqlc: Generate sql models and queries in Go
sqlc:
@$(call print, "Generating sql models and queries in Go")
@ -395,7 +404,7 @@ mobile-rpc:
#? vendor: Create a vendor directory with all dependencies
vendor:
@$(call print, "Re-creating vendor directory.")
rm -r vendor/; go mod vendor
rm -r vendor/; $(GOCC) mod vendor
#? apple: Build mobile RPC stubs and project template for iOS and macOS
apple: mobile-rpc

View File

@ -63,6 +63,9 @@ Finally, we also have an active
[Slack](https://lightning.engineering/slack.html) where protocol developers, application developers, testers and users gather to
discuss various aspects of `lnd` and also Lightning in general.
First-time contributors are [highly encouraged to start with code review
first](docs/review.md), before creating their own Pull Requests.
## Installation
In order to build from source, please see [the installation
instructions](docs/INSTALL.md).

664
accessman.go Normal file
View File

@ -0,0 +1,664 @@
package lnd
import (
"context"
"fmt"
"maps"
"sync"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btclog/v2"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnutils"
)
// accessMan is responsible for managing the server's access permissions.
type accessMan struct {
cfg *accessManConfig
// banScoreMtx is used for the server's ban tracking. If the server
// mutex is also going to be locked, ensure that this is locked after
// the server mutex.
banScoreMtx sync.RWMutex
// peerChanInfo is a mapping from remote public key to {bool, uint64}
// where the bool indicates that we have an open/closed channel with the
// peer and where the uint64 indicates the number of pending-open
// channels we currently have with them. This mapping will be used to
// determine access permissions for the peer. The map key is the
// string-version of the serialized public key.
//
// NOTE: This MUST be accessed with the banScoreMtx held.
peerChanInfo map[string]channeldb.ChanCount
// peerScores stores each connected peer's access status. The map key
// is the string-version of the serialized public key.
//
// NOTE: This MUST be accessed with the banScoreMtx held.
//
// TODO(yy): unify `peerScores` and `peerChanInfo` - there's no need to
// create two maps tracking essentially the same info. `numRestricted`
// can also be derived from `peerChanInfo`.
peerScores map[string]peerSlotStatus
// numRestricted tracks the number of peers with restricted access in
// peerScores. This MUST be accessed with the banScoreMtx held.
numRestricted int64
}
type accessManConfig struct {
// initAccessPerms checks the channeldb for initial access permissions
// and then populates the peerChanInfo and peerScores maps.
initAccessPerms func() (map[string]channeldb.ChanCount, error)
// shouldDisconnect determines whether we should disconnect a peer or
// not.
shouldDisconnect func(*btcec.PublicKey) (bool, error)
// maxRestrictedSlots is the number of restricted slots we'll allocate.
maxRestrictedSlots int64
}
func newAccessMan(cfg *accessManConfig) (*accessMan, error) {
a := &accessMan{
cfg: cfg,
peerChanInfo: make(map[string]channeldb.ChanCount),
peerScores: make(map[string]peerSlotStatus),
}
counts, err := a.cfg.initAccessPerms()
if err != nil {
return nil, err
}
// We'll populate the server's peerChanInfo map with the counts fetched
// via initAccessPerms. Also note that we haven't yet connected to the
// peers.
maps.Copy(a.peerChanInfo, counts)
acsmLog.Info("Access Manager initialized")
return a, nil
}
// hasPeer checks whether a given peer already exists in the internal maps.
func (a *accessMan) hasPeer(ctx context.Context,
pub string) (peerAccessStatus, bool) {
// Lock banScoreMtx for reading so that we can read the banning maps
// below.
a.banScoreMtx.RLock()
defer a.banScoreMtx.RUnlock()
count, found := a.peerChanInfo[pub]
if found {
if count.HasOpenOrClosedChan {
acsmLog.DebugS(ctx, "Peer has open/closed channel, "+
"assigning protected access")
// Exit early if the peer is no longer restricted.
return peerStatusProtected, true
}
if count.PendingOpenCount != 0 {
acsmLog.DebugS(ctx, "Peer has pending channel(s), "+
"assigning temporary access")
// Exit early if the peer is no longer restricted.
return peerStatusTemporary, true
}
return peerStatusRestricted, true
}
// Check if the peer is found in the scores map.
status, found := a.peerScores[pub]
if found {
acsmLog.DebugS(ctx, "Peer already has access", "access",
status.state)
return status.state, true
}
return peerStatusRestricted, false
}
// assignPeerPerms assigns a new peer its permissions. This does not track the
// access in the maps. This is intentional.
func (a *accessMan) assignPeerPerms(remotePub *btcec.PublicKey) (
peerAccessStatus, error) {
ctx := btclog.WithCtx(
context.TODO(), lnutils.LogPubKey("peer", remotePub),
)
peerMapKey := string(remotePub.SerializeCompressed())
acsmLog.DebugS(ctx, "Assigning permissions")
// Default is restricted unless the below filters say otherwise.
access, peerExist := a.hasPeer(ctx, peerMapKey)
// Exit early if the peer is not restricted.
if access != peerStatusRestricted {
return access, nil
}
// If we are here, it means the peer has peerStatusRestricted.
//
// Check whether this peer is banned.
shouldDisconnect, err := a.cfg.shouldDisconnect(remotePub)
if err != nil {
acsmLog.ErrorS(ctx, "Error checking disconnect status", err)
// Access is restricted here.
return access, err
}
if shouldDisconnect {
acsmLog.WarnS(ctx, "Peer is banned, assigning restricted access",
ErrGossiperBan)
// Access is restricted here.
return access, ErrGossiperBan
}
// If we've reached this point and access hasn't changed from
// restricted, then we need to check if we even have a slot for this
// peer.
acsmLog.DebugS(ctx, "Peer has no channels, assigning restricted access")
// If this is an existing peer, there's no need to check for slot limit.
if peerExist {
acsmLog.DebugS(ctx, "Skipped slot check for existing peer")
return access, nil
}
a.banScoreMtx.RLock()
defer a.banScoreMtx.RUnlock()
if a.numRestricted >= a.cfg.maxRestrictedSlots {
acsmLog.WarnS(ctx, "No more restricted slots available, "+
"denying peer", ErrNoMoreRestrictedAccessSlots,
"num_restricted", a.numRestricted, "max_restricted",
a.cfg.maxRestrictedSlots)
return access, ErrNoMoreRestrictedAccessSlots
}
return access, nil
}
// newPendingOpenChan is called after the pending-open channel has been
// committed to the database. This may transition a restricted-access peer to a
// temporary-access peer.
func (a *accessMan) newPendingOpenChan(remotePub *btcec.PublicKey) error {
a.banScoreMtx.Lock()
defer a.banScoreMtx.Unlock()
ctx := btclog.WithCtx(
context.TODO(), lnutils.LogPubKey("peer", remotePub),
)
acsmLog.DebugS(ctx, "Processing new pending open channel")
peerMapKey := string(remotePub.SerializeCompressed())
// Fetch the peer's access status from peerScores.
status, found := a.peerScores[peerMapKey]
if !found {
acsmLog.ErrorS(ctx, "Peer score not found", ErrNoPeerScore)
// If we didn't find the peer, we'll return an error.
return ErrNoPeerScore
}
switch status.state {
case peerStatusProtected:
acsmLog.DebugS(ctx, "Peer already protected, no change")
// If this peer's access status is protected, we don't need to
// do anything.
return nil
case peerStatusTemporary:
// If this peer's access status is temporary, we'll need to
// update the peerChanInfo map. The peer's access status will
// stay temporary.
peerCount, found := a.peerChanInfo[peerMapKey]
if !found {
// Error if we did not find any info in peerChanInfo.
acsmLog.ErrorS(ctx, "Pending peer info not found",
ErrNoPendingPeerInfo)
return ErrNoPendingPeerInfo
}
// Increment the pending channel amount.
peerCount.PendingOpenCount += 1
a.peerChanInfo[peerMapKey] = peerCount
acsmLog.DebugS(ctx, "Peer is temporary, incremented "+
"pending count",
"pending_count", peerCount.PendingOpenCount)
case peerStatusRestricted:
// If the peer's access status is restricted, then we can
// transition it to a temporary-access peer. We'll need to
// update numRestricted and also peerScores. We'll also need to
// update peerChanInfo.
peerCount := channeldb.ChanCount{
HasOpenOrClosedChan: false,
PendingOpenCount: 1,
}
a.peerChanInfo[peerMapKey] = peerCount
// A restricted-access slot has opened up.
oldRestricted := a.numRestricted
a.numRestricted -= 1
a.peerScores[peerMapKey] = peerSlotStatus{
state: peerStatusTemporary,
}
acsmLog.InfoS(ctx, "Peer transitioned restricted -> "+
"temporary (pending open)",
"old_restricted", oldRestricted,
"new_restricted", a.numRestricted)
default:
// This should not be possible.
err := fmt.Errorf("invalid peer access status %v for %x",
status.state, peerMapKey)
acsmLog.ErrorS(ctx, "Invalid peer access status", err)
return err
}
return nil
}
// newPendingCloseChan is called when a pending-open channel prematurely closes
// before the funding transaction has confirmed. This potentially demotes a
// temporary-access peer to a restricted-access peer. If no restricted-access
// slots are available, the peer will be disconnected.
func (a *accessMan) newPendingCloseChan(remotePub *btcec.PublicKey) error {
a.banScoreMtx.Lock()
defer a.banScoreMtx.Unlock()
ctx := btclog.WithCtx(
context.TODO(), lnutils.LogPubKey("peer", remotePub),
)
acsmLog.DebugS(ctx, "Processing pending channel close")
peerMapKey := string(remotePub.SerializeCompressed())
// Fetch the peer's access status from peerScores.
status, found := a.peerScores[peerMapKey]
if !found {
acsmLog.ErrorS(ctx, "Peer score not found", ErrNoPeerScore)
return ErrNoPeerScore
}
switch status.state {
case peerStatusProtected:
// If this peer is protected, we don't do anything.
acsmLog.DebugS(ctx, "Peer is protected, no change")
return nil
case peerStatusTemporary:
// If this peer is temporary, we need to check if it will
// revert to a restricted-access peer.
peerCount, found := a.peerChanInfo[peerMapKey]
if !found {
acsmLog.ErrorS(ctx, "Pending peer info not found",
ErrNoPendingPeerInfo)
// Error if we did not find any info in peerChanInfo.
return ErrNoPendingPeerInfo
}
currentNumPending := peerCount.PendingOpenCount - 1
acsmLog.DebugS(ctx, "Peer is temporary, decrementing "+
"pending count",
"pending_count", currentNumPending)
if currentNumPending == 0 {
// Remove the entry from peerChanInfo.
delete(a.peerChanInfo, peerMapKey)
// If this is the only pending-open channel for this
// peer and it's getting removed, attempt to demote
// this peer to a restricted peer.
if a.numRestricted == a.cfg.maxRestrictedSlots {
// There are no available restricted slots, so
// we need to disconnect this peer. We leave
// this up to the caller.
acsmLog.WarnS(ctx, "Peer last pending "+
"channel closed: ",
ErrNoMoreRestrictedAccessSlots,
"num_restricted", a.numRestricted,
"max_restricted", a.cfg.maxRestrictedSlots)
return ErrNoMoreRestrictedAccessSlots
}
// Otherwise, there is an available restricted-access
// slot, so we can demote this peer.
a.peerScores[peerMapKey] = peerSlotStatus{
state: peerStatusRestricted,
}
// Update numRestricted.
oldRestricted := a.numRestricted
a.numRestricted++
acsmLog.InfoS(ctx, "Peer transitioned "+
"temporary -> restricted "+
"(last pending closed)",
"old_restricted", oldRestricted,
"new_restricted", a.numRestricted)
return nil
}
// Else, we don't need to demote this peer since it has other
// pending-open channels with us.
peerCount.PendingOpenCount = currentNumPending
a.peerChanInfo[peerMapKey] = peerCount
acsmLog.DebugS(ctx, "Peer still has other pending channels",
"pending_count", currentNumPending)
return nil
case peerStatusRestricted:
// This should not be possible. This indicates an error.
err := fmt.Errorf("invalid peer access state transition: "+
"pending close for restricted peer %x", peerMapKey)
acsmLog.ErrorS(ctx, "Invalid peer access state transition", err)
return err
default:
// This should not be possible.
err := fmt.Errorf("invalid peer access status %v for %x",
status.state, peerMapKey)
acsmLog.ErrorS(ctx, "Invalid peer access status", err)
return err
}
}
// newOpenChan is called when a pending-open channel becomes an open channel
// (i.e. the funding transaction has confirmed). If the remote peer is a
// temporary-access peer, it will be promoted to a protected-access peer.
func (a *accessMan) newOpenChan(remotePub *btcec.PublicKey) error {
a.banScoreMtx.Lock()
defer a.banScoreMtx.Unlock()
ctx := btclog.WithCtx(
context.TODO(), lnutils.LogPubKey("peer", remotePub),
)
acsmLog.DebugS(ctx, "Processing new open channel")
peerMapKey := string(remotePub.SerializeCompressed())
// Fetch the peer's access status from peerScores.
status, found := a.peerScores[peerMapKey]
if !found {
// If we didn't find the peer, we'll return an error.
acsmLog.ErrorS(ctx, "Peer score not found", ErrNoPeerScore)
return ErrNoPeerScore
}
switch status.state {
case peerStatusProtected:
acsmLog.DebugS(ctx, "Peer already protected, no change")
// If the peer's state is already protected, we don't need to do
// anything more.
return nil
case peerStatusTemporary:
// If the peer's state is temporary, we'll upgrade the peer to
// a protected peer.
peerCount, found := a.peerChanInfo[peerMapKey]
if !found {
// Error if we did not find any info in peerChanInfo.
acsmLog.ErrorS(ctx, "Pending peer info not found",
ErrNoPendingPeerInfo)
return ErrNoPendingPeerInfo
}
peerCount.HasOpenOrClosedChan = true
peerCount.PendingOpenCount -= 1
a.peerChanInfo[peerMapKey] = peerCount
newStatus := peerSlotStatus{
state: peerStatusProtected,
}
a.peerScores[peerMapKey] = newStatus
acsmLog.InfoS(ctx, "Peer transitioned temporary -> "+
"protected (channel opened)")
return nil
case peerStatusRestricted:
// This should not be possible. For the server to receive a
// state-transition event via NewOpenChan, the server must have
// previously granted this peer "temporary" access. This
// temporary access would not have been revoked or downgraded
// without `CloseChannel` being called with the pending
// argument set to true. This means that an open-channel state
// transition would be impossible. Therefore, we can return an
// error.
err := fmt.Errorf("invalid peer access status: new open "+
"channel for restricted peer %x", peerMapKey)
acsmLog.ErrorS(ctx, "Invalid peer access status", err)
return err
default:
// This should not be possible.
err := fmt.Errorf("invalid peer access status %v for %x",
status.state, peerMapKey)
acsmLog.ErrorS(ctx, "Invalid peer access status", err)
return err
}
}
// checkAcceptIncomingConn checks whether, given the remote's public hex-
// encoded key, we should not accept this incoming connection or immediately
// disconnect. This does not assign to the server's peerScores maps. This is
// just an inbound filter that the brontide listeners use.
//
// TODO(yy): We should also consider removing this `checkAcceptIncomingConn`
// check as a) it doesn't check for ban score; and b) we should, and already
// have this check when we handle incoming connection in `InboundPeerConnected`.
func (a *accessMan) checkAcceptIncomingConn(remotePub *btcec.PublicKey) (
bool, error) {
ctx := btclog.WithCtx(
context.TODO(), lnutils.LogPubKey("peer", remotePub),
)
peerMapKey := string(remotePub.SerializeCompressed())
acsmLog.TraceS(ctx, "Checking incoming connection ban score")
a.banScoreMtx.RLock()
defer a.banScoreMtx.RUnlock()
_, found := a.peerChanInfo[peerMapKey]
// Exit early if found.
if found {
acsmLog.DebugS(ctx, "Peer found (protected/temporary), "+
"accepting")
return true, nil
}
_, found = a.peerScores[peerMapKey]
// Exit early if found.
if found {
acsmLog.DebugS(ctx, "Found existing peer, accepting")
return true, nil
}
acsmLog.DebugS(ctx, "Peer not found in counts, checking restricted "+
"slots")
// Check numRestricted to see if there is an available slot. In
// the future, it's possible to add better heuristics.
if a.numRestricted < a.cfg.maxRestrictedSlots {
// There is an available slot.
acsmLog.DebugS(ctx, "Restricted slot available, accepting ",
"num_restricted", a.numRestricted, "max_restricted",
a.cfg.maxRestrictedSlots)
return true, nil
}
// If there are no slots left, then we reject this connection.
acsmLog.WarnS(ctx, "No restricted slots available, rejecting ",
ErrNoMoreRestrictedAccessSlots, "num_restricted",
a.numRestricted, "max_restricted", a.cfg.maxRestrictedSlots)
return false, ErrNoMoreRestrictedAccessSlots
}
// addPeerAccess tracks a peer's access in the maps. This should be called when
// the peer has fully connected.
func (a *accessMan) addPeerAccess(remotePub *btcec.PublicKey,
access peerAccessStatus, inbound bool) {
ctx := btclog.WithCtx(
context.TODO(), lnutils.LogPubKey("peer", remotePub),
)
acsmLog.DebugS(ctx, "Adding peer access", "access", access)
// Add the remote public key to peerScores.
a.banScoreMtx.Lock()
defer a.banScoreMtx.Unlock()
peerMapKey := string(remotePub.SerializeCompressed())
// Exit early if this is an existing peer, which means it won't take
// another slot.
_, found := a.peerScores[peerMapKey]
if found {
acsmLog.DebugS(ctx, "Skipped taking restricted slot for "+
"existing peer")
return
}
a.peerScores[peerMapKey] = peerSlotStatus{state: access}
// Exit early if this is not a restricted peer.
if access != peerStatusRestricted {
acsmLog.DebugS(ctx, "Skipped taking restricted slot as peer "+
"already has access", "access", access)
return
}
// Increment numRestricted if this is an inbound connection.
if inbound {
oldRestricted := a.numRestricted
a.numRestricted++
acsmLog.DebugS(ctx, "Incremented restricted slots",
"old_restricted", oldRestricted,
"new_restricted", a.numRestricted)
return
}
// Otherwise, this is a newly created outbound connection. We won't
// place any restriction on it, instead, we will do a hot upgrade here
// to move it from restricted to temporary.
peerCount := channeldb.ChanCount{
HasOpenOrClosedChan: false,
PendingOpenCount: 0,
}
a.peerChanInfo[peerMapKey] = peerCount
a.peerScores[peerMapKey] = peerSlotStatus{
state: peerStatusTemporary,
}
acsmLog.InfoS(ctx, "Upgraded outbound peer: restricted -> temporary")
}
// removePeerAccess removes the peer's access from the maps. This should be
// called when the peer has been disconnected.
func (a *accessMan) removePeerAccess(ctx context.Context, peerPubKey string) {
acsmLog.DebugS(ctx, "Removing access:")
a.banScoreMtx.Lock()
defer a.banScoreMtx.Unlock()
status, found := a.peerScores[peerPubKey]
if !found {
acsmLog.InfoS(ctx, "Peer score not found during removal")
return
}
if status.state == peerStatusRestricted {
// If the status is restricted, then we decrement from
// numRestrictedSlots.
oldRestricted := a.numRestricted
a.numRestricted--
acsmLog.DebugS(ctx, "Decremented restricted slots",
"old_restricted", oldRestricted,
"new_restricted", a.numRestricted)
}
acsmLog.TraceS(ctx, "Deleting from peerScores:")
delete(a.peerScores, peerPubKey)
// We now check whether this peer has channels with us or not.
info, found := a.peerChanInfo[peerPubKey]
if !found {
acsmLog.DebugS(ctx, "Chan info not found during removal:")
return
}
// Exit early if the peer has channel(s) with us.
if info.HasOpenOrClosedChan {
acsmLog.DebugS(ctx, "Skipped removing peer with channels:")
return
}
// Skip removing the peer if it has pending open/close with us.
if info.PendingOpenCount != 0 {
acsmLog.DebugS(ctx, "Skipped removing peer with pending "+
"channels:")
return
}
// Given this peer has no channels with us, we can now remove it.
delete(a.peerChanInfo, peerPubKey)
acsmLog.TraceS(ctx, "Removed peer from peerChanInfo:")
}

814
accessman_test.go Normal file
View File

@ -0,0 +1,814 @@
package lnd
import (
"context"
"testing"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/stretchr/testify/require"
)
// assertInboundConnection asserts that we're able to accept an inbound
// connection successfully without any access permissions being violated.
func assertInboundConnection(t *testing.T, a *accessMan,
remotePub *btcec.PublicKey, status peerAccessStatus) {
remotePubSer := string(remotePub.SerializeCompressed())
isSlotAvailable, err := a.checkAcceptIncomingConn(remotePub)
require.NoError(t, err)
require.True(t, isSlotAvailable)
peerAccess, err := a.assignPeerPerms(remotePub)
require.NoError(t, err)
require.Equal(t, status, peerAccess)
a.addPeerAccess(remotePub, peerAccess, true)
peerScore, ok := a.peerScores[remotePubSer]
require.True(t, ok)
require.Equal(t, status, peerScore.state)
}
func assertAccessState(t *testing.T, a *accessMan, remotePub *btcec.PublicKey,
expectedStatus peerAccessStatus) {
remotePubSer := string(remotePub.SerializeCompressed())
peerScore, ok := a.peerScores[remotePubSer]
require.True(t, ok)
require.Equal(t, expectedStatus, peerScore.state)
}
// TestAccessManRestrictedSlots tests that the configurable number of
// restricted slots are properly allocated. It also tests that certain peers
// with access permissions are allowed to bypass the slot mechanism.
func TestAccessManRestrictedSlots(t *testing.T) {
t.Parallel()
// We'll pre-populate the map to mock the database fetch. We'll make
// three peers. One has an open/closed channel. One has both an open
// / closed channel and a pending channel. The last one has only a
// pending channel.
peerPriv1, err := btcec.NewPrivateKey()
require.NoError(t, err)
peerKey1 := peerPriv1.PubKey()
peerKeySer1 := string(peerKey1.SerializeCompressed())
peerPriv2, err := btcec.NewPrivateKey()
require.NoError(t, err)
peerKey2 := peerPriv2.PubKey()
peerKeySer2 := string(peerKey2.SerializeCompressed())
peerPriv3, err := btcec.NewPrivateKey()
require.NoError(t, err)
peerKey3 := peerPriv3.PubKey()
peerKeySer3 := string(peerKey3.SerializeCompressed())
var (
peer1PendingCount = 0
peer2PendingCount = 1
peer3PendingCount = 1
)
initPerms := func() (map[string]channeldb.ChanCount, error) {
return map[string]channeldb.ChanCount{
peerKeySer1: {
HasOpenOrClosedChan: true,
PendingOpenCount: uint64(peer1PendingCount),
},
peerKeySer2: {
HasOpenOrClosedChan: true,
PendingOpenCount: uint64(peer2PendingCount),
},
peerKeySer3: {
HasOpenOrClosedChan: false,
PendingOpenCount: uint64(peer3PendingCount),
},
}, nil
}
disconnect := func(*btcec.PublicKey) (bool, error) {
return false, nil
}
cfg := &accessManConfig{
initAccessPerms: initPerms,
shouldDisconnect: disconnect,
maxRestrictedSlots: 1,
}
a, err := newAccessMan(cfg)
require.NoError(t, err)
// Check that the peerChanInfo map is correctly populated with three
// peers.
require.Equal(t, 0, int(a.numRestricted))
require.Equal(t, 3, len(a.peerChanInfo))
peerCount1, ok := a.peerChanInfo[peerKeySer1]
require.True(t, ok)
require.True(t, peerCount1.HasOpenOrClosedChan)
require.Equal(t, peer1PendingCount, int(peerCount1.PendingOpenCount))
peerCount2, ok := a.peerChanInfo[peerKeySer2]
require.True(t, ok)
require.True(t, peerCount2.HasOpenOrClosedChan)
require.Equal(t, peer2PendingCount, int(peerCount2.PendingOpenCount))
peerCount3, ok := a.peerChanInfo[peerKeySer3]
require.True(t, ok)
require.False(t, peerCount3.HasOpenOrClosedChan)
require.Equal(t, peer3PendingCount, int(peerCount3.PendingOpenCount))
// We'll now start to connect the peers. We'll add a new fourth peer
// that will take up the restricted slot. The first three peers should
// be able to bypass this restricted slot mechanism.
peerPriv4, err := btcec.NewPrivateKey()
require.NoError(t, err)
peerKey4 := peerPriv4.PubKey()
// Follow the normal process of an incoming connection. We check if we
// can accommodate this peer in checkAcceptIncomingConn and then we
// assign its access permissions and then insert into the map.
assertInboundConnection(t, a, peerKey4, peerStatusRestricted)
// Connect the three peers. This should happen without any issue.
assertInboundConnection(t, a, peerKey1, peerStatusProtected)
assertInboundConnection(t, a, peerKey2, peerStatusProtected)
assertInboundConnection(t, a, peerKey3, peerStatusTemporary)
// Check that a pending-open channel promotes the restricted peer.
err = a.newPendingOpenChan(peerKey4)
require.NoError(t, err)
assertAccessState(t, a, peerKey4, peerStatusTemporary)
// Assert that accessman's internal state is updated with peer4. We
// expect this new peer to have 1 pending open count.
peerCount4, ok := a.peerChanInfo[string(peerKey4.SerializeCompressed())]
require.True(t, ok)
require.False(t, peerCount4.HasOpenOrClosedChan)
require.Equal(t, 1, int(peerCount4.PendingOpenCount))
// Check that an open channel promotes the temporary peer.
err = a.newOpenChan(peerKey3)
require.NoError(t, err)
assertAccessState(t, a, peerKey3, peerStatusProtected)
// Assert that accessman's internal state is updated with peer3. We
// expect this existing peer to decrement its pending open count and the
// flag `HasOpenOrClosedChan` should be true.
peerCount3, ok = a.peerChanInfo[peerKeySer3]
require.True(t, ok)
require.True(t, peerCount3.HasOpenOrClosedChan)
require.Equal(t, peer3PendingCount-1, int(peerCount3.PendingOpenCount))
// We should be able to accommodate a new peer.
peerPriv5, err := btcec.NewPrivateKey()
require.NoError(t, err)
peerKey5 := peerPriv5.PubKey()
assertInboundConnection(t, a, peerKey5, peerStatusRestricted)
// Check that a pending-close channel event for peer 4 demotes the
// peer.
err = a.newPendingCloseChan(peerKey4)
require.ErrorIs(t, err, ErrNoMoreRestrictedAccessSlots)
// Assert that peer4 is removed.
_, ok = a.peerChanInfo[string(peerKey4.SerializeCompressed())]
require.False(t, ok)
}
// TestAssignPeerPerms asserts that the peer's access status is correctly
// assigned.
func TestAssignPeerPerms(t *testing.T) {
t.Parallel()
// genPeerPub is a helper closure that generates a random public key.
genPeerPub := func() *btcec.PublicKey {
peerPriv, err := btcec.NewPrivateKey()
require.NoError(t, err)
return peerPriv.PubKey()
}
disconnect := func(_ *btcec.PublicKey) (bool, error) {
return true, nil
}
noDisconnect := func(_ *btcec.PublicKey) (bool, error) {
return false, nil
}
var testCases = []struct {
name string
peerPub *btcec.PublicKey
chanCount channeldb.ChanCount
shouldDisconnect func(*btcec.PublicKey) (bool, error)
numRestricted int
expectedStatus peerAccessStatus
expectedErr error
}{
// peer1 has a channel with us, and we expect it to have a
// protected status.
{
name: "peer with channels",
peerPub: genPeerPub(),
chanCount: channeldb.ChanCount{
HasOpenOrClosedChan: true,
},
shouldDisconnect: noDisconnect,
expectedStatus: peerStatusProtected,
expectedErr: nil,
},
// peer2 has a channel open and a pending channel with us, we
// expect it to have a protected status.
{
name: "peer with channels and pending channels",
peerPub: genPeerPub(),
chanCount: channeldb.ChanCount{
HasOpenOrClosedChan: true,
PendingOpenCount: 1,
},
shouldDisconnect: noDisconnect,
expectedStatus: peerStatusProtected,
expectedErr: nil,
},
// peer3 has a pending channel with us, and we expect it to have
// a temporary status.
{
name: "peer with pending channels",
peerPub: genPeerPub(),
chanCount: channeldb.ChanCount{
HasOpenOrClosedChan: false,
PendingOpenCount: 1,
},
shouldDisconnect: noDisconnect,
expectedStatus: peerStatusTemporary,
expectedErr: nil,
},
// peer4 has no channel with us, and we expect it to have a
// restricted status.
{
name: "peer with no channels",
peerPub: genPeerPub(),
chanCount: channeldb.ChanCount{
HasOpenOrClosedChan: false,
PendingOpenCount: 0,
},
shouldDisconnect: noDisconnect,
expectedStatus: peerStatusRestricted,
expectedErr: nil,
},
// peer5 has no channel with us, and we expect it to have a
// restricted status. We also expect the error `ErrGossiperBan`
// to be returned given we will use a mocked `shouldDisconnect`
// in this test to disconnect on peer5 only.
{
name: "peer with no channels and banned",
peerPub: genPeerPub(),
chanCount: channeldb.ChanCount{
HasOpenOrClosedChan: false,
PendingOpenCount: 0,
},
shouldDisconnect: disconnect,
expectedStatus: peerStatusRestricted,
expectedErr: ErrGossiperBan,
},
// peer6 has no channel with us, and we expect it to have a
// restricted status. Since this peer is seen, we don't expect
// the error `ErrNoMoreRestrictedAccessSlots` to be returned.
{
name: "peer with no channels and restricted",
peerPub: genPeerPub(),
chanCount: channeldb.ChanCount{
HasOpenOrClosedChan: false,
PendingOpenCount: 0,
},
shouldDisconnect: noDisconnect,
numRestricted: 1,
expectedStatus: peerStatusRestricted,
expectedErr: nil,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
peerStr := string(tc.peerPub.SerializeCompressed())
initPerms := func() (map[string]channeldb.ChanCount,
error) {
return map[string]channeldb.ChanCount{
peerStr: tc.chanCount,
}, nil
}
cfg := &accessManConfig{
initAccessPerms: initPerms,
shouldDisconnect: tc.shouldDisconnect,
maxRestrictedSlots: 1,
}
a, err := newAccessMan(cfg)
require.NoError(t, err)
// Initialize the internal state of the accessman.
a.numRestricted = int64(tc.numRestricted)
status, err := a.assignPeerPerms(tc.peerPub)
require.Equal(t, tc.expectedStatus, status)
require.ErrorIs(t, tc.expectedErr, err)
})
}
}
// TestAssignPeerPermsBypassRestriction asserts that when a peer has a channel
// with us, either it being open, pending, or closed, no restriction is placed
// on this peer.
func TestAssignPeerPermsBypassRestriction(t *testing.T) {
t.Parallel()
// genPeerPub is a helper closure that generates a random public key.
genPeerPub := func() *btcec.PublicKey {
peerPriv, err := btcec.NewPrivateKey()
require.NoError(t, err)
return peerPriv.PubKey()
}
// Mock shouldDisconnect to always return true and assert that it has no
// effect on the peer.
disconnect := func(_ *btcec.PublicKey) (bool, error) {
return true, nil
}
var testCases = []struct {
name string
peerPub *btcec.PublicKey
chanCount channeldb.ChanCount
expectedStatus peerAccessStatus
}{
// peer1 has a channel with us, and we expect it to have a
// protected status.
{
name: "peer with channels",
peerPub: genPeerPub(),
chanCount: channeldb.ChanCount{
HasOpenOrClosedChan: true,
},
expectedStatus: peerStatusProtected,
},
// peer2 has a channel open and a pending channel with us, we
// expect it to have a protected status.
{
name: "peer with channels and pending channels",
peerPub: genPeerPub(),
chanCount: channeldb.ChanCount{
HasOpenOrClosedChan: true,
PendingOpenCount: 1,
},
expectedStatus: peerStatusProtected,
},
// peer3 has a pending channel with us, and we expect it to have
// a temporary status.
{
name: "peer with pending channels",
peerPub: genPeerPub(),
chanCount: channeldb.ChanCount{
HasOpenOrClosedChan: false,
PendingOpenCount: 1,
},
expectedStatus: peerStatusTemporary,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
peerStr := string(tc.peerPub.SerializeCompressed())
initPerms := func() (map[string]channeldb.ChanCount,
error) {
return map[string]channeldb.ChanCount{
peerStr: tc.chanCount,
}, nil
}
// Config the accessman such that it has zero max slots
// and always return true on `shouldDisconnect`. We
// should see the peers in this test are not affected by
// these checks.
cfg := &accessManConfig{
initAccessPerms: initPerms,
shouldDisconnect: disconnect,
maxRestrictedSlots: 0,
}
a, err := newAccessMan(cfg)
require.NoError(t, err)
status, err := a.assignPeerPerms(tc.peerPub)
require.NoError(t, err)
require.Equal(t, tc.expectedStatus, status)
})
}
}
// TestAssignPeerPermsBypassExisting asserts that when the peer is a
// pre-existing peer, it won't be restricted.
func TestAssignPeerPermsBypassExisting(t *testing.T) {
t.Parallel()
// genPeerPub is a helper closure that generates a random public key.
genPeerPub := func() *btcec.PublicKey {
peerPriv, err := btcec.NewPrivateKey()
require.NoError(t, err)
return peerPriv.PubKey()
}
// peer1 exists in `peerChanInfo` map.
peer1 := genPeerPub()
peer1Str := string(peer1.SerializeCompressed())
// peer2 exists in `peerScores` map.
peer2 := genPeerPub()
peer2Str := string(peer2.SerializeCompressed())
// peer3 is a new peer.
peer3 := genPeerPub()
// Create params to init the accessman.
initPerms := func() (map[string]channeldb.ChanCount, error) {
return map[string]channeldb.ChanCount{
peer1Str: {},
}, nil
}
disconnect := func(*btcec.PublicKey) (bool, error) {
return false, nil
}
cfg := &accessManConfig{
initAccessPerms: initPerms,
shouldDisconnect: disconnect,
maxRestrictedSlots: 0,
}
a, err := newAccessMan(cfg)
require.NoError(t, err)
// Add peer2 to the `peerScores`.
a.peerScores[peer2Str] = peerSlotStatus{
state: peerStatusTemporary,
}
// Assigning to peer1 should not return an error.
status, err := a.assignPeerPerms(peer1)
require.NoError(t, err)
require.Equal(t, peerStatusRestricted, status)
// Assigning to peer2 should not return an error.
status, err = a.assignPeerPerms(peer2)
require.NoError(t, err)
require.Equal(t, peerStatusTemporary, status)
// Assigning to peer3 should return an error.
status, err = a.assignPeerPerms(peer3)
require.ErrorIs(t, err, ErrNoMoreRestrictedAccessSlots)
require.Equal(t, peerStatusRestricted, status)
}
// TestHasPeer asserts `hasPeer` returns the correct results.
func TestHasPeer(t *testing.T) {
t.Parallel()
ctx := context.Background()
// Create a testing accessMan.
a := &accessMan{
peerChanInfo: make(map[string]channeldb.ChanCount),
peerScores: make(map[string]peerSlotStatus),
}
// peer1 exists with an open channel.
peer1 := "peer1"
a.peerChanInfo[peer1] = channeldb.ChanCount{
HasOpenOrClosedChan: true,
}
peer1Access := peerStatusProtected
// peer2 exists with a pending channel.
peer2 := "peer2"
a.peerChanInfo[peer2] = channeldb.ChanCount{
PendingOpenCount: 1,
}
peer2Access := peerStatusTemporary
// peer3 exists without any channels.
peer3 := "peer3"
a.peerChanInfo[peer3] = channeldb.ChanCount{}
peer3Access := peerStatusRestricted
// peer4 exists with a score.
peer4 := "peer4"
peer4Access := peerStatusTemporary
a.peerScores[peer4] = peerSlotStatus{state: peer4Access}
// peer5 doesn't exist.
peer5 := "peer5"
// We now assert `hasPeer` returns the correct results.
//
// peer1 should be found with peerStatusProtected.
access, found := a.hasPeer(ctx, peer1)
require.True(t, found)
require.Equal(t, peer1Access, access)
// peer2 should be found with peerStatusTemporary.
access, found = a.hasPeer(ctx, peer2)
require.True(t, found)
require.Equal(t, peer2Access, access)
// peer3 should be found with peerStatusRestricted.
access, found = a.hasPeer(ctx, peer3)
require.True(t, found)
require.Equal(t, peer3Access, access)
// peer4 should be found with peerStatusTemporary.
access, found = a.hasPeer(ctx, peer4)
require.True(t, found)
require.Equal(t, peer4Access, access)
// peer5 should NOT be found.
access, found = a.hasPeer(ctx, peer5)
require.False(t, found)
require.Equal(t, peerStatusRestricted, access)
}
// TestAddPeerAccessInbound asserts the num of slots is correctly incremented
// only for a new inbound peer with restricted access.
func TestAddPeerAccessInbound(t *testing.T) {
t.Parallel()
// Create a testing accessMan.
a := &accessMan{
peerChanInfo: make(map[string]channeldb.ChanCount),
peerScores: make(map[string]peerSlotStatus),
}
// Create a testing key.
priv, err := btcec.NewPrivateKey()
require.NoError(t, err)
pub := priv.PubKey()
pubStr := string(pub.SerializeCompressed())
// Add this peer as an inbound peer with peerStatusRestricted.
a.addPeerAccess(pub, peerStatusRestricted, true)
// Assert the accessMan's internal state.
//
// We expect to see one peer found in the score map, and one slot is
// taken, and this peer is not found in the counts map.
require.Len(t, a.peerScores, 1)
require.Equal(t, int64(1), a.numRestricted)
require.NotContains(t, a.peerChanInfo, pubStr)
// The peer should be found in the score map.
score, ok := a.peerScores[pubStr]
require.True(t, ok)
expecedScore := peerSlotStatus{state: peerStatusRestricted}
require.Equal(t, expecedScore, score)
// Add this peer again, we expect the available slots to stay unchanged.
a.addPeerAccess(pub, peerStatusRestricted, true)
// Assert the internal state is not changed.
require.Len(t, a.peerScores, 1)
require.Equal(t, int64(1), a.numRestricted)
require.NotContains(t, a.peerChanInfo, pubStr)
// Reset the accessMan.
a = &accessMan{
peerChanInfo: make(map[string]channeldb.ChanCount),
peerScores: make(map[string]peerSlotStatus),
}
// Add this peer as an inbound peer with peerStatusTemporary.
a.addPeerAccess(pub, peerStatusTemporary, true)
// Assert the accessMan's internal state.
//
// We expect to see one peer found in the score map, and no slot is
// taken since this peer is not restricted.
require.Len(t, a.peerScores, 1)
require.Equal(t, int64(0), a.numRestricted)
// NOTE: in reality this is not possible as the peer must have been put
// into the map `peerChanInfo` before its perm can be upgraded.
require.NotContains(t, a.peerChanInfo, pubStr)
// The peer should be found in the score map.
score, ok = a.peerScores[pubStr]
require.True(t, ok)
expecedScore = peerSlotStatus{state: peerStatusTemporary}
require.Equal(t, expecedScore, score)
}
// TestAddPeerAccessOutbound asserts that outbound peer is not restricted and
// its perm is upgraded when it has peerStatusRestricted.
func TestAddPeerAccessOutbound(t *testing.T) {
t.Parallel()
// Create a testing accessMan.
a := &accessMan{
peerChanInfo: make(map[string]channeldb.ChanCount),
peerScores: make(map[string]peerSlotStatus),
}
// Create a testing key.
priv, err := btcec.NewPrivateKey()
require.NoError(t, err)
pub := priv.PubKey()
pubStr := string(pub.SerializeCompressed())
// Add this peer as an outbound peer with peerStatusRestricted.
a.addPeerAccess(pub, peerStatusRestricted, false)
// Assert the accessMan's internal state.
//
// We expect to see one peer found in the score map, and no slot is
// taken, and this peer is found in the counts map.
require.Len(t, a.peerScores, 1)
require.Equal(t, int64(0), a.numRestricted)
require.Contains(t, a.peerChanInfo, pubStr)
// The peer should be found in the score map.
score, ok := a.peerScores[pubStr]
require.True(t, ok)
// Its perm should be upgraded to temporary.
expecedScore := peerSlotStatus{state: peerStatusTemporary}
require.Equal(t, expecedScore, score)
// The peer should be found in the peer counts map.
count, ok := a.peerChanInfo[pubStr]
require.True(t, ok)
// The peer's count should be initialized correctly.
require.Zero(t, count.PendingOpenCount)
require.False(t, count.HasOpenOrClosedChan)
// Add this peer again, we expect the available slots to stay unchanged.
a.addPeerAccess(pub, peerStatusRestricted, true)
// Assert the internal state is not changed.
require.Len(t, a.peerScores, 1)
require.Equal(t, int64(0), a.numRestricted)
require.Contains(t, a.peerChanInfo, pubStr)
// Reset the accessMan.
a = &accessMan{
peerChanInfo: make(map[string]channeldb.ChanCount),
peerScores: make(map[string]peerSlotStatus),
}
// Add this peer as an inbound peer with peerStatusTemporary.
a.addPeerAccess(pub, peerStatusTemporary, true)
// Assert the accessMan's internal state.
//
// We expect to see one peer found in the score map, and no slot is
// taken since this peer is not restricted.
require.Len(t, a.peerScores, 1)
require.Equal(t, int64(0), a.numRestricted)
// NOTE: in reality this is not possible as the peer must have been put
// into the map `peerChanInfo` before its perm can be upgraded.
require.NotContains(t, a.peerChanInfo, pubStr)
// The peer should be found in the score map.
score, ok = a.peerScores[pubStr]
require.True(t, ok)
expecedScore = peerSlotStatus{state: peerStatusTemporary}
require.Equal(t, expecedScore, score)
}
// TestRemovePeerAccess asserts `removePeerAccess` correctly update the
// accessman's internal state based on the peer's status.
func TestRemovePeerAccess(t *testing.T) {
t.Parallel()
ctx := context.Background()
// Create a testing accessMan.
a := &accessMan{
peerChanInfo: make(map[string]channeldb.ChanCount),
peerScores: make(map[string]peerSlotStatus),
}
// numRestrictedExpected specifies the final value to expect once the
// test finishes.
var numRestrictedExpected int
// peer1 exists with an open channel, which should not be removed. Since
// it has protected status, the numRestricted should stay unchanged.
peer1 := "peer1"
a.peerChanInfo[peer1] = channeldb.ChanCount{
HasOpenOrClosedChan: true,
}
peer1Access := peerStatusProtected
a.peerScores[peer1] = peerSlotStatus{state: peer1Access}
// peer2 exists with a pending channel, which should not be removed.
// Since it has temporary status, the numRestricted should stay
// unchanged.
peer2 := "peer2"
a.peerChanInfo[peer2] = channeldb.ChanCount{
PendingOpenCount: 1,
}
peer2Access := peerStatusTemporary
a.peerScores[peer2] = peerSlotStatus{state: peer2Access}
// peer3 exists without any channels, which will be removed. Since it
// has restricted status, the numRestricted should be decremented.
peer3 := "peer3"
a.peerChanInfo[peer3] = channeldb.ChanCount{}
peer3Access := peerStatusRestricted
a.peerScores[peer3] = peerSlotStatus{state: peer3Access}
numRestrictedExpected--
// peer4 exists with a score and a temporary status, which will be
// removed.
peer4 := "peer4"
peer4Access := peerStatusTemporary
a.peerScores[peer4] = peerSlotStatus{state: peer4Access}
// peer5 doesn't exist, removing it will be a NOOP.
peer5 := "peer5"
// We now assert `removePeerAccess` behaves as expected.
//
// Remove peer1 should change nothing.
a.removePeerAccess(ctx, peer1)
// peer1 should be removed from peerScores but not peerChanInfo.
_, found := a.peerScores[peer1]
require.False(t, found)
_, found = a.peerChanInfo[peer1]
require.True(t, found)
// Remove peer2 should change nothing.
a.removePeerAccess(ctx, peer2)
// peer2 should be removed from peerScores but not peerChanInfo.
_, found = a.peerScores[peer2]
require.False(t, found)
_, found = a.peerChanInfo[peer2]
require.True(t, found)
// Remove peer3 should remove it from the maps.
a.removePeerAccess(ctx, peer3)
// peer3 should be removed from peerScores and peerChanInfo.
_, found = a.peerScores[peer3]
require.False(t, found)
_, found = a.peerChanInfo[peer3]
require.False(t, found)
// Remove peer4 should remove it from the maps.
a.removePeerAccess(ctx, peer4)
// peer4 should be removed from peerScores and NOT be found in
// peerChanInfo.
_, found = a.peerScores[peer4]
require.False(t, found)
_, found = a.peerChanInfo[peer4]
require.False(t, found)
// Remove peer5 should be NOOP.
a.removePeerAccess(ctx, peer5)
// peer5 should NOT be found.
_, found = a.peerScores[peer5]
require.False(t, found)
_, found = a.peerChanInfo[peer5]
require.False(t, found)
// Finally, assert the numRestricted is decremented as expected. Given
// we only have peer3 which has restricted access, it should decrement
// once.
//
// NOTE: The value is actually negative here, which is allowed in
// accessman.
require.EqualValues(t, numRestrictedExpected, a.numRestricted)
}

View File

@ -3,13 +3,25 @@ package aliasmgr
import (
"encoding/binary"
"fmt"
"maps"
"slices"
"sync"
"github.com/lightningnetwork/lnd/fn/v2"
"github.com/lightningnetwork/lnd/htlcswitch/hop"
"github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/lnwire"
)
// UpdateLinkAliases is a function type for a function that locates the active
// link that matches the given shortID and triggers an update based on the
// latest values of the alias manager.
type UpdateLinkAliases func(shortID lnwire.ShortChannelID) error
// ScidAliasMap is a map from a base short channel ID to a set of alias short
// channel IDs.
type ScidAliasMap map[lnwire.ShortChannelID][]lnwire.ShortChannelID
var (
// aliasBucket stores aliases as keys and their base SCIDs as values.
// This is used to populate the maps that the Manager uses. The keys
@ -47,17 +59,18 @@ var (
// operations.
byteOrder = binary.BigEndian
// startBlockHeight is the starting block height of the alias range.
startingBlockHeight = 16_000_000
// AliasStartBlockHeight is the starting block height of the alias
// range.
AliasStartBlockHeight uint32 = 16_000_000
// endBlockHeight is the ending block height of the alias range.
endBlockHeight = 16_250_000
// AliasEndBlockHeight is the ending block height of the alias range.
AliasEndBlockHeight uint32 = 16_250_000
// StartingAlias is the first alias ShortChannelID that will get
// assigned by RequestAlias. The starting BlockHeight is chosen so that
// legitimate SCIDs in integration tests aren't mistaken for an alias.
StartingAlias = lnwire.ShortChannelID{
BlockHeight: uint32(startingBlockHeight),
BlockHeight: AliasStartBlockHeight,
TxIndex: 0,
TxPosition: 0,
}
@ -68,6 +81,10 @@ var (
// errNoPeerAlias is returned when the peer's alias for a given
// channel is not found.
errNoPeerAlias = fmt.Errorf("no peer alias found")
// ErrAliasNotFound is returned when the alias is not found and can't
// be mapped to a base SCID.
ErrAliasNotFound = fmt.Errorf("alias not found")
)
// Manager is a struct that handles aliases for LND. It has an underlying
@ -77,10 +94,14 @@ var (
type Manager struct {
backend kvdb.Backend
// linkAliasUpdater is a function used by the alias manager to
// facilitate live update of aliases in other subsystems.
linkAliasUpdater UpdateLinkAliases
// baseToSet is a mapping from the "base" SCID to the set of aliases
// for this channel. This mapping includes all channels that
// negotiated the option-scid-alias feature bit.
baseToSet map[lnwire.ShortChannelID][]lnwire.ShortChannelID
baseToSet ScidAliasMap
// aliasToBase is a mapping that maps all aliases for a given channel
// to its base SCID. This is only used for channels that have
@ -98,9 +119,15 @@ type Manager struct {
}
// NewManager initializes an alias Manager from the passed database backend.
func NewManager(db kvdb.Backend) (*Manager, error) {
m := &Manager{backend: db}
m.baseToSet = make(map[lnwire.ShortChannelID][]lnwire.ShortChannelID)
func NewManager(db kvdb.Backend, linkAliasUpdater UpdateLinkAliases) (*Manager,
error) {
m := &Manager{
backend: db,
baseToSet: make(ScidAliasMap),
linkAliasUpdater: linkAliasUpdater,
}
m.aliasToBase = make(map[lnwire.ShortChannelID]lnwire.ShortChannelID)
m.peerAlias = make(map[lnwire.ChannelID]lnwire.ShortChannelID)
@ -215,12 +242,22 @@ func (m *Manager) populateMaps() error {
// AddLocalAlias adds a database mapping from the passed alias to the passed
// base SCID. The gossip boolean marks whether or not to create a mapping
// that the gossiper will use. It is set to false for the upgrade path where
// the feature-bit is toggled on and there are existing channels.
// the feature-bit is toggled on and there are existing channels. The linkUpdate
// flag is used to signal whether this function should also trigger an update
// on the htlcswitch scid alias maps.
func (m *Manager) AddLocalAlias(alias, baseScid lnwire.ShortChannelID,
gossip bool) error {
gossip, linkUpdate bool) error {
// We need to lock the manager for the whole duration of this method,
// except for the very last part where we call the link updater. In
// order for us to safely use a defer _and_ still be able to manually
// unlock, we use a sync.Once.
m.Lock()
defer m.Unlock()
unlockOnce := sync.Once{}
unlock := func() {
unlockOnce.Do(m.Unlock)
}
defer unlock()
err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
// If the caller does not want to allow the alias to be used
@ -270,6 +307,18 @@ func (m *Manager) AddLocalAlias(alias, baseScid lnwire.ShortChannelID,
m.aliasToBase[alias] = baseScid
}
// We definitely need to unlock the Manager before calling the link
// updater. If we don't, we'll deadlock. We use a sync.Once to ensure
// that we only unlock once.
unlock()
// Finally, we trigger a htlcswitch update if the flag is set, in order
// for any future htlc that references the added alias to be properly
// routed.
if linkUpdate {
return m.linkAliasUpdater(baseScid)
}
return nil
}
@ -340,6 +389,74 @@ func (m *Manager) DeleteSixConfs(baseScid lnwire.ShortChannelID) error {
return nil
}
// DeleteLocalAlias removes a mapping from the database and the Manager's maps.
func (m *Manager) DeleteLocalAlias(alias,
baseScid lnwire.ShortChannelID) error {
// We need to lock the manager for the whole duration of this method,
// except for the very last part where we call the link updater. In
// order for us to safely use a defer _and_ still be able to manually
// unlock, we use a sync.Once.
m.Lock()
unlockOnce := sync.Once{}
unlock := func() {
unlockOnce.Do(m.Unlock)
}
defer unlock()
err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
aliasToBaseBucket, err := tx.CreateTopLevelBucket(aliasBucket)
if err != nil {
return err
}
var aliasBytes [8]byte
byteOrder.PutUint64(aliasBytes[:], alias.ToUint64())
// If the user attempts to delete an alias that doesn't exist,
// we'll want to inform them about it and not just do nothing.
if aliasToBaseBucket.Get(aliasBytes[:]) == nil {
return ErrAliasNotFound
}
return aliasToBaseBucket.Delete(aliasBytes[:])
}, func() {})
if err != nil {
return err
}
// Now that the database state has been updated, we'll delete the
// mapping from the Manager's maps.
aliasSet, ok := m.baseToSet[baseScid]
if !ok {
return ErrAliasNotFound
}
// We'll filter the alias set and remove the alias from it.
aliasSet = fn.Filter(aliasSet, func(a lnwire.ShortChannelID) bool {
return a.ToUint64() != alias.ToUint64()
})
// If the alias set is empty, we'll delete the base SCID from the
// baseToSet map.
if len(aliasSet) == 0 {
delete(m.baseToSet, baseScid)
} else {
m.baseToSet[baseScid] = aliasSet
}
// Finally, we'll delete the aliasToBase mapping from the Manager's
// cache (but this is only set if we gossip the alias).
delete(m.aliasToBase, alias)
// We definitely need to unlock the Manager before calling the link
// updater. If we don't, we'll deadlock. We use a sync.Once to ensure
// that we only unlock once.
unlock()
return m.linkAliasUpdater(baseScid)
}
// PutPeerAlias stores the peer's alias SCID once we learn of it in the
// channel_ready message.
func (m *Manager) PutPeerAlias(chanID lnwire.ChannelID,
@ -392,6 +509,25 @@ func (m *Manager) GetPeerAlias(chanID lnwire.ChannelID) (lnwire.ShortChannelID,
func (m *Manager) RequestAlias() (lnwire.ShortChannelID, error) {
var nextAlias lnwire.ShortChannelID
m.RLock()
defer m.RUnlock()
// haveAlias returns true if the passed alias is already assigned to a
// channel in the baseToSet map.
haveAlias := func(maybeNextAlias lnwire.ShortChannelID) bool {
return fn.Any(
slices.Collect(maps.Values(m.baseToSet)),
func(aliasList []lnwire.ShortChannelID) bool {
return fn.Any(
aliasList,
func(alias lnwire.ShortChannelID) bool {
return alias == maybeNextAlias
},
)
},
)
}
err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
bucket, err := tx.CreateTopLevelBucket(aliasAllocBucket)
if err != nil {
@ -404,6 +540,29 @@ func (m *Manager) RequestAlias() (lnwire.ShortChannelID, error) {
// StartingAlias to it.
nextAlias = StartingAlias
// If the very first alias is already assigned, we'll
// keep incrementing until we find an unassigned alias.
// This is to avoid collision with custom added SCID
// aliases that fall into the same range as the ones we
// generate here monotonically. Those custom SCIDs are
// stored in a different bucket, but we can just check
// the in-memory map for simplicity.
for {
if !haveAlias(nextAlias) {
break
}
nextAlias = getNextScid(nextAlias)
// Abort if we've reached the end of the range.
if nextAlias.BlockHeight >=
AliasEndBlockHeight {
return fmt.Errorf("range for custom " +
"aliases exhausted")
}
}
var scratch [8]byte
byteOrder.PutUint64(scratch[:], nextAlias.ToUint64())
return bucket.Put(lastAliasKey, scratch[:])
@ -418,6 +577,26 @@ func (m *Manager) RequestAlias() (lnwire.ShortChannelID, error) {
)
nextAlias = getNextScid(lastScid)
// If the next alias is already assigned, we'll keep
// incrementing until we find an unassigned alias. This is to
// avoid collision with custom added SCID aliases that fall into
// the same range as the ones we generate here monotonically.
// Those custom SCIDs are stored in a different bucket, but we
// can just check the in-memory map for simplicity.
for {
if !haveAlias(nextAlias) {
break
}
nextAlias = getNextScid(nextAlias)
// Abort if we've reached the end of the range.
if nextAlias.BlockHeight >= AliasEndBlockHeight {
return fmt.Errorf("range for custom " +
"aliases exhausted")
}
}
var scratch [8]byte
byteOrder.PutUint64(scratch[:], nextAlias.ToUint64())
return bucket.Put(lastAliasKey, scratch[:])
@ -433,11 +612,11 @@ func (m *Manager) RequestAlias() (lnwire.ShortChannelID, error) {
// ListAliases returns a carbon copy of baseToSet. This is used by the rpc
// layer.
func (m *Manager) ListAliases() map[lnwire.ShortChannelID][]lnwire.ShortChannelID {
func (m *Manager) ListAliases() ScidAliasMap {
m.RLock()
defer m.RUnlock()
baseCopy := make(map[lnwire.ShortChannelID][]lnwire.ShortChannelID)
baseCopy := make(ScidAliasMap)
for k, v := range m.baseToSet {
setCopy := make([]lnwire.ShortChannelID, len(v))
@ -496,10 +675,10 @@ func getNextScid(last lnwire.ShortChannelID) lnwire.ShortChannelID {
// IsAlias returns true if the passed SCID is an alias. The function determines
// this by looking at the BlockHeight. If the BlockHeight is greater than
// startingBlockHeight and less than endBlockHeight, then it is an alias
// AliasStartBlockHeight and less than AliasEndBlockHeight, then it is an alias
// assigned by RequestAlias. These bounds only apply to aliases we generate.
// Our peers are free to use any range they choose.
func IsAlias(scid lnwire.ShortChannelID) bool {
return scid.BlockHeight >= uint32(startingBlockHeight) &&
scid.BlockHeight < uint32(endBlockHeight)
return scid.BlockHeight >= AliasStartBlockHeight &&
scid.BlockHeight < AliasEndBlockHeight
}

View File

@ -19,11 +19,16 @@ func TestAliasStorePeerAlias(t *testing.T) {
dbPath := filepath.Join(t.TempDir(), "testdb")
db, err := kvdb.Create(
kvdb.BoltBackendName, dbPath, true, kvdb.DefaultDBTimeout,
false,
)
require.NoError(t, err)
defer db.Close()
aliasStore, err := NewManager(db)
linkUpdater := func(shortID lnwire.ShortChannelID) error {
return nil
}
aliasStore, err := NewManager(db, linkUpdater)
require.NoError(t, err)
var chanID1 [32]byte
@ -48,11 +53,16 @@ func TestAliasStoreRequest(t *testing.T) {
dbPath := filepath.Join(t.TempDir(), "testdb")
db, err := kvdb.Create(
kvdb.BoltBackendName, dbPath, true, kvdb.DefaultDBTimeout,
false,
)
require.NoError(t, err)
defer db.Close()
aliasStore, err := NewManager(db)
linkUpdater := func(shortID lnwire.ShortChannelID) error {
return nil
}
aliasStore, err := NewManager(db, linkUpdater)
require.NoError(t, err)
// We'll assert that the very first alias we receive is StartingAlias.
@ -68,6 +78,119 @@ func TestAliasStoreRequest(t *testing.T) {
require.Equal(t, nextAlias, alias2)
}
// TestAliasLifecycle tests that the aliases can be created and deleted.
func TestAliasLifecycle(t *testing.T) {
t.Parallel()
// Create the backend database and use this to create the aliasStore.
dbPath := filepath.Join(t.TempDir(), "testdb")
db, err := kvdb.Create(
kvdb.BoltBackendName, dbPath, true, kvdb.DefaultDBTimeout,
false,
)
require.NoError(t, err)
defer db.Close()
updateChan := make(chan struct{}, 1)
linkUpdater := func(shortID lnwire.ShortChannelID) error {
updateChan <- struct{}{}
return nil
}
aliasStore, err := NewManager(db, linkUpdater)
require.NoError(t, err)
const (
base = uint64(123123123)
alias = uint64(456456456)
)
// Parse the aliases and base to short channel ID format.
baseScid := lnwire.NewShortChanIDFromInt(base)
aliasScid := lnwire.NewShortChanIDFromInt(alias)
aliasScid2 := lnwire.NewShortChanIDFromInt(alias + 1)
// Add the first alias.
err = aliasStore.AddLocalAlias(aliasScid, baseScid, false, true)
require.NoError(t, err)
// The link updater should be called.
<-updateChan
// Query the aliases and verify the results.
aliasList := aliasStore.GetAliases(baseScid)
require.Len(t, aliasList, 1)
require.Contains(t, aliasList, aliasScid)
// Add the second alias.
err = aliasStore.AddLocalAlias(aliasScid2, baseScid, false, true)
require.NoError(t, err)
// The link updater should be called.
<-updateChan
// Query the aliases and verify the results.
aliasList = aliasStore.GetAliases(baseScid)
require.Len(t, aliasList, 2)
require.Contains(t, aliasList, aliasScid)
require.Contains(t, aliasList, aliasScid2)
// Delete the first alias.
err = aliasStore.DeleteLocalAlias(aliasScid, baseScid)
require.NoError(t, err)
// The link updater should be called.
<-updateChan
// We expect to get an error if we attempt to delete the same alias
// again.
err = aliasStore.DeleteLocalAlias(aliasScid, baseScid)
require.ErrorIs(t, err, ErrAliasNotFound)
// The link updater should _not_ be called.
select {
case <-updateChan:
t.Fatal("link alias updater should not have been called")
default:
}
// Query the aliases and verify that first one doesn't exist anymore.
aliasList = aliasStore.GetAliases(baseScid)
require.Len(t, aliasList, 1)
require.Contains(t, aliasList, aliasScid2)
require.NotContains(t, aliasList, aliasScid)
// Delete the second alias.
err = aliasStore.DeleteLocalAlias(aliasScid2, baseScid)
require.NoError(t, err)
// The link updater should be called.
<-updateChan
// Query the aliases and verify that none exists.
aliasList = aliasStore.GetAliases(baseScid)
require.Len(t, aliasList, 0)
// We now request an alias generated by the aliasStore. This should give
// the first from the pre-defined list of allocated aliases.
firstRequested, err := aliasStore.RequestAlias()
require.NoError(t, err)
require.Equal(t, StartingAlias, firstRequested)
// We now manually add the next alias from the range as a custom alias.
secondAlias := getNextScid(firstRequested)
err = aliasStore.AddLocalAlias(secondAlias, baseScid, false, true)
require.NoError(t, err)
// When we now request another alias from the allocation list, we expect
// the third one (tx position 2) to be returned.
thirdRequested, err := aliasStore.RequestAlias()
require.NoError(t, err)
require.Equal(t, getNextScid(secondAlias), thirdRequested)
require.EqualValues(t, 2, thirdRequested.TxPosition)
}
// TestGetNextScid tests that given a current lnwire.ShortChannelID,
// getNextScid returns the expected alias to use next.
func TestGetNextScid(t *testing.T) {
@ -80,7 +203,7 @@ func TestGetNextScid(t *testing.T) {
name: "starting alias",
current: StartingAlias,
expected: lnwire.ShortChannelID{
BlockHeight: uint32(startingBlockHeight),
BlockHeight: AliasStartBlockHeight,
TxIndex: 0,
TxPosition: 1,
},

View File

@ -2,6 +2,7 @@ package autopilot
import (
"bytes"
"context"
"fmt"
"math/rand"
"net"
@ -11,6 +12,7 @@ import (
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/fn/v2"
"github.com/lightningnetwork/lnd/lnwire"
)
@ -166,8 +168,9 @@ type Agent struct {
pendingOpens map[NodeID]LocalChannel
pendingMtx sync.Mutex
quit chan struct{}
wg sync.WaitGroup
quit chan struct{}
wg sync.WaitGroup
cancel fn.Option[context.CancelFunc]
}
// New creates a new instance of the Agent instantiated using the passed
@ -202,17 +205,20 @@ func New(cfg Config, initialState []LocalChannel) (*Agent, error) {
func (a *Agent) Start() error {
var err error
a.started.Do(func() {
err = a.start()
ctx, cancel := context.WithCancel(context.Background())
a.cancel = fn.Some(cancel)
err = a.start(ctx)
})
return err
}
func (a *Agent) start() error {
func (a *Agent) start(ctx context.Context) error {
rand.Seed(time.Now().Unix())
log.Infof("Autopilot Agent starting")
a.wg.Add(1)
go a.controller()
go a.controller(ctx)
return nil
}
@ -230,6 +236,7 @@ func (a *Agent) Stop() error {
func (a *Agent) stop() error {
log.Infof("Autopilot Agent stopping")
a.cancel.WhenSome(func(fn context.CancelFunc) { fn() })
close(a.quit)
a.wg.Wait()
@ -401,7 +408,7 @@ func mergeChanState(pendingChans map[NodeID]LocalChannel,
// and external state changes as a result of decisions it makes w.r.t channel
// allocation, or attributes affecting its control loop being updated by the
// backing Lightning Node.
func (a *Agent) controller() {
func (a *Agent) controller(ctx context.Context) {
defer a.wg.Done()
// We'll start off by assigning our starting balance, and injecting
@ -502,6 +509,9 @@ func (a *Agent) controller() {
// immediately.
case <-a.quit:
return
case <-ctx.Done():
return
}
a.pendingMtx.Lock()
@ -539,7 +549,7 @@ func (a *Agent) controller() {
log.Infof("Triggering attachment directive dispatch, "+
"total_funds=%v", a.totalBalance)
err := a.openChans(availableFunds, numChans, totalChans)
err := a.openChans(ctx, availableFunds, numChans, totalChans)
if err != nil {
log.Errorf("Unable to open channels: %v", err)
}
@ -548,8 +558,8 @@ func (a *Agent) controller() {
// openChans queries the agent's heuristic for a set of channel candidates, and
// attempts to open channels to them.
func (a *Agent) openChans(availableFunds btcutil.Amount, numChans uint32,
totalChans []LocalChannel) error {
func (a *Agent) openChans(ctx context.Context, availableFunds btcutil.Amount,
numChans uint32, totalChans []LocalChannel) error {
// As channel size we'll use the maximum channel size available.
chanSize := a.cfg.Constraints.MaxChanSize()
@ -598,7 +608,9 @@ func (a *Agent) openChans(availableFunds btcutil.Amount, numChans uint32,
selfPubBytes := a.cfg.Self.SerializeCompressed()
nodes := make(map[NodeID]struct{})
addresses := make(map[NodeID][]net.Addr)
if err := a.cfg.Graph.ForEachNode(func(node Node) error {
if err := a.cfg.Graph.ForEachNode(ctx, func(_ context.Context,
node Node) error {
nID := NodeID(node.PubKey())
// If we come across ourselves, them we'll continue in
@ -636,7 +648,7 @@ func (a *Agent) openChans(availableFunds btcutil.Amount, numChans uint32,
// graph.
log.Debugf("Scoring %d nodes for chan_size=%v", len(nodes), chanSize)
scores, err := a.cfg.Heuristic.NodeScores(
a.cfg.Graph, totalChans, chanSize, nodes,
ctx, a.cfg.Graph, totalChans, chanSize, nodes,
)
if err != nil {
return fmt.Errorf("unable to calculate node scores : %w", err)

View File

@ -1,6 +1,7 @@
package autopilot
import (
"context"
"errors"
"fmt"
"net"
@ -85,9 +86,9 @@ func (m *mockHeuristic) Name() string {
return "mock"
}
func (m *mockHeuristic) NodeScores(g ChannelGraph, chans []LocalChannel,
chanSize btcutil.Amount, nodes map[NodeID]struct{}) (
map[NodeID]*NodeScore, error) {
func (m *mockHeuristic) NodeScores(_ context.Context, g ChannelGraph,
chans []LocalChannel, chanSize btcutil.Amount,
nodes map[NodeID]struct{}) (map[NodeID]*NodeScore, error) {
if m.nodeScoresArgs != nil {
directive := directiveArg{

View File

@ -1,6 +1,7 @@
package autopilot
import (
"context"
"fmt"
"sync"
)
@ -168,8 +169,10 @@ func betweennessCentrality(g *SimpleGraph, s int, centrality []float64) {
}
// Refresh recalculates and stores centrality values.
func (bc *BetweennessCentrality) Refresh(graph ChannelGraph) error {
cache, err := NewSimpleGraph(graph)
func (bc *BetweennessCentrality) Refresh(ctx context.Context,
graph ChannelGraph) error {
cache, err := NewSimpleGraph(ctx, graph)
if err != nil {
return err
}

View File

@ -1,6 +1,7 @@
package autopilot
import (
"context"
"fmt"
"testing"
@ -30,6 +31,9 @@ func TestBetweennessCentralityMetricConstruction(t *testing.T) {
// Tests that empty graph results in empty centrality result.
func TestBetweennessCentralityEmptyGraph(t *testing.T) {
t.Parallel()
ctx := context.Background()
centralityMetric, err := NewBetweennessCentralityMetric(1)
require.NoError(
t, err,
@ -42,7 +46,7 @@ func TestBetweennessCentralityEmptyGraph(t *testing.T) {
require.NoError(t, err, "unable to create graph")
success := t.Run(chanGraph.name, func(t1 *testing.T) {
err = centralityMetric.Refresh(graph)
err = centralityMetric.Refresh(ctx, graph)
require.NoError(t1, err)
centrality := centralityMetric.GetMetric(false)
@ -59,6 +63,9 @@ func TestBetweennessCentralityEmptyGraph(t *testing.T) {
// Test betweenness centrality calculating using an example graph.
func TestBetweennessCentralityWithNonEmptyGraph(t *testing.T) {
t.Parallel()
ctx := context.Background()
workers := []int{1, 3, 9, 100}
tests := []struct {
@ -100,7 +107,7 @@ func TestBetweennessCentralityWithNonEmptyGraph(t *testing.T) {
t1, graph, centralityTestGraph,
)
err = metric.Refresh(graph)
err = metric.Refresh(ctx, graph)
require.NoError(t1, err)
for _, expected := range tests {

View File

@ -1,6 +1,7 @@
package autopilot
import (
"context"
"fmt"
"github.com/btcsuite/btcd/btcutil"
@ -70,9 +71,9 @@ func (c *WeightedCombAttachment) Name() string {
// is the maximum possible improvement in connectivity.
//
// NOTE: This is a part of the AttachmentHeuristic interface.
func (c *WeightedCombAttachment) NodeScores(g ChannelGraph, chans []LocalChannel,
chanSize btcutil.Amount, nodes map[NodeID]struct{}) (
map[NodeID]*NodeScore, error) {
func (c *WeightedCombAttachment) NodeScores(ctx context.Context, g ChannelGraph,
chans []LocalChannel, chanSize btcutil.Amount,
nodes map[NodeID]struct{}) (map[NodeID]*NodeScore, error) {
// We now query each heuristic to determine the score they give to the
// nodes for the given channel size.
@ -81,7 +82,7 @@ func (c *WeightedCombAttachment) NodeScores(g ChannelGraph, chans []LocalChannel
log.Tracef("Getting scores from sub heuristic %v", h.Name())
s, err := h.NodeScores(
g, chans, chanSize, nodes,
ctx, g, chans, chanSize, nodes,
)
if err != nil {
return nil, fmt.Errorf("unable to get sub score: %w",

View File

@ -1,6 +1,7 @@
package autopilot
import (
"context"
"fmt"
"sync"
@ -80,9 +81,9 @@ func (s *ExternalScoreAttachment) SetNodeScores(targetHeuristic string,
// not known will get a score of 0.
//
// NOTE: This is a part of the AttachmentHeuristic interface.
func (s *ExternalScoreAttachment) NodeScores(g ChannelGraph, chans []LocalChannel,
chanSize btcutil.Amount, nodes map[NodeID]struct{}) (
map[NodeID]*NodeScore, error) {
func (s *ExternalScoreAttachment) NodeScores(_ context.Context, g ChannelGraph,
chans []LocalChannel, chanSize btcutil.Amount,
nodes map[NodeID]struct{}) (map[NodeID]*NodeScore, error) {
existingPeers := make(map[NodeID]struct{})
for _, c := range chans {

View File

@ -1,6 +1,7 @@
package autopilot_test
import (
"context"
"testing"
"github.com/btcsuite/btcd/btcec/v2"
@ -22,6 +23,7 @@ func randKey() (*btcec.PublicKey, error) {
// ExternalScoreAttachment correctly reflects the scores we set last.
func TestSetNodeScores(t *testing.T) {
t.Parallel()
ctx := context.Background()
const name = "externalscore"
@ -62,7 +64,7 @@ func TestSetNodeScores(t *testing.T) {
q[nID] = struct{}{}
}
resp, err := h.NodeScores(
nil, nil, btcutil.Amount(btcutil.SatoshiPerBitcoin), q,
ctx, nil, nil, btcutil.Amount(btcutil.SatoshiPerBitcoin), q,
)
if err != nil {
t.Fatal(err)

View File

@ -1,19 +1,16 @@
package autopilot
import (
"bytes"
"context"
"encoding/hex"
"net"
"sort"
"sync/atomic"
"time"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/btcsuite/btcd/btcutil"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/kvdb"
graphdb "github.com/lightningnetwork/lnd/graph/db"
"github.com/lightningnetwork/lnd/graph/db/models"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
)
@ -35,7 +32,7 @@ var (
//
// TODO(roasbeef): move inmpl to main package?
type databaseChannelGraph struct {
db *channeldb.ChannelGraph
db GraphSource
}
// A compile time assertion to ensure databaseChannelGraph meets the
@ -43,8 +40,8 @@ type databaseChannelGraph struct {
var _ ChannelGraph = (*databaseChannelGraph)(nil)
// ChannelGraphFromDatabase returns an instance of the autopilot.ChannelGraph
// backed by a live, open channeldb instance.
func ChannelGraphFromDatabase(db *channeldb.ChannelGraph) ChannelGraph {
// backed by a GraphSource.
func ChannelGraphFromDatabase(db GraphSource) ChannelGraph {
return &databaseChannelGraph{
db: db,
}
@ -54,11 +51,7 @@ func ChannelGraphFromDatabase(db *channeldb.ChannelGraph) ChannelGraph {
// channeldb.LightningNode. The wrapper method implement the autopilot.Node
// interface.
type dbNode struct {
db *channeldb.ChannelGraph
tx kvdb.RTx
node *channeldb.LightningNode
tx graphdb.NodeRTx
}
// A compile time assertion to ensure dbNode meets the autopilot.Node
@ -71,7 +64,7 @@ var _ Node = (*dbNode)(nil)
//
// NOTE: Part of the autopilot.Node interface.
func (d *dbNode) PubKey() [33]byte {
return d.node.PubKeyBytes
return d.tx.Node().PubKeyBytes
}
// Addrs returns a slice of publicly reachable public TCP addresses that the
@ -79,7 +72,7 @@ func (d *dbNode) PubKey() [33]byte {
//
// NOTE: Part of the autopilot.Node interface.
func (d *dbNode) Addrs() []net.Addr {
return d.node.Addresses
return d.tx.Node().Addresses
}
// ForEachChannel is a higher-order function that will be used to iterate
@ -88,44 +81,38 @@ func (d *dbNode) Addrs() []net.Addr {
// describes the active channel.
//
// NOTE: Part of the autopilot.Node interface.
func (d *dbNode) ForEachChannel(cb func(ChannelEdge) error) error {
return d.db.ForEachNodeChannelTx(d.tx, d.node.PubKeyBytes,
func(tx kvdb.RTx, ei *models.ChannelEdgeInfo, ep,
_ *models.ChannelEdgePolicy) error {
func (d *dbNode) ForEachChannel(ctx context.Context,
cb func(context.Context, ChannelEdge) error) error {
// Skip channels for which no outgoing edge policy is
// available.
//
// TODO(joostjager): Ideally the case where channels
// have a nil policy should be supported, as autopilot
// is not looking at the policies. For now, it is not
// easily possible to get a reference to the other end
// LightningNode object without retrieving the policy.
if ep == nil {
return nil
}
return d.tx.ForEachChannel(func(ei *models.ChannelEdgeInfo, ep,
_ *models.ChannelEdgePolicy) error {
node, err := d.db.FetchLightningNodeTx(
tx, ep.ToNode,
)
if err != nil {
return err
}
// Skip channels for which no outgoing edge policy is available.
//
// TODO(joostjager): Ideally the case where channels have a nil
// policy should be supported, as autopilot is not looking at
// the policies. For now, it is not easily possible to get a
// reference to the other end LightningNode object without
// retrieving the policy.
if ep == nil {
return nil
}
edge := ChannelEdge{
ChanID: lnwire.NewShortChanIDFromInt(
ep.ChannelID,
),
Capacity: ei.Capacity,
Peer: &dbNode{
tx: tx,
db: d.db,
node: node,
},
}
node, err := d.tx.FetchNode(ep.ToNode)
if err != nil {
return err
}
return cb(edge)
})
edge := ChannelEdge{
ChanID: lnwire.NewShortChanIDFromInt(ep.ChannelID),
Capacity: ei.Capacity,
Peer: &dbNode{
tx: node,
},
}
return cb(ctx, edge)
})
}
// ForEachNode is a higher-order function that should be called once for each
@ -133,352 +120,29 @@ func (d *dbNode) ForEachChannel(cb func(ChannelEdge) error) error {
// error, then execution should be terminated.
//
// NOTE: Part of the autopilot.ChannelGraph interface.
func (d *databaseChannelGraph) ForEachNode(cb func(Node) error) error {
return d.db.ForEachNode(func(tx kvdb.RTx, n *channeldb.LightningNode) error {
func (d *databaseChannelGraph) ForEachNode(ctx context.Context,
cb func(context.Context, Node) error) error {
return d.db.ForEachNode(func(nodeTx graphdb.NodeRTx) error {
// We'll skip over any node that doesn't have any advertised
// addresses. As we won't be able to reach them to actually
// open any channels.
if len(n.Addresses) == 0 {
if len(nodeTx.Node().Addresses) == 0 {
return nil
}
node := &dbNode{
db: d.db,
tx: tx,
node: n,
tx: nodeTx,
}
return cb(node)
return cb(ctx, node)
})
}
// addRandChannel creates a new channel two target nodes. This function is
// meant to aide in the generation of random graphs for use within test cases
// the exercise the autopilot package.
func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey,
capacity btcutil.Amount) (*ChannelEdge, *ChannelEdge, error) {
fetchNode := func(pub *btcec.PublicKey) (*channeldb.LightningNode, error) {
if pub != nil {
vertex, err := route.NewVertexFromBytes(
pub.SerializeCompressed(),
)
if err != nil {
return nil, err
}
dbNode, err := d.db.FetchLightningNode(vertex)
switch {
case err == channeldb.ErrGraphNodeNotFound:
fallthrough
case err == channeldb.ErrGraphNotFound:
graphNode := &channeldb.LightningNode{
HaveNodeAnnouncement: true,
Addresses: []net.Addr{
&net.TCPAddr{
IP: bytes.Repeat([]byte("a"), 16),
},
},
Features: lnwire.NewFeatureVector(
nil, lnwire.Features,
),
AuthSigBytes: testSig.Serialize(),
}
graphNode.AddPubKey(pub)
if err := d.db.AddLightningNode(graphNode); err != nil {
return nil, err
}
case err != nil:
return nil, err
}
return dbNode, nil
}
nodeKey, err := randKey()
if err != nil {
return nil, err
}
dbNode := &channeldb.LightningNode{
HaveNodeAnnouncement: true,
Addresses: []net.Addr{
&net.TCPAddr{
IP: bytes.Repeat([]byte("a"), 16),
},
},
Features: lnwire.NewFeatureVector(
nil, lnwire.Features,
),
AuthSigBytes: testSig.Serialize(),
}
dbNode.AddPubKey(nodeKey)
if err := d.db.AddLightningNode(dbNode); err != nil {
return nil, err
}
return dbNode, nil
}
vertex1, err := fetchNode(node1)
if err != nil {
return nil, nil, err
}
vertex2, err := fetchNode(node2)
if err != nil {
return nil, nil, err
}
var lnNode1, lnNode2 *btcec.PublicKey
if bytes.Compare(vertex1.PubKeyBytes[:], vertex2.PubKeyBytes[:]) == -1 {
lnNode1, _ = vertex1.PubKey()
lnNode2, _ = vertex2.PubKey()
} else {
lnNode1, _ = vertex2.PubKey()
lnNode2, _ = vertex1.PubKey()
}
chanID := randChanID()
edge := &models.ChannelEdgeInfo{
ChannelID: chanID.ToUint64(),
Capacity: capacity,
}
edge.AddNodeKeys(lnNode1, lnNode2, lnNode1, lnNode2)
if err := d.db.AddChannelEdge(edge); err != nil {
return nil, nil, err
}
edgePolicy := &models.ChannelEdgePolicy{
SigBytes: testSig.Serialize(),
ChannelID: chanID.ToUint64(),
LastUpdate: time.Now(),
TimeLockDelta: 10,
MinHTLC: 1,
MaxHTLC: lnwire.NewMSatFromSatoshis(capacity),
FeeBaseMSat: 10,
FeeProportionalMillionths: 10000,
MessageFlags: 1,
ChannelFlags: 0,
}
if err := d.db.UpdateEdgePolicy(edgePolicy); err != nil {
return nil, nil, err
}
edgePolicy = &models.ChannelEdgePolicy{
SigBytes: testSig.Serialize(),
ChannelID: chanID.ToUint64(),
LastUpdate: time.Now(),
TimeLockDelta: 10,
MinHTLC: 1,
MaxHTLC: lnwire.NewMSatFromSatoshis(capacity),
FeeBaseMSat: 10,
FeeProportionalMillionths: 10000,
MessageFlags: 1,
ChannelFlags: 1,
}
if err := d.db.UpdateEdgePolicy(edgePolicy); err != nil {
return nil, nil, err
}
return &ChannelEdge{
ChanID: chanID,
Capacity: capacity,
Peer: &dbNode{
db: d.db,
node: vertex1,
},
},
&ChannelEdge{
ChanID: chanID,
Capacity: capacity,
Peer: &dbNode{
db: d.db,
node: vertex2,
},
},
nil
}
func (d *databaseChannelGraph) addRandNode() (*btcec.PublicKey, error) {
nodeKey, err := randKey()
if err != nil {
return nil, err
}
dbNode := &channeldb.LightningNode{
HaveNodeAnnouncement: true,
Addresses: []net.Addr{
&net.TCPAddr{
IP: bytes.Repeat([]byte("a"), 16),
},
},
Features: lnwire.NewFeatureVector(
nil, lnwire.Features,
),
AuthSigBytes: testSig.Serialize(),
}
dbNode.AddPubKey(nodeKey)
if err := d.db.AddLightningNode(dbNode); err != nil {
return nil, err
}
return nodeKey, nil
}
// memChannelGraph is an implementation of the autopilot.ChannelGraph backed by
// an in-memory graph.
type memChannelGraph struct {
graph map[NodeID]*memNode
}
// A compile time assertion to ensure memChannelGraph meets the
// autopilot.ChannelGraph interface.
var _ ChannelGraph = (*memChannelGraph)(nil)
// newMemChannelGraph creates a new blank in-memory channel graph
// implementation.
func newMemChannelGraph() *memChannelGraph {
return &memChannelGraph{
graph: make(map[NodeID]*memNode),
}
}
// ForEachNode is a higher-order function that should be called once for each
// connected node within the channel graph. If the passed callback returns an
// error, then execution should be terminated.
//
// NOTE: Part of the autopilot.ChannelGraph interface.
func (m memChannelGraph) ForEachNode(cb func(Node) error) error {
for _, node := range m.graph {
if err := cb(node); err != nil {
return err
}
}
return nil
}
// randChanID generates a new random channel ID.
func randChanID() lnwire.ShortChannelID {
id := atomic.AddUint64(&chanIDCounter, 1)
return lnwire.NewShortChanIDFromInt(id)
}
// randKey returns a random public key.
func randKey() (*btcec.PublicKey, error) {
priv, err := btcec.NewPrivateKey()
if err != nil {
return nil, err
}
return priv.PubKey(), nil
}
// addRandChannel creates a new channel two target nodes. This function is
// meant to aide in the generation of random graphs for use within test cases
// the exercise the autopilot package.
func (m *memChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey,
capacity btcutil.Amount) (*ChannelEdge, *ChannelEdge, error) {
var (
vertex1, vertex2 *memNode
ok bool
)
if node1 != nil {
vertex1, ok = m.graph[NewNodeID(node1)]
if !ok {
vertex1 = &memNode{
pub: node1,
addrs: []net.Addr{
&net.TCPAddr{
IP: bytes.Repeat([]byte("a"), 16),
},
},
}
}
} else {
newPub, err := randKey()
if err != nil {
return nil, nil, err
}
vertex1 = &memNode{
pub: newPub,
addrs: []net.Addr{
&net.TCPAddr{
IP: bytes.Repeat([]byte("a"), 16),
},
},
}
}
if node2 != nil {
vertex2, ok = m.graph[NewNodeID(node2)]
if !ok {
vertex2 = &memNode{
pub: node2,
addrs: []net.Addr{
&net.TCPAddr{
IP: bytes.Repeat([]byte("a"), 16),
},
},
}
}
} else {
newPub, err := randKey()
if err != nil {
return nil, nil, err
}
vertex2 = &memNode{
pub: newPub,
addrs: []net.Addr{
&net.TCPAddr{
IP: bytes.Repeat([]byte("a"), 16),
},
},
}
}
edge1 := ChannelEdge{
ChanID: randChanID(),
Capacity: capacity,
Peer: vertex2,
}
vertex1.chans = append(vertex1.chans, edge1)
edge2 := ChannelEdge{
ChanID: randChanID(),
Capacity: capacity,
Peer: vertex1,
}
vertex2.chans = append(vertex2.chans, edge2)
m.graph[NewNodeID(vertex1.pub)] = vertex1
m.graph[NewNodeID(vertex2.pub)] = vertex2
return &edge1, &edge2, nil
}
func (m *memChannelGraph) addRandNode() (*btcec.PublicKey, error) {
newPub, err := randKey()
if err != nil {
return nil, err
}
vertex := &memNode{
pub: newPub,
addrs: []net.Addr{
&net.TCPAddr{
IP: bytes.Repeat([]byte("a"), 16),
},
},
}
m.graph[NewNodeID(newPub)] = vertex
return newPub, nil
}
// databaseChannelGraphCached wraps a channeldb.ChannelGraph instance with the
// necessary API to properly implement the autopilot.ChannelGraph interface.
type databaseChannelGraphCached struct {
db *channeldb.ChannelGraph
db GraphSource
}
// A compile time assertion to ensure databaseChannelGraphCached meets the
@ -487,7 +151,7 @@ var _ ChannelGraph = (*databaseChannelGraphCached)(nil)
// ChannelGraphFromCachedDatabase returns an instance of the
// autopilot.ChannelGraph backed by a live, open channeldb instance.
func ChannelGraphFromCachedDatabase(db *channeldb.ChannelGraph) ChannelGraph {
func ChannelGraphFromCachedDatabase(db GraphSource) ChannelGraph {
return &databaseChannelGraphCached{
db: db,
}
@ -498,7 +162,7 @@ func ChannelGraphFromCachedDatabase(db *channeldb.ChannelGraph) ChannelGraph {
// interface.
type dbNodeCached struct {
node route.Vertex
channels map[uint64]*channeldb.DirectedChannel
channels map[uint64]*graphdb.DirectedChannel
}
// A compile time assertion to ensure dbNodeCached meets the autopilot.Node
@ -527,7 +191,9 @@ func (nc dbNodeCached) Addrs() []net.Addr {
// describes the active channel.
//
// NOTE: Part of the autopilot.Node interface.
func (nc dbNodeCached) ForEachChannel(cb func(ChannelEdge) error) error {
func (nc dbNodeCached) ForEachChannel(ctx context.Context,
cb func(context.Context, ChannelEdge) error) error {
for cid, channel := range nc.channels {
edge := ChannelEdge{
ChanID: lnwire.NewShortChanIDFromInt(cid),
@ -537,7 +203,7 @@ func (nc dbNodeCached) ForEachChannel(cb func(ChannelEdge) error) error {
},
}
if err := cb(edge); err != nil {
if err := cb(ctx, edge); err != nil {
return err
}
}
@ -550,16 +216,19 @@ func (nc dbNodeCached) ForEachChannel(cb func(ChannelEdge) error) error {
// error, then execution should be terminated.
//
// NOTE: Part of the autopilot.ChannelGraph interface.
func (dc *databaseChannelGraphCached) ForEachNode(cb func(Node) error) error {
func (dc *databaseChannelGraphCached) ForEachNode(ctx context.Context,
cb func(context.Context, Node) error) error {
return dc.db.ForEachNodeCached(func(n route.Vertex,
channels map[uint64]*channeldb.DirectedChannel) error {
channels map[uint64]*graphdb.DirectedChannel) error {
if len(channels) > 0 {
node := dbNodeCached{
node: n,
channels: channels,
}
return cb(node)
return cb(ctx, node)
}
return nil
})
@ -604,9 +273,11 @@ func (m memNode) Addrs() []net.Addr {
// describes the active channel.
//
// NOTE: Part of the autopilot.Node interface.
func (m memNode) ForEachChannel(cb func(ChannelEdge) error) error {
func (m memNode) ForEachChannel(ctx context.Context,
cb func(context.Context, ChannelEdge) error) error {
for _, channel := range m.chans {
if err := cb(channel); err != nil {
if err := cb(ctx, channel); err != nil {
return err
}
}

View File

@ -1,12 +1,15 @@
package autopilot
import (
"context"
"net"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/wire"
graphdb "github.com/lightningnetwork/lnd/graph/db"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
)
// DefaultConfTarget is the default confirmation target for autopilot channels.
@ -33,7 +36,8 @@ type Node interface {
// iterate through all edges emanating from/to the target node. For
// each active channel, this function should be called with the
// populated ChannelEdge that describes the active channel.
ForEachChannel(func(ChannelEdge) error) error
ForEachChannel(context.Context, func(context.Context,
ChannelEdge) error) error
}
// LocalChannel is a simple struct which contains relevant details of a
@ -81,7 +85,7 @@ type ChannelGraph interface {
// ForEachNode is a higher-order function that should be called once
// for each connected node within the channel graph. If the passed
// callback returns an error, then execution should be terminated.
ForEachNode(func(Node) error) error
ForEachNode(context.Context, func(context.Context, Node) error) error
}
// NodeScore is a tuple mapping a NodeID to a score indicating the preference
@ -140,7 +144,7 @@ type AttachmentHeuristic interface {
//
// NOTE: A NodeID not found in the returned map is implicitly given a
// score of 0.
NodeScores(g ChannelGraph, chans []LocalChannel,
NodeScores(ctx context.Context, g ChannelGraph, chans []LocalChannel,
chanSize btcutil.Amount, nodes map[NodeID]struct{}) (
map[NodeID]*NodeScore, error)
}
@ -153,7 +157,7 @@ type NodeMetric interface {
Name() string
// Refresh refreshes the metric values based on the current graph.
Refresh(graph ChannelGraph) error
Refresh(ctx context.Context, graph ChannelGraph) error
// GetMetric returns the latest value of this metric. Values in the
// map are per node and can be in arbitrary domain. If normalize is
@ -216,3 +220,20 @@ type ChannelController interface {
// TODO(roasbeef): add force option?
CloseChannel(chanPoint *wire.OutPoint) error
}
// GraphSource represents read access to the channel graph.
type GraphSource interface {
// ForEachNode iterates through all the stored vertices/nodes in the
// graph, executing the passed callback with each node encountered. If
// the callback returns an error, then the transaction is aborted and
// the iteration stops early. Any operations performed on the NodeTx
// passed to the call-back are executed under the same read transaction.
ForEachNode(func(graphdb.NodeRTx) error) error
// ForEachNodeCached is similar to ForEachNode, but it utilizes the
// channel graph cache if one is available. It is less consistent than
// ForEachNode since any further calls are made across multiple
// transactions.
ForEachNodeCached(cb func(node route.Vertex,
chans map[uint64]*graphdb.DirectedChannel) error) error
}

View File

@ -1,7 +1,7 @@
package autopilot
import (
"github.com/btcsuite/btclog"
"github.com/btcsuite/btclog/v2"
"github.com/lightningnetwork/lnd/build"
)

View File

@ -1,12 +1,13 @@
package autopilot
import (
"context"
"fmt"
"sync"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/graph"
graphdb "github.com/lightningnetwork/lnd/graph/db"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire"
)
@ -36,7 +37,7 @@ type ManagerCfg struct {
// SubscribeTopology is used to get a subscription for topology changes
// on the network.
SubscribeTopology func() (*graph.TopologyClient, error)
SubscribeTopology func() (*graphdb.TopologyClient, error)
}
// Manager is struct that manages an autopilot agent, making it possible to
@ -266,8 +267,8 @@ func (m *Manager) StopAgent() error {
}
// QueryHeuristics queries the available autopilot heuristics for node scores.
func (m *Manager) QueryHeuristics(nodes []NodeID, localState bool) (
HeuristicScores, error) {
func (m *Manager) QueryHeuristics(ctx context.Context, nodes []NodeID,
localState bool) (HeuristicScores, error) {
m.Lock()
defer m.Unlock()
@ -278,7 +279,8 @@ func (m *Manager) QueryHeuristics(nodes []NodeID, localState bool) (
}
log.Debugf("Querying heuristics for %d nodes", len(n))
return m.queryHeuristics(n, localState)
return m.queryHeuristics(ctx, n, localState)
}
// HeuristicScores is an alias for a map that maps heuristic names to a map of
@ -289,8 +291,8 @@ type HeuristicScores map[string]map[NodeID]float64
// the agent's current active heuristic.
//
// NOTE: Must be called with the manager's lock.
func (m *Manager) queryHeuristics(nodes map[NodeID]struct{}, localState bool) (
HeuristicScores, error) {
func (m *Manager) queryHeuristics(ctx context.Context,
nodes map[NodeID]struct{}, localState bool) (HeuristicScores, error) {
// If we want to take the local state into action when querying the
// heuristics, we fetch it. If not we'll just pass an empty slice to
@ -338,7 +340,7 @@ func (m *Manager) queryHeuristics(nodes map[NodeID]struct{}, localState bool) (
}
s, err := h.NodeScores(
m.cfg.PilotCfg.Graph, totalChans, chanSize, nodes,
ctx, m.cfg.PilotCfg.Graph, totalChans, chanSize, nodes,
)
if err != nil {
return nil, fmt.Errorf("unable to get sub score: %w",

View File

@ -1,6 +1,7 @@
package autopilot
import (
"context"
prand "math/rand"
"time"
@ -78,9 +79,9 @@ func (p *PrefAttachment) Name() string {
// given to nodes already having high connectivity in the graph.
//
// NOTE: This is a part of the AttachmentHeuristic interface.
func (p *PrefAttachment) NodeScores(g ChannelGraph, chans []LocalChannel,
chanSize btcutil.Amount, nodes map[NodeID]struct{}) (
map[NodeID]*NodeScore, error) {
func (p *PrefAttachment) NodeScores(ctx context.Context, g ChannelGraph,
chans []LocalChannel, chanSize btcutil.Amount,
nodes map[NodeID]struct{}) (map[NodeID]*NodeScore, error) {
// We first run though the graph once in order to find the median
// channel size.
@ -88,8 +89,10 @@ func (p *PrefAttachment) NodeScores(g ChannelGraph, chans []LocalChannel,
allChans []btcutil.Amount
seenChans = make(map[uint64]struct{})
)
if err := g.ForEachNode(func(n Node) error {
err := n.ForEachChannel(func(e ChannelEdge) error {
if err := g.ForEachNode(ctx, func(ctx context.Context, n Node) error {
err := n.ForEachChannel(ctx, func(_ context.Context,
e ChannelEdge) error {
if _, ok := seenChans[e.ChanID.ToUint64()]; ok {
return nil
}
@ -114,15 +117,19 @@ func (p *PrefAttachment) NodeScores(g ChannelGraph, chans []LocalChannel,
// the graph.
var maxChans int
nodeChanNum := make(map[NodeID]int)
if err := g.ForEachNode(func(n Node) error {
if err := g.ForEachNode(ctx, func(ctx context.Context, n Node) error {
var nodeChans int
err := n.ForEachChannel(func(e ChannelEdge) error {
err := n.ForEachChannel(ctx, func(_ context.Context,
e ChannelEdge) error {
// Since connecting to nodes with a lot of small
// channels actually worsens our connectivity in the
// graph (we will potentially waste time trying to use
// these useless channels in path finding), we decrease
// the counter for such channels.
if e.Capacity < medianChanSize/minMedianChanSizeFraction {
if e.Capacity <
medianChanSize/minMedianChanSizeFraction {
nodeChans--
return nil
}

View File

@ -2,13 +2,21 @@ package autopilot
import (
"bytes"
"context"
"errors"
prand "math/rand"
"net"
"sync/atomic"
"testing"
"time"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/lightningnetwork/lnd/channeldb"
graphdb "github.com/lightningnetwork/lnd/graph/db"
"github.com/lightningnetwork/lnd/graph/db/models"
"github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/stretchr/testify/require"
)
@ -23,22 +31,39 @@ type testGraph interface {
addRandNode() (*btcec.PublicKey, error)
}
type testDBGraph struct {
db *graphdb.ChannelGraph
databaseChannelGraph
}
func newDiskChanGraph(t *testing.T) (testGraph, error) {
// Next, create channeldb for the first time.
cdb, err := channeldb.Open(t.TempDir())
if err != nil {
return nil, err
}
backend, err := kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{
DBPath: t.TempDir(),
DBFileName: "graph.db",
NoFreelistSync: true,
AutoCompact: false,
AutoCompactMinAge: kvdb.DefaultBoltAutoCompactMinAge,
DBTimeout: kvdb.DefaultDBTimeout,
})
require.NoError(t, err)
graphDB, err := graphdb.NewChannelGraph(&graphdb.Config{KVDB: backend})
require.NoError(t, err)
require.NoError(t, graphDB.Start())
t.Cleanup(func() {
require.NoError(t, cdb.Close())
require.NoError(t, graphDB.Stop())
})
return &databaseChannelGraph{
db: cdb.ChannelGraph(),
return &testDBGraph{
db: graphDB,
databaseChannelGraph: databaseChannelGraph{
db: graphDB,
},
}, nil
}
var _ testGraph = (*databaseChannelGraph)(nil)
var _ testGraph = (*testDBGraph)(nil)
func newMemChanGraph(_ *testing.T) (testGraph, error) {
return newMemChannelGraph(), nil
@ -63,6 +88,8 @@ var chanGraphs = []struct {
// TestPrefAttachmentSelectEmptyGraph ensures that when passed an
// empty graph, the NodeSores function always returns a score of 0.
func TestPrefAttachmentSelectEmptyGraph(t *testing.T) {
t.Parallel()
ctx := context.Background()
prefAttach := NewPrefAttachment()
// Create a random public key, which we will query to get a score for.
@ -83,7 +110,7 @@ func TestPrefAttachmentSelectEmptyGraph(t *testing.T) {
// attempt to get the score for this one node.
const walletFunds = btcutil.SatoshiPerBitcoin
scores, err := prefAttach.NodeScores(
graph, nil, walletFunds, nodes,
ctx, graph, nil, walletFunds, nodes,
)
require.NoError(t1, err)
@ -102,6 +129,7 @@ func TestPrefAttachmentSelectEmptyGraph(t *testing.T) {
// and the funds are appropriately allocated across each peer.
func TestPrefAttachmentSelectTwoVertexes(t *testing.T) {
t.Parallel()
ctx := context.Background()
prand.Seed(time.Now().Unix())
@ -132,10 +160,12 @@ func TestPrefAttachmentSelectTwoVertexes(t *testing.T) {
// Get the score for all nodes found in the graph at
// this point.
nodes := make(map[NodeID]struct{})
err = graph.ForEachNode(func(n Node) error {
nodes[n.PubKey()] = struct{}{}
return nil
})
err = graph.ForEachNode(ctx,
func(_ context.Context, n Node) error {
nodes[n.PubKey()] = struct{}{}
return nil
},
)
require.NoError(t1, err)
require.Len(t1, nodes, 3)
@ -144,7 +174,7 @@ func TestPrefAttachmentSelectTwoVertexes(t *testing.T) {
// attempt to get our candidates channel score given
// the current state of the graph.
candidates, err := prefAttach.NodeScores(
graph, nil, maxChanSize, nodes,
ctx, graph, nil, maxChanSize, nodes,
)
require.NoError(t1, err)
@ -183,6 +213,7 @@ func TestPrefAttachmentSelectTwoVertexes(t *testing.T) {
// allocate all funds to each vertex (up to the max channel size).
func TestPrefAttachmentSelectGreedyAllocation(t *testing.T) {
t.Parallel()
ctx := context.Background()
prand.Seed(time.Now().Unix())
@ -221,22 +252,25 @@ func TestPrefAttachmentSelectGreedyAllocation(t *testing.T) {
numNodes := 0
twoChans := false
nodes := make(map[NodeID]struct{})
err = graph.ForEachNode(func(n Node) error {
numNodes++
nodes[n.PubKey()] = struct{}{}
numChans := 0
err := n.ForEachChannel(func(c ChannelEdge) error {
numChans++
err = graph.ForEachNode(
ctx, func(ctx context.Context, n Node) error {
numNodes++
nodes[n.PubKey()] = struct{}{}
numChans := 0
err := n.ForEachChannel(ctx,
func(_ context.Context, c ChannelEdge) error { //nolint:ll
numChans++
return nil
},
)
if err != nil {
return err
}
twoChans = twoChans || (numChans == 2)
return nil
})
if err != nil {
return err
}
twoChans = twoChans || (numChans == 2)
return nil
})
require.NoError(t1, err)
require.EqualValues(t1, 3, numNodes)
@ -248,7 +282,7 @@ func TestPrefAttachmentSelectGreedyAllocation(t *testing.T) {
// result, the heuristic should try to greedily
// allocate funds to channels.
scores, err := prefAttach.NodeScores(
graph, nil, maxChanSize, nodes,
ctx, graph, nil, maxChanSize, nodes,
)
require.NoError(t1, err)
@ -266,7 +300,7 @@ func TestPrefAttachmentSelectGreedyAllocation(t *testing.T) {
// candidates of that size.
const remBalance = btcutil.SatoshiPerBitcoin * 0.5
scores, err = prefAttach.NodeScores(
graph, nil, remBalance, nodes,
ctx, graph, nil, remBalance, nodes,
)
require.NoError(t1, err)
@ -289,6 +323,7 @@ func TestPrefAttachmentSelectGreedyAllocation(t *testing.T) {
// of zero during scoring.
func TestPrefAttachmentSelectSkipNodes(t *testing.T) {
t.Parallel()
ctx := context.Background()
prand.Seed(time.Now().Unix())
@ -311,10 +346,13 @@ func TestPrefAttachmentSelectSkipNodes(t *testing.T) {
require.NoError(t1, err)
nodes := make(map[NodeID]struct{})
err = graph.ForEachNode(func(n Node) error {
nodes[n.PubKey()] = struct{}{}
return nil
})
err = graph.ForEachNode(
ctx, func(_ context.Context, n Node) error {
nodes[n.PubKey()] = struct{}{}
return nil
},
)
require.NoError(t1, err)
require.Len(t1, nodes, 2)
@ -322,7 +360,7 @@ func TestPrefAttachmentSelectSkipNodes(t *testing.T) {
// With our graph created, we'll now get the scores for
// all nodes in the graph.
scores, err := prefAttach.NodeScores(
graph, nil, maxChanSize, nodes,
ctx, graph, nil, maxChanSize, nodes,
)
require.NoError(t1, err)
@ -350,7 +388,7 @@ func TestPrefAttachmentSelectSkipNodes(t *testing.T) {
// then all nodes should have a score of zero, since we
// already got channels to them.
scores, err = prefAttach.NodeScores(
graph, chans, maxChanSize, nodes,
ctx, graph, chans, maxChanSize, nodes,
)
require.NoError(t1, err)
@ -363,3 +401,359 @@ func TestPrefAttachmentSelectSkipNodes(t *testing.T) {
}
}
}
// addRandChannel creates a new channel two target nodes. This function is
// meant to aide in the generation of random graphs for use within test cases
// the exercise the autopilot package.
func (d *testDBGraph) addRandChannel(node1, node2 *btcec.PublicKey,
capacity btcutil.Amount) (*ChannelEdge, *ChannelEdge, error) {
fetchNode := func(pub *btcec.PublicKey) (*models.LightningNode, error) {
if pub != nil {
vertex, err := route.NewVertexFromBytes(
pub.SerializeCompressed(),
)
if err != nil {
return nil, err
}
dbNode, err := d.db.FetchLightningNode(vertex)
switch {
case errors.Is(err, graphdb.ErrGraphNodeNotFound):
fallthrough
case errors.Is(err, graphdb.ErrGraphNotFound):
graphNode := &models.LightningNode{
HaveNodeAnnouncement: true,
Addresses: []net.Addr{&net.TCPAddr{
IP: bytes.Repeat(
[]byte("a"), 16,
),
}},
Features: lnwire.NewFeatureVector(
nil, lnwire.Features,
),
AuthSigBytes: testSig.Serialize(),
}
graphNode.AddPubKey(pub)
err := d.db.AddLightningNode(graphNode)
if err != nil {
return nil, err
}
case err != nil:
return nil, err
}
return dbNode, nil
}
nodeKey, err := randKey()
if err != nil {
return nil, err
}
dbNode := &models.LightningNode{
HaveNodeAnnouncement: true,
Addresses: []net.Addr{
&net.TCPAddr{
IP: bytes.Repeat([]byte("a"), 16),
},
},
Features: lnwire.NewFeatureVector(
nil, lnwire.Features,
),
AuthSigBytes: testSig.Serialize(),
}
dbNode.AddPubKey(nodeKey)
if err := d.db.AddLightningNode(dbNode); err != nil {
return nil, err
}
return dbNode, nil
}
vertex1, err := fetchNode(node1)
if err != nil {
return nil, nil, err
}
vertex2, err := fetchNode(node2)
if err != nil {
return nil, nil, err
}
var lnNode1, lnNode2 *btcec.PublicKey
if bytes.Compare(vertex1.PubKeyBytes[:], vertex2.PubKeyBytes[:]) == -1 {
lnNode1, _ = vertex1.PubKey()
lnNode2, _ = vertex2.PubKey()
} else {
lnNode1, _ = vertex2.PubKey()
lnNode2, _ = vertex1.PubKey()
}
chanID := randChanID()
edge := &models.ChannelEdgeInfo{
ChannelID: chanID.ToUint64(),
Capacity: capacity,
}
edge.AddNodeKeys(lnNode1, lnNode2, lnNode1, lnNode2)
if err := d.db.AddChannelEdge(edge); err != nil {
return nil, nil, err
}
edgePolicy := &models.ChannelEdgePolicy{
SigBytes: testSig.Serialize(),
ChannelID: chanID.ToUint64(),
LastUpdate: time.Now(),
TimeLockDelta: 10,
MinHTLC: 1,
MaxHTLC: lnwire.NewMSatFromSatoshis(capacity),
FeeBaseMSat: 10,
FeeProportionalMillionths: 10000,
MessageFlags: 1,
ChannelFlags: 0,
}
if err := d.db.UpdateEdgePolicy(edgePolicy); err != nil {
return nil, nil, err
}
edgePolicy = &models.ChannelEdgePolicy{
SigBytes: testSig.Serialize(),
ChannelID: chanID.ToUint64(),
LastUpdate: time.Now(),
TimeLockDelta: 10,
MinHTLC: 1,
MaxHTLC: lnwire.NewMSatFromSatoshis(capacity),
FeeBaseMSat: 10,
FeeProportionalMillionths: 10000,
MessageFlags: 1,
ChannelFlags: 1,
}
if err := d.db.UpdateEdgePolicy(edgePolicy); err != nil {
return nil, nil, err
}
return &ChannelEdge{
ChanID: chanID,
Capacity: capacity,
Peer: &dbNode{tx: &testNodeTx{
db: d,
node: vertex1,
}},
},
&ChannelEdge{
ChanID: chanID,
Capacity: capacity,
Peer: &dbNode{tx: &testNodeTx{
db: d,
node: vertex2,
}},
},
nil
}
func (d *testDBGraph) addRandNode() (*btcec.PublicKey, error) {
nodeKey, err := randKey()
if err != nil {
return nil, err
}
dbNode := &models.LightningNode{
HaveNodeAnnouncement: true,
Addresses: []net.Addr{
&net.TCPAddr{
IP: bytes.Repeat([]byte("a"), 16),
},
},
Features: lnwire.NewFeatureVector(
nil, lnwire.Features,
),
AuthSigBytes: testSig.Serialize(),
}
dbNode.AddPubKey(nodeKey)
if err := d.db.AddLightningNode(dbNode); err != nil {
return nil, err
}
return nodeKey, nil
}
// memChannelGraph is an implementation of the autopilot.ChannelGraph backed by
// an in-memory graph.
type memChannelGraph struct {
graph map[NodeID]*memNode
}
// A compile time assertion to ensure memChannelGraph meets the
// autopilot.ChannelGraph interface.
var _ ChannelGraph = (*memChannelGraph)(nil)
// newMemChannelGraph creates a new blank in-memory channel graph
// implementation.
func newMemChannelGraph() *memChannelGraph {
return &memChannelGraph{
graph: make(map[NodeID]*memNode),
}
}
// ForEachNode is a higher-order function that should be called once for each
// connected node within the channel graph. If the passed callback returns an
// error, then execution should be terminated.
//
// NOTE: Part of the autopilot.ChannelGraph interface.
func (m *memChannelGraph) ForEachNode(ctx context.Context,
cb func(context.Context, Node) error) error {
for _, node := range m.graph {
if err := cb(ctx, node); err != nil {
return err
}
}
return nil
}
// randChanID generates a new random channel ID.
func randChanID() lnwire.ShortChannelID {
id := atomic.AddUint64(&chanIDCounter, 1)
return lnwire.NewShortChanIDFromInt(id)
}
// randKey returns a random public key.
func randKey() (*btcec.PublicKey, error) {
priv, err := btcec.NewPrivateKey()
if err != nil {
return nil, err
}
return priv.PubKey(), nil
}
// addRandChannel creates a new channel two target nodes. This function is
// meant to aide in the generation of random graphs for use within test cases
// the exercise the autopilot package.
func (m *memChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey,
capacity btcutil.Amount) (*ChannelEdge, *ChannelEdge, error) {
var (
vertex1, vertex2 *memNode
ok bool
)
if node1 != nil {
vertex1, ok = m.graph[NewNodeID(node1)]
if !ok {
vertex1 = &memNode{
pub: node1,
addrs: []net.Addr{&net.TCPAddr{
IP: bytes.Repeat([]byte("a"), 16),
}},
}
}
} else {
newPub, err := randKey()
if err != nil {
return nil, nil, err
}
vertex1 = &memNode{
pub: newPub,
addrs: []net.Addr{
&net.TCPAddr{
IP: bytes.Repeat([]byte("a"), 16),
},
},
}
}
if node2 != nil {
vertex2, ok = m.graph[NewNodeID(node2)]
if !ok {
vertex2 = &memNode{
pub: node2,
addrs: []net.Addr{&net.TCPAddr{
IP: bytes.Repeat([]byte("a"), 16),
}},
}
}
} else {
newPub, err := randKey()
if err != nil {
return nil, nil, err
}
vertex2 = &memNode{
pub: newPub,
addrs: []net.Addr{
&net.TCPAddr{
IP: bytes.Repeat([]byte("a"), 16),
},
},
}
}
edge1 := ChannelEdge{
ChanID: randChanID(),
Capacity: capacity,
Peer: vertex2,
}
vertex1.chans = append(vertex1.chans, edge1)
edge2 := ChannelEdge{
ChanID: randChanID(),
Capacity: capacity,
Peer: vertex1,
}
vertex2.chans = append(vertex2.chans, edge2)
m.graph[NewNodeID(vertex1.pub)] = vertex1
m.graph[NewNodeID(vertex2.pub)] = vertex2
return &edge1, &edge2, nil
}
func (m *memChannelGraph) addRandNode() (*btcec.PublicKey, error) {
newPub, err := randKey()
if err != nil {
return nil, err
}
vertex := &memNode{
pub: newPub,
addrs: []net.Addr{
&net.TCPAddr{
IP: bytes.Repeat([]byte("a"), 16),
},
},
}
m.graph[NewNodeID(newPub)] = vertex
return newPub, nil
}
type testNodeTx struct {
db *testDBGraph
node *models.LightningNode
}
func (t *testNodeTx) Node() *models.LightningNode {
return t.node
}
func (t *testNodeTx) ForEachChannel(f func(*models.ChannelEdgeInfo,
*models.ChannelEdgePolicy, *models.ChannelEdgePolicy) error) error {
return t.db.db.ForEachNodeChannel(t.node.PubKeyBytes, func(
edge *models.ChannelEdgeInfo, policy1,
policy2 *models.ChannelEdgePolicy) error {
return f(edge, policy1, policy2)
})
}
func (t *testNodeTx) FetchNode(pub route.Vertex) (graphdb.NodeRTx, error) {
node, err := t.db.db.FetchLightningNode(pub)
if err != nil {
return nil, err
}
return &testNodeTx{
db: t.db,
node: node,
}, nil
}
var _ graphdb.NodeRTx = (*testNodeTx)(nil)

View File

@ -1,5 +1,7 @@
package autopilot
import "context"
// diameterCutoff is used to discard nodes in the diameter calculation.
// It is the multiplier for the eccentricity of the highest-degree node,
// serving as a cutoff to discard all nodes with a smaller hop distance. This
@ -20,7 +22,7 @@ type SimpleGraph struct {
// NewSimpleGraph creates a simplified graph from the current channel graph.
// Returns an error if the channel graph iteration fails due to underlying
// failure.
func NewSimpleGraph(g ChannelGraph) (*SimpleGraph, error) {
func NewSimpleGraph(ctx context.Context, g ChannelGraph) (*SimpleGraph, error) {
nodes := make(map[NodeID]int)
adj := make(map[int][]int)
nextIndex := 0
@ -42,17 +44,22 @@ func NewSimpleGraph(g ChannelGraph) (*SimpleGraph, error) {
return nodeIndex
}
// Iterate over each node and each channel and update the adj and the node
// index.
err := g.ForEachNode(func(node Node) error {
// Iterate over each node and each channel and update the adj and the
// node index.
err := g.ForEachNode(ctx, func(ctx context.Context, node Node) error {
u := getNodeIndex(node)
return node.ForEachChannel(func(edge ChannelEdge) error {
v := getNodeIndex(edge.Peer)
return node.ForEachChannel(
ctx, func(_ context.Context,
edge ChannelEdge) error {
adj[u] = append(adj[u], v)
return nil
})
v := getNodeIndex(edge.Peer)
adj[u] = append(adj[u], v)
return nil
},
)
})
if err != nil {
return nil, err
@ -85,9 +92,7 @@ func NewSimpleGraph(g ChannelGraph) (*SimpleGraph, error) {
func maxVal(mapping map[int]uint32) uint32 {
maxValue := uint32(0)
for _, value := range mapping {
if maxValue < value {
maxValue = value
}
maxValue = max(maxValue, value)
}
return maxValue
}

View File

@ -1,6 +1,7 @@
package autopilot
import (
"context"
"runtime"
"github.com/btcsuite/btcd/btcutil"
@ -50,12 +51,12 @@ func (g *TopCentrality) Name() string {
// As our current implementation of betweenness centrality is non-incremental,
// NodeScores will recalculate the centrality values on every call, which is
// slow for large graphs.
func (g *TopCentrality) NodeScores(graph ChannelGraph, chans []LocalChannel,
chanSize btcutil.Amount, nodes map[NodeID]struct{}) (
map[NodeID]*NodeScore, error) {
func (g *TopCentrality) NodeScores(ctx context.Context, graph ChannelGraph,
chans []LocalChannel, chanSize btcutil.Amount,
nodes map[NodeID]struct{}) (map[NodeID]*NodeScore, error) {
// Calculate betweenness centrality for the whole graph.
if err := g.centralityMetric.Refresh(graph); err != nil {
if err := g.centralityMetric.Refresh(ctx, graph); err != nil {
return nil, err
}

View File

@ -1,6 +1,7 @@
package autopilot
import (
"context"
"testing"
"github.com/btcsuite/btcd/btcec/v2"
@ -58,7 +59,7 @@ func testTopCentrality(t *testing.T, graph testGraph,
// Attempt to get centrality scores and expect
// that the result equals with the expected set.
scores, err := topCentrality.NodeScores(
graph, channels, chanSize, nodes,
context.Background(), graph, channels, chanSize, nodes,
)
require.NoError(t, err)

View File

@ -5,6 +5,7 @@ import (
"sync"
"github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/sqldb"
)
// errSolo is a sentinel error indicating that the requester should re-run the
@ -55,8 +56,20 @@ func (b *batch) run() {
for i, req := range b.reqs {
err := req.Update(tx)
if err != nil {
failIdx = i
return err
// If we get a serialization error, we
// want the underlying SQL retry
// mechanism to retry the entire batch.
// Otherwise, we can succeed in an
// sqldb retry and still re-execute the
// failing request individually.
dbErr := sqldb.MapSQLError(err)
if !sqldb.IsSerializationError(dbErr) {
failIdx = i
return err
}
return dbErr
}
}
return nil

74
batch/batch_test.go Normal file
View File

@ -0,0 +1,74 @@
package batch
import (
"errors"
"path/filepath"
"sync"
"testing"
"time"
"github.com/btcsuite/btcwallet/walletdb"
"github.com/lightningnetwork/lnd/kvdb"
"github.com/stretchr/testify/require"
)
func TestRetry(t *testing.T) {
dbDir := t.TempDir()
dbName := filepath.Join(dbDir, "weks.db")
db, err := walletdb.Create(
"bdb", dbName, true, kvdb.DefaultDBTimeout, false,
)
if err != nil {
t.Fatalf("unable to create walletdb: %v", err)
}
t.Cleanup(func() {
db.Close()
})
var (
mu sync.Mutex
called int
)
sched := NewTimeScheduler(db, &mu, time.Second)
// First, we construct a request that should retry individually and
// execute it non-lazily. It should still return the error the second
// time.
req := &Request{
Update: func(tx kvdb.RwTx) error {
called++
return errors.New("test")
},
}
err = sched.Execute(req)
// Check and reset the called counter.
mu.Lock()
require.Equal(t, 2, called)
called = 0
mu.Unlock()
require.ErrorContains(t, err, "test")
// Now, we construct a request that should NOT retry because it returns
// a serialization error, which should cause the underlying postgres
// transaction to retry. Since we aren't using postgres, this will
// cause the transaction to not be retried at all.
req = &Request{
Update: func(tx kvdb.RwTx) error {
called++
return errors.New("could not serialize access")
},
}
err = sched.Execute(req)
// Check the called counter.
mu.Lock()
require.Equal(t, 1, called)
mu.Unlock()
require.ErrorContains(t, err, "could not serialize access")
}

View File

@ -7,12 +7,13 @@ import (
"net"
"time"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/lightningnetwork/lnd/keychain"
)
// defaultHandshakes is the maximum number of handshakes that can be done in
// parallel.
const defaultHandshakes = 1000
const defaultHandshakes = 50
// Listener is an implementation of a net.Conn which executes an authenticated
// key exchange and message encryption protocol dubbed "Machine" after
@ -24,6 +25,10 @@ type Listener struct {
tcp *net.TCPListener
// shouldAccept is a closure that determines if we should accept the
// incoming connection or not based on its public key.
shouldAccept func(*btcec.PublicKey) (bool, error)
handshakeSema chan struct{}
conns chan maybeConn
quit chan struct{}
@ -34,8 +39,8 @@ var _ net.Listener = (*Listener)(nil)
// NewListener returns a new net.Listener which enforces the Brontide scheme
// during both initial connection establishment and data transfer.
func NewListener(localStatic keychain.SingleKeyECDH,
listenAddr string) (*Listener, error) {
func NewListener(localStatic keychain.SingleKeyECDH, listenAddr string,
shouldAccept func(*btcec.PublicKey) (bool, error)) (*Listener, error) {
addr, err := net.ResolveTCPAddr("tcp", listenAddr)
if err != nil {
@ -50,6 +55,7 @@ func NewListener(localStatic keychain.SingleKeyECDH,
brontideListener := &Listener{
localStatic: localStatic,
tcp: l,
shouldAccept: shouldAccept,
handshakeSema: make(chan struct{}, defaultHandshakes),
conns: make(chan maybeConn),
quit: make(chan struct{}),
@ -193,6 +199,28 @@ func (l *Listener) doHandshake(conn net.Conn) {
return
}
// Call the shouldAccept closure to see if the remote node's public key
// is allowed according to our banning heuristic. This is here because
// we do not learn the remote node's public static key until we've
// received and validated Act 3.
remoteKey := brontideConn.RemotePub()
if remoteKey == nil {
connErr := fmt.Errorf("no remote pubkey")
brontideConn.conn.Close()
l.rejectConn(rejectedConnErr(connErr, remoteAddr))
return
}
accepted, acceptErr := l.shouldAccept(remoteKey)
if !accepted {
// Reject the connection.
brontideConn.conn.Close()
l.rejectConn(rejectedConnErr(acceptErr, remoteAddr))
return
}
l.acceptConn(brontideConn)
}
@ -255,3 +283,9 @@ func (l *Listener) Close() error {
func (l *Listener) Addr() net.Addr {
return l.tcp.Addr()
}
// DisabledBanClosure is used in places that NewListener is invoked to bypass
// the ban-scoring.
func DisabledBanClosure(p *btcec.PublicKey) (bool, error) {
return true, nil
}

View File

@ -35,7 +35,7 @@ func makeListener() (*Listener, *lnwire.NetAddress, error) {
addr := "localhost:0"
// Our listener will be local, and the connection remote.
listener, err := NewListener(localKeyECDH, addr)
listener, err := NewListener(localKeyECDH, addr, DisabledBanClosure)
if err != nil {
return nil, nil, err
}
@ -85,12 +85,12 @@ func establishTestConnection(t testing.TB) (net.Conn, net.Conn, error) {
remote := <-remoteConnChan
if remote.err != nil {
return nil, nil, err
return nil, nil, remote.err
}
local := <-localConnChan
if local.err != nil {
return nil, nil, err
return nil, nil, local.err
}
t.Cleanup(func() {

99
build/config.go Normal file
View File

@ -0,0 +1,99 @@
package build
import (
"fmt"
"github.com/btcsuite/btclog/v2"
)
const (
callSiteOff = "off"
callSiteShort = "short"
callSiteLong = "long"
defaultLogCompressor = Gzip
// DefaultMaxLogFiles is the default maximum number of log files to
// keep.
DefaultMaxLogFiles = 10
// DefaultMaxLogFileSize is the default maximum log file size in MB.
DefaultMaxLogFileSize = 20
)
// LogConfig holds logging configuration options.
//
//nolint:ll
type LogConfig struct {
Console *consoleLoggerCfg `group:"console" namespace:"console" description:"The logger writing to stdout and stderr."`
File *FileLoggerConfig `group:"file" namespace:"file" description:"The logger writing to LND's standard log file."`
NoCommitHash bool `long:"no-commit-hash" description:"If set, the commit-hash of the current build will not be included in log lines by default."`
}
// Validate validates the LogConfig struct values.
func (c *LogConfig) Validate() error {
if !SupportedLogCompressor(c.File.Compressor) {
return fmt.Errorf("invalid log compressor: %v",
c.File.Compressor)
}
return nil
}
// LoggerConfig holds options for a particular logger.
//
//nolint:ll
type LoggerConfig struct {
Disable bool `long:"disable" description:"Disable this logger."`
NoTimestamps bool `long:"no-timestamps" description:"Omit timestamps from log lines."`
CallSite string `long:"call-site" description:"Include the call-site of each log line." choice:"off" choice:"short" choice:"long"`
}
// DefaultLogConfig returns the default logging config options.
func DefaultLogConfig() *LogConfig {
return &LogConfig{
Console: defaultConsoleLoggerCfg(),
File: &FileLoggerConfig{
Compressor: defaultLogCompressor,
MaxLogFiles: DefaultMaxLogFiles,
MaxLogFileSize: DefaultMaxLogFileSize,
LoggerConfig: &LoggerConfig{
CallSite: callSiteOff,
},
},
}
}
// HandlerOptions returns the set of btclog.HandlerOptions that the state of the
// config struct translates to.
func (cfg *LoggerConfig) HandlerOptions() []btclog.HandlerOption {
opts := []btclog.HandlerOption{
// We wrap the logger provided by the logging library with
// another layer of abstraction with the handlerSet, and so we
// need to increase the default skip depth by 1.
btclog.WithCallSiteSkipDepth(btclog.DefaultSkipDepth + 1),
}
if cfg.NoTimestamps {
opts = append(opts, btclog.WithNoTimestamp())
}
switch cfg.CallSite {
case callSiteShort:
opts = append(opts, btclog.WithCallerFlags(btclog.Lshortfile))
case callSiteLong:
opts = append(opts, btclog.WithCallerFlags(btclog.Llongfile))
}
return opts
}
// FileLoggerConfig extends LoggerConfig with specific log file options.
//
//nolint:ll
type FileLoggerConfig struct {
*LoggerConfig `yaml:",inline"`
Compressor string `long:"compressor" description:"Compression algorithm to use when rotating logs." choice:"gzip" choice:"zstd"`
MaxLogFiles int `long:"max-files" description:"Maximum logfiles to keep (0 for no rotation)"`
MaxLogFileSize int `long:"max-file-size" description:"Maximum logfile size in MB"`
}

112
build/config_dev.go Normal file
View File

@ -0,0 +1,112 @@
//go:build dev
// +build dev
package build
import (
"fmt"
"strings"
btclogv1 "github.com/btcsuite/btclog"
"github.com/btcsuite/btclog/v2"
)
const (
resetSeq = "0"
boldSeq = "1"
faintSeq = "2"
esc = '\x1b'
csi = string(esc) + "["
)
// consoleLoggerCfg extends the LoggerConfig struct by adding a Color option
// which is only available for a console logger.
//
//nolint:ll
type consoleLoggerCfg struct {
*LoggerConfig `yaml:",inline"`
Style bool `long:"style" description:"If set, the output will be styled with color and fonts"`
}
// defaultConsoleLoggerCfg returns the default consoleLoggerCfg for the dev
// console logger.
func defaultConsoleLoggerCfg() *consoleLoggerCfg {
return &consoleLoggerCfg{
LoggerConfig: &LoggerConfig{
CallSite: callSiteShort,
},
}
}
// HandlerOptions returns the set of btclog.HandlerOptions that the state of the
// config struct translates to.
func (cfg *consoleLoggerCfg) HandlerOptions() []btclog.HandlerOption {
opts := cfg.LoggerConfig.HandlerOptions()
if !cfg.Style {
return opts
}
return append(
opts, btclog.WithStyledLevel(
func(l btclogv1.Level) string {
return styleString(
fmt.Sprintf("[%s]", l),
boldSeq,
string(ansiColoSeq(l)),
)
},
),
btclog.WithStyledCallSite(
func(file string, line int) string {
str := fmt.Sprintf("%s:%d", file, line)
return styleString(str, faintSeq)
},
),
btclog.WithStyledKeys(func(key string) string {
return styleString(key, faintSeq)
}),
)
}
func styleString(s string, styles ...string) string {
if len(styles) == 0 {
return s
}
seq := strings.Join(styles, ";")
if seq == "" {
return s
}
return fmt.Sprintf("%s%sm%s%sm", csi, seq, s, csi+resetSeq)
}
type ansiColorSeq string
const (
ansiColorSeqDarkTeal ansiColorSeq = "38;5;30"
ansiColorSeqDarkBlue ansiColorSeq = "38;5;63"
ansiColorSeqLightBlue ansiColorSeq = "38;5;86"
ansiColorSeqYellow ansiColorSeq = "38;5;192"
ansiColorSeqRed ansiColorSeq = "38;5;204"
ansiColorSeqPink ansiColorSeq = "38;5;134"
)
func ansiColoSeq(l btclogv1.Level) ansiColorSeq {
switch l {
case btclog.LevelTrace:
return ansiColorSeqDarkTeal
case btclog.LevelDebug:
return ansiColorSeqDarkBlue
case btclog.LevelWarn:
return ansiColorSeqYellow
case btclog.LevelError:
return ansiColorSeqRed
case btclog.LevelCritical:
return ansiColorSeqPink
default:
return ansiColorSeqLightBlue
}
}

22
build/config_prod.go Normal file
View File

@ -0,0 +1,22 @@
//go:build !dev
// +build !dev
package build
// consoleLoggerCfg embeds the LoggerConfig struct along with any extensions
// specific to a production deployment.
//
//nolint:ll
type consoleLoggerCfg struct {
*LoggerConfig `yaml:",inline"`
}
// defaultConsoleLoggerCfg returns the default consoleLoggerCfg for the prod
// console logger.
func defaultConsoleLoggerCfg() *consoleLoggerCfg {
return &consoleLoggerCfg{
LoggerConfig: &LoggerConfig{
CallSite: callSiteOff,
},
}
}

214
build/handler_sets.go Normal file
View File

@ -0,0 +1,214 @@
package build
import (
"context"
"log/slog"
btclogv1 "github.com/btcsuite/btclog"
"github.com/btcsuite/btclog/v2"
)
// handlerSet is an implementation of Handler that abstracts away multiple
// Handlers.
type handlerSet struct {
level btclogv1.Level
set []btclog.Handler
}
// newHandlerSet constructs a new HandlerSet.
func newHandlerSet(level btclogv1.Level, set ...btclog.Handler) *handlerSet {
h := &handlerSet{
set: set,
level: level,
}
h.SetLevel(level)
return h
}
// Enabled reports whether the handler handles records at the given level.
//
// NOTE: this is part of the slog.Handler interface.
func (h *handlerSet) Enabled(ctx context.Context, level slog.Level) bool {
for _, handler := range h.set {
if !handler.Enabled(ctx, level) {
return false
}
}
return true
}
// Handle handles the Record.
//
// NOTE: this is part of the slog.Handler interface.
func (h *handlerSet) Handle(ctx context.Context, record slog.Record) error {
for _, handler := range h.set {
if err := handler.Handle(ctx, record); err != nil {
return err
}
}
return nil
}
// WithAttrs returns a new Handler whose attributes consist of both the
// receiver's attributes and the arguments.
//
// NOTE: this is part of the slog.Handler interface.
func (h *handlerSet) WithAttrs(attrs []slog.Attr) slog.Handler {
newSet := &reducedSet{set: make([]slog.Handler, len(h.set))}
for i, handler := range h.set {
newSet.set[i] = handler.WithAttrs(attrs)
}
return newSet
}
// WithGroup returns a new Handler with the given group appended to the
// receiver's existing groups.
//
// NOTE: this is part of the slog.Handler interface.
func (h *handlerSet) WithGroup(name string) slog.Handler {
newSet := &reducedSet{set: make([]slog.Handler, len(h.set))}
for i, handler := range h.set {
newSet.set[i] = handler.WithGroup(name)
}
return newSet
}
// SubSystem creates a new Handler with the given sub-system tag.
//
// NOTE: this is part of the Handler interface.
func (h *handlerSet) SubSystem(tag string) btclog.Handler {
newSet := &handlerSet{set: make([]btclog.Handler, len(h.set))}
for i, handler := range h.set {
newSet.set[i] = handler.SubSystem(tag)
}
return newSet
}
// SetLevel changes the logging level of the Handler to the passed
// level.
//
// NOTE: this is part of the btclog.Handler interface.
func (h *handlerSet) SetLevel(level btclogv1.Level) {
for _, handler := range h.set {
handler.SetLevel(level)
}
h.level = level
}
// Level returns the current logging level of the Handler.
//
// NOTE: this is part of the btclog.Handler interface.
func (h *handlerSet) Level() btclogv1.Level {
return h.level
}
// WithPrefix returns a copy of the Handler but with the given string prefixed
// to each log message.
//
// NOTE: this is part of the btclog.Handler interface.
func (h *handlerSet) WithPrefix(prefix string) btclog.Handler {
newSet := &handlerSet{set: make([]btclog.Handler, len(h.set))}
for i, handler := range h.set {
newSet.set[i] = handler.WithPrefix(prefix)
}
return newSet
}
// A compile-time check to ensure that handlerSet implements btclog.Handler.
var _ btclog.Handler = (*handlerSet)(nil)
// reducedSet is an implementation of the slog.Handler interface which is
// itself backed by multiple slog.Handlers. This is used by the handlerSet
// WithGroup and WithAttrs methods so that we can apply the WithGroup and
// WithAttrs to the underlying handlers in the set. These calls, however,
// produce slog.Handlers and not btclog.Handlers. So the reducedSet represents
// the resulting set produced.
type reducedSet struct {
set []slog.Handler
}
// Enabled reports whether the handler handles records at the given level.
//
// NOTE: this is part of the slog.Handler interface.
func (r *reducedSet) Enabled(ctx context.Context, level slog.Level) bool {
for _, handler := range r.set {
if !handler.Enabled(ctx, level) {
return false
}
}
return true
}
// Handle handles the Record.
//
// NOTE: this is part of the slog.Handler interface.
func (r *reducedSet) Handle(ctx context.Context, record slog.Record) error {
for _, handler := range r.set {
if err := handler.Handle(ctx, record); err != nil {
return err
}
}
return nil
}
// WithAttrs returns a new Handler whose attributes consist of both the
// receiver's attributes and the arguments.
//
// NOTE: this is part of the slog.Handler interface.
func (r *reducedSet) WithAttrs(attrs []slog.Attr) slog.Handler {
newSet := &reducedSet{set: make([]slog.Handler, len(r.set))}
for i, handler := range r.set {
newSet.set[i] = handler.WithAttrs(attrs)
}
return newSet
}
// WithGroup returns a new Handler with the given group appended to the
// receiver's existing groups.
//
// NOTE: this is part of the slog.Handler interface.
func (r *reducedSet) WithGroup(name string) slog.Handler {
newSet := &reducedSet{set: make([]slog.Handler, len(r.set))}
for i, handler := range r.set {
newSet.set[i] = handler.WithGroup(name)
}
return newSet
}
// A compile-time check to ensure that handlerSet implements slog.Handler.
var _ slog.Handler = (*reducedSet)(nil)
// subLogGenerator implements the SubLogCreator backed by a Handler.
type subLogGenerator struct {
handler btclog.Handler
}
// newSubLogGenerator constructs a new subLogGenerator from a Handler.
func newSubLogGenerator(handler btclog.Handler) *subLogGenerator {
return &subLogGenerator{
handler: handler,
}
}
// Logger returns a new logger for a particular sub-system.
//
// NOTE: this is part of the SubLogCreator interface.
func (b *subLogGenerator) Logger(subsystemTag string) btclog.Logger {
handler := b.handler.SubSystem(subsystemTag)
return btclog.NewSLogger(handler)
}
// A compile-time check to ensure that handlerSet implements slog.Handler.
var _ SubLogCreator = (*subLogGenerator)(nil)

38
build/handlers.go Normal file
View File

@ -0,0 +1,38 @@
package build
import (
"os"
"github.com/btcsuite/btclog/v2"
)
// NewDefaultLogHandlers returns the standard console logger and rotating log
// writer handlers that we generally want to use. It also applies the various
// config options to the loggers.
func NewDefaultLogHandlers(cfg *LogConfig,
rotator *RotatingLogWriter) []btclog.Handler {
var handlers []btclog.Handler
consoleLogHandler := btclog.NewDefaultHandler(
os.Stdout, cfg.Console.HandlerOptions()...,
)
logFileHandler := btclog.NewDefaultHandler(
rotator, cfg.File.HandlerOptions()...,
)
maybeAddLogger := func(cmdOptionDisable bool, handler btclog.Handler) {
if !cmdOptionDisable {
handlers = append(handlers, handler)
}
}
switch LoggingType {
case LogTypeStdOut:
maybeAddLogger(cfg.Console.Disable, consoleLogHandler)
case LogTypeDefault:
maybeAddLogger(cfg.Console.Disable, consoleLogHandler)
maybeAddLogger(cfg.File.Disable, logFileHandler)
}
return handlers
}

View File

@ -1,11 +1,9 @@
package build
import (
"fmt"
"io"
"strings"
"os"
"github.com/btcsuite/btclog"
"github.com/btcsuite/btclog/v2"
)
// LogType is an indicating the type of logging specified by the build flag.
@ -36,15 +34,30 @@ func (t LogType) String() string {
}
}
// LogWriter is a stub type whose behavior can be changed using the build flags
// "stdlog" and "nolog". The default behavior is to write to both stdout and the
// RotatorPipe. Passing "stdlog" will cause it only to write to stdout, and
// "nolog" implements Write as a no-op.
type LogWriter struct {
// RotatorPipe is the write-end pipe for writing to the log rotator. It
// is written to by the Write method of the LogWriter type. This only
// needs to be set if neither the stdlog or nolog builds are set.
RotatorPipe *io.PipeWriter
// Declare the supported log file compressors as exported consts for easier use
// from other projects.
const (
// Gzip is the default compressor.
Gzip = "gzip"
// Zstd is a modern compressor that compresses better than Gzip, in less
// time.
Zstd = "zstd"
)
// logCompressors maps the identifier for each supported compression algorithm
// to the extension used for the compressed log files.
var logCompressors = map[string]string{
Gzip: "gz",
Zstd: "zst",
}
// SupportedLogCompressor returns whether or not logCompressor is a supported
// compression algorithm for log files.
func SupportedLogCompressor(logCompressor string) bool {
_, ok := logCompressors[logCompressor]
return ok
}
// NewSubLogger constructs a new subsystem log from the current LogWriter
@ -80,8 +93,10 @@ func NewSubLogger(subsystem string,
// that they share the same backend, since all output is written
// to std out.
case LogTypeStdOut:
backend := btclog.NewBackend(&LogWriter{})
logger := backend.Logger(subsystem)
backend := btclog.NewDefaultHandler(os.Stdout)
logger := btclog.NewSLogger(
backend.SubSystem(subsystem),
)
// Set the logging level of the stdout logger to use the
// configured logging level specified by build flags.
@ -95,114 +110,3 @@ func NewSubLogger(subsystem string,
// For any other configurations, we'll disable logging.
return btclog.Disabled
}
// SubLoggers is a type that holds a map of subsystem loggers keyed by their
// subsystem name.
type SubLoggers map[string]btclog.Logger
// LeveledSubLogger provides the ability to retrieve the subsystem loggers of
// a logger and set their log levels individually or all at once.
type LeveledSubLogger interface {
// SubLoggers returns the map of all registered subsystem loggers.
SubLoggers() SubLoggers
// SupportedSubsystems returns a slice of strings containing the names
// of the supported subsystems. Should ideally correspond to the keys
// of the subsystem logger map and be sorted.
SupportedSubsystems() []string
// SetLogLevel assigns an individual subsystem logger a new log level.
SetLogLevel(subsystemID string, logLevel string)
// SetLogLevels assigns all subsystem loggers the same new log level.
SetLogLevels(logLevel string)
}
// ParseAndSetDebugLevels attempts to parse the specified debug level and set
// the levels accordingly on the given logger. An appropriate error is returned
// if anything is invalid.
func ParseAndSetDebugLevels(level string, logger LeveledSubLogger) error {
// Split at the delimiter.
levels := strings.Split(level, ",")
if len(levels) == 0 {
return fmt.Errorf("invalid log level: %v", level)
}
// If the first entry has no =, treat is as the log level for all
// subsystems.
globalLevel := levels[0]
if !strings.Contains(globalLevel, "=") {
// Validate debug log level.
if !validLogLevel(globalLevel) {
str := "the specified debug level [%v] is invalid"
return fmt.Errorf(str, globalLevel)
}
// Change the logging level for all subsystems.
logger.SetLogLevels(globalLevel)
// The rest will target specific subsystems.
levels = levels[1:]
}
// Go through the subsystem/level pairs while detecting issues and
// update the log levels accordingly.
for _, logLevelPair := range levels {
if !strings.Contains(logLevelPair, "=") {
str := "the specified debug level contains an " +
"invalid subsystem/level pair [%v]"
return fmt.Errorf(str, logLevelPair)
}
// Extract the specified subsystem and log level.
fields := strings.Split(logLevelPair, "=")
if len(fields) != 2 {
str := "the specified debug level has an invalid " +
"format [%v] -- use format subsystem1=level1," +
"subsystem2=level2"
return fmt.Errorf(str, logLevelPair)
}
subsysID, logLevel := fields[0], fields[1]
subLoggers := logger.SubLoggers()
// Validate subsystem.
if _, exists := subLoggers[subsysID]; !exists {
str := "the specified subsystem [%v] is invalid -- " +
"supported subsystems are %v"
return fmt.Errorf(
str, subsysID, logger.SupportedSubsystems(),
)
}
// Validate log level.
if !validLogLevel(logLevel) {
str := "the specified debug level [%v] is invalid"
return fmt.Errorf(str, logLevel)
}
logger.SetLogLevel(subsysID, logLevel)
}
return nil
}
// validLogLevel returns whether or not logLevel is a valid debug log level.
func validLogLevel(logLevel string) bool {
switch logLevel {
case "trace":
fallthrough
case "debug":
fallthrough
case "info":
fallthrough
case "warn":
fallthrough
case "error":
fallthrough
case "critical":
fallthrough
case "off":
return true
}
return false
}

View File

@ -3,17 +3,6 @@
package build
import "os"
// LoggingType is a log type that writes to both stdout and the log rotator, if
// present.
const LoggingType = LogTypeDefault
// Write writes the byte slice to both stdout and the log rotator, if present.
func (w *LogWriter) Write(b []byte) (int, error) {
os.Stdout.Write(b)
if w.RotatorPipe != nil {
w.RotatorPipe.Write(b)
}
return len(b), nil
}

View File

@ -5,8 +5,3 @@ package build
// LoggingType is a log type that writes no logs.
const LoggingType = LogTypeNone
// Write is a noop.
func (w *LogWriter) Write(b []byte) (int, error) {
return len(b), nil
}

View File

@ -1,7 +1,9 @@
package build
import (
"github.com/btcsuite/btclog"
"context"
"github.com/btcsuite/btclog/v2"
)
// ShutdownLogger wraps an existing logger with a shutdown function which will
@ -41,3 +43,16 @@ func (s *ShutdownLogger) Critical(v ...interface{}) {
s.Logger.Info("Sending request for shutdown")
s.shutdown()
}
// CriticalS writes a structured log with the given message and key-value pair
// attributes with LevelCritical to the log. It will then call the shutdown
// logger's shutdown function to prompt safe shutdown.
//
// Note: it is part of the btclog.Logger interface.
func (s *ShutdownLogger) CriticalS(ctx context.Context, msg string, err error,
attr ...interface{}) {
s.Logger.CriticalS(ctx, msg, err, attr...)
s.Logger.Info("Sending request for shutdown")
s.shutdown()
}

View File

@ -3,13 +3,5 @@
package build
import "os"
// LoggingType is a log type that only writes to stdout.
const LoggingType = LogTypeStdOut
// Write writes the provided byte slice to stdout.
func (w *LogWriter) Write(b []byte) (int, error) {
os.Stdout.Write(b)
return len(b), nil
}

View File

@ -3,7 +3,7 @@ package build_test
import (
"testing"
"github.com/btcsuite/btclog"
"github.com/btcsuite/btclog/v2"
"github.com/lightningnetwork/lnd/build"
"github.com/stretchr/testify/require"
)

View File

@ -1,153 +1,105 @@
package build
import (
"compress/gzip"
"fmt"
"io"
"os"
"path/filepath"
"sort"
"github.com/btcsuite/btclog"
"github.com/jrick/logrotate/rotator"
"github.com/klauspost/compress/zstd"
)
// RotatingLogWriter is a wrapper around the LogWriter that supports log file
// rotation.
type RotatingLogWriter struct {
logWriter *LogWriter
// pipe is the write-end pipe for writing to the log rotator.
pipe *io.PipeWriter
backendLog *btclog.Backend
logRotator *rotator.Rotator
subsystemLoggers SubLoggers
rotator *rotator.Rotator
}
// A compile time check to ensure RotatingLogWriter implements the
// LeveledSubLogger interface.
var _ LeveledSubLogger = (*RotatingLogWriter)(nil)
// NewRotatingLogWriter creates a new file rotating log writer.
//
// NOTE: `InitLogRotator` must be called to set up log rotation after creating
// the writer.
func NewRotatingLogWriter() *RotatingLogWriter {
logWriter := &LogWriter{}
backendLog := btclog.NewBackend(logWriter)
return &RotatingLogWriter{
logWriter: logWriter,
backendLog: backendLog,
subsystemLoggers: SubLoggers{},
}
}
// GenSubLogger creates a new sublogger. A shutdown callback function
// is provided to be able to shutdown in case of a critical error.
func (r *RotatingLogWriter) GenSubLogger(tag string, shutdown func()) btclog.Logger {
logger := r.backendLog.Logger(tag)
return NewShutdownLogger(logger, shutdown)
}
// RegisterSubLogger registers a new subsystem logger.
func (r *RotatingLogWriter) RegisterSubLogger(subsystem string,
logger btclog.Logger) {
r.subsystemLoggers[subsystem] = logger
return &RotatingLogWriter{}
}
// InitLogRotator initializes the log file rotator to write logs to logFile and
// create roll files in the same directory. It should be called as early on
// startup and possible and must be closed on shutdown by calling `Close`.
func (r *RotatingLogWriter) InitLogRotator(logFile string, maxLogFileSize int,
maxLogFiles int) error {
func (r *RotatingLogWriter) InitLogRotator(cfg *FileLoggerConfig,
logFile string) error {
logDir, _ := filepath.Split(logFile)
err := os.MkdirAll(logDir, 0700)
if err != nil {
return fmt.Errorf("failed to create log directory: %w", err)
}
r.logRotator, err = rotator.New(
logFile, int64(maxLogFileSize*1024), false, maxLogFiles,
r.rotator, err = rotator.New(
logFile, int64(cfg.MaxLogFileSize*1024), false, cfg.MaxLogFiles,
)
if err != nil {
return fmt.Errorf("failed to create file rotator: %w", err)
}
// Reject unknown compressors.
if !SupportedLogCompressor(cfg.Compressor) {
return fmt.Errorf("unknown log compressor: %v", cfg.Compressor)
}
var c rotator.Compressor
switch cfg.Compressor {
case Gzip:
c = gzip.NewWriter(nil)
case Zstd:
c, err = zstd.NewWriter(nil)
if err != nil {
return fmt.Errorf("failed to create zstd compressor: "+
"%w", err)
}
}
// Apply the compressor and its file suffix to the log rotator.
r.rotator.SetCompressor(c, logCompressors[cfg.Compressor])
// Run rotator as a goroutine now but make sure we catch any errors
// that happen in case something with the rotation goes wrong during
// runtime (like running out of disk space or not being allowed to
// create a new logfile for whatever reason).
pr, pw := io.Pipe()
go func() {
err := r.logRotator.Run(pr)
err := r.rotator.Run(pr)
if err != nil {
_, _ = fmt.Fprintf(os.Stderr,
"failed to run file rotator: %v\n", err)
}
}()
r.logWriter.RotatorPipe = pw
r.pipe = pw
return nil
}
// Write writes the byte slice to the log rotator, if present.
func (r *RotatingLogWriter) Write(b []byte) (int, error) {
if r.rotator != nil {
return r.rotator.Write(b)
}
return len(b), nil
}
// Close closes the underlying log rotator if it has already been created.
func (r *RotatingLogWriter) Close() error {
if r.logRotator != nil {
return r.logRotator.Close()
if r.rotator != nil {
return r.rotator.Close()
}
return nil
}
// SubLoggers returns all currently registered subsystem loggers for this log
// writer.
//
// NOTE: This is part of the LeveledSubLogger interface.
func (r *RotatingLogWriter) SubLoggers() SubLoggers {
return r.subsystemLoggers
}
// SupportedSubsystems returns a sorted string slice of all keys in the
// subsystems map, corresponding to the names of the subsystems.
//
// NOTE: This is part of the LeveledSubLogger interface.
func (r *RotatingLogWriter) SupportedSubsystems() []string {
// Convert the subsystemLoggers map keys to a string slice.
subsystems := make([]string, 0, len(r.subsystemLoggers))
for subsysID := range r.subsystemLoggers {
subsystems = append(subsystems, subsysID)
}
// Sort the subsystems for stable display.
sort.Strings(subsystems)
return subsystems
}
// SetLogLevel sets the logging level for provided subsystem. Invalid
// subsystems are ignored. Uninitialized subsystems are dynamically created as
// needed.
//
// NOTE: This is part of the LeveledSubLogger interface.
func (r *RotatingLogWriter) SetLogLevel(subsystemID string, logLevel string) {
// Ignore invalid subsystems.
logger, ok := r.subsystemLoggers[subsystemID]
if !ok {
return
}
// Defaults to info if the log level is invalid.
level, _ := btclog.LevelFromString(logLevel)
logger.SetLevel(level)
}
// SetLogLevels sets the log level for all subsystem loggers to the passed
// level. It also dynamically creates the subsystem loggers as needed, so it
// can be used to initialize the logging system.
//
// NOTE: This is part of the LeveledSubLogger interface.
func (r *RotatingLogWriter) SetLogLevels(logLevel string) {
// Configure all sub-systems with the new logging level. Dynamically
// create loggers as needed.
for subsystemID := range r.subsystemLoggers {
r.SetLogLevel(subsystemID, logLevel)
}
}

View File

@ -1,112 +0,0 @@
package build
import "github.com/btcsuite/btclog"
// PrefixLog is a pass-through logger that adds a prefix to every logged line.
type PrefixLog struct {
log btclog.Logger
prefix string
}
// NewPrefixLog instantiates a new prefixed logger.
func NewPrefixLog(prefix string, log btclog.Logger) *PrefixLog {
return &PrefixLog{
prefix: prefix,
log: log,
}
}
// addFormatPrefix prepends the prefix to a format string.
func (p *PrefixLog) addFormatPrefix(s string) string {
return p.prefix + " " + s
}
// addArgsPrefix prepends the prefix to a list of arguments.
func (p *PrefixLog) addArgsPrefix(args []interface{}) []interface{} {
return append([]interface{}{p.prefix}, args...)
}
// Tracef formats message according to format specifier and writes to to log
// with LevelTrace.
func (p *PrefixLog) Tracef(format string, params ...interface{}) {
p.log.Tracef(p.addFormatPrefix(format), params...)
}
// Debugf formats message according to format specifier and writes to log with
// LevelDebug.
func (p *PrefixLog) Debugf(format string, params ...interface{}) {
p.log.Debugf(p.addFormatPrefix(format), params...)
}
// Infof formats message according to format specifier and writes to log with
// LevelInfo.
func (p *PrefixLog) Infof(format string, params ...interface{}) {
p.log.Infof(p.addFormatPrefix(format), params...)
}
// Warnf formats message according to format specifier and writes to to log with
// LevelWarn.
func (p *PrefixLog) Warnf(format string, params ...interface{}) {
p.log.Warnf(p.addFormatPrefix(format), params...)
}
// Errorf formats message according to format specifier and writes to to log
// with LevelError.
func (p *PrefixLog) Errorf(format string, params ...interface{}) {
p.log.Errorf(p.addFormatPrefix(format), params...)
}
// Criticalf formats message according to format specifier and writes to log
// with LevelCritical.
func (p *PrefixLog) Criticalf(format string, params ...interface{}) {
p.log.Criticalf(p.addFormatPrefix(format), params...)
}
// Trace formats message using the default formats for its operands and writes
// to log with LevelTrace.
func (p *PrefixLog) Trace(v ...interface{}) {
p.log.Trace(p.addArgsPrefix(v)...)
}
// Debug formats message using the default formats for its operands and writes
// to log with LevelDebug.
func (p *PrefixLog) Debug(v ...interface{}) {
p.log.Debug(p.addArgsPrefix(v)...)
}
// Info formats message using the default formats for its operands and writes to
// log with LevelInfo.
func (p *PrefixLog) Info(v ...interface{}) {
p.log.Info(p.addArgsPrefix(v)...)
}
// Warn formats message using the default formats for its operands and writes to
// log with LevelWarn.
func (p *PrefixLog) Warn(v ...interface{}) {
p.log.Warn(p.addArgsPrefix(v)...)
}
// Error formats message using the default formats for its operands and writes
// to log with LevelError.
func (p *PrefixLog) Error(v ...interface{}) {
p.log.Error(p.addArgsPrefix(v)...)
}
// Critical formats message using the default formats for its operands and
// writes to log with LevelCritical.
func (p *PrefixLog) Critical(v ...interface{}) {
p.log.Critical(p.addArgsPrefix(v)...)
}
// Level returns the current logging level.
func (p *PrefixLog) Level() btclog.Level {
return p.log.Level()
}
// SetLevel changes the logging level to the passed level.
func (p *PrefixLog) SetLevel(level btclog.Level) {
p.log.SetLevel(level)
}
// Assert that PrefixLog fulfills the btclog.Logger interface.
var _ btclog.Logger = &PrefixLog{}

263
build/sub_logger.go Normal file
View File

@ -0,0 +1,263 @@
package build
import (
"fmt"
"sort"
"strings"
"sync"
"github.com/btcsuite/btclog/v2"
)
// SubLogCreator can be used to create a new logger for a particular subsystem.
type SubLogCreator interface {
// Logger returns a new logger for a particular subsystem.
Logger(subsystemTag string) btclog.Logger
}
// SubLoggerManager manages a set of subsystem loggers. Level updates will be
// applied to all the loggers managed by the manager.
type SubLoggerManager struct {
genLogger SubLogCreator
loggers SubLoggers
mu sync.Mutex
}
// A compile time check to ensure SubLoggerManager implements the
// LeveledSubLogger interface.
var _ LeveledSubLogger = (*SubLoggerManager)(nil)
// NewSubLoggerManager constructs a new SubLoggerManager.
func NewSubLoggerManager(handlers ...btclog.Handler) *SubLoggerManager {
return &SubLoggerManager{
loggers: make(SubLoggers),
genLogger: newSubLogGenerator(
newHandlerSet(btclog.LevelInfo, handlers...),
),
}
}
// GenSubLogger creates a new sub-logger and adds it to the set managed by the
// SubLoggerManager. A shutdown callback function is provided to be able to shut
// down in case of a critical error.
func (r *SubLoggerManager) GenSubLogger(subsystem string,
shutdown func()) btclog.Logger {
// Create a new logger with the given subsystem tag.
logger := r.genLogger.Logger(subsystem)
// Wrap the new logger in a Shutdown logger so that the shutdown
// call back is called if a critical log is ever written via this new
// logger.
l := NewShutdownLogger(logger, shutdown)
r.RegisterSubLogger(subsystem, l)
return l
}
// RegisterSubLogger registers the given logger under the given subsystem name.
func (r *SubLoggerManager) RegisterSubLogger(subsystem string,
logger btclog.Logger) {
// Add the new logger to the set of loggers managed by the manager.
r.mu.Lock()
r.loggers[subsystem] = logger
r.mu.Unlock()
}
// SubLoggers returns all currently registered subsystem loggers for this log
// writer.
//
// NOTE: This is part of the LeveledSubLogger interface.
func (r *SubLoggerManager) SubLoggers() SubLoggers {
r.mu.Lock()
defer r.mu.Unlock()
return r.loggers
}
// SupportedSubsystems returns a sorted string slice of all keys in the
// subsystems map, corresponding to the names of the subsystems.
//
// NOTE: This is part of the LeveledSubLogger interface.
func (r *SubLoggerManager) SupportedSubsystems() []string {
r.mu.Lock()
defer r.mu.Unlock()
// Convert the subsystemLoggers map keys to a string slice.
subsystems := make([]string, 0, len(r.loggers))
for subsysID := range r.loggers {
subsystems = append(subsystems, subsysID)
}
// Sort the subsystems for stable display.
sort.Strings(subsystems)
return subsystems
}
// SetLogLevel sets the logging level for provided subsystem. Invalid
// subsystems are ignored. Uninitialized subsystems are dynamically created as
// needed.
//
// NOTE: This is part of the LeveledSubLogger interface.
func (r *SubLoggerManager) SetLogLevel(subsystemID string, logLevel string) {
r.mu.Lock()
defer r.mu.Unlock()
r.setLogLevelUnsafe(subsystemID, logLevel)
}
// setLogLevelUnsafe sets the logging level for provided subsystem. Invalid
// subsystems are ignored. Uninitialized subsystems are dynamically created as
// needed.
//
// NOTE: the SubLoggerManager mutex must be held before calling this method.
func (r *SubLoggerManager) setLogLevelUnsafe(subsystemID string,
logLevel string) {
// Ignore invalid subsystems.
logger, ok := r.loggers[subsystemID]
if !ok {
return
}
// Defaults to info if the log level is invalid.
level, _ := btclog.LevelFromString(logLevel)
logger.SetLevel(level)
}
// SetLogLevels sets the log level for all subsystem loggers to the passed
// level. It also dynamically creates the subsystem loggers as needed, so it
// can be used to initialize the logging system.
//
// NOTE: This is part of the LeveledSubLogger interface.
func (r *SubLoggerManager) SetLogLevels(logLevel string) {
r.mu.Lock()
defer r.mu.Unlock()
// Configure all sub-systems with the new logging level. Dynamically
// create loggers as needed.
for subsystemID := range r.loggers {
r.setLogLevelUnsafe(subsystemID, logLevel)
}
}
// SubLoggers is a type that holds a map of subsystem loggers keyed by their
// subsystem name.
type SubLoggers map[string]btclog.Logger
// LeveledSubLogger provides the ability to retrieve the subsystem loggers of
// a logger and set their log levels individually or all at once.
type LeveledSubLogger interface {
// SubLoggers returns the map of all registered subsystem loggers.
SubLoggers() SubLoggers
// SupportedSubsystems returns a slice of strings containing the names
// of the supported subsystems. Should ideally correspond to the keys
// of the subsystem logger map and be sorted.
SupportedSubsystems() []string
// SetLogLevel assigns an individual subsystem logger a new log level.
SetLogLevel(subsystemID string, logLevel string)
// SetLogLevels assigns all subsystem loggers the same new log level.
SetLogLevels(logLevel string)
}
// ParseAndSetDebugLevels attempts to parse the specified debug level and set
// the levels accordingly on the given logger. An appropriate error is returned
// if anything is invalid.
func ParseAndSetDebugLevels(level string, logger LeveledSubLogger) error {
// Split at the delimiter.
levels := strings.Split(level, ",")
if len(levels) == 0 {
return fmt.Errorf("invalid log level: %v", level)
}
// If the first entry has no =, treat is as the log level for all
// subsystems.
globalLevel := levels[0]
if !strings.Contains(globalLevel, "=") {
// Validate debug log level.
if !validLogLevel(globalLevel) {
str := "the specified debug level [%v] is invalid"
return fmt.Errorf(str, globalLevel)
}
// Change the logging level for all subsystems.
logger.SetLogLevels(globalLevel)
// The rest will target specific subsystems.
levels = levels[1:]
}
// Go through the subsystem/level pairs while detecting issues and
// update the log levels accordingly.
for _, logLevelPair := range levels {
if !strings.Contains(logLevelPair, "=") {
str := "the specified debug level contains an " +
"invalid subsystem/level pair [%v]"
return fmt.Errorf(str, logLevelPair)
}
// Extract the specified subsystem and log level.
fields := strings.Split(logLevelPair, "=")
if len(fields) != 2 {
str := "the specified debug level has an invalid " +
"format [%v] -- use format subsystem1=level1," +
"subsystem2=level2"
return fmt.Errorf(str, logLevelPair)
}
subsysID, logLevel := fields[0], fields[1]
subLoggers := logger.SubLoggers()
// Validate subsystem.
if _, exists := subLoggers[subsysID]; !exists {
str := "the specified subsystem [%v] is invalid -- " +
"supported subsystems are %v"
return fmt.Errorf(
str, subsysID, logger.SupportedSubsystems(),
)
}
// Validate log level.
if !validLogLevel(logLevel) {
str := "the specified debug level [%v] is invalid"
return fmt.Errorf(str, logLevel)
}
logger.SetLogLevel(subsysID, logLevel)
}
return nil
}
// validLogLevel returns whether or not logLevel is a valid debug log level.
func validLogLevel(logLevel string) bool {
switch logLevel {
case "trace":
fallthrough
case "debug":
fallthrough
case "info":
fallthrough
case "warn":
fallthrough
case "error":
fallthrough
case "critical":
fallthrough
case "off":
return true
}
return false
}

View File

@ -6,9 +6,13 @@
package build
import (
"context"
"encoding/hex"
"fmt"
"runtime/debug"
"strings"
"github.com/btcsuite/btclog/v2"
)
var (
@ -40,10 +44,10 @@ const (
AppMajor uint = 0
// AppMinor defines the minor version of this binary.
AppMinor uint = 18
AppMinor uint = 19
// AppPatch defines the application patch for this binary.
AppPatch uint = 00
AppPatch uint = 3
// AppPreRelease MUST only contain characters from semanticAlphabet per
// the semantic versioning spec.
@ -101,3 +105,22 @@ func Tags() []string {
return strings.Split(RawTags, ",")
}
// WithBuildInfo derives a child context with the build information attached as
// attributes. At the moment, this only includes the current build's commit
// hash.
func WithBuildInfo(ctx context.Context, cfg *LogConfig) (context.Context,
error) {
if cfg.NoCommitHash {
return ctx, nil
}
// Convert the commit hash to a byte slice.
commitHash, err := hex.DecodeString(CommitHash)
if err != nil {
return nil, fmt.Errorf("unable to decode commit hash: %w", err)
}
return btclog.WithCtx(ctx, btclog.Hex3("rev", commitHash)), nil
}

152
chainio/README.md Normal file
View File

@ -0,0 +1,152 @@
# Chainio
`chainio` is a package designed to provide blockchain data access to various
subsystems within `lnd`. When a new block is received, it is encapsulated in a
`Blockbeat` object and disseminated to all registered consumers. Consumers may
receive these updates either concurrently or sequentially, based on their
registration configuration, ensuring that each subsystem maintains a
synchronized view of the current block state.
The main components include:
- `Blockbeat`: An interface that provides information about the block.
- `Consumer`: An interface that specifies how subsystems handle the blockbeat.
- `BlockbeatDispatcher`: The core service responsible for receiving each block
and distributing it to all consumers.
Additionally, the `BeatConsumer` struct provides a partial implementation of
the `Consumer` interface. This struct helps reduce code duplication, allowing
subsystems to avoid re-implementing the `ProcessBlock` method and provides a
commonly used `NotifyBlockProcessed` method.
### Register a Consumer
Consumers within the same queue are notified **sequentially**, while all queues
are notified **concurrently**. A queue consists of a slice of consumers, which
are notified in left-to-right order. Developers are responsible for determining
dependencies in block consumption across subsystems: independent subsystems
should be notified concurrently, whereas dependent subsystems should be
notified sequentially.
To notify the consumers concurrently, put them in different queues,
```go
// consumer1 and consumer2 will be notified concurrently.
queue1 := []chainio.Consumer{consumer1}
blockbeatDispatcher.RegisterQueue(consumer1)
queue2 := []chainio.Consumer{consumer2}
blockbeatDispatcher.RegisterQueue(consumer2)
```
To notify the consumers sequentially, put them in the same queue,
```go
// consumers will be notified sequentially via,
// consumer1 -> consumer2 -> consumer3
queue := []chainio.Consumer{
consumer1,
consumer2,
consumer3,
}
blockbeatDispatcher.RegisterQueue(queue)
```
### Implement the `Consumer` Interface
Implementing the `Consumer` interface is straightforward. Below is an example
of how
[`sweep.TxPublisher`](https://github.com/lightningnetwork/lnd/blob/5cec466fad44c582a64cfaeb91f6d5fd302fcf85/sweep/fee_bumper.go#L310)
implements this interface.
To start, embed the partial implementation `chainio.BeatConsumer`, which
already provides the `ProcessBlock` implementation and commonly used
`NotifyBlockProcessed` method, and exposes `BlockbeatChan` for the consumer to
receive blockbeats.
```go
type TxPublisher struct {
started atomic.Bool
stopped atomic.Bool
chainio.BeatConsumer
...
```
We should also remember to initialize this `BeatConsumer`,
```go
...
// Mount the block consumer.
tp.BeatConsumer = chainio.NewBeatConsumer(tp.quit, tp.Name())
```
Finally, in the main event loop, read from `BlockbeatChan`, process the
received blockbeat, and, crucially, call `tp.NotifyBlockProcessed` to inform
the blockbeat dispatcher that processing is complete.
```go
for {
select {
case beat := <-tp.BlockbeatChan:
// Consume this blockbeat, usually it means updating the subsystem
// using the new block data.
// Notify we've processed the block.
tp.NotifyBlockProcessed(beat, nil)
...
```
### Existing Queues
Currently, we have a single queue of consumers dedicated to handling force
closures. This queue includes `ChainArbitrator`, `UtxoSweeper`, and
`TxPublisher`, with `ChainArbitrator` managing two internal consumers:
`chainWatcher` and `ChannelArbitrator`. The blockbeat flows sequentially
through the chain as follows: `ChainArbitrator => chainWatcher =>
ChannelArbitrator => UtxoSweeper => TxPublisher`. The following diagram
illustrates the flow within the public subsystems.
```mermaid
sequenceDiagram
autonumber
participant bb as BlockBeat
participant cc as ChainArb
participant us as UtxoSweeper
participant tp as TxPublisher
note left of bb: 0. received block x,<br>dispatching...
note over bb,cc: 1. send block x to ChainArb,<br>wait for its done signal
bb->>cc: block x
rect rgba(165, 0, 85, 0.8)
critical signal processed
cc->>bb: processed block
option Process error or timeout
bb->>bb: error and exit
end
end
note over bb,us: 2. send block x to UtxoSweeper, wait for its done signal
bb->>us: block x
rect rgba(165, 0, 85, 0.8)
critical signal processed
us->>bb: processed block
option Process error or timeout
bb->>bb: error and exit
end
end
note over bb,tp: 3. send block x to TxPublisher, wait for its done signal
bb->>tp: block x
rect rgba(165, 0, 85, 0.8)
critical signal processed
tp->>bb: processed block
option Process error or timeout
bb->>bb: error and exit
end
end
```

54
chainio/blockbeat.go Normal file
View File

@ -0,0 +1,54 @@
package chainio
import (
"fmt"
"github.com/btcsuite/btclog/v2"
"github.com/lightningnetwork/lnd/chainntnfs"
)
// Beat implements the Blockbeat interface. It contains the block epoch and a
// customized logger.
//
// TODO(yy): extend this to check for confirmation status - which serves as the
// single source of truth, to avoid the potential race between receiving blocks
// and `GetTransactionDetails/RegisterSpendNtfn/RegisterConfirmationsNtfn`.
type Beat struct {
// epoch is the current block epoch the blockbeat is aware of.
epoch chainntnfs.BlockEpoch
// log is the customized logger for the blockbeat which prints the
// block height.
log btclog.Logger
}
// Compile-time check to ensure Beat satisfies the Blockbeat interface.
var _ Blockbeat = (*Beat)(nil)
// NewBeat creates a new beat with the specified block epoch and a customized
// logger.
func NewBeat(epoch chainntnfs.BlockEpoch) *Beat {
b := &Beat{
epoch: epoch,
}
// Create a customized logger for the blockbeat.
logPrefix := fmt.Sprintf("Height[%6d]:", b.Height())
b.log = clog.WithPrefix(logPrefix)
return b
}
// Height returns the height of the block epoch.
//
// NOTE: Part of the Blockbeat interface.
func (b *Beat) Height() int32 {
return b.epoch.Height
}
// logger returns the logger for the blockbeat.
//
// NOTE: Part of the private blockbeat interface.
func (b *Beat) logger() btclog.Logger {
return b.log
}

28
chainio/blockbeat_test.go Normal file
View File

@ -0,0 +1,28 @@
package chainio
import (
"errors"
"testing"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/stretchr/testify/require"
)
var errDummy = errors.New("dummy error")
// TestNewBeat tests the NewBeat and Height functions.
func TestNewBeat(t *testing.T) {
t.Parallel()
// Create a testing epoch.
epoch := chainntnfs.BlockEpoch{
Height: 1,
}
// Create the beat and check the internal state.
beat := NewBeat(epoch)
require.Equal(t, epoch, beat.epoch)
// Check the height function.
require.Equal(t, epoch.Height, beat.Height())
}

113
chainio/consumer.go Normal file
View File

@ -0,0 +1,113 @@
package chainio
// BeatConsumer defines a supplementary component that should be used by
// subsystems which implement the `Consumer` interface. It partially implements
// the `Consumer` interface by providing the method `ProcessBlock` such that
// subsystems don't need to re-implement it.
//
// While inheritance is not commonly used in Go, subsystems embedding this
// struct cannot pass the interface check for `Consumer` because the `Name`
// method is not implemented, which gives us a "mortise and tenon" structure.
// In addition to reducing code duplication, this design allows `ProcessBlock`
// to work on the concrete type `Beat` to access its internal states.
type BeatConsumer struct {
// BlockbeatChan is a channel to receive blocks from Blockbeat. The
// received block contains the best known height and the txns confirmed
// in this block.
BlockbeatChan chan Blockbeat
// name is the name of the consumer which embeds the BlockConsumer.
name string
// quit is a channel that closes when the BlockConsumer is shutting
// down.
//
// NOTE: this quit channel should be mounted to the same quit channel
// used by the subsystem.
quit chan struct{}
// errChan is a buffered chan that receives an error returned from
// processing this block.
errChan chan error
}
// NewBeatConsumer creates a new BlockConsumer.
func NewBeatConsumer(quit chan struct{}, name string) BeatConsumer {
// Refuse to start `lnd` if the quit channel is not initialized. We
// treat this case as if we are facing a nil pointer dereference, as
// there's no point to return an error here, which will cause the node
// to fail to be started anyway.
if quit == nil {
panic("quit channel is nil")
}
b := BeatConsumer{
BlockbeatChan: make(chan Blockbeat),
name: name,
errChan: make(chan error, 1),
quit: quit,
}
return b
}
// ProcessBlock takes a blockbeat and sends it to the consumer's blockbeat
// channel. It will send it to the subsystem's BlockbeatChan, and block until
// the processed result is received from the subsystem. The subsystem must call
// `NotifyBlockProcessed` after it has finished processing the block.
//
// NOTE: part of the `chainio.Consumer` interface.
func (b *BeatConsumer) ProcessBlock(beat Blockbeat) error {
// Update the current height.
beat.logger().Tracef("set current height for [%s]", b.name)
select {
// Send the beat to the blockbeat channel. It's expected that the
// consumer will read from this channel and process the block. Once
// processed, it should return the error or nil to the beat.Err chan.
case b.BlockbeatChan <- beat:
beat.logger().Tracef("Sent blockbeat to [%s]", b.name)
case <-b.quit:
beat.logger().Debugf("[%s] received shutdown before sending "+
"beat", b.name)
return nil
}
// Check the consumer's err chan. We expect the consumer to call
// `beat.NotifyBlockProcessed` to send the error back here.
select {
case err := <-b.errChan:
beat.logger().Tracef("[%s] processed beat: err=%v", b.name, err)
return err
case <-b.quit:
beat.logger().Debugf("[%s] received shutdown", b.name)
}
return nil
}
// NotifyBlockProcessed signals that the block has been processed. It takes the
// blockbeat being processed and an error resulted from processing it. This
// error is then sent back to the consumer's err chan to unblock
// `ProcessBlock`.
//
// NOTE: This method must be called by the subsystem after it has finished
// processing the block.
func (b *BeatConsumer) NotifyBlockProcessed(beat Blockbeat, err error) {
// Update the current height.
beat.logger().Tracef("[%s]: notifying beat processed", b.name)
select {
case b.errChan <- err:
beat.logger().Tracef("[%s]: notified beat processed, err=%v",
b.name, err)
case <-b.quit:
beat.logger().Debugf("[%s] received shutdown before notifying "+
"beat processed", b.name)
}
}

202
chainio/consumer_test.go Normal file
View File

@ -0,0 +1,202 @@
package chainio
import (
"testing"
"time"
"github.com/lightningnetwork/lnd/fn/v2"
"github.com/stretchr/testify/require"
)
// TestNewBeatConsumer tests the NewBeatConsumer function.
func TestNewBeatConsumer(t *testing.T) {
t.Parallel()
quitChan := make(chan struct{})
name := "test"
// Test the NewBeatConsumer function.
b := NewBeatConsumer(quitChan, name)
// Assert the state.
require.Equal(t, quitChan, b.quit)
require.Equal(t, name, b.name)
require.NotNil(t, b.BlockbeatChan)
}
// TestProcessBlockSuccess tests when the block is processed successfully, no
// error is returned.
func TestProcessBlockSuccess(t *testing.T) {
t.Parallel()
// Create a test consumer.
quitChan := make(chan struct{})
b := NewBeatConsumer(quitChan, "test")
// Create a mock beat.
mockBeat := &MockBlockbeat{}
defer mockBeat.AssertExpectations(t)
mockBeat.On("logger").Return(clog)
// Mock the consumer's err chan.
consumerErrChan := make(chan error, 1)
b.errChan = consumerErrChan
// Call the method under test.
resultChan := make(chan error, 1)
go func() {
resultChan <- b.ProcessBlock(mockBeat)
}()
// Assert the beat is sent to the blockbeat channel.
beat, err := fn.RecvOrTimeout(b.BlockbeatChan, time.Second)
require.NoError(t, err)
require.Equal(t, mockBeat, beat)
// Send nil to the consumer's error channel.
consumerErrChan <- nil
// Assert the result of ProcessBlock is nil.
result, err := fn.RecvOrTimeout(resultChan, time.Second)
require.NoError(t, err)
require.Nil(t, result)
}
// TestProcessBlockConsumerQuitBeforeSend tests when the consumer is quit
// before sending the beat, the method returns immediately.
func TestProcessBlockConsumerQuitBeforeSend(t *testing.T) {
t.Parallel()
// Create a test consumer.
quitChan := make(chan struct{})
b := NewBeatConsumer(quitChan, "test")
// Create a mock beat.
mockBeat := &MockBlockbeat{}
defer mockBeat.AssertExpectations(t)
mockBeat.On("logger").Return(clog)
// Call the method under test.
resultChan := make(chan error, 1)
go func() {
resultChan <- b.ProcessBlock(mockBeat)
}()
// Instead of reading the BlockbeatChan, close the quit channel.
close(quitChan)
// Assert ProcessBlock returned nil.
result, err := fn.RecvOrTimeout(resultChan, time.Second)
require.NoError(t, err)
require.Nil(t, result)
}
// TestProcessBlockConsumerQuitAfterSend tests when the consumer is quit after
// sending the beat, the method returns immediately.
func TestProcessBlockConsumerQuitAfterSend(t *testing.T) {
t.Parallel()
// Create a test consumer.
quitChan := make(chan struct{})
b := NewBeatConsumer(quitChan, "test")
// Create a mock beat.
mockBeat := &MockBlockbeat{}
defer mockBeat.AssertExpectations(t)
mockBeat.On("logger").Return(clog)
// Mock the consumer's err chan.
consumerErrChan := make(chan error, 1)
b.errChan = consumerErrChan
// Call the method under test.
resultChan := make(chan error, 1)
go func() {
resultChan <- b.ProcessBlock(mockBeat)
}()
// Assert the beat is sent to the blockbeat channel.
beat, err := fn.RecvOrTimeout(b.BlockbeatChan, time.Second)
require.NoError(t, err)
require.Equal(t, mockBeat, beat)
// Instead of sending nil to the consumer's error channel, close the
// quit channel.
close(quitChan)
// Assert ProcessBlock returned nil.
result, err := fn.RecvOrTimeout(resultChan, time.Second)
require.NoError(t, err)
require.Nil(t, result)
}
// TestNotifyBlockProcessedSendErr asserts the error can be sent and read by
// the beat via NotifyBlockProcessed.
func TestNotifyBlockProcessedSendErr(t *testing.T) {
t.Parallel()
// Create a test consumer.
quitChan := make(chan struct{})
b := NewBeatConsumer(quitChan, "test")
// Create a mock beat.
mockBeat := &MockBlockbeat{}
defer mockBeat.AssertExpectations(t)
mockBeat.On("logger").Return(clog)
// Mock the consumer's err chan.
consumerErrChan := make(chan error, 1)
b.errChan = consumerErrChan
// Call the method under test.
done := make(chan error)
go func() {
defer close(done)
b.NotifyBlockProcessed(mockBeat, errDummy)
}()
// Assert the error is sent to the beat's err chan.
result, err := fn.RecvOrTimeout(consumerErrChan, time.Second)
require.NoError(t, err)
require.ErrorIs(t, result, errDummy)
// Assert the done channel is closed.
result, err = fn.RecvOrTimeout(done, time.Second)
require.NoError(t, err)
require.Nil(t, result)
}
// TestNotifyBlockProcessedOnQuit asserts NotifyBlockProcessed exits
// immediately when the quit channel is closed.
func TestNotifyBlockProcessedOnQuit(t *testing.T) {
t.Parallel()
// Create a test consumer.
quitChan := make(chan struct{})
b := NewBeatConsumer(quitChan, "test")
// Create a mock beat.
mockBeat := &MockBlockbeat{}
defer mockBeat.AssertExpectations(t)
mockBeat.On("logger").Return(clog)
// Mock the consumer's err chan - we don't buffer it so it will block
// on sending the error.
consumerErrChan := make(chan error)
b.errChan = consumerErrChan
// Call the method under test.
done := make(chan error)
go func() {
defer close(done)
b.NotifyBlockProcessed(mockBeat, errDummy)
}()
// Close the quit channel so the method will return.
close(b.quit)
// Assert the done channel is closed.
result, err := fn.RecvOrTimeout(done, time.Second)
require.NoError(t, err)
require.Nil(t, result)
}

366
chainio/dispatcher.go Normal file
View File

@ -0,0 +1,366 @@
package chainio
import (
"errors"
"fmt"
"sync"
"sync/atomic"
"time"
"github.com/btcsuite/btclog/v2"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/lnutils"
"golang.org/x/sync/errgroup"
)
// DefaultProcessBlockTimeout is the timeout value used when waiting for one
// consumer to finish processing the new block epoch.
var DefaultProcessBlockTimeout = 60 * time.Second
// ErrProcessBlockTimeout is the error returned when a consumer takes too long
// to process the block.
var ErrProcessBlockTimeout = errors.New("process block timeout")
// BlockbeatDispatcher is a service that handles dispatching new blocks to
// `lnd`'s subsystems. During startup, subsystems that are block-driven should
// implement the `Consumer` interface and register themselves via
// `RegisterQueue`. When two subsystems are independent of each other, they
// should be registered in different queues so blocks are notified concurrently.
// Otherwise, when living in the same queue, the subsystems are notified of the
// new blocks sequentially, which means it's critical to understand the
// relationship of these systems to properly handle the order.
type BlockbeatDispatcher struct {
wg sync.WaitGroup
// notifier is used to receive new block epochs.
notifier chainntnfs.ChainNotifier
// beat is the latest blockbeat received.
beat Blockbeat
// consumerQueues is a map of consumers that will receive blocks. Its
// key is a unique counter and its value is a queue of consumers. Each
// queue is notified concurrently, and consumers in the same queue is
// notified sequentially.
consumerQueues map[uint32][]Consumer
// counter is used to assign a unique id to each queue.
counter atomic.Uint32
// quit is used to signal the BlockbeatDispatcher to stop.
quit chan struct{}
// queryHeightChan is used to receive queries on the current height of
// the dispatcher.
queryHeightChan chan *query
}
// query is used to fetch the internal state of the dispatcher.
type query struct {
// respChan is used to send back the current height back to the caller.
//
// NOTE: This channel must be buffered.
respChan chan int32
}
// newQuery creates a query to be used to fetch the internal state of the
// dispatcher.
func newQuery() *query {
return &query{
respChan: make(chan int32, 1),
}
}
// NewBlockbeatDispatcher returns a new blockbeat dispatcher instance.
func NewBlockbeatDispatcher(n chainntnfs.ChainNotifier) *BlockbeatDispatcher {
return &BlockbeatDispatcher{
notifier: n,
quit: make(chan struct{}),
consumerQueues: make(map[uint32][]Consumer),
queryHeightChan: make(chan *query, 1),
}
}
// RegisterQueue takes a list of consumers and registers them in the same
// queue.
//
// NOTE: these consumers are notified sequentially.
func (b *BlockbeatDispatcher) RegisterQueue(consumers []Consumer) {
qid := b.counter.Add(1)
b.consumerQueues[qid] = append(b.consumerQueues[qid], consumers...)
clog.Infof("Registered queue=%d with %d blockbeat consumers", qid,
len(consumers))
for _, c := range consumers {
clog.Debugf("Consumer [%s] registered in queue %d", c.Name(),
qid)
}
}
// Start starts the blockbeat dispatcher - it registers a block notification
// and monitors and dispatches new blocks in a goroutine. It will refuse to
// start if there are no registered consumers.
func (b *BlockbeatDispatcher) Start() error {
// Make sure consumers are registered.
if len(b.consumerQueues) == 0 {
return fmt.Errorf("no consumers registered")
}
// Start listening to new block epochs. We should get a notification
// with the current best block immediately.
blockEpochs, err := b.notifier.RegisterBlockEpochNtfn(nil)
if err != nil {
return fmt.Errorf("register block epoch ntfn: %w", err)
}
clog.Infof("BlockbeatDispatcher is starting with %d consumer queues",
len(b.consumerQueues))
defer clog.Debug("BlockbeatDispatcher started")
b.wg.Add(1)
go b.dispatchBlocks(blockEpochs)
return nil
}
// Stop shuts down the blockbeat dispatcher.
func (b *BlockbeatDispatcher) Stop() {
clog.Info("BlockbeatDispatcher is stopping")
defer clog.Debug("BlockbeatDispatcher stopped")
// Signal the dispatchBlocks goroutine to stop.
close(b.quit)
b.wg.Wait()
}
func (b *BlockbeatDispatcher) log() btclog.Logger {
// There's no guarantee that the `b.beat` is initialized when the
// dispatcher shuts down, especially in the case where the node is
// running as a remote signer, which doesn't have a chainbackend. In
// that case we will use the package logger.
if b.beat == nil {
return clog
}
return b.beat.logger()
}
// dispatchBlocks listens to new block epoch and dispatches it to all the
// consumers. Each queue is notified concurrently, and the consumers in the
// same queue are notified sequentially.
//
// NOTE: Must be run as a goroutine.
func (b *BlockbeatDispatcher) dispatchBlocks(
blockEpochs *chainntnfs.BlockEpochEvent) {
defer b.wg.Done()
defer blockEpochs.Cancel()
for {
select {
case blockEpoch, ok := <-blockEpochs.Epochs:
if !ok {
clog.Debugf("Block epoch channel closed")
return
}
// Log a separator so it's easier to identify when a
// new block arrives for subsystems.
clog.Debugf("%v", lnutils.NewSeparatorClosure())
clog.Debugf("Received new block %v at height %d, "+
"notifying consumers...", blockEpoch.Hash,
blockEpoch.Height)
// Record the time it takes the consumer to process
// this block.
start := time.Now()
// Update the current block epoch.
b.beat = NewBeat(*blockEpoch)
// Notify all consumers.
err := b.notifyQueues()
if err != nil {
b.log().Errorf("Notify block failed: %v", err)
}
b.log().Debugf("Notified all consumers on new block "+
"in %v", time.Since(start))
// A query has been made to fetch the current height, we now
// send the height from its current beat.
case query := <-b.queryHeightChan:
// The beat may not be set yet, e.g., during the startup
// the query is made before the block epoch being sent.
height := int32(0)
if b.beat != nil {
height = b.beat.Height()
}
query.respChan <- height
case <-b.quit:
b.log().Debugf("BlockbeatDispatcher quit signal " +
"received")
return
}
}
}
// CurrentHeight returns the current best height known to the dispatcher. 0 is
// returned if the dispatcher is shutting down.
func (b *BlockbeatDispatcher) CurrentHeight() int32 {
query := newQuery()
select {
case b.queryHeightChan <- query:
case <-b.quit:
clog.Debugf("BlockbeatDispatcher quit before query")
return 0
}
select {
case height := <-query.respChan:
clog.Debugf("Responded current height: %v", height)
return height
case <-b.quit:
clog.Debugf("BlockbeatDispatcher quit before response")
return 0
}
}
// notifyQueues notifies each queue concurrently about the latest block epoch.
func (b *BlockbeatDispatcher) notifyQueues() error {
// errChans is a map of channels that will be used to receive errors
// returned from notifying the consumers.
errChans := make(map[uint32]chan error, len(b.consumerQueues))
// Notify each queue in goroutines.
for qid, consumers := range b.consumerQueues {
b.log().Debugf("Notifying queue=%d with %d consumers", qid,
len(consumers))
// Create a signal chan.
errChan := make(chan error, 1)
errChans[qid] = errChan
// Notify each queue concurrently.
go func(qid uint32, c []Consumer, beat Blockbeat) {
// Notify each consumer in this queue sequentially.
errChan <- DispatchSequential(beat, c)
}(qid, consumers, b.beat)
}
// Wait for all consumers in each queue to finish.
for qid, errChan := range errChans {
select {
case err := <-errChan:
if err != nil {
return fmt.Errorf("queue=%d got err: %w", qid,
err)
}
b.log().Debugf("Notified queue=%d", qid)
case <-b.quit:
b.log().Debugf("BlockbeatDispatcher quit signal " +
"received, exit notifyQueues")
return nil
}
}
return nil
}
// DispatchSequential takes a list of consumers and notify them about the new
// epoch sequentially. It requires the consumer to finish processing the block
// within the specified time, otherwise a timeout error is returned.
func DispatchSequential(b Blockbeat, consumers []Consumer) error {
for _, c := range consumers {
// Send the beat to the consumer.
err := notifyAndWait(b, c, DefaultProcessBlockTimeout)
if err != nil {
b.logger().Errorf("Failed to process block: %v", err)
return err
}
}
return nil
}
// DispatchConcurrent notifies each consumer concurrently about the blockbeat.
// It requires the consumer to finish processing the block within the specified
// time, otherwise a timeout error is returned.
func DispatchConcurrent(b Blockbeat, consumers []Consumer) error {
eg := &errgroup.Group{}
// Notify each queue in goroutines.
for _, c := range consumers {
// Notify each consumer concurrently.
eg.Go(func() error {
// Send the beat to the consumer.
err := notifyAndWait(b, c, DefaultProcessBlockTimeout)
// Exit early if there's no error.
if err == nil {
return nil
}
b.logger().Errorf("Consumer=%v failed to process "+
"block: %v", c.Name(), err)
return err
})
}
// Wait for all consumers in each queue to finish.
if err := eg.Wait(); err != nil {
return err
}
return nil
}
// notifyAndWait sends the blockbeat to the specified consumer. It requires the
// consumer to finish processing the block within the specified time, otherwise
// a timeout error is returned.
func notifyAndWait(b Blockbeat, c Consumer, timeout time.Duration) error {
b.logger().Debugf("Waiting for consumer[%s] to process it", c.Name())
// Record the time it takes the consumer to process this block.
start := time.Now()
errChan := make(chan error, 1)
go func() {
errChan <- c.ProcessBlock(b)
}()
// We expect the consumer to finish processing this block under 30s,
// otherwise a timeout error is returned.
select {
case err := <-errChan:
if err == nil {
break
}
return fmt.Errorf("%s got err in ProcessBlock: %w", c.Name(),
err)
case <-time.After(timeout):
return fmt.Errorf("consumer %s: %w", c.Name(),
ErrProcessBlockTimeout)
}
b.logger().Debugf("Consumer[%s] processed block in %v", c.Name(),
time.Since(start))
return nil
}

440
chainio/dispatcher_test.go Normal file
View File

@ -0,0 +1,440 @@
package chainio
import (
"testing"
"time"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/fn/v2"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
// TestNotifyAndWaitOnConsumerErr asserts when the consumer returns an error,
// it's returned by notifyAndWait.
func TestNotifyAndWaitOnConsumerErr(t *testing.T) {
t.Parallel()
// Create a mock consumer.
consumer := &MockConsumer{}
defer consumer.AssertExpectations(t)
consumer.On("Name").Return("mocker")
// Create a mock beat.
mockBeat := &MockBlockbeat{}
defer mockBeat.AssertExpectations(t)
mockBeat.On("logger").Return(clog)
// Mock ProcessBlock to return an error.
consumer.On("ProcessBlock", mockBeat).Return(errDummy).Once()
// Call the method under test.
err := notifyAndWait(mockBeat, consumer, DefaultProcessBlockTimeout)
// We expect the error to be returned.
require.ErrorIs(t, err, errDummy)
}
// TestNotifyAndWaitOnConsumerErr asserts when the consumer successfully
// processed the beat, no error is returned.
func TestNotifyAndWaitOnConsumerSuccess(t *testing.T) {
t.Parallel()
// Create a mock consumer.
consumer := &MockConsumer{}
defer consumer.AssertExpectations(t)
consumer.On("Name").Return("mocker")
// Create a mock beat.
mockBeat := &MockBlockbeat{}
defer mockBeat.AssertExpectations(t)
mockBeat.On("logger").Return(clog)
// Mock ProcessBlock to return nil.
consumer.On("ProcessBlock", mockBeat).Return(nil).Once()
// Call the method under test.
err := notifyAndWait(mockBeat, consumer, DefaultProcessBlockTimeout)
// We expect a nil error to be returned.
require.NoError(t, err)
}
// TestNotifyAndWaitOnConsumerTimeout asserts when the consumer times out
// processing the block, the timeout error is returned.
func TestNotifyAndWaitOnConsumerTimeout(t *testing.T) {
t.Parallel()
// Set timeout to be 10ms.
processBlockTimeout := 10 * time.Millisecond
// Create a mock consumer.
consumer := &MockConsumer{}
defer consumer.AssertExpectations(t)
consumer.On("Name").Return("mocker")
// Create a mock beat.
mockBeat := &MockBlockbeat{}
defer mockBeat.AssertExpectations(t)
mockBeat.On("logger").Return(clog)
// Mock ProcessBlock to return nil but blocks on returning.
consumer.On("ProcessBlock", mockBeat).Return(nil).Run(
func(args mock.Arguments) {
// Sleep one second to block on the method.
time.Sleep(processBlockTimeout * 100)
}).Once()
// Call the method under test.
err := notifyAndWait(mockBeat, consumer, processBlockTimeout)
// We expect a timeout error to be returned.
require.ErrorIs(t, err, ErrProcessBlockTimeout)
}
// TestDispatchSequential checks that the beat is sent to the consumers
// sequentially.
func TestDispatchSequential(t *testing.T) {
t.Parallel()
// Create three mock consumers.
consumer1 := &MockConsumer{}
defer consumer1.AssertExpectations(t)
consumer1.On("Name").Return("mocker1")
consumer2 := &MockConsumer{}
defer consumer2.AssertExpectations(t)
consumer2.On("Name").Return("mocker2")
consumer3 := &MockConsumer{}
defer consumer3.AssertExpectations(t)
consumer3.On("Name").Return("mocker3")
consumers := []Consumer{consumer1, consumer2, consumer3}
// Create a mock beat.
mockBeat := &MockBlockbeat{}
defer mockBeat.AssertExpectations(t)
mockBeat.On("logger").Return(clog)
// prevConsumer specifies the previous consumer that was called.
var prevConsumer string
// Mock the ProcessBlock on consumers to return immediately.
consumer1.On("ProcessBlock", mockBeat).Return(nil).Run(
func(args mock.Arguments) {
// Check the order of the consumers.
//
// The first consumer should have no previous consumer.
require.Empty(t, prevConsumer)
// Set the consumer as the previous consumer.
prevConsumer = consumer1.Name()
}).Once()
consumer2.On("ProcessBlock", mockBeat).Return(nil).Run(
func(args mock.Arguments) {
// Check the order of the consumers.
//
// The second consumer should see consumer1.
require.Equal(t, consumer1.Name(), prevConsumer)
// Set the consumer as the previous consumer.
prevConsumer = consumer2.Name()
}).Once()
consumer3.On("ProcessBlock", mockBeat).Return(nil).Run(
func(args mock.Arguments) {
// Check the order of the consumers.
//
// The third consumer should see consumer2.
require.Equal(t, consumer2.Name(), prevConsumer)
// Set the consumer as the previous consumer.
prevConsumer = consumer3.Name()
}).Once()
// Call the method under test.
err := DispatchSequential(mockBeat, consumers)
require.NoError(t, err)
// Check the previous consumer is the last consumer.
require.Equal(t, consumer3.Name(), prevConsumer)
}
// TestRegisterQueue tests the RegisterQueue function.
func TestRegisterQueue(t *testing.T) {
t.Parallel()
// Create two mock consumers.
consumer1 := &MockConsumer{}
defer consumer1.AssertExpectations(t)
consumer1.On("Name").Return("mocker1")
consumer2 := &MockConsumer{}
defer consumer2.AssertExpectations(t)
consumer2.On("Name").Return("mocker2")
consumers := []Consumer{consumer1, consumer2}
// Create a mock chain notifier.
mockNotifier := &chainntnfs.MockChainNotifier{}
defer mockNotifier.AssertExpectations(t)
// Create a new dispatcher.
b := NewBlockbeatDispatcher(mockNotifier)
// Register the consumers.
b.RegisterQueue(consumers)
// Assert that the consumers have been registered.
//
// We should have one queue.
require.Len(t, b.consumerQueues, 1)
// The queue should have two consumers.
queue, ok := b.consumerQueues[1]
require.True(t, ok)
require.Len(t, queue, 2)
}
// TestStartDispatcher tests the Start method.
func TestStartDispatcher(t *testing.T) {
t.Parallel()
// Create a mock chain notifier.
mockNotifier := &chainntnfs.MockChainNotifier{}
defer mockNotifier.AssertExpectations(t)
// Create a new dispatcher.
b := NewBlockbeatDispatcher(mockNotifier)
// Start the dispatcher without consumers should return an error.
err := b.Start()
require.Error(t, err)
// Create a consumer and register it.
consumer := &MockConsumer{}
defer consumer.AssertExpectations(t)
consumer.On("Name").Return("mocker1")
b.RegisterQueue([]Consumer{consumer})
// Mock the chain notifier to return an error.
mockNotifier.On("RegisterBlockEpochNtfn",
mock.Anything).Return(nil, errDummy).Once()
// Start the dispatcher now should return the error.
err = b.Start()
require.ErrorIs(t, err, errDummy)
// Mock the chain notifier to return a valid notifier.
blockEpochs := &chainntnfs.BlockEpochEvent{}
mockNotifier.On("RegisterBlockEpochNtfn",
mock.Anything).Return(blockEpochs, nil).Once()
// Start the dispatcher now should not return an error.
err = b.Start()
require.NoError(t, err)
}
// TestDispatchBlocks asserts the blocks are properly dispatched to the queues.
func TestDispatchBlocks(t *testing.T) {
t.Parallel()
// Create a mock chain notifier.
mockNotifier := &chainntnfs.MockChainNotifier{}
defer mockNotifier.AssertExpectations(t)
// Create a new dispatcher.
b := NewBlockbeatDispatcher(mockNotifier)
// Create the beat and attach it to the dispatcher.
epoch := chainntnfs.BlockEpoch{Height: 1}
beat := NewBeat(epoch)
b.beat = beat
// Create a consumer and register it.
consumer := &MockConsumer{}
defer consumer.AssertExpectations(t)
consumer.On("Name").Return("mocker1")
b.RegisterQueue([]Consumer{consumer})
// Mock the consumer to return nil error on ProcessBlock. This
// implicitly asserts that the step `notifyQueues` is successfully
// reached in the `dispatchBlocks` method.
consumer.On("ProcessBlock", mock.Anything).Return(nil).Once()
// Create a test epoch chan.
epochChan := make(chan *chainntnfs.BlockEpoch, 1)
blockEpochs := &chainntnfs.BlockEpochEvent{
Epochs: epochChan,
Cancel: func() {},
}
// Call the method in a goroutine.
done := make(chan struct{})
b.wg.Add(1)
go func() {
defer close(done)
b.dispatchBlocks(blockEpochs)
}()
// Send an epoch.
epoch = chainntnfs.BlockEpoch{Height: 2}
epochChan <- &epoch
// Wait for the dispatcher to process the epoch.
time.Sleep(100 * time.Millisecond)
// Stop the dispatcher.
b.Stop()
// We expect the dispatcher to stop immediately.
_, err := fn.RecvOrTimeout(done, time.Second)
require.NoError(t, err)
}
// TestNotifyQueuesSuccess checks when the dispatcher successfully notifies all
// the queues, no error is returned.
func TestNotifyQueuesSuccess(t *testing.T) {
t.Parallel()
// Create two mock consumers.
consumer1 := &MockConsumer{}
defer consumer1.AssertExpectations(t)
consumer1.On("Name").Return("mocker1")
consumer2 := &MockConsumer{}
defer consumer2.AssertExpectations(t)
consumer2.On("Name").Return("mocker2")
// Create two queues.
queue1 := []Consumer{consumer1}
queue2 := []Consumer{consumer2}
// Create a mock chain notifier.
mockNotifier := &chainntnfs.MockChainNotifier{}
defer mockNotifier.AssertExpectations(t)
// Create a mock beat.
mockBeat := &MockBlockbeat{}
defer mockBeat.AssertExpectations(t)
mockBeat.On("logger").Return(clog)
// Create a new dispatcher.
b := NewBlockbeatDispatcher(mockNotifier)
// Register the queues.
b.RegisterQueue(queue1)
b.RegisterQueue(queue2)
// Attach the blockbeat.
b.beat = mockBeat
// Mock the consumers to return nil error on ProcessBlock for
// both calls.
consumer1.On("ProcessBlock", mockBeat).Return(nil).Once()
consumer2.On("ProcessBlock", mockBeat).Return(nil).Once()
// Notify the queues. The mockers will be asserted in the end to
// validate the calls.
err := b.notifyQueues()
require.NoError(t, err)
}
// TestNotifyQueuesError checks when one of the queue returns an error, this
// error is returned by the method.
func TestNotifyQueuesError(t *testing.T) {
t.Parallel()
// Create a mock consumer.
consumer := &MockConsumer{}
defer consumer.AssertExpectations(t)
consumer.On("Name").Return("mocker1")
// Create one queue.
queue := []Consumer{consumer}
// Create a mock chain notifier.
mockNotifier := &chainntnfs.MockChainNotifier{}
defer mockNotifier.AssertExpectations(t)
// Create a mock beat.
mockBeat := &MockBlockbeat{}
defer mockBeat.AssertExpectations(t)
mockBeat.On("logger").Return(clog)
// Create a new dispatcher.
b := NewBlockbeatDispatcher(mockNotifier)
// Register the queues.
b.RegisterQueue(queue)
// Attach the blockbeat.
b.beat = mockBeat
// Mock the consumer to return an error on ProcessBlock.
consumer.On("ProcessBlock", mockBeat).Return(errDummy).Once()
// Notify the queues. The mockers will be asserted in the end to
// validate the calls.
err := b.notifyQueues()
require.ErrorIs(t, err, errDummy)
}
// TestCurrentHeight asserts `CurrentHeight` returns the expected block height.
func TestCurrentHeight(t *testing.T) {
t.Parallel()
testHeight := int32(1000)
// Create a mock chain notifier.
mockNotifier := &chainntnfs.MockChainNotifier{}
defer mockNotifier.AssertExpectations(t)
// Create a mock beat.
mockBeat := &MockBlockbeat{}
defer mockBeat.AssertExpectations(t)
mockBeat.On("logger").Return(clog)
mockBeat.On("Height").Return(testHeight).Once()
// Create a mock consumer.
consumer := &MockConsumer{}
defer consumer.AssertExpectations(t)
consumer.On("Name").Return("mocker1")
// Create one queue.
queue := []Consumer{consumer}
// Create a new dispatcher.
b := NewBlockbeatDispatcher(mockNotifier)
// Register the queues.
b.RegisterQueue(queue)
// Attach the blockbeat.
b.beat = mockBeat
// Mock the chain notifier to return a valid notifier.
blockEpochs := &chainntnfs.BlockEpochEvent{
Cancel: func() {},
}
mockNotifier.On("RegisterBlockEpochNtfn",
mock.Anything).Return(blockEpochs, nil).Once()
// Start the dispatcher now should not return an error.
err := b.Start()
require.NoError(t, err)
// Make a query on the current height and assert it equals to
// testHeight.
height := b.CurrentHeight()
require.Equal(t, testHeight, height)
// Stop the dispatcher.
b.Stop()
// Make a query on the current height and assert it equals to 0.
height = b.CurrentHeight()
require.Zero(t, height)
}

53
chainio/interface.go Normal file
View File

@ -0,0 +1,53 @@
package chainio
import "github.com/btcsuite/btclog/v2"
// Blockbeat defines an interface that can be used by subsystems to retrieve
// block data. It is sent by the BlockbeatDispatcher to all the registered
// consumers whenever a new block is received. Once the consumer finishes
// processing the block, it must signal it by calling `NotifyBlockProcessed`.
//
// The blockchain is a state machine - whenever there's a state change, it's
// manifested in a block. The blockbeat is a way to notify subsystems of this
// state change, and to provide them with the data they need to process it. In
// other words, subsystems must react to this state change and should consider
// being driven by the blockbeat in their own state machines.
type Blockbeat interface {
// blockbeat is a private interface that's only used in this package.
blockbeat
// Height returns the current block height.
Height() int32
}
// blockbeat defines a set of private methods used in this package to make
// interaction with the blockbeat easier.
type blockbeat interface {
// logger returns the internal logger used by the blockbeat which has a
// block height prefix.
logger() btclog.Logger
}
// Consumer defines a blockbeat consumer interface. Subsystems that need block
// info must implement it.
type Consumer interface {
// TODO(yy): We should also define the start methods used by the
// consumers such that when implementing the interface, the consumer
// will always be started with a blockbeat. This cannot be enforced at
// the moment as we need refactor all the start methods to only take a
// beat.
//
// Start(beat Blockbeat) error
// Name returns a human-readable string for this subsystem.
Name() string
// ProcessBlock takes a blockbeat and processes it. It should not
// return until the subsystem has updated its state based on the block
// data.
//
// NOTE: The consumer must try its best to NOT return an error. If an
// error is returned from processing the block, it means the subsystem
// cannot react to onchain state changes and lnd will shutdown.
ProcessBlock(b Blockbeat) error
}

32
chainio/log.go Normal file
View File

@ -0,0 +1,32 @@
package chainio
import (
"github.com/btcsuite/btclog/v2"
"github.com/lightningnetwork/lnd/build"
)
// Subsystem defines the logging code for this subsystem.
const Subsystem = "CHIO"
// clog is a logger that is initialized with no output filters. This means the
// package will not perform any logging by default until the caller requests
// it.
var clog btclog.Logger
// The default amount of logging is none.
func init() {
UseLogger(build.NewSubLogger(Subsystem, nil))
}
// DisableLog disables all library log output. Logging output is disabled by
// default until UseLogger is called.
func DisableLog() {
UseLogger(btclog.Disabled)
}
// UseLogger uses a specified Logger to output package logging info. This
// should be used in preference to SetLogWriter if the caller is also using
// btclog.
func UseLogger(logger btclog.Logger) {
clog = logger
}

50
chainio/mocks.go Normal file
View File

@ -0,0 +1,50 @@
package chainio
import (
"github.com/btcsuite/btclog/v2"
"github.com/stretchr/testify/mock"
)
// MockConsumer is a mock implementation of the Consumer interface.
type MockConsumer struct {
mock.Mock
}
// Compile-time constraint to ensure MockConsumer implements Consumer.
var _ Consumer = (*MockConsumer)(nil)
// Name returns a human-readable string for this subsystem.
func (m *MockConsumer) Name() string {
args := m.Called()
return args.String(0)
}
// ProcessBlock takes a blockbeat and processes it. A receive-only error chan
// must be returned.
func (m *MockConsumer) ProcessBlock(b Blockbeat) error {
args := m.Called(b)
return args.Error(0)
}
// MockBlockbeat is a mock implementation of the Blockbeat interface.
type MockBlockbeat struct {
mock.Mock
}
// Compile-time constraint to ensure MockBlockbeat implements Blockbeat.
var _ Blockbeat = (*MockBlockbeat)(nil)
// Height returns the current block height.
func (m *MockBlockbeat) Height() int32 {
args := m.Called()
return args.Get(0).(int32)
}
// logger returns the logger for the blockbeat.
func (m *MockBlockbeat) logger() btclog.Logger {
args := m.Called()
return args.Get(0).(btclog.Logger)
}

View File

@ -15,7 +15,7 @@ import (
"github.com/btcsuite/btcwallet/chain"
"github.com/lightningnetwork/lnd/blockcache"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/fn/v2"
"github.com/lightningnetwork/lnd/queue"
)
@ -139,6 +139,7 @@ func (b *BitcoindNotifier) Stop() error {
// Shutdown the rpc client, this gracefully disconnects from bitcoind,
// and cleans up all related resources.
b.chainConn.Stop()
b.chainConn.WaitForShutdown()
close(b.quit)
b.wg.Wait()
@ -170,6 +171,8 @@ func (b *BitcoindNotifier) Started() bool {
}
func (b *BitcoindNotifier) startNotifier() error {
chainntnfs.Log.Infof("bitcoind notifier starting...")
// Connect to bitcoind, and register for notifications on connected,
// and disconnected blocks.
if err := b.chainConn.Start(); err != nil {
@ -206,6 +209,8 @@ func (b *BitcoindNotifier) startNotifier() error {
// startup.
atomic.StoreInt32(&b.active, 1)
chainntnfs.Log.Debugf("bitcoind notifier started")
return nil
}
@ -255,7 +260,7 @@ out:
// TODO(wilmer): add retry logic if rescan fails?
b.wg.Add(1)
//nolint:lll
//nolint:ll
go func(msg *chainntnfs.HistoricalConfDispatch) {
defer b.wg.Done()
@ -300,7 +305,7 @@ out:
// TODO(wilmer): add retry logic if rescan fails?
b.wg.Add(1)
//nolint:lll
//nolint:ll
go func(msg *chainntnfs.HistoricalSpendDispatch) {
defer b.wg.Done()
@ -490,7 +495,7 @@ out:
func (b *BitcoindNotifier) handleRelevantTx(tx *btcutil.Tx,
mempool bool, height uint32) {
// If this is a mempool spend, we'll ask the mempool notifier to hanlde
// If this is a mempool spend, we'll ask the mempool notifier to handle
// it.
if mempool {
err := b.memNotifier.ProcessRelevantSpendTx(tx)
@ -664,8 +669,14 @@ func (b *BitcoindNotifier) handleBlockConnected(block chainntnfs.BlockEpoch) err
// satisfy any client requests based upon the new block.
b.bestBlock = block
err = b.txNotifier.NotifyHeight(uint32(block.Height))
if err != nil {
return fmt.Errorf("unable to notify height: %w", err)
}
b.notifyBlockEpochs(block.Height, block.Hash, block.BlockHeader)
return b.txNotifier.NotifyHeight(uint32(block.Height))
return nil
}
// notifyBlockEpochs notifies all registered block epoch clients of the newly
@ -822,8 +833,16 @@ func (b *BitcoindNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
return nil, err
}
if uint32(blockHeight) > ntfn.HistoricalDispatch.StartHeight {
ntfn.HistoricalDispatch.StartHeight = uint32(blockHeight)
spentHeight := uint32(blockHeight)
chainntnfs.Log.Debugf("Outpoint(%v) has spent at height %v",
outpoint, spentHeight)
// Since the tx has already been spent at spentHeight, the
// heightHint specified by the caller is no longer relevant. We
// now update the starting height to be the spent height to make
// sure we won't miss it in the rescan.
if spentHeight != ntfn.HistoricalDispatch.StartHeight {
ntfn.HistoricalDispatch.StartHeight = spentHeight
}
}

View File

@ -11,6 +11,7 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/integration/rpctest"
"github.com/btcsuite/btcd/rpcclient"
"github.com/btcsuite/btcwallet/chain"
"github.com/lightningnetwork/lnd/blockcache"
"github.com/lightningnetwork/lnd/chainntnfs"
@ -37,11 +38,7 @@ var (
func initHintCache(t *testing.T) *channeldb.HeightHintCache {
t.Helper()
db, err := channeldb.Open(t.TempDir())
require.NoError(t, err, "unable to create db")
t.Cleanup(func() {
require.NoError(t, db.Close())
})
db := channeldb.OpenForTesting(t, t.TempDir())
testCfg := channeldb.CacheConfig{
QueryDisable: false,
@ -107,17 +104,69 @@ func syncNotifierWithMiner(t *testing.T, notifier *BitcoindNotifier,
"err=%v, minerHeight=%v, bitcoindHeight=%v",
err, minerHeight, bitcoindHeight)
}
// Get the num of connections the miner has. We expect it to
// have at least one connection with the chain backend.
count, err := miner.Client.GetConnectionCount()
require.NoError(t, err)
if count != 0 {
continue
}
// Reconnect the miner and the chain backend.
//
// NOTE: The connection should have been made before we perform
// the `syncNotifierWithMiner`. However, due to an unknown
// reason, the miner may refuse to process the inbound
// connection made by the bitcoind node, causing the connection
// to fail. It's possible there's a bug in the handshake between
// the two nodes.
//
// A normal flow is, bitcoind starts a v2 handshake flow, which
// btcd will fail and disconnect. Upon seeing this
// disconnection, bitcoind will try a v1 handshake and succeeds.
// The failed flow is, upon seeing the v2 handshake, btcd
// doesn't seem to perform the disconnect. Instead an EOF
// websocket error is found.
//
// TODO(yy): Fix the above bug in `btcd`. This can be reproduced
// using `make flakehunter-unit pkg=$pkg case=$case`, with,
// `case=TestHistoricalConfDetailsNoTxIndex/rpc_polling_enabled`
// `pkg=chainntnfs/bitcoindnotify`.
// Also need to modify the temp dir logic so we can save the
// debug logs.
// This bug is likely to be fixed when we implement the
// encrypted p2p conn, or when we properly fix the shutdown
// issues in all our RPC conns.
t.Log("Expected to the chain backend to have one conn with " +
"the miner, instead it's disconnected!")
// We now ask the miner to add the chain backend back.
host := fmt.Sprintf(
"127.0.0.1:%s", notifier.chainParams.DefaultPort,
)
// NOTE:AddNode must take a host that has the format
// `host:port`, otherwise the default port will be used. Check
// `normalizeAddress` in btcd for details.
err = miner.Client.AddNode(host, rpcclient.ANAdd)
require.NoError(t, err, "Failed to connect miner to the chain "+
"backend")
}
}
// TestHistoricalConfDetailsTxIndex ensures that we correctly retrieve
// historical confirmation details using the backend node's txindex.
func TestHistoricalConfDetailsTxIndex(t *testing.T) {
t.Run("rpc polling enabled", func(st *testing.T) {
success := t.Run("rpc polling enabled", func(st *testing.T) {
st.Parallel()
testHistoricalConfDetailsTxIndex(st, true)
})
if !success {
return
}
t.Run("rpc polling disabled", func(st *testing.T) {
st.Parallel()
testHistoricalConfDetailsTxIndex(st, false)
@ -130,7 +179,7 @@ func testHistoricalConfDetailsTxIndex(t *testing.T, rpcPolling bool) {
)
bitcoindConn := unittest.NewBitcoindBackend(
t, unittest.NetParams, miner.P2PAddress(), true, rpcPolling,
t, unittest.NetParams, miner, true, rpcPolling,
)
hintCache := initHintCache(t)
@ -140,8 +189,6 @@ func testHistoricalConfDetailsTxIndex(t *testing.T, rpcPolling bool) {
t, bitcoindConn, hintCache, hintCache, blockCache,
)
syncNotifierWithMiner(t, notifier, miner)
// A transaction unknown to the node should not be found within the
// txindex even if it is enabled, so we should not proceed with any
// fallback methods.
@ -211,11 +258,15 @@ func testHistoricalConfDetailsTxIndex(t *testing.T, rpcPolling bool) {
// historical confirmation details using the set of fallback methods when the
// backend node's txindex is disabled.
func TestHistoricalConfDetailsNoTxIndex(t *testing.T) {
t.Run("rpc polling enabled", func(st *testing.T) {
success := t.Run("rpc polling enabled", func(st *testing.T) {
st.Parallel()
testHistoricalConfDetailsNoTxIndex(st, true)
})
if !success {
return
}
t.Run("rpc polling disabled", func(st *testing.T) {
st.Parallel()
testHistoricalConfDetailsNoTxIndex(st, false)
@ -226,13 +277,15 @@ func testHistoricalConfDetailsNoTxIndex(t *testing.T, rpcpolling bool) {
miner := unittest.NewMiner(t, unittest.NetParams, nil, true, 25)
bitcoindConn := unittest.NewBitcoindBackend(
t, unittest.NetParams, miner.P2PAddress(), false, rpcpolling,
t, unittest.NetParams, miner, false, rpcpolling,
)
hintCache := initHintCache(t)
blockCache := blockcache.NewBlockCache(10000)
notifier := setUpNotifier(t, bitcoindConn, hintCache, hintCache, blockCache)
notifier := setUpNotifier(
t, bitcoindConn, hintCache, hintCache, blockCache,
)
// Since the node has its txindex disabled, we fall back to scanning the
// chain manually. A transaction unknown to the network should not be
@ -241,7 +294,11 @@ func testHistoricalConfDetailsNoTxIndex(t *testing.T, rpcpolling bool) {
copy(unknownHash[:], bytes.Repeat([]byte{0x10}, 32))
unknownConfReq, err := chainntnfs.NewConfRequest(&unknownHash, testScript)
require.NoError(t, err, "unable to create conf request")
broadcastHeight := syncNotifierWithMiner(t, notifier, miner)
// Get the current best height.
_, broadcastHeight, err := miner.Client.GetBestBlock()
require.NoError(t, err, "unable to retrieve miner's current height")
_, txStatus, err := notifier.historicalConfDetails(
unknownConfReq, uint32(broadcastHeight), uint32(broadcastHeight),
)

View File

@ -17,7 +17,7 @@ import (
"github.com/btcsuite/btcwallet/chain"
"github.com/lightningnetwork/lnd/blockcache"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/fn/v2"
"github.com/lightningnetwork/lnd/queue"
)
@ -129,11 +129,6 @@ func New(config *rpcclient.ConnConfig, chainParams *chaincfg.Params,
quit: make(chan struct{}),
}
// Disable connecting to btcd within the rpcclient.New method. We
// defer establishing the connection to our .Start() method.
config.DisableConnectOnNew = true
config.DisableAutoReconnect = false
ntfnCallbacks := &rpcclient.NotificationHandlers{
OnBlockConnected: notifier.onBlockConnected,
OnBlockDisconnected: notifier.onBlockDisconnected,
@ -185,7 +180,7 @@ func (b *BtcdNotifier) Stop() error {
// Shutdown the rpc client, this gracefully disconnects from btcd, and
// cleans up all related resources.
b.chainConn.Shutdown()
b.chainConn.Stop()
close(b.quit)
b.wg.Wait()
@ -209,7 +204,11 @@ func (b *BtcdNotifier) Stop() error {
return nil
}
// startNotifier is the main starting point for the BtcdNotifier. It connects
// to btcd and start the main dispatcher goroutine.
func (b *BtcdNotifier) startNotifier() error {
chainntnfs.Log.Infof("btcd notifier starting...")
// Start our concurrent queues before starting the chain connection, to
// ensure onBlockConnected and onRedeemingTx callbacks won't be
// blocked.
@ -224,6 +223,17 @@ func (b *BtcdNotifier) startNotifier() error {
return err
}
// Before we fetch the best block/block height we need to register the
// notifications for connected blocks, otherwise we might think we are
// at an earlier block height because during block notification
// registration we might have already mined some new blocks. Hence we
// will not get notified accordingly.
if err := b.chainConn.NotifyBlocks(); err != nil {
b.txUpdates.Stop()
b.chainUpdates.Stop()
return err
}
currentHash, currentHeight, err := b.chainConn.GetBestBlock()
if err != nil {
b.txUpdates.Stop()
@ -249,12 +259,6 @@ func (b *BtcdNotifier) startNotifier() error {
BlockHeader: &bestBlock.Header,
}
if err := b.chainConn.NotifyBlocks(); err != nil {
b.txUpdates.Stop()
b.chainUpdates.Stop()
return err
}
b.wg.Add(1)
go b.notificationDispatcher()
@ -262,6 +266,8 @@ func (b *BtcdNotifier) startNotifier() error {
// startup.
atomic.StoreInt32(&b.active, 1)
chainntnfs.Log.Debugf("btcd notifier started")
return nil
}
@ -371,7 +377,7 @@ out:
// TODO(wilmer): add retry logic if rescan fails?
b.wg.Add(1)
//nolint:lll
//nolint:ll
go func(msg *chainntnfs.HistoricalConfDispatch) {
defer b.wg.Done()
@ -544,7 +550,7 @@ out:
func (b *BtcdNotifier) handleRelevantTx(tx *btcutil.Tx,
mempool bool, height uint32) {
// If this is a mempool spend, we'll ask the mempool notifier to hanlde
// If this is a mempool spend, we'll ask the mempool notifier to handle
// it.
if mempool {
err := b.memNotifier.ProcessRelevantSpendTx(tx)
@ -730,11 +736,16 @@ func (b *BtcdNotifier) handleBlockConnected(epoch chainntnfs.BlockEpoch) error {
// satisfy any client requests based upon the new block.
b.bestBlock = epoch
err = b.txNotifier.NotifyHeight(uint32(epoch.Height))
if err != nil {
return fmt.Errorf("unable to notify height: %w", err)
}
b.notifyBlockEpochs(
epoch.Height, epoch.Hash, epoch.BlockHeader,
)
return b.txNotifier.NotifyHeight(uint32(epoch.Height))
return nil
}
// notifyBlockEpochs notifies all registered block epoch clients of the newly
@ -922,15 +933,25 @@ func (b *BtcdNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
"block %v: %v", blockHash, err)
}
if uint32(blockHeader.Height) > ntfn.HistoricalDispatch.StartHeight {
spentHeight := uint32(blockHeader.Height)
chainntnfs.Log.Debugf("Outpoint(%v) has spent at height %v",
outpoint, spentHeight)
// Since the tx has already been spent at spentHeight, the
// heightHint specified by the caller is no longer relevant. We
// now update the starting height to be the spent height to make
// sure we won't miss it in the rescan.
if spentHeight != ntfn.HistoricalDispatch.StartHeight {
startHash, err = b.chainConn.GetBlockHash(
int64(blockHeader.Height),
int64(spentHeight),
)
if err != nil {
return nil, fmt.Errorf("unable to get block "+
"hash for height %d: %v",
blockHeader.Height, err)
}
ntfn.HistoricalDispatch.StartHeight = spentHeight
}
}

View File

@ -33,11 +33,7 @@ var (
func initHintCache(t *testing.T) *channeldb.HeightHintCache {
t.Helper()
db, err := channeldb.Open(t.TempDir())
require.NoError(t, err, "unable to create db")
t.Cleanup(func() {
require.NoError(t, db.Close())
})
db := channeldb.OpenForTesting(t, t.TempDir())
testCfg := channeldb.CacheConfig{
QueryDisable: false,

View File

@ -13,7 +13,7 @@ import (
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/fn/v2"
)
var (
@ -70,28 +70,28 @@ func (t TxConfStatus) String() string {
}
}
// notifierOptions is a set of functional options that allow callers to further
// NotifierOptions is a set of functional options that allow callers to further
// modify the type of chain event notifications they receive.
type notifierOptions struct {
// includeBlock if true, then the dispatched confirmation notification
type NotifierOptions struct {
// IncludeBlock if true, then the dispatched confirmation notification
// will include the block that mined the transaction.
includeBlock bool
IncludeBlock bool
}
// defaultNotifierOptions returns the set of default options for the notifier.
func defaultNotifierOptions() *notifierOptions {
return &notifierOptions{}
// DefaultNotifierOptions returns the set of default options for the notifier.
func DefaultNotifierOptions() *NotifierOptions {
return &NotifierOptions{}
}
// NotifierOption is a functional option that allows a caller to modify the
// events received from the notifier.
type NotifierOption func(*notifierOptions)
type NotifierOption func(*NotifierOptions)
// WithIncludeBlock is an optional argument that allows the calelr to specify
// WithIncludeBlock is an optional argument that allows the caller to specify
// that the block that mined a transaction should be included in the response.
func WithIncludeBlock() NotifierOption {
return func(o *notifierOptions) {
o.includeBlock = true
return func(o *NotifierOptions) {
o.IncludeBlock = true
}
}
@ -258,6 +258,9 @@ type ConfirmationEvent struct {
// channels.
func NewConfirmationEvent(numConfs uint32, cancel func()) *ConfirmationEvent {
return &ConfirmationEvent{
// We cannot rely on the subscriber to immediately read from
// the channel so we need to create a larger buffer to avoid
// blocking the notifier.
Confirmed: make(chan *TxConfirmation, 1),
Updates: make(chan uint32, numConfs),
NegativeConf: make(chan int32, 1),

View File

@ -1,7 +1,7 @@
package chainntnfs
import (
"github.com/btcsuite/btclog"
"github.com/btcsuite/btclog/v2"
"github.com/lightningnetwork/lnd/build"
)

View File

@ -211,7 +211,7 @@ func (m *MempoolNotifier) findRelevantInputs(tx *btcutil.Tx) (inputsWithTx,
// If found, save it to watchedInputs to notify the
// subscriber later.
Log.Infof("Found input %s, spent in %s", op, txid)
Log.Debugf("Found input %s, spent in %s", op, txid)
// Construct the spend details.
details := &SpendDetail{

View File

@ -3,7 +3,7 @@ package chainntnfs
import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/fn/v2"
"github.com/stretchr/testify/mock"
)

View File

@ -168,6 +168,8 @@ func (n *NeutrinoNotifier) Started() bool {
}
func (n *NeutrinoNotifier) startNotifier() error {
chainntnfs.Log.Infof("neutrino notifier starting...")
// Start our concurrent queues before starting the rescan, to ensure
// onFilteredBlockConnected and onRelavantTx callbacks won't be
// blocked.
@ -234,6 +236,8 @@ func (n *NeutrinoNotifier) startNotifier() error {
// startup.
atomic.StoreInt32(&n.active, 1)
chainntnfs.Log.Debugf("neutrino notifier started")
return nil
}
@ -439,7 +443,7 @@ func (n *NeutrinoNotifier) notificationDispatcher() {
// potentially long rescans.
n.wg.Add(1)
//nolint:lll
//nolint:ll
go func(msg *chainntnfs.HistoricalConfDispatch) {
defer n.wg.Done()
@ -689,10 +693,16 @@ func (n *NeutrinoNotifier) handleBlockConnected(newBlock *filteredBlock) error {
n.bestBlock.Height = int32(newBlock.height)
n.bestBlock.BlockHeader = newBlock.header
err = n.txNotifier.NotifyHeight(newBlock.height)
if err != nil {
return fmt.Errorf("unable to notify height: %w", err)
}
n.notifyBlockEpochs(
int32(newBlock.height), &newBlock.hash, newBlock.header,
)
return n.txNotifier.NotifyHeight(newBlock.height)
return nil
}
// getFilteredBlock is a utility to retrieve the full filtered block from a block epoch.

View File

@ -12,11 +12,15 @@ import (
// TestInterfaces executes the generic notifier test suite against a bitcoind
// powered chain notifier.
func TestInterfaces(t *testing.T) {
t.Run("bitcoind", func(st *testing.T) {
success := t.Run("bitcoind", func(st *testing.T) {
st.Parallel()
chainntnfstest.TestInterfaces(st, "bitcoind")
})
if !success {
return
}
t.Run("bitcoind rpc polling", func(st *testing.T) {
st.Parallel()
chainntnfstest.TestInterfaces(st, "bitcoind-rpc-polling")

View File

@ -41,9 +41,8 @@ func testSingleConfirmationNotification(miner *rpctest.Harness,
// function.
txid, pkScript, err := chainntnfs.GetTestTxidAndScript(miner)
require.NoError(t, err, "unable to create test tx")
if err := chainntnfs.WaitForMempoolTx(miner, txid); err != nil {
t.Fatalf("tx not relayed to miner: %v", err)
}
err = chainntnfs.WaitForMempoolTx(miner, txid)
require.NoError(t, err, "tx not relayed to miner")
_, currentHeight, err := miner.Client.GetBestBlock()
require.NoError(t, err, "unable to get current height")
@ -68,6 +67,11 @@ func testSingleConfirmationNotification(miner *rpctest.Harness,
blockHash, err := miner.Client.Generate(1)
require.NoError(t, err, "unable to generate single block")
// Assert the above tx is mined in the block.
block, err := miner.Client.GetBlock(blockHash[0])
require.NoError(t, err)
require.Len(t, block.Transactions, 2, "block does not contain tx")
select {
case confInfo := <-confIntent.Confirmed:
if !confInfo.BlockHash.IsEqual(blockHash[0]) {
@ -1906,10 +1910,8 @@ func TestInterfaces(t *testing.T, targetBackEnd string) {
// Initialize a height hint cache for each notifier.
tempDir := t.TempDir()
db, err := channeldb.Open(tempDir)
if err != nil {
t.Fatalf("unable to create db: %v", err)
}
db := channeldb.OpenForTesting(t, tempDir)
testCfg := channeldb.CacheConfig{
QueryDisable: false,
}
@ -1930,7 +1932,7 @@ func TestInterfaces(t *testing.T, targetBackEnd string) {
case "bitcoind":
var bitcoindConn *chain.BitcoindConn
bitcoindConn = unittest.NewBitcoindBackend(
t, unittest.NetParams, p2pAddr, true, false,
t, unittest.NetParams, miner, true, false,
)
newNotifier = func() (chainntnfs.TestChainNotifier, error) {
return bitcoindnotify.New(
@ -1942,7 +1944,7 @@ func TestInterfaces(t *testing.T, targetBackEnd string) {
case "bitcoind-rpc-polling":
var bitcoindConn *chain.BitcoindConn
bitcoindConn = unittest.NewBitcoindBackend(
t, unittest.NetParams, p2pAddr, true, true,
t, unittest.NetParams, miner, true, true,
)
newNotifier = func() (chainntnfs.TestChainNotifier, error) {
return bitcoindnotify.New(

View File

@ -244,20 +244,25 @@ type ConfNtfn struct {
// notification is to be sent.
NumConfirmations uint32
// Event contains references to the channels that the notifications are to
// be sent over.
// Event contains references to the channels that the notifications are
// to be sent over.
Event *ConfirmationEvent
// HeightHint is the minimum height in the chain that we expect to find
// this txid.
HeightHint uint32
// dispatched is false if the confirmed notification has not been sent yet.
// dispatched is false if the confirmed notification has not been sent
// yet.
dispatched bool
// includeBlock is true if the dispatched notification should also have
// the block included with it.
includeBlock bool
// numConfsLeft is the number of confirmations left to be sent to the
// subscriber.
numConfsLeft uint32
}
// HistoricalConfDispatch parametrizes a manual rescan for a particular
@ -554,7 +559,7 @@ func NewTxNotifier(startHeight uint32, reorgSafetyLimit uint32,
// and register a confirmation notification.
func (n *TxNotifier) newConfNtfn(txid *chainhash.Hash,
pkScript []byte, numConfs, heightHint uint32,
opts *notifierOptions) (*ConfNtfn, error) {
opts *NotifierOptions) (*ConfNtfn, error) {
// An accompanying output script must always be provided.
if len(pkScript) == 0 {
@ -588,7 +593,8 @@ func (n *TxNotifier) newConfNtfn(txid *chainhash.Hash,
n.CancelConf(confRequest, confID)
}),
HeightHint: heightHint,
includeBlock: opts.includeBlock,
includeBlock: opts.IncludeBlock,
numConfsLeft: numConfs,
}, nil
}
@ -610,7 +616,7 @@ func (n *TxNotifier) RegisterConf(txid *chainhash.Hash, pkScript []byte,
default:
}
opts := defaultNotifierOptions()
opts := DefaultNotifierOptions()
for _, optFunc := range optFuncs {
optFunc(opts)
}
@ -664,8 +670,8 @@ func (n *TxNotifier) RegisterConf(txid *chainhash.Hash, pkScript []byte,
// already been found, we'll attempt to deliver them immediately
// to this client.
Log.Debugf("Attempting to dispatch confirmation for %v on "+
"registration since rescan has finished",
ntfn.ConfRequest)
"registration since rescan has finished, conf_id=%v",
ntfn.ConfRequest, ntfn.ConfID)
// The default notification we assigned above includes the
// block along with the rest of the details. However not all
@ -679,9 +685,13 @@ func (n *TxNotifier) RegisterConf(txid *chainhash.Hash, pkScript []byte,
confDetails = &confDetailsCopy
}
err := n.dispatchConfDetails(ntfn, confDetails)
if err != nil {
return nil, err
// Deliver the details to the whole conf set where this ntfn
// lives in.
for _, subscriber := range confSet.ntfns {
err := n.dispatchConfDetails(subscriber, confDetails)
if err != nil {
return nil, err
}
}
return &ConfRegistration{
@ -912,10 +922,16 @@ func (n *TxNotifier) dispatchConfDetails(
// If there are no conf details to dispatch or if the notification has
// already been dispatched, then we can skip dispatching to this
// client.
if details == nil || ntfn.dispatched {
Log.Debugf("Skipping dispatch of conf details(%v) for "+
"request %v, dispatched=%v", details, ntfn.ConfRequest,
ntfn.dispatched)
if details == nil {
Log.Debugf("Skipped dispatching nil conf details for request "+
"%v, conf_id=%v", ntfn.ConfRequest, ntfn.ConfID)
return nil
}
if ntfn.dispatched {
Log.Debugf("Skipped dispatched conf details for request %v "+
"conf_id=%v", ntfn.ConfRequest, ntfn.ConfID)
return nil
}
@ -925,16 +941,16 @@ func (n *TxNotifier) dispatchConfDetails(
// we'll dispatch a confirmation notification to the caller.
confHeight := details.BlockHeight + ntfn.NumConfirmations - 1
if confHeight <= n.currentHeight {
Log.Debugf("Dispatching %v confirmation notification for %v",
ntfn.NumConfirmations, ntfn.ConfRequest)
Log.Debugf("Dispatching %v confirmation notification for "+
"conf_id=%v, %v", ntfn.NumConfirmations, ntfn.ConfID,
ntfn.ConfRequest)
// We'll send a 0 value to the Updates channel,
// indicating that the transaction/output script has already
// been confirmed.
select {
case ntfn.Event.Updates <- 0:
case <-n.quit:
return ErrTxNotifierExiting
err := n.notifyNumConfsLeft(ntfn, 0)
if err != nil {
return err
}
select {
@ -944,8 +960,8 @@ func (n *TxNotifier) dispatchConfDetails(
return ErrTxNotifierExiting
}
} else {
Log.Debugf("Queueing %v confirmation notification for %v at tip ",
ntfn.NumConfirmations, ntfn.ConfRequest)
Log.Debugf("Queueing %v confirmation notification for %v at "+
"tip", ntfn.NumConfirmations, ntfn.ConfRequest)
// Otherwise, we'll keep track of the notification
// request by the height at which we should dispatch the
@ -961,10 +977,9 @@ func (n *TxNotifier) dispatchConfDetails(
// confirmations are left for the transaction/output script to
// be confirmed.
numConfsLeft := confHeight - n.currentHeight
select {
case ntfn.Event.Updates <- numConfsLeft:
case <-n.quit:
return ErrTxNotifierExiting
err := n.notifyNumConfsLeft(ntfn, numConfsLeft)
if err != nil {
return err
}
}
@ -1729,10 +1744,9 @@ func (n *TxNotifier) NotifyHeight(height uint32) error {
continue
}
select {
case ntfn.Event.Updates <- numConfsLeft:
case <-n.quit:
return ErrTxNotifierExiting
err := n.notifyNumConfsLeft(ntfn, numConfsLeft)
if err != nil {
return err
}
}
}
@ -1743,9 +1757,6 @@ func (n *TxNotifier) NotifyHeight(height uint32) error {
for ntfn := range n.ntfnsByConfirmHeight[height] {
confSet := n.confNotifications[ntfn.ConfRequest]
Log.Debugf("Dispatching %v confirmation notification for %v",
ntfn.NumConfirmations, ntfn.ConfRequest)
// The default notification we assigned above includes the
// block along with the rest of the details. However not all
// clients want the block, so we make a copy here w/o the block
@ -1755,6 +1766,20 @@ func (n *TxNotifier) NotifyHeight(height uint32) error {
confDetails.Block = nil
}
// If the `confDetails` has already been sent before, we'll
// skip it and continue processing the next one.
if ntfn.dispatched {
Log.Debugf("Skipped dispatched conf details for "+
"request %v conf_id=%v", ntfn.ConfRequest,
ntfn.ConfID)
continue
}
Log.Debugf("Dispatching %v confirmation notification for "+
"conf_id=%v, %v", ntfn.NumConfirmations, ntfn.ConfID,
ntfn.ConfRequest)
select {
case ntfn.Event.Confirmed <- &confDetails:
ntfn.dispatched = true
@ -1833,6 +1858,9 @@ func (n *TxNotifier) DisconnectTip(blockHeight uint32) error {
default:
}
// We also reset the num of confs update.
ntfn.numConfsLeft = ntfn.NumConfirmations
// Then, we'll check if the current
// transaction/output script was included in the
// block currently being disconnected. If it
@ -2069,3 +2097,30 @@ func (n *TxNotifier) TearDown() {
}
}
}
// notifyNumConfsLeft sends the number of confirmations left to the
// notification subscriber through the Event.Updates channel.
//
// NOTE: must be used with the TxNotifier's lock held.
func (n *TxNotifier) notifyNumConfsLeft(ntfn *ConfNtfn, num uint32) error {
// If the number left is no less than the recorded value, we can skip
// sending it as it means this same value has already been sent before.
if num >= ntfn.numConfsLeft {
Log.Debugf("Skipped dispatched update (numConfsLeft=%v) for "+
"request %v conf_id=%v", num, ntfn.ConfRequest,
ntfn.ConfID)
return nil
}
// Update the number of confirmations left to the notification.
ntfn.numConfsLeft = num
select {
case ntfn.Event.Updates <- num:
case <-n.quit:
return ErrTxNotifierExiting
}
return nil
}

View File

@ -173,7 +173,7 @@ func TestTxNotifierRegistrationValidation(t *testing.T) {
for _, testCase := range testCases {
testCase := testCase
t.Run(testCase.name, func(t *testing.T) {
success := t.Run(testCase.name, func(t *testing.T) {
hintCache := newMockHintCache()
n := chainntnfs.NewTxNotifier(
10, chainntnfs.ReorgSafetyLimit, hintCache, hintCache,
@ -201,6 +201,10 @@ func TestTxNotifierRegistrationValidation(t *testing.T) {
"\"%v\", got \"%v\"", testCase.err, err)
}
})
if !success {
return
}
}
}

View File

@ -22,6 +22,14 @@ var BitcoinTestNetParams = BitcoinNetParams{
CoinType: keychain.CoinTypeTestnet,
}
// BitcoinTestNet4Params contains parameters specific to the 4th version of the
// test network.
var BitcoinTestNet4Params = BitcoinNetParams{
Params: &bitcoinCfg.TestNet4Params,
RPCPort: "48334",
CoinType: keychain.CoinTypeTestnet,
}
// BitcoinMainNetParams contains parameters specific to the current Bitcoin
// mainnet.
var BitcoinMainNetParams = BitcoinNetParams{
@ -53,8 +61,9 @@ var BitcoinRegTestNetParams = BitcoinNetParams{
CoinType: keychain.CoinTypeTestnet,
}
// IsTestnet tests if the givern params correspond to a testnet
// parameter configuration.
// IsTestnet tests if the given params correspond to a testnet parameter
// configuration.
func IsTestnet(params *BitcoinNetParams) bool {
return params.Params.Net == bitcoinWire.TestNet3
return params.Params.Net == bitcoinWire.TestNet3 ||
params.Params.Net == bitcoinWire.TestNet4
}

View File

@ -23,7 +23,8 @@ import (
"github.com/lightningnetwork/lnd/chainntnfs/btcdnotify"
"github.com/lightningnetwork/lnd/chainntnfs/neutrinonotify"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/fn/v2"
"github.com/lightningnetwork/lnd/graph/db/models"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/kvdb"
@ -63,6 +64,14 @@ type Config struct {
// state.
ChanStateDB *channeldb.ChannelStateDB
// AuxLeafStore is an optional store that can be used to store auxiliary
// leaves for certain custom channel types.
AuxLeafStore fn.Option[lnwallet.AuxLeafStore]
// AuxSigner is an optional signer that can be used to sign auxiliary
// leaves for certain custom channel types.
AuxSigner fn.Option[lnwallet.AuxSigner]
// BlockCache is the main cache for storing block information.
BlockCache *blockcache.BlockCache
@ -213,7 +222,7 @@ type ChainControl struct {
// the parts that can be purely constructed from the passed in global
// configuration and doesn't need any wallet instance yet.
//
//nolint:lll
//nolint:ll
func NewPartialChainControl(cfg *Config) (*PartialChainControl, func(), error) {
cc := &PartialChainControl{
Cfg: cfg,
@ -376,8 +385,7 @@ func NewPartialChainControl(cfg *Config) (*PartialChainControl, func(), error) {
)
cc.ChainSource = bitcoindConn.NewBitcoindClient()
// If we're not in regtest mode, then we'll attempt to use a
// proper fee estimator for testnet.
// Initialize config to connect to bitcoind RPC.
rpcConfig := &rpcclient.ConnConfig{
Host: bitcoindHost,
User: bitcoindMode.RPCUser,
@ -387,7 +395,9 @@ func NewPartialChainControl(cfg *Config) (*PartialChainControl, func(), error) {
DisableTLS: true,
HTTPPostMode: true,
}
if !cfg.Bitcoin.RegTest {
// If feeurl is not provided, use bitcoind's fee estimator.
if cfg.Fee.URL == "" {
log.Infof("Initializing bitcoind backed fee estimator "+
"in %s mode", bitcoindMode.EstimateMode)
@ -651,9 +661,8 @@ func NewPartialChainControl(cfg *Config) (*PartialChainControl, func(), error) {
return checkOutboundPeers(chainRPC.Client)
}
// If we're not in simnet or regtest mode, then we'll attempt
// to use a proper fee estimator for testnet.
if !cfg.Bitcoin.SimNet && !cfg.Bitcoin.RegTest {
// If feeurl is not provided, use btcd's fee estimator.
if cfg.Fee.URL == "" {
log.Info("Initializing btcd backed fee estimator")
// Finally, we'll re-initialize the fee estimator, as
@ -829,6 +838,15 @@ var (
0x01, 0xea, 0x33, 0x09, 0x00, 0x00, 0x00, 0x00,
})
// BitcoinTestnet4Genesis is the genesis hash of Bitcoin's testnet4
// chain.
BitcoinTestnet4Genesis = chainhash.Hash([chainhash.HashSize]byte{
0x43, 0xf0, 0x8b, 0xda, 0xb0, 0x50, 0xe3, 0x5b,
0x56, 0x7c, 0x86, 0x4b, 0x91, 0xf4, 0x7f, 0x50,
0xae, 0x72, 0x5a, 0xe2, 0xde, 0x53, 0xbc, 0xfb,
0xba, 0xf2, 0x84, 0xda, 0x00, 0x00, 0x00, 0x00,
})
// BitcoinSignetGenesis is the genesis hash of Bitcoin's signet chain.
BitcoinSignetGenesis = chainhash.Hash([chainhash.HashSize]byte{
0xf6, 0x1e, 0xee, 0x3b, 0x63, 0xa3, 0x80, 0xa4,
@ -860,8 +878,8 @@ var (
ChainDNSSeeds = map[chainhash.Hash][][2]string{
BitcoinMainnetGenesis: {
{
"nodes.lightning.directory",
"soa.nodes.lightning.directory",
"nodes.lightning.wiki",
"soa.nodes.lightning.wiki",
},
{
"lseed.bitcoinstats.com",
@ -870,14 +888,22 @@ var (
BitcoinTestnetGenesis: {
{
"test.nodes.lightning.directory",
"soa.nodes.lightning.directory",
"test.nodes.lightning.wiki",
"soa.nodes.lightning.wiki",
},
},
BitcoinTestnet4Genesis: {
{
"test4.nodes.lightning.wiki",
"soa.nodes.lightning.wiki",
},
},
BitcoinSignetGenesis: {
{
"ln.signet.secp.tech",
"signet.nodes.lightning.wiki",
"soa.nodes.lightning.wiki",
},
},
}

View File

@ -1,7 +1,7 @@
package chainreg
import (
"github.com/btcsuite/btclog"
"github.com/btcsuite/btclog/v2"
"github.com/lightningnetwork/lnd/build"
)

View File

@ -11,7 +11,7 @@ import (
"github.com/btcsuite/btcwallet/chain"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb"
graphdb "github.com/lightningnetwork/lnd/graph/db"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/routing/chainview"
)
@ -94,7 +94,7 @@ func (n *NoChainBackend) DisconnectedBlocks() <-chan *chainview.FilteredBlock {
return make(chan *chainview.FilteredBlock)
}
func (n *NoChainBackend) UpdateFilter([]channeldb.EdgePoint, uint32) error {
func (n *NoChainBackend) UpdateFilter([]graphdb.EdgePoint, uint32) error {
return nil
}

View File

@ -1,7 +1,7 @@
package chanacceptor
import (
"github.com/btcsuite/btclog"
"github.com/btcsuite/btclog/v2"
"github.com/lightningnetwork/lnd/build"
)

View File

@ -249,7 +249,7 @@ func (r *RPCAcceptor) sendAcceptRequests(errChan chan error,
acceptRequests := make(map[[32]byte]*chanAcceptInfo)
for {
//nolint:lll
//nolint:ll
select {
// Consume requests passed to us from our Accept() function and
// send them into our stream.
@ -356,6 +356,30 @@ func (r *RPCAcceptor) sendAcceptRequests(errChan chan error,
):
commitmentType = lnrpc.CommitmentType_SIMPLE_TAPROOT
case channelFeatures.OnlyContains(
lnwire.SimpleTaprootOverlayChansRequired,
lnwire.ZeroConfRequired,
lnwire.ScidAliasRequired,
):
commitmentType = lnrpc.CommitmentType_SIMPLE_TAPROOT_OVERLAY
case channelFeatures.OnlyContains(
lnwire.SimpleTaprootOverlayChansRequired,
lnwire.ZeroConfRequired,
):
commitmentType = lnrpc.CommitmentType_SIMPLE_TAPROOT_OVERLAY
case channelFeatures.OnlyContains(
lnwire.SimpleTaprootOverlayChansRequired,
lnwire.ScidAliasRequired,
):
commitmentType = lnrpc.CommitmentType_SIMPLE_TAPROOT_OVERLAY
case channelFeatures.OnlyContains(
lnwire.SimpleTaprootOverlayChansRequired,
):
commitmentType = lnrpc.CommitmentType_SIMPLE_TAPROOT_OVERLAY
case channelFeatures.OnlyContains(
lnwire.StaticRemoteKeyRequired,
):

View File

@ -2,12 +2,10 @@ package chanbackup
import (
"fmt"
"net"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/fn/v2"
)
// LiveChannelSource is an interface that allows us to query for the set of
@ -19,23 +17,14 @@ type LiveChannelSource interface {
// FetchChannel attempts to locate a live channel identified by the
// passed chanPoint. Optionally an existing db tx can be supplied.
FetchChannel(tx kvdb.RTx, chanPoint wire.OutPoint) (
*channeldb.OpenChannel, error)
}
// AddressSource is an interface that allows us to query for the set of
// addresses a node can be connected to.
type AddressSource interface {
// AddrsForNode returns all known addresses for the target node public
// key.
AddrsForNode(nodePub *btcec.PublicKey) ([]net.Addr, error)
FetchChannel(chanPoint wire.OutPoint) (*channeldb.OpenChannel, error)
}
// assembleChanBackup attempts to assemble a static channel backup for the
// passed open channel. The backup includes all information required to restore
// the channel, as well as addressing information so we can find the peer and
// reconnect to them to initiate the protocol.
func assembleChanBackup(addrSource AddressSource,
func assembleChanBackup(addrSource channeldb.AddrSource,
openChan *channeldb.OpenChannel) (*Single, error) {
log.Debugf("Crafting backup for ChannelPoint(%v)",
@ -43,25 +32,70 @@ func assembleChanBackup(addrSource AddressSource,
// First, we'll query the channel source to obtain all the addresses
// that are associated with the peer for this channel.
nodeAddrs, err := addrSource.AddrsForNode(openChan.IdentityPub)
known, nodeAddrs, err := addrSource.AddrsForNode(openChan.IdentityPub)
if err != nil {
return nil, err
}
if !known {
return nil, fmt.Errorf("node unknown by address source")
}
single := NewSingle(openChan, nodeAddrs)
return &single, nil
}
// buildCloseTxInputs generates inputs needed to force close a channel from
// an open channel. Anyone having these inputs and the signer, can sign the
// force closure transaction. Warning! If the channel state updates, an attempt
// to close the channel using this method with outdated CloseTxInputs can result
// in loss of funds! This may happen if an outdated channel backup is attempted
// to be used to force close the channel.
func buildCloseTxInputs(
targetChan *channeldb.OpenChannel) fn.Option[CloseTxInputs] {
log.Debugf("Crafting CloseTxInputs for ChannelPoint(%v)",
targetChan.FundingOutpoint)
localCommit := targetChan.LocalCommitment
if localCommit.CommitTx == nil {
log.Infof("CommitTx is nil for ChannelPoint(%v), "+
"skipping CloseTxInputs. This is possible when "+
"DLP is active.", targetChan.FundingOutpoint)
return fn.None[CloseTxInputs]()
}
// We need unsigned force close tx and the counterparty's signature.
inputs := CloseTxInputs{
CommitTx: localCommit.CommitTx,
CommitSig: localCommit.CommitSig,
}
// In case of a taproot channel, commit height is needed as well to
// produce verification nonce for the taproot channel using shachain.
if targetChan.ChanType.IsTaproot() {
inputs.CommitHeight = localCommit.CommitHeight
}
// In case of a custom taproot channel, TapscriptRoot is needed as well.
if targetChan.ChanType.HasTapscriptRoot() {
inputs.TapscriptRoot = targetChan.TapscriptRoot
}
return fn.Some(inputs)
}
// FetchBackupForChan attempts to create a plaintext static channel backup for
// the target channel identified by its channel point. If we're unable to find
// the target channel, then an error will be returned.
func FetchBackupForChan(chanPoint wire.OutPoint, chanSource LiveChannelSource,
addrSource AddressSource) (*Single, error) {
addrSource channeldb.AddrSource) (*Single, error) {
// First, we'll query the channel source to see if the channel is known
// and open within the database.
targetChan, err := chanSource.FetchChannel(nil, chanPoint)
targetChan, err := chanSource.FetchChannel(chanPoint)
if err != nil {
// If we can't find the channel, then we return with an error,
// as we have nothing to backup.
@ -81,7 +115,7 @@ func FetchBackupForChan(chanPoint wire.OutPoint, chanSource LiveChannelSource,
// FetchStaticChanBackups will return a plaintext static channel back up for
// all known active/open channels within the passed channel source.
func FetchStaticChanBackups(chanSource LiveChannelSource,
addrSource AddressSource) ([]Single, error) {
addrSource channeldb.AddrSource) ([]Single, error) {
// First, we'll query the backup source for information concerning all
// currently open and available channels.

View File

@ -8,7 +8,6 @@ import (
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/kvdb"
"github.com/stretchr/testify/require"
)
@ -40,7 +39,7 @@ func (m *mockChannelSource) FetchAllChannels() ([]*channeldb.OpenChannel, error)
return chans, nil
}
func (m *mockChannelSource) FetchChannel(_ kvdb.RTx, chanPoint wire.OutPoint) (
func (m *mockChannelSource) FetchChannel(chanPoint wire.OutPoint) (
*channeldb.OpenChannel, error) {
if m.failQuery {
@ -62,20 +61,19 @@ func (m *mockChannelSource) addAddrsForNode(nodePub *btcec.PublicKey, addrs []ne
m.addrs[nodeKey] = addrs
}
func (m *mockChannelSource) AddrsForNode(nodePub *btcec.PublicKey) ([]net.Addr, error) {
func (m *mockChannelSource) AddrsForNode(nodePub *btcec.PublicKey) (bool,
[]net.Addr, error) {
if m.failQuery {
return nil, fmt.Errorf("fail")
return false, nil, fmt.Errorf("fail")
}
var nodeKey [33]byte
copy(nodeKey[:], nodePub.SerializeCompressed())
addrs, ok := m.addrs[nodeKey]
if !ok {
return nil, fmt.Errorf("can't find addr")
}
return addrs, nil
return ok, addrs, nil
}
// TestFetchBackupForChan tests that we're able to construct a single channel
@ -158,6 +156,8 @@ func TestFetchStaticChanBackups(t *testing.T) {
chanSource.chans[randomChan2.FundingOutpoint] = randomChan2
chanSource.addAddrsForNode(randomChan1.IdentityPub, []net.Addr{addr1})
chanSource.addAddrsForNode(randomChan2.IdentityPub, []net.Addr{addr2})
chanSource.addAddrsForNode(randomChan2.IdentityPub, []net.Addr{addr3})
chanSource.addAddrsForNode(randomChan2.IdentityPub, []net.Addr{addr4})
// With the channel source populated, we'll now attempt to create a set
// of backups for all the channels. This should succeed, as all items

View File

@ -2,10 +2,13 @@ package chanbackup
import (
"fmt"
"io"
"os"
"path/filepath"
"time"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnrpc"
)
const (
@ -17,6 +20,10 @@ const (
// file that we'll use to atomically update the primary back up file
// when new channel are detected.
DefaultTempBackupFileName = "temp-dont-use.backup"
// DefaultChanBackupArchiveDirName is the default name of the directory
// that we'll use to store old channel backups.
DefaultChanBackupArchiveDirName = "chan-backup-archives"
)
var (
@ -44,28 +51,40 @@ type MultiFile struct {
// tempFile is an open handle to the temp back up file.
tempFile *os.File
// archiveDir is the directory where we'll store old channel backups.
archiveDir string
// noBackupArchive indicates whether old backups should be deleted
// rather than archived.
noBackupArchive bool
}
// NewMultiFile create a new multi-file instance at the target location on the
// file system.
func NewMultiFile(fileName string) *MultiFile {
func NewMultiFile(fileName string, noBackupArchive bool) *MultiFile {
// We'll our temporary backup file in the very same directory as the
// main backup file.
backupFileDir := filepath.Dir(fileName)
tempFileName := filepath.Join(
backupFileDir, DefaultTempBackupFileName,
)
archiveDir := filepath.Join(
backupFileDir, DefaultChanBackupArchiveDirName,
)
return &MultiFile{
fileName: fileName,
tempFileName: tempFileName,
fileName: fileName,
tempFileName: tempFileName,
archiveDir: archiveDir,
noBackupArchive: noBackupArchive,
}
}
// UpdateAndSwap will attempt write a new temporary backup file to disk with
// the newBackup encoded, then atomically swap (via rename) the old file for
// the new file by updating the name of the new file to the old.
// the new file by updating the name of the new file to the old. It also checks
// if the old file should be archived first before swapping it.
func (b *MultiFile) UpdateAndSwap(newBackup PackedMulti) error {
// If the main backup file isn't set, then we can't proceed.
if b.fileName == "" {
@ -117,6 +136,12 @@ func (b *MultiFile) UpdateAndSwap(newBackup PackedMulti) error {
return fmt.Errorf("unable to close file: %w", err)
}
// Archive the old channel backup file before replacing.
if err := b.createArchiveFile(); err != nil {
return fmt.Errorf("unable to archive old channel "+
"backup file: %w", err)
}
// Finally, we'll attempt to atomically rename the temporary file to
// the main back up file. If this succeeds, then we'll only have a
// single file on disk once this method exits.
@ -147,3 +172,74 @@ func (b *MultiFile) ExtractMulti(keyChain keychain.KeyRing) (*Multi, error) {
packedMulti := PackedMulti(multiBytes)
return packedMulti.Unpack(keyChain)
}
// createArchiveFile creates an archive file with a timestamped name in the
// specified archive directory, and copies the contents of the main backup file
// to the new archive file.
func (b *MultiFile) createArchiveFile() error {
// User can skip archiving of old backup files to save disk space.
if b.noBackupArchive {
log.Debug("Skipping archive of old backup file as configured")
return nil
}
// Check for old channel backup file.
oldFileExists := lnrpc.FileExists(b.fileName)
if !oldFileExists {
log.Debug("No old channel backup file to archive")
return nil
}
log.Infof("Archiving old channel backup to %v", b.archiveDir)
// Generate archive file path with timestamped name.
baseFileName := filepath.Base(b.fileName)
timestamp := time.Now().Format("2006-01-02-15-04-05")
archiveFileName := fmt.Sprintf("%s-%s", baseFileName, timestamp)
archiveFilePath := filepath.Join(b.archiveDir, archiveFileName)
oldBackupFile, err := os.Open(b.fileName)
if err != nil {
return fmt.Errorf("unable to open old channel backup file: "+
"%w", err)
}
defer func() {
err := oldBackupFile.Close()
if err != nil {
log.Errorf("unable to close old channel backup file: "+
"%v", err)
}
}()
// Ensure the archive directory exists. If it doesn't we create it.
const archiveDirPermissions = 0o700
err = os.MkdirAll(b.archiveDir, archiveDirPermissions)
if err != nil {
return fmt.Errorf("unable to create archive directory: %w", err)
}
// Create new archive file.
archiveFile, err := os.Create(archiveFilePath)
if err != nil {
return fmt.Errorf("unable to create archive file: %w", err)
}
defer func() {
err := archiveFile.Close()
if err != nil {
log.Errorf("unable to close archive file: %v", err)
}
}()
// Copy contents of old backup to the newly created archive files.
_, err = io.Copy(archiveFile, oldBackupFile)
if err != nil {
return fmt.Errorf("unable to copy to archive file: %w", err)
}
err = archiveFile.Sync()
if err != nil {
return fmt.Errorf("unable to sync archive file: %w", err)
}
return nil
}

View File

@ -45,6 +45,121 @@ func assertFileDeleted(t *testing.T, filePath string) {
}
}
// TestUpdateAndSwapWithArchive test that we're able to properly swap out old
// backups on disk with new ones. In addition, we check for noBackupArchive to
// ensure that the archive file is created when it's set to false, and not
// created when it's set to true.
func TestUpdateAndSwapWithArchive(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
noBackupArchive bool
}{
// Test with noBackupArchive set to true - should not create
// archive.
{
name: "no archive file",
noBackupArchive: true,
},
// Test with noBackupArchive set to false - should create
// archive.
{
name: "with archive file",
noBackupArchive: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
tempTestDir := t.TempDir()
fileName := filepath.Join(
tempTestDir, DefaultBackupFileName,
)
tempFileName := filepath.Join(
tempTestDir, DefaultTempBackupFileName,
)
backupFile := NewMultiFile(fileName, tc.noBackupArchive)
// To start with, we'll make a random byte slice that'll
// pose as our packed multi backup.
newPackedMulti, err := makeFakePackedMulti()
require.NoError(t, err)
// With our backup created, we'll now attempt to swap
// out this backup, for the old one.
err = backupFile.UpdateAndSwap(newPackedMulti)
require.NoError(t, err)
// If we read out the file on disk, then it should match
// exactly what we wrote. The temp backup file should
// also be gone.
assertBackupMatches(t, fileName, newPackedMulti)
assertFileDeleted(t, tempFileName)
// Now that we know this is a valid test case, we'll
// make a new packed multi to swap out this current one.
newPackedMulti2, err := makeFakePackedMulti()
require.NoError(t, err)
// We'll then attempt to swap the old version for this
// new one.
err = backupFile.UpdateAndSwap(newPackedMulti2)
require.NoError(t, err)
// Once again, the file written on disk should have been
// properly swapped out with the new instance.
assertBackupMatches(t, fileName, newPackedMulti2)
// Additionally, we shouldn't be able to find the temp
// backup file on disk, as it should be deleted each
// time.
assertFileDeleted(t, tempFileName)
// Now check if archive was created when noBackupArchive
// is false.
archiveDir := filepath.Join(
filepath.Dir(fileName),
DefaultChanBackupArchiveDirName,
)
// When noBackupArchive is true, no new archive file
// should be created.
//
// NOTE: In a real environment, the archive directory
// might exist with older backups before the feature is
// disabled, but for test simplicity (since we clean up
// the directory between test cases), we verify the
// directory doesn't exist at all.
if tc.noBackupArchive {
require.NoDirExists(t, archiveDir)
return
}
// Otherwise we expect an archive to be created.
files, err := os.ReadDir(archiveDir)
require.NoError(t, err)
require.Len(t, files, 1)
// Verify the archive contents match the previous
// backup.
archiveFile := filepath.Join(
archiveDir, files[0].Name(),
)
// The archived content should match the previous backup
// (newPackedMulti) that was just swapped out.
assertBackupMatches(t, archiveFile, newPackedMulti)
// Clean up the archive directory.
os.RemoveAll(archiveDir)
})
}
}
// TestUpdateAndSwap test that we're able to properly swap out old backups on
// disk with new ones. Additionally, after a swap operation succeeds, then each
// time we should only have the main backup file on disk, as the temporary file
@ -52,114 +167,112 @@ func assertFileDeleted(t *testing.T, filePath string) {
func TestUpdateAndSwap(t *testing.T) {
t.Parallel()
tempTestDir := t.TempDir()
// Check that when the main file name is blank, an error is returned.
backupFile := NewMultiFile("", false)
err := backupFile.UpdateAndSwap(PackedMulti(nil))
require.ErrorIs(t, err, ErrNoBackupFileExists)
testCases := []struct {
fileName string
tempFileName string
name string
oldTempExists bool
valid bool
}{
// Main file name is blank, should fail.
{
fileName: "",
valid: false,
},
// Old temporary file still exists, should be removed. Only one
// file should remain.
{
fileName: filepath.Join(
tempTestDir, DefaultBackupFileName,
),
tempFileName: filepath.Join(
tempTestDir, DefaultTempBackupFileName,
),
name: "remove old temp file",
oldTempExists: true,
valid: true,
},
// Old temp doesn't exist, should swap out file, only a single
// file remains.
{
fileName: filepath.Join(
tempTestDir, DefaultBackupFileName,
),
tempFileName: filepath.Join(
tempTestDir, DefaultTempBackupFileName,
),
valid: true,
name: "swap out file",
oldTempExists: false,
},
}
for i, testCase := range testCases {
backupFile := NewMultiFile(testCase.fileName)
// To start with, we'll make a random byte slice that'll pose
// as our packed multi backup.
newPackedMulti, err := makeFakePackedMulti()
if err != nil {
t.Fatalf("unable to make test backup: %v", err)
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
tempTestDir := t.TempDir()
// If the old temporary file is meant to exist, then we'll
// create it now as an empty file.
if testCase.oldTempExists {
f, err := os.Create(testCase.tempFileName)
if err != nil {
t.Fatalf("unable to create temp file: %v", err)
fileName := filepath.Join(
tempTestDir, DefaultBackupFileName,
)
tempFileName := filepath.Join(
tempTestDir, DefaultTempBackupFileName,
)
backupFile := NewMultiFile(fileName, false)
// To start with, we'll make a random byte slice that'll
// pose as our packed multi backup.
newPackedMulti, err := makeFakePackedMulti()
require.NoError(t, err)
// If the old temporary file is meant to exist, then
// we'll create it now as an empty file.
if tc.oldTempExists {
f, err := os.Create(tempFileName)
require.NoError(t, err)
require.NoError(t, f.Close())
// TODO(roasbeef): mock out fs calls?
}
require.NoError(t, f.Close())
// TODO(roasbeef): mock out fs calls?
}
// With our backup created, we'll now attempt to swap
// out this backup, for the old one.
err = backupFile.UpdateAndSwap(newPackedMulti)
require.NoError(t, err)
// With our backup created, we'll now attempt to swap out this
// backup, for the old one.
err = backupFile.UpdateAndSwap(PackedMulti(newPackedMulti))
switch {
// If this is a valid test case, and we failed, then we'll
// return an error.
case err != nil && testCase.valid:
t.Fatalf("#%v, unable to swap file: %v", i, err)
// If we read out the file on disk, then it should match
// exactly what we wrote. The temp backup file should
// also be gone.
assertBackupMatches(t, fileName, newPackedMulti)
assertFileDeleted(t, tempFileName)
// If this is an invalid test case, and we passed it, then
// we'll return an error.
case err == nil && !testCase.valid:
t.Fatalf("#%v file swap should have failed: %v", i, err)
}
// Now that we know this is a valid test case, we'll
// make a new packed multi to swap out this current one.
newPackedMulti2, err := makeFakePackedMulti()
require.NoError(t, err)
if !testCase.valid {
continue
}
// We'll then attempt to swap the old version for this
// new one.
err = backupFile.UpdateAndSwap(newPackedMulti2)
require.NoError(t, err)
// If we read out the file on disk, then it should match
// exactly what we wrote. The temp backup file should also be
// gone.
assertBackupMatches(t, testCase.fileName, newPackedMulti)
assertFileDeleted(t, testCase.tempFileName)
// Once again, the file written on disk should have been
// properly swapped out with the new instance.
assertBackupMatches(t, fileName, newPackedMulti2)
// Now that we know this is a valid test case, we'll make a new
// packed multi to swap out this current one.
newPackedMulti2, err := makeFakePackedMulti()
if err != nil {
t.Fatalf("unable to make test backup: %v", err)
}
// Additionally, we shouldn't be able to find the temp
// backup file on disk, as it should be deleted each
// time.
assertFileDeleted(t, tempFileName)
// We'll then attempt to swap the old version for this new one.
err = backupFile.UpdateAndSwap(PackedMulti(newPackedMulti2))
if err != nil {
t.Fatalf("unable to swap file: %v", err)
}
// Now check if archive was created when noBackupArchive
// is false.
archiveDir := filepath.Join(
filepath.Dir(fileName),
DefaultChanBackupArchiveDirName,
)
files, err := os.ReadDir(archiveDir)
require.NoError(t, err)
require.Len(t, files, 1)
// Once again, the file written on disk should have been
// properly swapped out with the new instance.
assertBackupMatches(t, testCase.fileName, newPackedMulti2)
// Verify the archive contents match the previous
// backup.
archiveFile := filepath.Join(
archiveDir, files[0].Name(),
)
// Additionally, we shouldn't be able to find the temp backup
// file on disk, as it should be deleted each time.
assertFileDeleted(t, testCase.tempFileName)
// The archived content should match the previous backup
// (newPackedMulti) that was just swapped out.
assertBackupMatches(t, archiveFile, newPackedMulti)
// Clean up the archive directory.
os.RemoveAll(archiveDir)
})
}
}
@ -238,7 +351,7 @@ func TestExtractMulti(t *testing.T) {
}
for i, testCase := range testCases {
// First, we'll make our backup file with the specified name.
backupFile := NewMultiFile(testCase.fileName)
backupFile := NewMultiFile(testCase.fileName, false)
// With our file made, we'll now attempt to read out the
// multi-file.
@ -274,3 +387,86 @@ func TestExtractMulti(t *testing.T) {
assertMultiEqual(t, &unpackedMulti, freshUnpackedMulti)
}
}
// TestCreateArchiveFile tests that we're able to create an archive file
// with a timestamped name in the specified archive directory, and copy the
// contents of the main backup file to the new archive file.
func TestCreateArchiveFile(t *testing.T) {
t.Parallel()
// First, we'll create a temporary directory for our test files.
tempDir := t.TempDir()
archiveDir := filepath.Join(tempDir, DefaultChanBackupArchiveDirName)
// Next, we'll create a test backup file and write some content to it.
backupFile := filepath.Join(tempDir, DefaultBackupFileName)
testContent := []byte("test backup content")
err := os.WriteFile(backupFile, testContent, 0644)
require.NoError(t, err)
tests := []struct {
name string
setup func()
noBackupArchive bool
wantError bool
}{
{
name: "successful archive",
noBackupArchive: false,
},
{
name: "skip archive when disabled",
noBackupArchive: true,
},
{
name: "invalid archive directory permissions",
setup: func() {
// Create dir with no write permissions.
err := os.MkdirAll(archiveDir, 0500)
require.NoError(t, err)
},
noBackupArchive: false,
wantError: true,
},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
defer os.RemoveAll(archiveDir)
if tc.setup != nil {
tc.setup()
}
multiFile := NewMultiFile(
backupFile, tc.noBackupArchive,
)
err := multiFile.createArchiveFile()
if tc.wantError {
require.Error(t, err)
return
}
require.NoError(t, err)
// If archiving is disabled, verify no archive was
// created.
if tc.noBackupArchive {
require.NoDirExists(t, archiveDir)
return
}
// Verify archive exists and content matches.
files, err := os.ReadDir(archiveDir)
require.NoError(t, err)
require.Len(t, files, 1)
archivedContent, err := os.ReadFile(
filepath.Join(archiveDir, files[0].Name()),
)
require.NoError(t, err)
assertBackupMatches(t, backupFile, archivedContent)
})
}
}

View File

@ -1,7 +1,7 @@
package chanbackup
import (
"github.com/btcsuite/btclog"
"github.com/btcsuite/btclog/v2"
"github.com/lightningnetwork/lnd/build"
)

View File

@ -22,7 +22,9 @@ func TestMultiPackUnpack(t *testing.T) {
t.Fatalf("unable to gen channel: %v", err)
}
single := NewSingle(channel, []net.Addr{addr1, addr2})
single := NewSingle(
channel, []net.Addr{addr1, addr2, addr3, addr4},
)
originalSingles = append(originalSingles, single)
multi.StaticBackups = append(multi.StaticBackups, single)

View File

@ -6,6 +6,7 @@ import (
"net"
"os"
"sync"
"sync/atomic"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/channeldb"
@ -48,6 +49,19 @@ type ChannelEvent struct {
NewChans []ChannelWithAddrs
}
// manualUpdate holds a group of channel state updates and an error channel
// to send back an error happened upon update processing or file updating.
type manualUpdate struct {
// singles hold channels backups. They can be either new or known
// channels in the Swapper.
singles []Single
// errChan is the channel to send an error back. If the update handling
// and the subsequent file updating succeeds, nil is sent.
// The channel must have capacity of 1 to prevent Swapper blocking.
errChan chan error
}
// ChannelSubscription represents an intent to be notified of any updates to
// the primary channel state.
type ChannelSubscription struct {
@ -80,7 +94,12 @@ type ChannelNotifier interface {
// be used to implement a system that always keeps the multi-chan backup file
// on disk in a consistent state for safety purposes.
type SubSwapper struct {
// started tracks whether the SubSwapper has been started and ensures
// it can only be started once.
started sync.Once
// stopped tracks whether the SubSwapper has been stopped and ensures
// it can only be stopped once.
stopped sync.Once
// backupState are the set of SCBs for all open channels we know of.
@ -90,6 +109,8 @@ type SubSwapper struct {
// over.
chanEvents *ChannelSubscription
manualUpdates chan manualUpdate
// keyRing is the main key ring that will allow us to pack the new
// multi backup.
keyRing keychain.KeyRing
@ -98,6 +119,11 @@ type SubSwapper struct {
quit chan struct{}
wg sync.WaitGroup
// isActive tracks whether the SubSwapper is active and ready to receive
// messages. It is used to prevent manual updates from being sent to the
// SubSwapper after it has been stopped or not yet started.
isActive atomic.Bool
}
// NewSubSwapper creates a new instance of the SubSwapper given the starting
@ -126,11 +152,12 @@ func NewSubSwapper(startingChans []Single, chanNotifier ChannelNotifier,
}
return &SubSwapper{
backupState: backupState,
chanEvents: chanEvents,
keyRing: keyRing,
Swapper: backupSwapper,
quit: make(chan struct{}),
backupState: backupState,
chanEvents: chanEvents,
keyRing: keyRing,
Swapper: backupSwapper,
quit: make(chan struct{}),
manualUpdates: make(chan manualUpdate),
}, nil
}
@ -146,6 +173,7 @@ func (s *SubSwapper) Start() error {
if err := s.updateBackupFile(); err != nil {
startErr = fmt.Errorf("unable to refresh backup "+
"file: %v", err)
return
}
@ -162,9 +190,55 @@ func (s *SubSwapper) Stop() error {
log.Infof("chanbackup.SubSwapper shutting down...")
defer log.Debug("chanbackup.SubSwapper shutdown complete")
// Mark the SubSwapper as not running.
s.isActive.Store(false)
close(s.quit)
s.wg.Wait()
})
return nil
}
// ManualUpdate inserts/updates channel states into the swapper. The updates
// are processed in another goroutine. The method waits for the updates to be
// fully processed and the file to be updated on-disk before returning.
func (s *SubSwapper) ManualUpdate(singles []Single) error {
if !s.isActive.Load() {
return fmt.Errorf("swapper is not active, cannot perform " +
"manual update")
}
// Create the channel to send an error back. If the update handling
// and the subsequent file updating succeeds, nil is sent.
// The channel must have capacity of 1 to prevent Swapper blocking.
errChan := make(chan error, 1)
// Create the update object to insert into the processing loop.
update := manualUpdate{
singles: singles,
errChan: errChan,
}
select {
case s.manualUpdates <- update:
case <-s.quit:
return fmt.Errorf("swapper stopped when sending manual update")
}
// Wait for processing, block on errChan.
select {
case err := <-errChan:
if err != nil {
return fmt.Errorf("processing of manual update "+
"failed: %w", err)
}
case <-s.quit:
return fmt.Errorf("swapper stopped when waiting for outcome")
}
// Success.
return nil
}
@ -244,7 +318,8 @@ func (s *SubSwapper) updateBackupFile(closedChans ...wire.OutPoint) error {
// backupFileUpdater is the primary goroutine of the SubSwapper which is
// responsible for listening for changes to the channel, and updating the
// persistent multi backup state with a new packed multi of the latest channel
// state.
// state. Once active, it will process subscription updates and manual updates
// until the SubSwapper is stopped.
func (s *SubSwapper) backupUpdater() {
// Ensure that once we exit, we'll cancel our active channel
// subscription.
@ -253,6 +328,9 @@ func (s *SubSwapper) backupUpdater() {
log.Debugf("SubSwapper's backupUpdater is active!")
// Mark the SubSwapper as active.
s.isActive.Store(true)
for {
select {
// The channel state has been modified! We'll evaluate all
@ -267,9 +345,10 @@ func (s *SubSwapper) backupUpdater() {
log.Debugf("Adding channel %v to backup state",
newChan.FundingOutpoint)
s.backupState[newChan.FundingOutpoint] = NewSingle(
single := NewSingle(
newChan.OpenChannel, newChan.Addrs,
)
s.backupState[newChan.FundingOutpoint] = single
}
// For all closed channels, we'll remove the prior
@ -293,13 +372,45 @@ func (s *SubSwapper) backupUpdater() {
"num_old_chans=%v, num_new_chans=%v",
oldStateSize, newStateSize)
// With out new state constructed, we'll, atomically
// Without new state constructed, we'll, atomically
// update the on-disk backup state.
if err := s.updateBackupFile(closedChans...); err != nil {
log.Errorf("unable to update backup file: %v",
err)
}
// We received a manual update. Handle it and update the file.
case manualUpdate := <-s.manualUpdates:
oldStateSize := len(s.backupState)
// For all open channels, we'll create a new SCB given
// the required information.
for _, single := range manualUpdate.singles {
log.Debugf("Manual update of channel %v",
single.FundingOutpoint)
s.backupState[single.FundingOutpoint] = single
}
newStateSize := len(s.backupState)
log.Infof("Updating on-disk multi SCB backup: "+
"num_old_chans=%v, num_new_chans=%v",
oldStateSize, newStateSize)
// Without new state constructed, we'll, atomically
// update the on-disk backup state.
err := s.updateBackupFile()
if err != nil {
log.Errorf("unable to update backup file: %v",
err)
}
// Send the error (or nil) to the caller of
// ManualUpdate. The error channel must have capacity of
// 1 not to block here.
manualUpdate.errChan <- err
// TODO(roasbeef): refresh periodically on a time basis due to
// possible addr changes from node

View File

@ -277,4 +277,18 @@ func TestSubSwapperUpdater(t *testing.T) {
// Verify that the new set of backups, now has one less after the
// sub-swapper switches the new set with the old.
assertExpectedBackupSwap(t, swapper, subSwapper, keyRing, backupSet)
// Check ManualUpdate method.
channel, err := genRandomOpenChannelShell()
require.NoError(t, err)
single := NewSingle(channel, nil)
backupSet[channel.FundingOutpoint] = single
require.NoError(t, subSwapper.ManualUpdate([]Single{single}))
// Verify that the state of the backup is as expected.
assertExpectedBackupSwap(t, swapper, subSwapper, keyRing, backupSet)
// Check the case ManualUpdate returns an error.
swapper.fail = true
require.Error(t, subSwapper.ManualUpdate([]Single{single}))
}

View File

@ -40,9 +40,11 @@ type PeerConnector interface {
// the channel. In addition a LinkNode will be created for each new peer as
// well, in order to expose the addressing information required to locate to
// and connect to each peer in order to initiate the recovery protocol.
// The number of channels that were successfully restored is returned.
func Recover(backups []Single, restorer ChannelRestorer,
peerConnector PeerConnector) error {
peerConnector PeerConnector) (int, error) {
var numRestored int
for i, backup := range backups {
log.Infof("Restoring ChannelPoint(%v) to disk: ",
backup.FundingOutpoint)
@ -57,9 +59,10 @@ func Recover(backups []Single, restorer ChannelRestorer,
continue
}
if err != nil {
return err
return numRestored, err
}
numRestored++
log.Infof("Attempting to connect to node=%x (addrs=%v) to "+
"restore ChannelPoint(%v)",
backup.RemoteNodePub.SerializeCompressed(),
@ -70,7 +73,7 @@ func Recover(backups []Single, restorer ChannelRestorer,
backup.RemoteNodePub, backup.Addresses,
)
if err != nil {
return err
return numRestored, err
}
// TODO(roasbeef): to handle case where node has changed addrs,
@ -80,7 +83,7 @@ func Recover(backups []Single, restorer ChannelRestorer,
// * just to to fresh w/ call to node addrs and de-dup?
}
return nil
return numRestored, nil
}
// TODO(roasbeef): more specific keychain interface?
@ -88,16 +91,17 @@ func Recover(backups []Single, restorer ChannelRestorer,
// UnpackAndRecoverSingles is a one-shot method, that given a set of packed
// single channel backups, will restore the channel state to a channel shell,
// and also reach out to connect to any of the known node addresses for that
// channel. It is assumes that after this method exists, if a connection we
// able to be established, then then PeerConnector will continue to attempt to
// re-establish a persistent connection in the background.
// channel. It is assumes that after this method exists, if a connection was
// established, then the PeerConnector will continue to attempt to re-establish
// a persistent connection in the background. The number of channels that were
// successfully restored is returned.
func UnpackAndRecoverSingles(singles PackedSingles,
keyChain keychain.KeyRing, restorer ChannelRestorer,
peerConnector PeerConnector) error {
peerConnector PeerConnector) (int, error) {
chanBackups, err := singles.Unpack(keyChain)
if err != nil {
return err
return 0, err
}
return Recover(chanBackups, restorer, peerConnector)
@ -106,16 +110,17 @@ func UnpackAndRecoverSingles(singles PackedSingles,
// UnpackAndRecoverMulti is a one-shot method, that given a set of packed
// multi-channel backups, will restore the channel states to channel shells,
// and also reach out to connect to any of the known node addresses for that
// channel. It is assumes that after this method exists, if a connection we
// able to be established, then then PeerConnector will continue to attempt to
// re-establish a persistent connection in the background.
// channel. It is assumes that after this method exists, if a connection was
// established, then the PeerConnector will continue to attempt to re-establish
// a persistent connection in the background. The number of channels that were
// successfully restored is returned.
func UnpackAndRecoverMulti(packedMulti PackedMulti,
keyChain keychain.KeyRing, restorer ChannelRestorer,
peerConnector PeerConnector) error {
peerConnector PeerConnector) (int, error) {
chanBackups, err := packedMulti.Unpack(keyChain)
if err != nil {
return err
return 0, err
}
return Recover(chanBackups.StaticBackups, restorer, peerConnector)

View File

@ -2,7 +2,7 @@ package chanbackup
import (
"bytes"
"fmt"
"errors"
"net"
"testing"
@ -11,6 +11,12 @@ import (
"github.com/stretchr/testify/require"
)
var (
errRestoreFail = errors.New("restore fail")
errConnectFail = errors.New("connect fail")
)
type mockChannelRestorer struct {
fail bool
@ -19,7 +25,7 @@ type mockChannelRestorer struct {
func (m *mockChannelRestorer) RestoreChansFromSingles(...Single) error {
if m.fail {
return fmt.Errorf("fail")
return errRestoreFail
}
m.callCount++
@ -33,11 +39,11 @@ type mockPeerConnector struct {
callCount int
}
func (m *mockPeerConnector) ConnectPeer(node *btcec.PublicKey,
addrs []net.Addr) error {
func (m *mockPeerConnector) ConnectPeer(_ *btcec.PublicKey,
_ []net.Addr) error {
if m.fail {
return fmt.Errorf("fail")
return errConnectFail
}
m.callCount++
@ -59,16 +65,13 @@ func TestUnpackAndRecoverSingles(t *testing.T) {
var packedBackups PackedSingles
for i := 0; i < numSingles; i++ {
channel, err := genRandomOpenChannelShell()
if err != nil {
t.Fatalf("unable make channel: %v", err)
}
require.NoError(t, err)
single := NewSingle(channel, nil)
var b bytes.Buffer
if err := single.PackToWriter(&b, keyRing); err != nil {
t.Fatalf("unable to pack single: %v", err)
}
err = single.PackToWriter(&b, keyRing)
require.NoError(t, err)
backups = append(backups, single)
packedBackups = append(packedBackups, b.Bytes())
@ -83,54 +86,47 @@ func TestUnpackAndRecoverSingles(t *testing.T) {
// If we make the channel restore fail, then the entire method should
// as well
chanRestorer.fail = true
err := UnpackAndRecoverSingles(
_, err := UnpackAndRecoverSingles(
packedBackups, keyRing, &chanRestorer, &peerConnector,
)
if err == nil {
t.Fatalf("restoration should have failed")
}
require.ErrorIs(t, err, errRestoreFail)
chanRestorer.fail = false
// If we make the peer connector fail, then the entire method should as
// well
peerConnector.fail = true
err = UnpackAndRecoverSingles(
_, err = UnpackAndRecoverSingles(
packedBackups, keyRing, &chanRestorer, &peerConnector,
)
if err == nil {
t.Fatalf("restoration should have failed")
}
require.ErrorIs(t, err, errConnectFail)
chanRestorer.callCount--
peerConnector.fail = false
// Next, we'll ensure that if all the interfaces function as expected,
// then the channels will properly be unpacked and restored.
err = UnpackAndRecoverSingles(
numRestored, err := UnpackAndRecoverSingles(
packedBackups, keyRing, &chanRestorer, &peerConnector,
)
require.NoError(t, err, "unable to recover chans")
require.NoError(t, err)
require.EqualValues(t, numSingles, numRestored)
// Both the restorer, and connector should have been called 10 times,
// once for each backup.
if chanRestorer.callCount != numSingles {
t.Fatalf("expected %v calls, instead got %v",
numSingles, chanRestorer.callCount)
}
if peerConnector.callCount != numSingles {
t.Fatalf("expected %v calls, instead got %v",
numSingles, peerConnector.callCount)
}
require.EqualValues(
t, numSingles, chanRestorer.callCount, "restorer call count",
)
require.EqualValues(
t, numSingles, peerConnector.callCount, "peer call count",
)
// If we modify the keyRing, then unpacking should fail.
keyRing.Fail = true
err = UnpackAndRecoverSingles(
_, err = UnpackAndRecoverSingles(
packedBackups, keyRing, &chanRestorer, &peerConnector,
)
if err == nil {
t.Fatalf("unpacking should have failed")
}
require.ErrorContains(t, err, "fail")
// TODO(roasbeef): verify proper call args
}
@ -148,9 +144,7 @@ func TestUnpackAndRecoverMulti(t *testing.T) {
backups := make([]Single, 0, numSingles)
for i := 0; i < numSingles; i++ {
channel, err := genRandomOpenChannelShell()
if err != nil {
t.Fatalf("unable make channel: %v", err)
}
require.NoError(t, err)
single := NewSingle(channel, nil)
@ -162,9 +156,8 @@ func TestUnpackAndRecoverMulti(t *testing.T) {
}
var b bytes.Buffer
if err := multi.PackToWriter(&b, keyRing); err != nil {
t.Fatalf("unable to pack multi: %v", err)
}
err := multi.PackToWriter(&b, keyRing)
require.NoError(t, err)
// Next, we'll pack the set of singles into a packed multi, and also
// create the set of interfaces we need to carry out the remainder of
@ -177,54 +170,47 @@ func TestUnpackAndRecoverMulti(t *testing.T) {
// If we make the channel restore fail, then the entire method should
// as well
chanRestorer.fail = true
err := UnpackAndRecoverMulti(
_, err = UnpackAndRecoverMulti(
packedMulti, keyRing, &chanRestorer, &peerConnector,
)
if err == nil {
t.Fatalf("restoration should have failed")
}
require.ErrorIs(t, err, errRestoreFail)
chanRestorer.fail = false
// If we make the peer connector fail, then the entire method should as
// well
peerConnector.fail = true
err = UnpackAndRecoverMulti(
_, err = UnpackAndRecoverMulti(
packedMulti, keyRing, &chanRestorer, &peerConnector,
)
if err == nil {
t.Fatalf("restoration should have failed")
}
require.ErrorIs(t, err, errConnectFail)
chanRestorer.callCount--
peerConnector.fail = false
// Next, we'll ensure that if all the interfaces function as expected,
// then the channels will properly be unpacked and restored.
err = UnpackAndRecoverMulti(
numRestored, err := UnpackAndRecoverMulti(
packedMulti, keyRing, &chanRestorer, &peerConnector,
)
require.NoError(t, err, "unable to recover chans")
require.NoError(t, err)
require.EqualValues(t, numSingles, numRestored)
// Both the restorer, and connector should have been called 10 times,
// once for each backup.
if chanRestorer.callCount != numSingles {
t.Fatalf("expected %v calls, instead got %v",
numSingles, chanRestorer.callCount)
}
if peerConnector.callCount != numSingles {
t.Fatalf("expected %v calls, instead got %v",
numSingles, peerConnector.callCount)
}
require.EqualValues(
t, numSingles, chanRestorer.callCount, "restorer call count",
)
require.EqualValues(
t, numSingles, peerConnector.callCount, "peer call count",
)
// If we modify the keyRing, then unpacking should fail.
keyRing.Fail = true
err = UnpackAndRecoverMulti(
_, err = UnpackAndRecoverMulti(
packedMulti, keyRing, &chanRestorer, &peerConnector,
)
if err == nil {
t.Fatalf("unpacking should have failed")
}
require.ErrorContains(t, err, "fail")
// TODO(roasbeef): verify proper call args
}

View File

@ -2,6 +2,7 @@ package chanbackup
import (
"bytes"
"errors"
"fmt"
"io"
"net"
@ -11,6 +12,7 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/fn/v2"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnencrypt"
"github.com/lightningnetwork/lnd/lnwire"
@ -52,8 +54,55 @@ const (
// SimpleTaprootVersion is a version that denotes this channel is using
// the musig2 based taproot commitment format.
SimpleTaprootVersion = 5
// TapscriptRootVersion is a version that denotes this is a MuSig2
// channel with a top level tapscript commitment.
TapscriptRootVersion = 6
// closeTxVersionMask is the byte mask used that is ORed to version byte
// on wire indicating that the backup has CloseTxInputs.
closeTxVersionMask = 1 << 7
)
// Encode returns encoding of the version to put into channel backup.
// Argument "closeTx" specifies if the backup includes force close transaction.
func (v SingleBackupVersion) Encode(closeTx bool) byte {
encoded := byte(v)
// If the backup includes closing transaction, set this bit in the
// encoded version.
if closeTx {
encoded |= closeTxVersionMask
}
return encoded
}
// DecodeVersion decodes the encoding of the version from a channel backup.
// It returns the version and if the backup includes the force close tx.
func DecodeVersion(encoded byte) (SingleBackupVersion, bool) {
// Find if it has a closing transaction by inspecting the bit.
closeTx := (encoded & closeTxVersionMask) != 0
// The version byte also encodes the closeTxVersion feature, so we
// extract it here and return it separately to the backup version.
version := SingleBackupVersion(encoded &^ closeTxVersionMask)
return version, closeTx
}
// IsTaproot returns if this is a backup of a taproot channel. This will also be
// true for simple taproot overlay channels when a version is added.
func (v SingleBackupVersion) IsTaproot() bool {
return v == SimpleTaprootVersion || v == TapscriptRootVersion
}
// HasTapscriptRoot returns true if the channel is using a top level tapscript
// root commitment.
func (v SingleBackupVersion) HasTapscriptRoot() bool {
return v == TapscriptRootVersion
}
// Single is a static description of an existing channel that can be used for
// the purposes of backing up. The fields in this struct allow a node to
// recover the settled funds within a channel in the case of partial or
@ -138,11 +187,47 @@ type Single struct {
//
// - ScriptEnforcedLeaseVersion
LeaseExpiry uint32
// CloseTxInputs contains data needed to produce a force close tx
// using for example the "chantools scbforceclose" command.
//
// The field is optional.
CloseTxInputs fn.Option[CloseTxInputs]
}
// CloseTxInputs contains data needed to produce a force close transaction
// using for example the "chantools scbforceclose" command.
type CloseTxInputs struct {
// CommitTx is the latest version of the commitment state, broadcast
// able by us, but not signed. It can be signed by for example the
// "chantools scbforceclose" command.
CommitTx *wire.MsgTx
// CommitSig is one half of the signature required to fully complete
// the script for the commitment transaction above. This is the
// signature signed by the remote party for our version of the
// commitment transactions.
CommitSig []byte
// CommitHeight is the update number that this ChannelDelta represents
// the total number of commitment updates to this point. This can be
// viewed as sort of a "commitment height" as this number is
// monotonically increasing.
//
// This field is filled only for taproot channels.
CommitHeight uint64
// TapscriptRoot is the root of the tapscript tree that will be used to
// create the funding output. This is an optional field that should
// only be set for overlay taproot channels (HasTapscriptRoot).
TapscriptRoot fn.Option[chainhash.Hash]
}
// NewSingle creates a new static channel backup based on an existing open
// channel. We also pass in the set of addresses that we used in the past to
// connect to the channel peer.
// connect to the channel peer. If possible, we include the data needed to
// produce a force close transaction from the most recent state using externally
// provided private key.
func NewSingle(channel *channeldb.OpenChannel,
nodeAddrs []net.Addr) Single {
@ -218,7 +303,11 @@ func NewSingle(channel *channeldb.OpenChannel,
switch {
case channel.ChanType.IsTaproot():
single.Version = SimpleTaprootVersion
if channel.ChanType.HasTapscriptRoot() {
single.Version = TapscriptRootVersion
} else {
single.Version = SimpleTaprootVersion
}
case channel.ChanType.HasLeaseExpiration():
single.Version = ScriptEnforcedLeaseVersion
@ -237,9 +326,18 @@ func NewSingle(channel *channeldb.OpenChannel,
single.Version = DefaultSingleVersion
}
// Include unsigned force-close transaction for the most recent channel
// state as well as the data needed to produce the signature, given the
// private key is provided separately.
single.CloseTxInputs = buildCloseTxInputs(channel)
return single
}
// errEmptyTapscriptRoot is returned by Serialize if field TapscriptRoot is
// empty, when it should be filled according to the channel version.
var errEmptyTapscriptRoot = errors.New("field TapscriptRoot is not filled")
// Serialize attempts to write out the serialized version of the target
// StaticChannelBackup into the passed io.Writer.
func (s *Single) Serialize(w io.Writer) error {
@ -252,6 +350,7 @@ func (s *Single) Serialize(w io.Writer) error {
case AnchorsZeroFeeHtlcTxCommitVersion:
case ScriptEnforcedLeaseVersion:
case SimpleTaprootVersion:
case TapscriptRootVersion:
default:
return fmt.Errorf("unable to serialize w/ unknown "+
"version: %v", s.Version)
@ -320,6 +419,60 @@ func (s *Single) Serialize(w io.Writer) error {
}
}
// Encode version enum and hasCloseTx flag to version byte.
version := s.Version.Encode(s.CloseTxInputs.IsSome())
// Serialize CloseTxInputs if it is provided. Fill err if it fails.
err := fn.MapOptionZ(s.CloseTxInputs, func(inputs CloseTxInputs) error {
err := inputs.CommitTx.Serialize(&singleBytes)
if err != nil {
return err
}
err = lnwire.WriteElements(
&singleBytes,
uint16(len(inputs.CommitSig)), inputs.CommitSig,
)
if err != nil {
return err
}
if !s.Version.IsTaproot() {
return nil
}
// Write fields needed for taproot channels.
err = lnwire.WriteElements(
&singleBytes, inputs.CommitHeight,
)
if err != nil {
return err
}
if s.Version.HasTapscriptRoot() {
opt := inputs.TapscriptRoot
var tapscriptRoot chainhash.Hash
tapscriptRoot, err = opt.UnwrapOrErr(
errEmptyTapscriptRoot,
)
if err != nil {
return err
}
err = lnwire.WriteElements(
&singleBytes, tapscriptRoot[:],
)
if err != nil {
return err
}
}
return nil
})
if err != nil {
return fmt.Errorf("failed to encode CloseTxInputs: %w", err)
}
// TODO(yy): remove the type assertion when we finished refactoring db
// into using write buffer.
buf, ok := w.(*bytes.Buffer)
@ -329,7 +482,7 @@ func (s *Single) Serialize(w io.Writer) error {
return lnwire.WriteElements(
buf,
byte(s.Version),
version,
uint16(len(singleBytes.Bytes())),
singleBytes.Bytes(),
)
@ -420,7 +573,9 @@ func (s *Single) Deserialize(r io.Reader) error {
return err
}
s.Version = SingleBackupVersion(version)
// Decode version byte to version enum and hasCloseTx flag.
var hasCloseTx bool
s.Version, hasCloseTx = DecodeVersion(version)
switch s.Version {
case DefaultSingleVersion:
@ -429,6 +584,7 @@ func (s *Single) Deserialize(r io.Reader) error {
case AnchorsZeroFeeHtlcTxCommitVersion:
case ScriptEnforcedLeaseVersion:
case SimpleTaprootVersion:
case TapscriptRootVersion:
default:
return fmt.Errorf("unable to de-serialize w/ unknown "+
"version: %v", s.Version)
@ -533,6 +689,50 @@ func (s *Single) Deserialize(r io.Reader) error {
}
}
if !hasCloseTx {
return nil
}
// Deserialize CloseTxInputs if it is present in serialized data.
commitTx := &wire.MsgTx{}
if err := commitTx.Deserialize(r); err != nil {
return err
}
var commitSigLen uint16
if err := lnwire.ReadElement(r, &commitSigLen); err != nil {
return err
}
commitSig := make([]byte, commitSigLen)
if err := lnwire.ReadElement(r, commitSig); err != nil {
return err
}
var commitHeight uint64
if s.Version.IsTaproot() {
err := lnwire.ReadElement(r, &commitHeight)
if err != nil {
return err
}
}
tapscriptRootOpt := fn.None[chainhash.Hash]()
if s.Version.HasTapscriptRoot() {
var tapscriptRoot chainhash.Hash
err := lnwire.ReadElement(r, tapscriptRoot[:])
if err != nil {
return err
}
tapscriptRootOpt = fn.Some(tapscriptRoot)
}
s.CloseTxInputs = fn.Some(CloseTxInputs{
CommitTx: commitTx,
CommitSig: commitSig,
CommitHeight: commitHeight,
TapscriptRoot: tapscriptRootOpt,
})
return nil
}

View File

@ -13,10 +13,12 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/fn/v2"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnencrypt"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/shachain"
"github.com/lightningnetwork/lnd/tor"
"github.com/stretchr/testify/require"
)
@ -35,6 +37,15 @@ var (
addr1, _ = net.ResolveTCPAddr("tcp", "10.0.0.2:9000")
addr2, _ = net.ResolveTCPAddr("tcp", "10.0.0.3:9000")
addr3 = &tor.OnionAddr{
OnionService: "3g2upl4pq6kufc4m.onion",
Port: 9735,
}
addr4 = &lnwire.OpaqueAddrs{
// The first byte must be an address type we are not yet aware
// of for it to be a valid OpaqueAddrs.
Payload: []byte{math.MaxUint8, 1, 2, 3, 4},
}
)
func assertSingleEqual(t *testing.T, a, b Single) {
@ -95,6 +106,29 @@ func assertSingleEqual(t *testing.T, a, b Single) {
a.Addresses[i], b.Addresses[i])
}
}
// Make sure that CloseTxInputs are present either in both backups,
// or in none of them.
require.Equal(t, a.CloseTxInputs.IsSome(), b.CloseTxInputs.IsSome())
if a.CloseTxInputs.IsSome() {
// Cache CloseTxInputs into short variables.
ai := a.CloseTxInputs.UnwrapOrFail(t)
bi := b.CloseTxInputs.UnwrapOrFail(t)
// Compare serialized unsigned transactions.
var abuf, bbuf bytes.Buffer
require.NoError(t, ai.CommitTx.Serialize(&abuf))
require.NoError(t, bi.CommitTx.Serialize(&bbuf))
aBytes := abuf.Bytes()
bBytes := bbuf.Bytes()
require.Equal(t, aBytes, bBytes)
// Compare counterparty's signature and commit height.
require.Equal(t, ai.CommitSig, bi.CommitSig)
require.Equal(t, ai.CommitHeight, bi.CommitHeight)
require.Equal(t, ai.TapscriptRoot, bi.TapscriptRoot)
}
}
func genRandomOpenChannelShell() (*channeldb.OpenChannel, error) {
@ -124,7 +158,7 @@ func genRandomOpenChannelShell() (*channeldb.OpenChannel, error) {
isInitiator = true
}
chanType := channeldb.ChannelType(rand.Intn(8))
chanType := channeldb.ChannelType(rand.Intn(1 << 12))
localCfg := channeldb.ChannelConfig{
ChannelStateBounds: channeldb.ChannelStateBounds{},
@ -184,6 +218,29 @@ func genRandomOpenChannelShell() (*channeldb.OpenChannel, error) {
},
}
var localCommit channeldb.ChannelCommitment
if chanType.IsTaproot() {
var commitSig [64]byte
if _, err := rand.Read(commitSig[:]); err != nil {
return nil, err
}
localCommit = channeldb.ChannelCommitment{
CommitTx: sampleCommitTx,
CommitSig: commitSig[:],
CommitHeight: rand.Uint64(),
}
}
var tapscriptRootOption fn.Option[chainhash.Hash]
if chanType.HasTapscriptRoot() {
var tapscriptRoot chainhash.Hash
if _, err := rand.Read(tapscriptRoot[:]); err != nil {
return nil, err
}
tapscriptRootOption = fn.Some(tapscriptRoot)
}
return &channeldb.OpenChannel{
ChainHash: chainHash,
ChanType: chanType,
@ -196,10 +253,61 @@ func genRandomOpenChannelShell() (*channeldb.OpenChannel, error) {
IdentityPub: pub,
LocalChanCfg: localCfg,
RemoteChanCfg: remoteCfg,
LocalCommitment: localCommit,
RevocationProducer: shaChainProducer,
TapscriptRoot: tapscriptRootOption,
}, nil
}
// TestVersionEncoding tests encoding and decoding of version byte.
func TestVersionEncoding(t *testing.T) {
cases := []struct {
version SingleBackupVersion
hasCloseTx bool
versionByte byte
}{
{
version: DefaultSingleVersion,
hasCloseTx: false,
versionByte: DefaultSingleVersion,
},
{
version: DefaultSingleVersion,
hasCloseTx: true,
versionByte: DefaultSingleVersion | closeTxVersionMask,
},
{
version: AnchorsCommitVersion,
hasCloseTx: false,
versionByte: AnchorsCommitVersion,
},
{
version: AnchorsCommitVersion,
hasCloseTx: true,
versionByte: AnchorsCommitVersion | closeTxVersionMask,
},
}
for _, tc := range cases {
gotVersionByte := tc.version.Encode(tc.hasCloseTx)
require.Equal(t, tc.versionByte, gotVersionByte)
gotVersion, gotHasCloseTx := DecodeVersion(tc.versionByte)
require.Equal(t, tc.version, gotVersion)
require.Equal(t, tc.hasCloseTx, gotHasCloseTx)
}
}
var sampleCommitTx = &wire.MsgTx{
TxIn: []*wire.TxIn{
{PreviousOutPoint: wire.OutPoint{Hash: [32]byte{1}}},
},
TxOut: []*wire.TxOut{
{Value: 1e8, PkScript: []byte("1")},
{Value: 2e8, PkScript: []byte("2")},
},
}
// TestSinglePackUnpack tests that we're able to unpack a previously packed
// channel backup.
func TestSinglePackUnpack(t *testing.T) {
@ -211,7 +319,9 @@ func TestSinglePackUnpack(t *testing.T) {
channel, err := genRandomOpenChannelShell()
require.NoError(t, err, "unable to gen open channel")
singleChanBackup := NewSingle(channel, []net.Addr{addr1, addr2})
singleChanBackup := NewSingle(
channel, []net.Addr{addr1, addr2, addr3, addr4},
)
keyRing := &lnencrypt.MockKeyRing{}
@ -220,6 +330,9 @@ func TestSinglePackUnpack(t *testing.T) {
// decode/encode the final SCB.
version SingleBackupVersion
// closeTxInputs is the data needed to produce a force close tx.
closeTxInputs fn.Option[CloseTxInputs]
// valid tests us if this test case should pass or not.
valid bool
}{
@ -250,16 +363,111 @@ func TestSinglePackUnpack(t *testing.T) {
valid: true,
},
// The new taproot channel version should
// pack/unpack with no problem.
{
version: SimpleTaprootVersion,
valid: true,
},
// The new tapscript root channel version should pack/unpack
// with no problem.
{
version: TapscriptRootVersion,
valid: true,
},
// A non-default version, atm this should result in a failure.
{
version: 99,
valid: false,
},
// Versions with CloseTxInputs.
{
version: DefaultSingleVersion,
closeTxInputs: fn.Some(CloseTxInputs{
CommitTx: sampleCommitTx,
CommitSig: []byte("signature"),
}),
valid: true,
},
{
version: TweaklessCommitVersion,
closeTxInputs: fn.Some(CloseTxInputs{
CommitTx: sampleCommitTx,
CommitSig: []byte("signature"),
}),
valid: true,
},
{
version: AnchorsCommitVersion,
closeTxInputs: fn.Some(CloseTxInputs{
CommitTx: sampleCommitTx,
CommitSig: []byte("signature"),
}),
valid: true,
},
{
version: ScriptEnforcedLeaseVersion,
closeTxInputs: fn.Some(CloseTxInputs{
CommitTx: sampleCommitTx,
CommitSig: []byte("signature"),
}),
valid: true,
},
{
version: SimpleTaprootVersion,
closeTxInputs: fn.Some(CloseTxInputs{
CommitTx: sampleCommitTx,
CommitSig: []byte("signature"),
CommitHeight: 42,
}),
valid: true,
},
{
version: TapscriptRootVersion,
closeTxInputs: fn.Some(CloseTxInputs{
CommitTx: sampleCommitTx,
CommitSig: []byte("signature"),
CommitHeight: 42,
TapscriptRoot: fn.Some(chainhash.Hash{1}),
}),
valid: true,
},
{
version: 99,
closeTxInputs: fn.Some(CloseTxInputs{
CommitTx: sampleCommitTx,
CommitSig: []byte("signature"),
}),
valid: false,
},
{
version: 99,
closeTxInputs: fn.Some(CloseTxInputs{
CommitTx: sampleCommitTx,
CommitSig: []byte("signature"),
CommitHeight: 42,
}),
valid: false,
},
{
version: TapscriptRootVersion,
closeTxInputs: fn.Some(CloseTxInputs{
CommitTx: sampleCommitTx,
CommitSig: []byte("signature"),
CommitHeight: 42,
// TapscriptRoot is not filled.
}),
valid: false,
},
}
for i, versionCase := range versionTestCases {
// First, we'll re-assign SCB version to what was indicated in
// the test case.
singleChanBackup.Version = versionCase.version
singleChanBackup.CloseTxInputs = versionCase.closeTxInputs
var b bytes.Buffer
@ -438,7 +646,9 @@ func TestSingleUnconfirmedChannel(t *testing.T) {
channel.ShortChannelID.BlockHeight = 0
channel.FundingBroadcastHeight = fundingBroadcastHeight
singleChanBackup := NewSingle(channel, []net.Addr{addr1, addr2})
singleChanBackup := NewSingle(
channel, []net.Addr{addr1, addr2, addr3, addr4},
)
keyRing := &lnencrypt.MockKeyRing{}
// Pack it and then unpack it again to make sure everything is written

View File

@ -1,7 +1,7 @@
package chanfitness
import (
"github.com/btcsuite/btclog"
"github.com/btcsuite/btclog/v2"
"github.com/lightningnetwork/lnd/build"
)

View File

@ -2,24 +2,13 @@ package lnd
import (
"fmt"
"net"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/chanbackup"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/channelnotifier"
)
// addrSource is an interface that allow us to get the addresses for a target
// node. We'll need this in order to be able to properly proxy the
// notifications to create SCBs.
type addrSource interface {
// AddrsForNode returns all known addresses for the target node public
// key.
AddrsForNode(nodePub *btcec.PublicKey) ([]net.Addr, error)
}
// channelNotifier is an implementation of the chanbackup.ChannelNotifier
// interface using the existing channelnotifier.ChannelNotifier struct. This
// implementation allows us to satisfy all the dependencies of the
@ -32,7 +21,7 @@ type channelNotifier struct {
// addrs is an implementation of the addrSource interface that allows
// us to get the latest set of addresses for a given node. We'll need
// this to be able to create an SCB for new channels.
addrs addrSource
addrs channeldb.AddrSource
}
// SubscribeChans requests a new channel subscription relative to the initial
@ -56,7 +45,7 @@ func (c *channelNotifier) SubscribeChans(startingChans map[wire.OutPoint]struct{
// chanUpdates channel to inform subscribers about new pending or
// confirmed channels.
sendChanOpenUpdate := func(newOrPendingChan *channeldb.OpenChannel) {
nodeAddrs, err := c.addrs.AddrsForNode(
_, nodeAddrs, err := c.addrs.AddrsForNode(
newOrPendingChan.IdentityPub,
)
if err != nil {

83
channeldb/addr_source.go Normal file
View File

@ -0,0 +1,83 @@
package channeldb
import (
"errors"
"net"
"github.com/btcsuite/btcd/btcec/v2"
)
// AddrSource is an interface that allow us to get the addresses for a target
// node. It may combine the results of multiple address sources.
type AddrSource interface {
// AddrsForNode returns all known addresses for the target node public
// key. The returned boolean must indicate if the given node is unknown
// to the backing source.
AddrsForNode(nodePub *btcec.PublicKey) (bool, []net.Addr, error)
}
// multiAddrSource is an implementation of AddrSource which gathers all the
// known addresses for a given node from multiple backends and de-duplicates the
// results.
type multiAddrSource struct {
sources []AddrSource
}
// NewMultiAddrSource constructs a new AddrSource which will query all the
// provided sources for a node's addresses and will then de-duplicate the
// results.
func NewMultiAddrSource(sources ...AddrSource) AddrSource {
return &multiAddrSource{
sources: sources,
}
}
// AddrsForNode returns all known addresses for the target node public key. It
// queries all the address sources provided and de-duplicates the results. The
// returned boolean is false only if none of the backing sources know of the
// node.
//
// NOTE: this implements the AddrSource interface.
func (c *multiAddrSource) AddrsForNode(nodePub *btcec.PublicKey) (bool,
[]net.Addr, error) {
if len(c.sources) == 0 {
return false, nil, errors.New("no address sources")
}
// The multiple address sources will likely contain duplicate addresses,
// so we use a map here to de-dup them.
dedupedAddrs := make(map[string]net.Addr)
// known will be set to true if any backing source is aware of the node.
var known bool
// Iterate over all the address sources and query each one for the
// addresses it has for the node in question.
for _, src := range c.sources {
isKnown, addrs, err := src.AddrsForNode(nodePub)
if err != nil {
return false, nil, err
}
if isKnown {
known = true
}
for _, addr := range addrs {
dedupedAddrs[addr.String()] = addr
}
}
// Convert the map into a list we can return.
addrs := make([]net.Addr, 0, len(dedupedAddrs))
for _, addr := range dedupedAddrs {
addrs = append(addrs, addr)
}
return known, addrs, nil
}
// A compile-time check to ensure that multiAddrSource implements the AddrSource
// interface.
var _ AddrSource = (*multiAddrSource)(nil)

View File

@ -0,0 +1,149 @@
package channeldb
import (
"net"
"testing"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
var (
addr1 = &net.TCPAddr{IP: (net.IP)([]byte{0x1}), Port: 1}
addr2 = &net.TCPAddr{IP: (net.IP)([]byte{0x2}), Port: 2}
addr3 = &net.TCPAddr{IP: (net.IP)([]byte{0x3}), Port: 3}
)
// TestMultiAddrSource tests that the multiAddrSource correctly merges and
// deduplicates the results of a set of AddrSource implementations.
func TestMultiAddrSource(t *testing.T) {
t.Parallel()
var pk1 = newTestPubKey(t)
t.Run("both sources have results", func(t *testing.T) {
t.Parallel()
var (
src1 = newMockAddrSource(t)
src2 = newMockAddrSource(t)
)
t.Cleanup(func() {
src1.AssertExpectations(t)
src2.AssertExpectations(t)
})
// Let source 1 know of 2 addresses (addr 1 and 2) for node 1.
src1.On("AddrsForNode", pk1).Return(
true, []net.Addr{addr1, addr2}, nil,
).Once()
// Let source 2 know of 2 addresses (addr 2 and 3) for node 1.
src2.On("AddrsForNode", pk1).Return(
true, []net.Addr{addr2, addr3}, nil,
[]net.Addr{addr2, addr3}, nil,
).Once()
// Create a multi-addr source that consists of both source 1
// and 2.
multiSrc := NewMultiAddrSource(src1, src2)
// Query it for the addresses known for node 1. The results
// should contain addr 1, 2 and 3.
known, addrs, err := multiSrc.AddrsForNode(pk1)
require.NoError(t, err)
require.True(t, known)
require.ElementsMatch(t, addrs, []net.Addr{addr1, addr2, addr3})
})
t.Run("only once source has results", func(t *testing.T) {
t.Parallel()
var (
src1 = newMockAddrSource(t)
src2 = newMockAddrSource(t)
)
t.Cleanup(func() {
src1.AssertExpectations(t)
src2.AssertExpectations(t)
})
// Let source 1 know of address 1 for node 1.
src1.On("AddrsForNode", pk1).Return(
true, []net.Addr{addr1}, nil,
).Once()
src2.On("AddrsForNode", pk1).Return(false, nil, nil).Once()
// Create a multi-addr source that consists of both source 1
// and 2.
multiSrc := NewMultiAddrSource(src1, src2)
// Query it for the addresses known for node 1. The results
// should contain addr 1.
known, addrs, err := multiSrc.AddrsForNode(pk1)
require.NoError(t, err)
require.True(t, known)
require.ElementsMatch(t, addrs, []net.Addr{addr1})
})
t.Run("unknown address", func(t *testing.T) {
t.Parallel()
var (
src1 = newMockAddrSource(t)
src2 = newMockAddrSource(t)
)
t.Cleanup(func() {
src1.AssertExpectations(t)
src2.AssertExpectations(t)
})
// Create a multi-addr source that consists of both source 1
// and 2. Neither source known of node 1.
multiSrc := NewMultiAddrSource(src1, src2)
src1.On("AddrsForNode", pk1).Return(false, nil, nil).Once()
src2.On("AddrsForNode", pk1).Return(false, nil, nil).Once()
// Query it for the addresses known for node 1. It should return
// false to indicate that the node is unknown to all backing
// sources.
known, addrs, err := multiSrc.AddrsForNode(pk1)
require.NoError(t, err)
require.False(t, known)
require.Empty(t, addrs)
})
}
type mockAddrSource struct {
t *testing.T
mock.Mock
}
var _ AddrSource = (*mockAddrSource)(nil)
func newMockAddrSource(t *testing.T) *mockAddrSource {
return &mockAddrSource{t: t}
}
func (m *mockAddrSource) AddrsForNode(pub *btcec.PublicKey) (bool, []net.Addr,
error) {
args := m.Called(pub)
if args.Get(1) == nil {
return args.Bool(0), nil, args.Error(2)
}
addrs, ok := args.Get(1).([]net.Addr)
require.True(m.t, ok)
return args.Bool(0), addrs, args.Error(2)
}
func newTestPubKey(t *testing.T) *btcec.PublicKey {
priv, err := btcec.NewPrivateKey()
require.NoError(t, err)
return priv.PubKey()
}

View File

@ -19,8 +19,9 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcwallet/walletdb"
"github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/fn/v2"
graphdb "github.com/lightningnetwork/lnd/graph/db"
"github.com/lightningnetwork/lnd/graph/db/models"
"github.com/lightningnetwork/lnd/htlcswitch/hop"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
@ -226,28 +227,109 @@ const (
// A tlv type definition used to serialize an outpoint's indexStatus
// for use in the outpoint index.
indexStatusType tlv.Type = 0
// A tlv type definition used to serialize and deserialize a KeyLocator
// from the database.
keyLocType tlv.Type = 1
// A tlv type used to serialize and deserialize the
// `InitialLocalBalance` field.
initialLocalBalanceType tlv.Type = 2
// A tlv type used to serialize and deserialize the
// `InitialRemoteBalance` field.
initialRemoteBalanceType tlv.Type = 3
// A tlv type definition used to serialize and deserialize the
// confirmed ShortChannelID for a zero-conf channel.
realScidType tlv.Type = 4
// A tlv type definition used to serialize and deserialize the
// Memo for the channel channel.
channelMemoType tlv.Type = 5
)
// openChannelTlvData houses the new data fields that are stored for each
// channel in a TLV stream within the root bucket. This is stored as a TLV
// stream appended to the existing hard-coded fields in the channel's root
// bucket. New fields being added to the channel state should be added here.
//
// NOTE: This struct is used for serialization purposes only and its fields
// should be accessed via the OpenChannel struct while in memory.
type openChannelTlvData struct {
// revokeKeyLoc is the key locator for the revocation key.
revokeKeyLoc tlv.RecordT[tlv.TlvType1, keyLocRecord]
// initialLocalBalance is the initial local balance of the channel.
initialLocalBalance tlv.RecordT[tlv.TlvType2, uint64]
// initialRemoteBalance is the initial remote balance of the channel.
initialRemoteBalance tlv.RecordT[tlv.TlvType3, uint64]
// realScid is the real short channel ID of the channel corresponding to
// the on-chain outpoint.
realScid tlv.RecordT[tlv.TlvType4, lnwire.ShortChannelID]
// memo is an optional text field that gives context to the user about
// the channel.
memo tlv.OptionalRecordT[tlv.TlvType5, []byte]
// tapscriptRoot is the optional Tapscript root the channel funding
// output commits to.
tapscriptRoot tlv.OptionalRecordT[tlv.TlvType6, [32]byte]
// customBlob is an optional TLV encoded blob of data representing
// custom channel funding information.
customBlob tlv.OptionalRecordT[tlv.TlvType7, tlv.Blob]
}
// encode serializes the openChannelTlvData to the given io.Writer.
func (c *openChannelTlvData) encode(w io.Writer) error {
tlvRecords := []tlv.Record{
c.revokeKeyLoc.Record(),
c.initialLocalBalance.Record(),
c.initialRemoteBalance.Record(),
c.realScid.Record(),
}
c.memo.WhenSome(func(memo tlv.RecordT[tlv.TlvType5, []byte]) {
tlvRecords = append(tlvRecords, memo.Record())
})
c.tapscriptRoot.WhenSome(
func(root tlv.RecordT[tlv.TlvType6, [32]byte]) {
tlvRecords = append(tlvRecords, root.Record())
},
)
c.customBlob.WhenSome(func(blob tlv.RecordT[tlv.TlvType7, tlv.Blob]) {
tlvRecords = append(tlvRecords, blob.Record())
})
// Create the tlv stream.
tlvStream, err := tlv.NewStream(tlvRecords...)
if err != nil {
return err
}
return tlvStream.Encode(w)
}
// decode deserializes the openChannelTlvData from the given io.Reader.
func (c *openChannelTlvData) decode(r io.Reader) error {
memo := c.memo.Zero()
tapscriptRoot := c.tapscriptRoot.Zero()
blob := c.customBlob.Zero()
// Create the tlv stream.
tlvStream, err := tlv.NewStream(
c.revokeKeyLoc.Record(),
c.initialLocalBalance.Record(),
c.initialRemoteBalance.Record(),
c.realScid.Record(),
memo.Record(),
tapscriptRoot.Record(),
blob.Record(),
)
if err != nil {
return err
}
tlvs, err := tlvStream.DecodeWithParsedTypes(r)
if err != nil {
return err
}
if _, ok := tlvs[memo.TlvType()]; ok {
c.memo = tlv.SomeRecordT(memo)
}
if _, ok := tlvs[tapscriptRoot.TlvType()]; ok {
c.tapscriptRoot = tlv.SomeRecordT(tapscriptRoot)
}
if _, ok := tlvs[c.customBlob.TlvType()]; ok {
c.customBlob = tlv.SomeRecordT(blob)
}
return nil
}
// indexStatus is an enum-like type that describes what state the
// outpoint is in. Currently only two possible values.
type indexStatus uint8
@ -325,6 +407,11 @@ const (
// SimpleTaprootFeatureBit indicates that the simple-taproot-chans
// feature bit was negotiated during the lifetime of the channel.
SimpleTaprootFeatureBit ChannelType = 1 << 10
// TapscriptRootBit indicates that this is a MuSig2 channel with a top
// level tapscript commitment. This MUST be set along with the
// SimpleTaprootFeatureBit.
TapscriptRootBit ChannelType = 1 << 11
)
// IsSingleFunder returns true if the channel type if one of the known single
@ -395,6 +482,12 @@ func (c ChannelType) IsTaproot() bool {
return c&SimpleTaprootFeatureBit == SimpleTaprootFeatureBit
}
// HasTapscriptRoot returns true if the channel is using a top level tapscript
// root commitment.
func (c ChannelType) HasTapscriptRoot() bool {
return c&TapscriptRootBit == TapscriptRootBit
}
// ChannelStateBounds are the parameters from OpenChannel and AcceptChannel
// that are responsible for providing bounds on the state space of the abstract
// channel state. These values must be remembered for normal channel operation
@ -496,6 +589,53 @@ type ChannelConfig struct {
HtlcBasePoint keychain.KeyDescriptor
}
// commitTlvData stores all the optional data that may be stored as a TLV stream
// at the _end_ of the normal serialized commit on disk.
type commitTlvData struct {
// customBlob is a custom blob that may store extra data for custom
// channels.
customBlob tlv.OptionalRecordT[tlv.TlvType1, tlv.Blob]
}
// encode encodes the aux data into the passed io.Writer.
func (c *commitTlvData) encode(w io.Writer) error {
var tlvRecords []tlv.Record
c.customBlob.WhenSome(func(blob tlv.RecordT[tlv.TlvType1, tlv.Blob]) {
tlvRecords = append(tlvRecords, blob.Record())
})
// Create the tlv stream.
tlvStream, err := tlv.NewStream(tlvRecords...)
if err != nil {
return err
}
return tlvStream.Encode(w)
}
// decode attempts to decode the aux data from the passed io.Reader.
func (c *commitTlvData) decode(r io.Reader) error {
blob := c.customBlob.Zero()
tlvStream, err := tlv.NewStream(
blob.Record(),
)
if err != nil {
return err
}
tlvs, err := tlvStream.DecodeWithParsedTypes(r)
if err != nil {
return err
}
if _, ok := tlvs[c.customBlob.TlvType()]; ok {
c.customBlob = tlv.SomeRecordT(blob)
}
return nil
}
// ChannelCommitment is a snapshot of the commitment state at a particular
// point in the commitment chain. With each state transition, a snapshot of the
// current state along with all non-settled HTLCs are recorded. These snapshots
@ -562,6 +702,11 @@ type ChannelCommitment struct {
// able by us.
CommitTx *wire.MsgTx
// CustomBlob is an optional blob that can be used to store information
// specific to a custom channel type. This may track some custom
// specific state for this given commitment.
CustomBlob fn.Option[tlv.Blob]
// CommitSig is one half of the signature required to fully complete
// the script for the commitment transaction above. This is the
// signature signed by the remote party for our version of the
@ -571,9 +716,26 @@ type ChannelCommitment struct {
// Htlcs is the set of HTLC's that are pending at this particular
// commitment height.
Htlcs []HTLC
}
// TODO(roasbeef): pending commit pointer?
// * lets just walk through
// amendTlvData updates the channel with the given auxiliary TLV data.
func (c *ChannelCommitment) amendTlvData(auxData commitTlvData) {
auxData.customBlob.WhenSomeV(func(blob tlv.Blob) {
c.CustomBlob = fn.Some(blob)
})
}
// extractTlvData creates a new commitTlvData from the given commitment.
func (c *ChannelCommitment) extractTlvData() commitTlvData {
var auxData commitTlvData
c.CustomBlob.WhenSome(func(blob tlv.Blob) {
auxData.customBlob = tlv.SomeRecordT(
tlv.NewPrimitiveRecord[tlv.TlvType1](blob),
)
})
return auxData
}
// ChannelStatus is a bit vector used to indicate whether an OpenChannel is in
@ -867,6 +1029,16 @@ type OpenChannel struct {
// channel that will be useful to our future selves.
Memo []byte
// TapscriptRoot is an optional tapscript root used to derive the MuSig2
// funding output.
TapscriptRoot fn.Option[chainhash.Hash]
// CustomBlob is an optional blob that can be used to store information
// specific to a custom channel type. This information is only created
// at channel funding time, and after wards is to be considered
// immutable.
CustomBlob fn.Option[tlv.Blob]
// TODO(roasbeef): eww
Db *ChannelStateDB
@ -1025,6 +1197,64 @@ func (c *OpenChannel) SetBroadcastHeight(height uint32) {
c.FundingBroadcastHeight = height
}
// amendTlvData updates the channel with the given auxiliary TLV data.
func (c *OpenChannel) amendTlvData(auxData openChannelTlvData) {
c.RevocationKeyLocator = auxData.revokeKeyLoc.Val.KeyLocator
c.InitialLocalBalance = lnwire.MilliSatoshi(
auxData.initialLocalBalance.Val,
)
c.InitialRemoteBalance = lnwire.MilliSatoshi(
auxData.initialRemoteBalance.Val,
)
c.confirmedScid = auxData.realScid.Val
auxData.memo.WhenSomeV(func(memo []byte) {
c.Memo = memo
})
auxData.tapscriptRoot.WhenSomeV(func(h [32]byte) {
c.TapscriptRoot = fn.Some[chainhash.Hash](h)
})
auxData.customBlob.WhenSomeV(func(blob tlv.Blob) {
c.CustomBlob = fn.Some(blob)
})
}
// extractTlvData creates a new openChannelTlvData from the given channel.
func (c *OpenChannel) extractTlvData() openChannelTlvData {
auxData := openChannelTlvData{
revokeKeyLoc: tlv.NewRecordT[tlv.TlvType1](
keyLocRecord{c.RevocationKeyLocator},
),
initialLocalBalance: tlv.NewPrimitiveRecord[tlv.TlvType2](
uint64(c.InitialLocalBalance),
),
initialRemoteBalance: tlv.NewPrimitiveRecord[tlv.TlvType3](
uint64(c.InitialRemoteBalance),
),
realScid: tlv.NewRecordT[tlv.TlvType4](
c.confirmedScid,
),
}
if len(c.Memo) != 0 {
auxData.memo = tlv.SomeRecordT(
tlv.NewPrimitiveRecord[tlv.TlvType5](c.Memo),
)
}
c.TapscriptRoot.WhenSome(func(h chainhash.Hash) {
auxData.tapscriptRoot = tlv.SomeRecordT(
tlv.NewPrimitiveRecord[tlv.TlvType6, [32]byte](h),
)
})
c.CustomBlob.WhenSome(func(blob tlv.Blob) {
auxData.customBlob = tlv.SomeRecordT(
tlv.NewPrimitiveRecord[tlv.TlvType7](blob),
)
})
return auxData
}
// Refresh updates the in-memory channel state using the latest state observed
// on disk.
func (c *OpenChannel) Refresh() error {
@ -1101,7 +1331,7 @@ func fetchChanBucket(tx kvdb.RTx, nodeKey *btcec.PublicKey,
// With the bucket for the node and chain fetched, we can now go down
// another level, for this channel itself.
var chanPointBuf bytes.Buffer
if err := writeOutpoint(&chanPointBuf, outPoint); err != nil {
if err := graphdb.WriteOutpoint(&chanPointBuf, outPoint); err != nil {
return nil, err
}
chanBucket := chainBucket.NestedReadBucket(chanPointBuf.Bytes())
@ -1148,7 +1378,7 @@ func fetchChanBucketRw(tx kvdb.RwTx, nodeKey *btcec.PublicKey,
// With the bucket for the node and chain fetched, we can now go down
// another level, for this channel itself.
var chanPointBuf bytes.Buffer
if err := writeOutpoint(&chanPointBuf, outPoint); err != nil {
if err := graphdb.WriteOutpoint(&chanPointBuf, outPoint); err != nil {
return nil, err
}
chanBucket := chainBucket.NestedReadWriteBucket(chanPointBuf.Bytes())
@ -1193,7 +1423,8 @@ func (c *OpenChannel) fullSync(tx kvdb.RwTx) error {
}
var chanPointBuf bytes.Buffer
if err := writeOutpoint(&chanPointBuf, &c.FundingOutpoint); err != nil {
err := graphdb.WriteOutpoint(&chanPointBuf, &c.FundingOutpoint)
if err != nil {
return err
}
@ -1460,7 +1691,7 @@ var (
// DeriveMusig2Shachain derives a shachain producer for the taproot channel
// from normal shachain revocation root.
func DeriveMusig2Shachain(revRoot shachain.Producer) (shachain.Producer, error) { //nolint:lll
func DeriveMusig2Shachain(revRoot shachain.Producer) (shachain.Producer, error) { //nolint:ll
// In order to obtain the revocation root hash to create the taproot
// revocation, we'll encode the producer into a buffer, then use that
// to derive the shachain root needed.
@ -2351,6 +2582,12 @@ type HTLC struct {
// HTLC. It is stored in the ExtraData field, which is used to store
// a TLV stream of additional information associated with the HTLC.
BlindingPoint lnwire.BlindingPointRecord
// CustomRecords is a set of custom TLV records that are associated with
// this HTLC. These records are used to store additional information
// about the HTLC that is not part of the standard HTLC fields. This
// field is encoded within the ExtraData field.
CustomRecords lnwire.CustomRecords
}
// serializeExtraData encodes a TLV stream of extra data to be stored with a
@ -2369,6 +2606,11 @@ func (h *HTLC) serializeExtraData() error {
records = append(records, &b)
})
records, err := h.CustomRecords.ExtendRecordProducers(records)
if err != nil {
return err
}
return h.ExtraData.PackRecords(records...)
}
@ -2390,8 +2632,19 @@ func (h *HTLC) deserializeExtraData() error {
if val, ok := tlvMap[h.BlindingPoint.TlvType()]; ok && val == nil {
h.BlindingPoint = tlv.SomeRecordT(blindingPoint)
// Remove the entry from the TLV map. Anything left in the map
// will be included in the custom records field.
delete(tlvMap, h.BlindingPoint.TlvType())
}
// Set the custom records field to the remaining TLV records.
customRecords, err := lnwire.NewCustomRecords(tlvMap)
if err != nil {
return err
}
h.CustomRecords = customRecords
return nil
}
@ -2529,6 +2782,8 @@ func (h *HTLC) Copy() HTLC {
copy(clone.Signature[:], h.Signature)
copy(clone.RHash[:], h.RHash[:])
copy(clone.ExtraData, h.ExtraData)
clone.BlindingPoint = h.BlindingPoint
clone.CustomRecords = h.CustomRecords.Copy()
return clone
}
@ -2690,6 +2945,14 @@ func serializeCommitDiff(w io.Writer, diff *CommitDiff) error { // nolint: dupl
}
}
// We'll also encode the commit aux data stream here. We do this here
// rather than above (at the call to serializeChanCommit), to ensure
// backwards compat for reads to existing non-custom channels.
auxData := diff.Commitment.extractTlvData()
if err := auxData.encode(w); err != nil {
return fmt.Errorf("unable to write aux data: %w", err)
}
return nil
}
@ -2750,6 +3013,17 @@ func deserializeCommitDiff(r io.Reader) (*CommitDiff, error) {
}
}
// As a final step, we'll read out any aux commit data that we have at
// the end of this byte stream. We do this here to ensure backward
// compatibility, as otherwise we risk erroneously reading into the
// wrong field.
var auxData commitTlvData
if err := auxData.decode(r); err != nil {
return nil, fmt.Errorf("unable to decode aux data: %w", err)
}
d.Commitment.amendTlvData(auxData)
return &d, nil
}
@ -2872,7 +3146,7 @@ func (c *OpenChannel) RemoteCommitChainTip() (*CommitDiff, error) {
return nil, err
}
return cd, err
return cd, nil
}
// UnsignedAckedUpdates retrieves the persisted unsigned acked remote log
@ -3550,7 +3824,7 @@ func (c *OpenChannel) CloseChannel(summary *ChannelCloseSummary,
}
var chanPointBuf bytes.Buffer
err := writeOutpoint(&chanPointBuf, &c.FundingOutpoint)
err := graphdb.WriteOutpoint(&chanPointBuf, &c.FundingOutpoint)
if err != nil {
return err
}
@ -3728,6 +4002,13 @@ func (c *OpenChannel) Snapshot() *ChannelSnapshot {
},
}
localCommit.CustomBlob.WhenSome(func(blob tlv.Blob) {
blobCopy := make([]byte, len(blob))
copy(blobCopy, blob)
snapshot.ChannelCommitment.CustomBlob = fn.Some(blobCopy)
})
// Copy over the current set of HTLCs to ensure the caller can't mutate
// our internal state.
snapshot.Htlcs = make([]HTLC, len(localCommit.Htlcs))
@ -3823,6 +4104,34 @@ func (c *OpenChannel) AbsoluteThawHeight() (uint32, error) {
return c.ThawHeight, nil
}
// DeriveHeightHint derives the block height for the channel opening.
func (c *OpenChannel) DeriveHeightHint() uint32 {
// As a height hint, we'll try to use the opening height, but if the
// channel isn't yet open, then we'll use the height it was broadcast
// at. This may be an unconfirmed zero-conf channel.
heightHint := c.ShortChanID().BlockHeight
if heightHint == 0 {
heightHint = c.BroadcastHeight()
}
// Since no zero-conf state is stored in a channel backup, the below
// logic will not be triggered for restored, zero-conf channels. Set
// the height hint for zero-conf channels.
if c.IsZeroConf() {
if c.ZeroConfConfirmed() {
// If the zero-conf channel is confirmed, we'll use the
// confirmed SCID's block height.
heightHint = c.ZeroConfRealScid().BlockHeight
} else {
// The zero-conf channel is unconfirmed. We'll need to
// use the FundingBroadcastHeight.
heightHint = c.BroadcastHeight()
}
}
return heightHint
}
func putChannelCloseSummary(tx kvdb.RwTx, chanID []byte,
summary *ChannelCloseSummary, lastChanState *OpenChannel) error {
@ -4030,32 +4339,9 @@ func putChanInfo(chanBucket kvdb.RwBucket, channel *OpenChannel) error {
return err
}
// Convert balance fields into uint64.
localBalance := uint64(channel.InitialLocalBalance)
remoteBalance := uint64(channel.InitialRemoteBalance)
// Create the tlv stream.
tlvStream, err := tlv.NewStream(
// Write the RevocationKeyLocator as the first entry in a tlv
// stream.
MakeKeyLocRecord(
keyLocType, &channel.RevocationKeyLocator,
),
tlv.MakePrimitiveRecord(
initialLocalBalanceType, &localBalance,
),
tlv.MakePrimitiveRecord(
initialRemoteBalanceType, &remoteBalance,
),
MakeScidRecord(realScidType, &channel.confirmedScid),
tlv.MakePrimitiveRecord(channelMemoType, &channel.Memo),
)
if err != nil {
return err
}
if err := tlvStream.Encode(&w); err != nil {
return err
auxData := channel.extractTlvData()
if err := auxData.encode(&w); err != nil {
return fmt.Errorf("unable to encode aux data: %w", err)
}
if err := chanBucket.Put(chanInfoKey, w.Bytes()); err != nil {
@ -4142,6 +4428,12 @@ func putChanCommitment(chanBucket kvdb.RwBucket, c *ChannelCommitment,
return err
}
// Before we write to disk, we'll also write our aux data as well.
auxData := c.extractTlvData()
if err := auxData.encode(&b); err != nil {
return fmt.Errorf("unable to write aux data: %w", err)
}
return chanBucket.Put(commitKey, b.Bytes())
}
@ -4244,45 +4536,14 @@ func fetchChanInfo(chanBucket kvdb.RBucket, channel *OpenChannel) error {
}
}
// Create balance fields in uint64, and Memo field as byte slice.
var (
localBalance uint64
remoteBalance uint64
memo []byte
)
// Create the tlv stream.
tlvStream, err := tlv.NewStream(
// Write the RevocationKeyLocator as the first entry in a tlv
// stream.
MakeKeyLocRecord(
keyLocType, &channel.RevocationKeyLocator,
),
tlv.MakePrimitiveRecord(
initialLocalBalanceType, &localBalance,
),
tlv.MakePrimitiveRecord(
initialRemoteBalanceType, &remoteBalance,
),
MakeScidRecord(realScidType, &channel.confirmedScid),
tlv.MakePrimitiveRecord(channelMemoType, &memo),
)
if err != nil {
return err
var auxData openChannelTlvData
if err := auxData.decode(r); err != nil {
return fmt.Errorf("unable to decode aux data: %w", err)
}
if err := tlvStream.Decode(r); err != nil {
return err
}
// Attach the balance fields.
channel.InitialLocalBalance = lnwire.MilliSatoshi(localBalance)
channel.InitialRemoteBalance = lnwire.MilliSatoshi(remoteBalance)
// Attach the memo field if non-empty.
if len(memo) > 0 {
channel.Memo = memo
}
// Assign all the relevant fields from the aux data into the actual
// open channel.
channel.amendTlvData(auxData)
channel.Packager = NewChannelPackager(channel.ShortChannelID)
@ -4318,7 +4579,9 @@ func deserializeChanCommit(r io.Reader) (ChannelCommitment, error) {
return c, nil
}
func fetchChanCommitment(chanBucket kvdb.RBucket, local bool) (ChannelCommitment, error) {
func fetchChanCommitment(chanBucket kvdb.RBucket,
local bool) (ChannelCommitment, error) {
var commitKey []byte
if local {
commitKey = append(chanCommitmentKey, byte(0x00))
@ -4332,7 +4595,23 @@ func fetchChanCommitment(chanBucket kvdb.RBucket, local bool) (ChannelCommitment
}
r := bytes.NewReader(commitBytes)
return deserializeChanCommit(r)
chanCommit, err := deserializeChanCommit(r)
if err != nil {
return ChannelCommitment{}, fmt.Errorf("unable to decode "+
"chan commit: %w", err)
}
// We'll also check to see if we have any aux data stored as the end of
// the stream.
var auxData commitTlvData
if err := auxData.decode(r); err != nil {
return ChannelCommitment{}, fmt.Errorf("unable to decode "+
"chan aux data: %w", err)
}
chanCommit.amendTlvData(auxData)
return chanCommit, nil
}
func fetchChanCommitments(chanBucket kvdb.RBucket, channel *OpenChannel) error {
@ -4440,6 +4719,25 @@ func deleteThawHeight(chanBucket kvdb.RwBucket) error {
return chanBucket.Delete(frozenChanKey)
}
// keyLocRecord is a wrapper struct around keychain.KeyLocator to implement the
// tlv.RecordProducer interface.
type keyLocRecord struct {
keychain.KeyLocator
}
// Record creates a Record out of a KeyLocator using the passed Type and the
// EKeyLocator and DKeyLocator functions. The size will always be 8 as
// KeyFamily is uint32 and the Index is uint32.
//
// NOTE: This is part of the tlv.RecordProducer interface.
func (k *keyLocRecord) Record() tlv.Record {
// Note that we set the type here as zero, as when used with a
// tlv.RecordT, the type param will be used as the type.
return tlv.MakeStaticRecord(
0, &k.KeyLocator, 8, EKeyLocator, DKeyLocator,
)
}
// EKeyLocator is an encoder for keychain.KeyLocator.
func EKeyLocator(w io.Writer, val interface{}, buf *[8]byte) error {
if v, ok := val.(*keychain.KeyLocator); ok {
@ -4468,22 +4766,6 @@ func DKeyLocator(r io.Reader, val interface{}, buf *[8]byte, l uint64) error {
return tlv.NewTypeForDecodingErr(val, "keychain.KeyLocator", l, 8)
}
// MakeKeyLocRecord creates a Record out of a KeyLocator using the passed
// Type and the EKeyLocator and DKeyLocator functions. The size will always be
// 8 as KeyFamily is uint32 and the Index is uint32.
func MakeKeyLocRecord(typ tlv.Type, keyLoc *keychain.KeyLocator) tlv.Record {
return tlv.MakeStaticRecord(typ, keyLoc, 8, EKeyLocator, DKeyLocator)
}
// MakeScidRecord creates a Record out of a ShortChannelID using the passed
// Type and the EShortChannelID and DShortChannelID functions. The size will
// always be 8 for the ShortChannelID.
func MakeScidRecord(typ tlv.Type, scid *lnwire.ShortChannelID) tlv.Record {
return tlv.MakeStaticRecord(
typ, scid, 8, lnwire.EShortChannelID, lnwire.DShortChannelID,
)
}
// ShutdownInfo contains various info about the shutdown initiation of a
// channel.
type ShutdownInfo struct {

View File

@ -2,6 +2,7 @@ package channeldb
import (
"bytes"
"encoding/hex"
"math/rand"
"net"
"reflect"
@ -10,19 +11,22 @@ import (
"testing"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
_ "github.com/btcsuite/btcwallet/walletdb/bdb"
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/clock"
"github.com/lightningnetwork/lnd/fn/v2"
"github.com/lightningnetwork/lnd/graph/db/models"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/lnmock"
"github.com/lightningnetwork/lnd/lntest/channels"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/shachain"
"github.com/lightningnetwork/lnd/tlv"
"github.com/stretchr/testify/require"
@ -42,8 +46,20 @@ var (
}
privKey, pubKey = btcec.PrivKeyFromBytes(key[:])
testRBytes, _ = hex.DecodeString("8ce2bc69281ce27da07e6683571319d18e" +
"949ddfa2965fb6caa1bf0314f882d7")
testSBytes, _ = hex.DecodeString("299105481d63e0f4bc2a88121167221b67" +
"00d72a0ead154c03be696a292d24ae")
testRScalar = new(btcec.ModNScalar)
testSScalar = new(btcec.ModNScalar)
_ = testRScalar.SetByteSlice(testRBytes)
_ = testSScalar.SetByteSlice(testSBytes)
testSig = ecdsa.NewSignature(testRScalar, testSScalar)
wireSig, _ = lnwire.NewSigFromSignature(testSig)
testPub = route.Vertex{2, 202, 4}
testClock = clock.NewTestClock(testNow)
// defaultPendingHeight is the default height at which we set
@ -91,6 +107,10 @@ type testChannelParams struct {
// openChannel is set to true if the channel should be fully marked as
// open if this is false, the channel will be left in pending state.
openChannel bool
// closedChannel is set to true if the channel should be marked as
// closed after opening it.
closedChannel bool
}
// testChannelOption is a functional option which can be used to alter the
@ -113,6 +133,21 @@ func openChannelOption() testChannelOption {
}
}
// closedChannelOption is an option which can be used to create a test channel
// that is closed.
func closedChannelOption() testChannelOption {
return func(params *testChannelParams) {
params.closedChannel = true
}
}
// pubKeyOption is an option which can be used to set the remote's pubkey.
func pubKeyOption(pubKey *btcec.PublicKey) testChannelOption {
return func(params *testChannelParams) {
params.channel.IdentityPub = pubKey
}
}
// localHtlcsOption is an option which allows setting of htlcs on the local
// commitment.
func localHtlcsOption(htlcs []HTLC) testChannelOption {
@ -173,7 +208,7 @@ func fundingPointOption(chanPoint wire.OutPoint) testChannelOption {
}
// channelIDOption is an option which sets the short channel ID of the channel.
var channelIDOption = func(chanID lnwire.ShortChannelID) testChannelOption {
func channelIDOption(chanID lnwire.ShortChannelID) testChannelOption {
return func(params *testChannelParams) {
params.channel.ShortChannelID = chanID
}
@ -215,6 +250,17 @@ func createTestChannel(t *testing.T, cdb *ChannelStateDB,
err = params.channel.MarkAsOpen(params.channel.ShortChannelID)
require.NoError(t, err, "unable to mark channel open")
if params.closedChannel {
// Set the other public keys so that serialization doesn't
// panic.
err = params.channel.CloseChannel(&ChannelCloseSummary{
RemotePub: params.channel.IdentityPub,
RemoteCurrentRevocation: params.channel.IdentityPub,
RemoteNextRevocation: params.channel.IdentityPub,
})
require.NoError(t, err, "unable to close channel")
}
return params.channel
}
@ -326,6 +372,9 @@ func createTestChannelState(t *testing.T, cdb *ChannelStateDB) *OpenChannel {
uniqueOutputIndex.Add(1)
op := wire.OutPoint{Hash: key, Index: uniqueOutputIndex.Load()}
var tapscriptRoot chainhash.Hash
copy(tapscriptRoot[:], bytes.Repeat([]byte{1}, 32))
return &OpenChannel{
ChanType: SingleFunderBit | FrozenBit,
ChainHash: key,
@ -347,6 +396,7 @@ func createTestChannelState(t *testing.T, cdb *ChannelStateDB) *OpenChannel {
FeePerKw: btcutil.Amount(5000),
CommitTx: channels.TestFundingTx,
CommitSig: bytes.Repeat([]byte{1}, 71),
CustomBlob: fn.Some([]byte{1, 2, 3}),
},
RemoteCommitment: ChannelCommitment{
CommitHeight: 0,
@ -356,6 +406,7 @@ func createTestChannelState(t *testing.T, cdb *ChannelStateDB) *OpenChannel {
FeePerKw: btcutil.Amount(5000),
CommitTx: channels.TestFundingTx,
CommitSig: bytes.Repeat([]byte{1}, 71),
CustomBlob: fn.Some([]byte{4, 5, 6}),
},
NumConfsRequired: 4,
RemoteCurrentRevocation: privKey.PubKey(),
@ -368,6 +419,9 @@ func createTestChannelState(t *testing.T, cdb *ChannelStateDB) *OpenChannel {
ThawHeight: uint32(defaultPendingHeight),
InitialLocalBalance: lnwire.MilliSatoshi(9000),
InitialRemoteBalance: lnwire.MilliSatoshi(3000),
Memo: []byte("test"),
TapscriptRoot: fn.Some(tapscriptRoot),
CustomBlob: fn.Some([]byte{1, 2, 3}),
}
}
@ -575,24 +629,32 @@ func assertCommitmentEqual(t *testing.T, a, b *ChannelCommitment) {
func assertRevocationLogEntryEqual(t *testing.T, c *ChannelCommitment,
r *RevocationLog) {
t.Helper()
// Check the common fields.
require.EqualValues(
t, r.CommitTxHash, c.CommitTx.TxHash(), "CommitTx mismatch",
t, r.CommitTxHash.Val, c.CommitTx.TxHash(), "CommitTx mismatch",
)
// Now check the common fields from the HTLCs.
require.Equal(t, len(r.HTLCEntries), len(c.Htlcs), "HTLCs len mismatch")
for i, rHtlc := range r.HTLCEntries {
cHtlc := c.Htlcs[i]
require.Equal(t, rHtlc.RHash, cHtlc.RHash, "RHash mismatch")
require.Equal(t, rHtlc.Amt, cHtlc.Amt.ToSatoshis(),
"Amt mismatch")
require.Equal(t, rHtlc.RefundTimeout, cHtlc.RefundTimeout,
"RefundTimeout mismatch")
require.EqualValues(t, rHtlc.OutputIndex, cHtlc.OutputIndex,
"OutputIndex mismatch")
require.Equal(t, rHtlc.Incoming, cHtlc.Incoming,
"Incoming mismatch")
require.Equal(t, rHtlc.RHash.Val[:], cHtlc.RHash[:], "RHash")
require.Equal(
t, rHtlc.Amt.Val.Int(), cHtlc.Amt.ToSatoshis(), "Amt",
)
require.Equal(
t, rHtlc.RefundTimeout.Val, cHtlc.RefundTimeout,
"RefundTimeout",
)
require.EqualValues(
t, rHtlc.OutputIndex.Val, cHtlc.OutputIndex,
"OutputIndex",
)
require.Equal(
t, rHtlc.Incoming.Val, cHtlc.Incoming, "Incoming",
)
}
}
@ -657,6 +719,7 @@ func TestChannelStateTransition(t *testing.T) {
CommitTx: newTx,
CommitSig: newSig,
Htlcs: htlcs,
CustomBlob: fn.Some([]byte{4, 5, 6}),
}
// First update the local node's broadcastable state and also add a
@ -694,9 +757,14 @@ func TestChannelStateTransition(t *testing.T) {
// have been updated.
updatedChannel, err := cdb.FetchOpenChannels(channel.IdentityPub)
require.NoError(t, err, "unable to fetch updated channel")
assertCommitmentEqual(t, &commitment, &updatedChannel[0].LocalCommitment)
assertCommitmentEqual(
t, &commitment, &updatedChannel[0].LocalCommitment,
)
numDiskUpdates, err := updatedChannel[0].CommitmentHeight()
require.NoError(t, err, "unable to read commitment height from disk")
if numDiskUpdates != uint64(commitment.CommitHeight) {
t.Fatalf("num disk updates doesn't match: %v vs %v",
numDiskUpdates, commitment.CommitHeight)
@ -799,10 +867,10 @@ func TestChannelStateTransition(t *testing.T) {
// Check the output indexes are saved as expected.
require.EqualValues(
t, dummyLocalOutputIndex, diskPrevCommit.OurOutputIndex,
t, dummyLocalOutputIndex, diskPrevCommit.OurOutputIndex.Val,
)
require.EqualValues(
t, dummyRemoteOutIndex, diskPrevCommit.TheirOutputIndex,
t, dummyRemoteOutIndex, diskPrevCommit.TheirOutputIndex.Val,
)
// The two deltas (the original vs the on-disk version) should
@ -844,10 +912,10 @@ func TestChannelStateTransition(t *testing.T) {
// Check the output indexes are saved as expected.
require.EqualValues(
t, dummyLocalOutputIndex, diskPrevCommit.OurOutputIndex,
t, dummyLocalOutputIndex, diskPrevCommit.OurOutputIndex.Val,
)
require.EqualValues(
t, dummyRemoteOutIndex, diskPrevCommit.TheirOutputIndex,
t, dummyRemoteOutIndex, diskPrevCommit.TheirOutputIndex.Val,
)
assertRevocationLogEntryEqual(t, &oldRemoteCommit, prevCommit)
@ -1642,6 +1710,24 @@ func TestHTLCsExtraData(t *testing.T) {
),
}
// Custom channel data htlc with a blinding point.
customDataHTLC := HTLC{
Signature: testSig.Serialize(),
Incoming: false,
Amt: 10,
RHash: key,
RefundTimeout: 1,
OnionBlob: lnmock.MockOnion(),
BlindingPoint: tlv.SomeRecordT(
tlv.NewPrimitiveRecord[lnwire.BlindingPointTlvType](
pubKey,
),
),
CustomRecords: map[uint64][]byte{
uint64(lnwire.MinCustomRecordsTlvType + 3): {1, 2, 3},
},
}
testCases := []struct {
name string
htlcs []HTLC
@ -1663,6 +1749,7 @@ func TestHTLCsExtraData(t *testing.T) {
mockHtlc,
blindingPointHTLC,
mockHtlc,
customDataHTLC,
},
},
}

View File

@ -11,38 +11,13 @@ import (
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
graphdb "github.com/lightningnetwork/lnd/graph/db"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/shachain"
"github.com/lightningnetwork/lnd/tlv"
)
// writeOutpoint writes an outpoint to the passed writer using the minimal
// amount of bytes possible.
func writeOutpoint(w io.Writer, o *wire.OutPoint) error {
if _, err := w.Write(o.Hash[:]); err != nil {
return err
}
if err := binary.Write(w, byteOrder, o.Index); err != nil {
return err
}
return nil
}
// readOutpoint reads an outpoint from the passed reader that was previously
// written using the writeOutpoint struct.
func readOutpoint(r io.Reader, o *wire.OutPoint) error {
if _, err := io.ReadFull(r, o.Hash[:]); err != nil {
return err
}
if err := binary.Read(r, byteOrder, &o.Index); err != nil {
return err
}
return nil
}
// UnknownElementType is an error returned when the codec is unable to encode or
// decode a particular type.
type UnknownElementType struct {
@ -98,7 +73,7 @@ func WriteElement(w io.Writer, element interface{}) error {
}
case wire.OutPoint:
return writeOutpoint(w, &e)
return graphdb.WriteOutpoint(w, &e)
case lnwire.ShortChannelID:
if err := binary.Write(w, byteOrder, e.ToUint64()); err != nil {
@ -218,7 +193,7 @@ func WriteElement(w io.Writer, element interface{}) error {
}
case net.Addr:
if err := serializeAddr(w, e); err != nil {
if err := graphdb.SerializeAddr(w, e); err != nil {
return err
}
@ -228,7 +203,7 @@ func WriteElement(w io.Writer, element interface{}) error {
}
for _, addr := range e {
if err := serializeAddr(w, addr); err != nil {
if err := graphdb.SerializeAddr(w, addr); err != nil {
return err
}
}
@ -288,7 +263,7 @@ func ReadElement(r io.Reader, element interface{}) error {
}
case *wire.OutPoint:
return readOutpoint(r, e)
return graphdb.ReadOutpoint(r, e)
case *lnwire.ShortChannelID:
var a uint64
@ -451,7 +426,7 @@ func ReadElement(r io.Reader, element interface{}) error {
}
case *net.Addr:
addr, err := deserializeAddr(r)
addr, err := graphdb.DeserializeAddr(r)
if err != nil {
return err
}
@ -465,7 +440,7 @@ func ReadElement(r io.Reader, element interface{}) error {
*e = make([]net.Addr, numAddrs)
for i := uint32(0); i < numAddrs; i++ {
addr, err := deserializeAddr(r)
addr, err := graphdb.DeserializeAddr(r)
if err != nil {
return err
}

Some files were not shown because too many files have changed in this diff Show More