Browse Source

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>
pull/2938/head
Rusty Russell 5 years ago
committed by Christian Decker
parent
commit
3eebd0cc20
  1. 44
      lightningd/chaintopology.c
  2. 33
      lightningd/chaintopology.h
  3. 3
      lightningd/peer_control.c
  4. 45
      tests/test_misc.py

44
lightningd/chaintopology.c

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

33
lightningd/chaintopology.h

@ -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 */

3
lightningd/peer_control.c

@ -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);
}

45
tests/test_misc.py

@ -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…
Cancel
Save