mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 05:12:45 +01:00
lightningd: add flag for whether we're synced, and callback infrastructure.
We consider ourselves synced when bitcoind is synced and we're synced with that. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
faded9a9cf
commit
3eebd0cc20
@ -559,6 +559,31 @@ static void next_updatefee_timer(struct chain_topology *topo)
|
||||
start_fee_estimate, topo));
|
||||
}
|
||||
|
||||
struct sync_waiter {
|
||||
/* Linked from chain_topology->sync_waiters */
|
||||
struct list_node list;
|
||||
void (*cb)(struct chain_topology *topo, void *arg);
|
||||
void *arg;
|
||||
};
|
||||
|
||||
static void destroy_sync_waiter(struct sync_waiter *waiter)
|
||||
{
|
||||
list_del(&waiter->list);
|
||||
}
|
||||
|
||||
void topology_add_sync_waiter_(const tal_t *ctx,
|
||||
struct chain_topology *topo,
|
||||
void (*cb)(struct chain_topology *topo,
|
||||
void *arg),
|
||||
void *arg)
|
||||
{
|
||||
struct sync_waiter *w = tal(ctx, struct sync_waiter);
|
||||
w->cb = cb;
|
||||
w->arg = arg;
|
||||
list_add_tail(topo->sync_waiters, &w->list);
|
||||
tal_add_destructor(w, destroy_sync_waiter);
|
||||
}
|
||||
|
||||
/* Once we're run out of new blocks to add, call this. */
|
||||
static void updates_complete(struct chain_topology *topo)
|
||||
{
|
||||
@ -579,6 +604,23 @@ static void updates_complete(struct chain_topology *topo)
|
||||
topo->prev_tip = topo->tip;
|
||||
}
|
||||
|
||||
/* If bitcoind is synced, we're now synced. */
|
||||
if (topo->bitcoind->synced && !topology_synced(topo)) {
|
||||
struct sync_waiter *w;
|
||||
struct list_head *list = topo->sync_waiters;
|
||||
|
||||
/* Mark topology_synced() before callbacks. */
|
||||
topo->sync_waiters = NULL;
|
||||
|
||||
while ((w = list_pop(list, struct sync_waiter, list))) {
|
||||
/* In case it doesn't free itself. */
|
||||
tal_del_destructor(w, destroy_sync_waiter);
|
||||
tal_steal(list, w);
|
||||
w->cb(topo, w->arg);
|
||||
}
|
||||
tal_free(list);
|
||||
}
|
||||
|
||||
/* Try again soon. */
|
||||
next_topology_timer(topo);
|
||||
}
|
||||
@ -911,6 +953,8 @@ void setup_topology(struct chain_topology *topo,
|
||||
{
|
||||
memset(&topo->feerate, 0, sizeof(topo->feerate));
|
||||
topo->timers = timers;
|
||||
topo->sync_waiters = tal(topo, struct list_head);
|
||||
list_head_init(topo->sync_waiters);
|
||||
|
||||
topo->min_blockheight = min_blockheight;
|
||||
topo->max_blockheight = max_blockheight;
|
||||
|
@ -100,6 +100,11 @@ struct chain_topology {
|
||||
/* How often to poll. */
|
||||
u32 poll_seconds;
|
||||
|
||||
/* struct sync_waiters waiting for us to catch up with bitcoind (and
|
||||
* once that has caught up with the network). NULL if we're already
|
||||
* caught up. */
|
||||
struct list_head *sync_waiters;
|
||||
|
||||
/* The bitcoind. */
|
||||
struct bitcoind *bitcoind;
|
||||
|
||||
@ -171,6 +176,34 @@ void begin_topology(struct chain_topology *topo);
|
||||
|
||||
struct txlocator *locate_tx(const void *ctx, const struct chain_topology *topo, const struct bitcoin_txid *txid);
|
||||
|
||||
static inline bool topology_synced(const struct chain_topology *topo)
|
||||
{
|
||||
return topo->sync_waiters == NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* topology_add_sync_waiter: wait for lightningd to sync with bitcoin network
|
||||
* @ctx: context to allocate the waiter from.
|
||||
* @topo: chain topology
|
||||
* @cb: callback to call when we're synced.
|
||||
* @arg: arg for @cb
|
||||
*
|
||||
* topology_synced() must be false when this is called. It will be true
|
||||
* when @cb is called. @cb will not be called if @ctx is freed first.
|
||||
*/
|
||||
void topology_add_sync_waiter_(const tal_t *ctx,
|
||||
struct chain_topology *topo,
|
||||
void (*cb)(struct chain_topology *topo,
|
||||
void *arg),
|
||||
void *arg);
|
||||
#define topology_add_sync_waiter(ctx, topo, cb, arg) \
|
||||
topology_add_sync_waiter_((ctx), (topo), \
|
||||
typesafe_cb_preargs(void, void *, \
|
||||
(cb), (arg), \
|
||||
struct chain_topology *), \
|
||||
(arg))
|
||||
|
||||
|
||||
/* In channel_control.c */
|
||||
void notify_feerate_change(struct lightningd *ld);
|
||||
#endif /* LIGHTNING_LIGHTNINGD_CHAINTOPOLOGY_H */
|
||||
|
@ -1557,6 +1557,9 @@ static struct command_result *json_getinfo(struct command *cmd,
|
||||
if (!cmd->ld->topology->bitcoind->synced)
|
||||
json_add_string(response, "warning_bitcoind_sync",
|
||||
"Bitcoind is not up-to-date with network.");
|
||||
else if (!topology_synced(cmd->ld->topology))
|
||||
json_add_string(response, "warning_lightningd_sync",
|
||||
"Still loading latest blocks from bitcoind.");
|
||||
|
||||
return command_success(cmd, response);
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
from bitcoin.rpc import RawProxy
|
||||
from fixtures import * # noqa: F401,F403
|
||||
from flaky import flaky # noqa: F401
|
||||
from lightning import RpcError
|
||||
from utils import DEVELOPER, VALGRIND, sync_blockheight, only_one, wait_for, TailableProc
|
||||
from threading import Event
|
||||
from utils import DEVELOPER, TIMEOUT, VALGRIND, sync_blockheight, only_one, wait_for, TailableProc
|
||||
from ephemeral_port_reserve import reserve
|
||||
|
||||
import json
|
||||
@ -133,6 +135,47 @@ def test_bitcoin_ibd(node_factory, bitcoind):
|
||||
assert 'warning_bitcoind_sync' not in l1.rpc.getinfo()
|
||||
|
||||
|
||||
def test_lightningd_still_loading(node_factory, bitcoind, executor):
|
||||
"""Test that we recognize we haven't got all blocks from bitcoind"""
|
||||
|
||||
mock_release = Event()
|
||||
|
||||
# This is slow enough that we're going to notice.
|
||||
def mock_getblock(r):
|
||||
conf_file = os.path.join(bitcoind.bitcoin_dir, 'bitcoin.conf')
|
||||
brpc = RawProxy(btc_conf_file=conf_file)
|
||||
if r['params'][0] == slow_blockid:
|
||||
mock_release.wait(TIMEOUT)
|
||||
return {
|
||||
"result": brpc._call(r['method'], *r['params']),
|
||||
"error": None,
|
||||
"id": r['id']
|
||||
}
|
||||
|
||||
# Start it once, make sure it gets a second block (thus writes into db)
|
||||
l1 = node_factory.get_node()
|
||||
bitcoind.generate_block(1)
|
||||
sync_blockheight(bitcoind, [l1])
|
||||
l1.stop()
|
||||
|
||||
# Now make sure it's behind.
|
||||
bitcoind.generate_block(2)
|
||||
|
||||
# Make it slow grabbing the final block.
|
||||
slow_blockid = bitcoind.rpc.getblockhash(bitcoind.rpc.getblockcount())
|
||||
l1.daemon.rpcproxy.mock_rpc('getblock', mock_getblock)
|
||||
|
||||
l1.start()
|
||||
|
||||
# It will warn about being out-of-sync.
|
||||
assert 'warning_bitcoind_sync' not in l1.rpc.getinfo()
|
||||
assert 'warning_lightningd_sync' in l1.rpc.getinfo()
|
||||
|
||||
# Release the mock, and it will recover.
|
||||
mock_release.set()
|
||||
wait_for(lambda: 'warning_lightningd_sync' not in l1.rpc.getinfo())
|
||||
|
||||
|
||||
def test_ping(node_factory):
|
||||
l1, l2 = node_factory.line_graph(2, fundchannel=False)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user