lightningd: Load persisted channels on startup

This is the big one, and it's completely anticlimactic: it loads all
channels that have reached opening and are not marked as
closingd_complete into memory, that's it.

Signed-off-by: Christian Decker <decker.christian@gmail.com>
This commit is contained in:
Christian Decker 2017-08-17 15:30:24 +02:00 committed by Rusty Russell
parent 4e6f9787b6
commit e51d261f51
6 changed files with 89 additions and 18 deletions

View File

@ -267,6 +267,15 @@ int main(int argc, char *argv[])
/* FIXME: Load from peers. */
0);
/* Load peers from database */
wallet_channels_load_active(ld->wallet, &ld->peers);
struct peer *peer;
list_for_each(&ld->peers, peer, list) {
populate_peer(ld, peer);
peer->seed = tal(peer, struct privkey);
derive_peer_seed(ld, peer->seed, &peer->id);
}
/* Create RPC socket (if any) */
setup_jsonrpc(&ld->dstate, ld->dstate.rpc_filename);

View File

@ -550,6 +550,23 @@ static struct wallet_channel *peer_channel_new(struct wallet *w,
return wc;
}
void populate_peer(struct lightningd *ld, struct peer *peer)
{
const char *idname;
struct pubkey *id = &peer->id;
idname = type_to_string(peer, struct pubkey, id);
peer->ld = ld;
/* Max 128k per peer. */
peer->log_book = new_log_book(peer, 128*1024,
get_log_level(ld->dstate.log_book));
peer->log = new_log(peer, peer->log_book, "peer %s:", idname);
set_log_outfn(peer->log_book, copy_to_parent_log, peer);
tal_free(idname);
tal_add_destructor(peer, destroy_peer);
}
void add_peer(struct lightningd *ld, u64 unique_id,
int fd, const struct pubkey *id,
const struct crypto_state *cs)
@ -598,14 +615,6 @@ void add_peer(struct lightningd *ld, u64 unique_id,
/* peer->channel gets populated as soon as we start opening a channel */
peer->channel = NULL;
idname = type_to_string(peer, struct pubkey, id);
/* Max 128k per peer. */
peer->log_book = new_log_book(peer, 128*1024,
get_log_level(ld->dstate.log_book));
peer->log = new_log(peer, peer->log_book, "peer %s:", idname);
set_log_outfn(peer->log_book, copy_to_parent_log, peer);
/* FIXME: Don't assume protocol here! */
if (!netaddr_from_fd(fd, SOCK_STREAM, IPPROTO_TCP, &peer->netaddr)) {
log_unusual(ld->log, "Failed to get netaddr for outgoing: %s",
@ -613,11 +622,14 @@ void add_peer(struct lightningd *ld, u64 unique_id,
tal_free(peer);
return;
}
list_add_tail(&ld->peers, &peer->list);
populate_peer(ld, peer);
idname = type_to_string(peer, struct pubkey, id);
netname = netaddr_name(idname, &peer->netaddr);
log_info(peer->log, "Connected from %s", netname);
tal_free(idname);
list_add_tail(&ld->peers, &peer->list);
tal_add_destructor(peer, destroy_peer);
/* Let gossip handle it from here. */
peer->owner = peer->ld->gossip;

View File

@ -158,6 +158,18 @@ void add_peer(struct lightningd *ld, u64 unique_id,
int fd, const struct pubkey *id,
const struct crypto_state *cs);
/**
* populate_peer -- Populate daemon fields in a peer
*
* @ld: the daemon to wire the peer into
* @peer: the peer to populate
*
* Creating a new peer, or loading a peer from the database we need to
* populate a number of fields, e.g., the logging handler and the
* pointer to the daemon. populate_peer does exactly that.
*/
void populate_peer(struct lightningd *ld, struct peer *peer);
/* Could be configurable. */
#define OUR_CHANNEL_FLAGS CHANNEL_FLAGS_ANNOUNCE_CHANNEL

View File

@ -47,10 +47,10 @@ def setupBitcoind():
bitcoind.rpc.generate(1)
def wait_for(success, timeout=30):
def wait_for(success, timeout=30, interval=0.1):
start_time = time.time()
while not success() and time.time() < start_time + timeout:
pass
time.sleep(interval)
if time.time() > start_time + timeout:
raise ValueError("Error waiting for {}", success)
@ -875,17 +875,21 @@ class LightningDTests(BaseLightningDTests):
for n in (l1, l2):
assert(n.db_query('SELECT COUNT(id) as count FROM channels;')[0]['count'] == 1)
l1.daemon.stop()
# Let the other side notice, then stop it
wait_for(lambda: not l2.rpc.getpeers()['peers'][0]['connected'])
l2.daemon.stop()
# Let the other side notice, then stop it
wait_for(lambda: not l1.rpc.getpeers()['peers'][0]['connected'])
#l1.daemon.stop()
# Now restart l1 and it should reload peers/channels from the DB
l1.daemon.start()
l2.daemon.start()
wait_for(lambda: len(l2.rpc.getpeers()['peers']) == 1)
#wait_for(lambda: len(l1.rpc.getpeers()['peers']) == 1)
wait_for(lambda: len([p for p in l1.rpc.getpeers()['peers'] if p['connected']]), interval=1)
wait_for(lambda: len([p for p in l2.rpc.getpeers()['peers'] if p['connected']]), interval=1)
# Now make sure this is really functional by sending a payment
self.pay(l1, l2, 10000)
class LegacyLightningDTests(BaseLightningDTests):

View File

@ -591,6 +591,27 @@ bool wallet_channel_load(struct wallet *w, const u64 id,
return ok;
}
bool wallet_channels_load_active(struct wallet *w, struct list_head *peers)
{
bool ok = true;
/* Channels are active if they have reached at least the
* opening state and they are not marked as complete */
sqlite3_stmt *stmt = db_query(
__func__, w->db, "SELECT %s FROM channels WHERE state >= %d AND state != %d;",
channel_fields, OPENINGD, CLOSINGD_COMPLETE);
int count = 0;
while (ok && stmt && sqlite3_step(stmt) == SQLITE_ROW) {
struct wallet_channel *c = talz(w, struct wallet_channel);
ok &= wallet_stmt2channel(w, stmt, c);
list_add(peers, &c->peer->list);
count++;
}
log_debug(w->log, "Loaded %d channels from DB", count);
sqlite3_finalize(stmt);
return ok;
}
static char* db_serialize_signature(const tal_t *ctx, secp256k1_ecdsa_signature* sig)
{
u8 buf[64];

View File

@ -4,6 +4,7 @@
#include "config.h"
#include "db.h"
#include <ccan/crypto/shachain/shachain.h>
#include <ccan/list/list.h>
#include <ccan/tal/tal.h>
#include <lightningd/channel_config.h>
#include <lightningd/utxo.h>
@ -197,4 +198,16 @@ bool wallet_channel_config_load(struct wallet *w, const u64 id,
*/
bool wallet_peer_by_nodeid(struct wallet *w, const struct pubkey *nodeid,
struct peer *peer);
/**
* wlalet_channels_load_active -- Load persisted active channels into the peers
*
* @w: wallet to load from
* @peers: list_head to load channels/peers into
*
* Be sure to call this only once on startup since it'll append peers
* loaded from the database to the list without checking.
*/
bool wallet_channels_load_active(struct wallet *w, struct list_head *peers);
#endif /* WALLET_WALLET_H */