Browse Source

db: save and restore "sendpay" commands.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa-0.6.1
Rusty Russell 9 years ago
parent
commit
04a07fd90e
  1. 183
      daemon/db.c
  2. 12
      daemon/db.h
  3. 103
      daemon/pay.c
  4. 8
      daemon/pay.h
  5. 5
      daemon/test/test.sh

183
daemon/db.c

@ -7,6 +7,7 @@
#include "log.h"
#include "names.h"
#include "netaddr.h"
#include "pay.h"
#include "routing.h"
#include "secrets.h"
#include "utils.h"
@ -953,6 +954,109 @@ static void db_load_peers(struct lightningd_state *dstate)
connect_htlc_src(dstate);
}
static const char *pubkeys_to_hex(const tal_t *ctx,
secp256k1_context *secpctx,
const struct pubkey *ids)
{
u8 *ders = tal_arr(ctx, u8, PUBKEY_DER_LEN * tal_count(ids));
size_t i;
for (i = 0; i < tal_count(ids); i++)
pubkey_to_der(secpctx, ders + i * PUBKEY_DER_LEN, &ids[i]);
return tal_hexstr(ctx, ders, tal_count(ders));
}
static struct pubkey *pubkeys_from_arr(const tal_t *ctx,
secp256k1_context *secpctx,
const void *blob, size_t len)
{
struct pubkey *ids;
size_t i;
if (len % PUBKEY_DER_LEN)
fatal("ids array bad length %zu", len);
ids = tal_arr(ctx, struct pubkey, len / PUBKEY_DER_LEN);
for (i = 0; i < tal_count(ids); i++) {
if (!pubkey_from_der(secpctx, blob, PUBKEY_DER_LEN, &ids[i]))
fatal("ids array invalid %zu", i);
blob = (const u8 *)blob + PUBKEY_DER_LEN;
}
return ids;
}
static void db_load_pay(struct lightningd_state *dstate)
{
int err;
sqlite3_stmt *stmt;
char *ctx = tal(dstate, char);
err = sqlite3_prepare_v2(dstate->db->sql, "SELECT * FROM pay;", -1,
&stmt, NULL);
if (err != SQLITE_OK)
fatal("db_load_pay:prepare gave %s:%s",
sqlite3_errstr(err), sqlite3_errmsg(dstate->db->sql));
/* CREATE TABLE pay (rhash "SQL_RHASH", msatoshis INT, ids BLOB, htlc_peer "SQL_PUBKEY", htlc_id INT, r "SQL_R", fail BLOB, PRIMARY KEY(rhash)); */
while ((err = sqlite3_step(stmt)) != SQLITE_DONE) {
struct sha256 rhash;
struct htlc *htlc;
struct pubkey *peer_id;
u64 htlc_id, msatoshis;
struct pubkey *ids;
struct rval *r;
void *fail;
if (err != SQLITE_ROW)
fatal("db_load_pay:step gave %s:%s",
sqlite3_errstr(err),
sqlite3_errmsg(dstate->db->sql));
if (sqlite3_column_count(stmt) != 7)
fatal("db_load_pay:step gave %i cols, not 7",
sqlite3_column_count(stmt));
sha256_from_sql(stmt, 0, &rhash);
msatoshis = sqlite3_column_int64(stmt, 1);
ids = pubkeys_from_arr(ctx, dstate->secpctx,
sqlite3_column_blob(stmt, 2),
sqlite3_column_bytes(stmt, 2));
if (sqlite3_column_type(stmt, 3) == SQLITE_NULL)
peer_id = NULL;
else {
peer_id = tal(ctx, struct pubkey);
pubkey_from_sql(dstate->secpctx, stmt, 3, peer_id);
}
htlc_id = sqlite3_column_int64(stmt, 4);
if (sqlite3_column_type(stmt, 5) == SQLITE_NULL)
r = NULL;
else {
r = tal(ctx, struct rval);
from_sql_blob(stmt, 5, r, sizeof(*r));
}
fail = tal_sql_blob(ctx, stmt, 6);
/* Exactly one of these must be set. */
if (!fail + !peer_id + !r != 2)
fatal("db_load_pay: not exactly one set:"
" fail=%p peer_id=%p r=%p",
fail, peer_id, r);
if (peer_id) {
struct peer *peer = find_peer(dstate, peer_id);
if (!peer)
fatal("db_load_pay: unknown peer");
htlc = htlc_get(&peer->htlcs, htlc_id, LOCAL);
if (!htlc)
fatal("db_load_pay: unknown htlc");
} else
htlc = NULL;
if (!pay_add(dstate, &rhash, msatoshis, ids, htlc, fail, r))
fatal("db_load_pay: could not add pay");
}
tal_free(ctx);
}
static void db_load_addresses(struct lightningd_state *dstate)
{
int err;
@ -993,6 +1097,7 @@ static void db_load(struct lightningd_state *dstate)
db_load_wallet(dstate);
db_load_addresses(dstate);
db_load_peers(dstate);
db_load_pay(dstate);
}
void db_init(struct lightningd_state *dstate)
@ -1033,6 +1138,7 @@ void db_init(struct lightningd_state *dstate)
/* Set up tables. */
errmsg = db_exec(dstate, dstate,
"CREATE TABLE wallet (privkey "SQL_PRIVKEY");"
"CREATE TABLE pay (rhash "SQL_RHASH", msatoshis INT, ids BLOB, htlc_peer "SQL_PUBKEY", htlc_id INT, r "SQL_R", fail BLOB, PRIMARY KEY(rhash));"
"CREATE TABLE anchors (peer "SQL_PUBKEY", txid "SQL_TXID", idx INT, amount INT, ok_depth INT, min_depth INT, bool ours, PRIMARY KEY(peer));"
/* FIXME: state in primary key is overkill: just need side */
"CREATE TABLE htlcs (peer "SQL_PUBKEY", id INT, state TEXT, msatoshis INT, expiry INT, rhash "SQL_RHASH", r "SQL_R", routing "SQL_ROUTING", src_peer "SQL_PUBKEY", src_id INT, fail BLOB, PRIMARY KEY(peer, id, state));"
@ -1654,3 +1760,80 @@ bool db_update_their_closing(struct peer *peer)
tal_free(ctx);
return !errmsg;
}
bool db_new_pay_command(struct lightningd_state *dstate,
const struct sha256 *rhash,
const struct pubkey *ids,
u64 msatoshis,
const struct htlc *htlc)
{
const char *errmsg, *ctx = tal(dstate, char);
log_debug(dstate->base_log, "%s", __func__);
log_add_struct(dstate->base_log, "(%s)", struct sha256, rhash);
assert(!dstate->db->in_transaction);
/* CREATE TABLE pay (rhash "SQL_RHASH", msatoshis INT, ids BLOB, htlc_peer "SQL_PUBKEY", htlc_id INT, r "SQL_R", fail BLOB, PRIMARY KEY(rhash)); */
errmsg = db_exec(ctx, dstate, "INSERT INTO pay VALUES (x'%s', %"PRIu64", x'%s', x'%s', %"PRIu64", NULL, NULL);",
tal_hexstr(ctx, rhash, sizeof(*rhash)),
msatoshis,
pubkeys_to_hex(ctx, dstate->secpctx, ids),
pubkey_to_hexstr(ctx, dstate->secpctx, htlc->peer->id),
htlc->id);
if (errmsg)
log_broken(dstate->base_log, "%s:%s", __func__, errmsg);
tal_free(ctx);
return !errmsg;
}
bool db_replace_pay_command(struct lightningd_state *dstate,
const struct sha256 *rhash,
const struct pubkey *ids,
u64 msatoshis,
const struct htlc *htlc)
{
const char *errmsg, *ctx = tal(dstate, char);
log_debug(dstate->base_log, "%s", __func__);
log_add_struct(dstate->base_log, "(%s)", struct sha256, rhash);
assert(!dstate->db->in_transaction);
/* CREATE TABLE pay (rhash "SQL_RHASH", msatoshis INT, ids BLOB, htlc_peer "SQL_PUBKEY", htlc_id INT, r "SQL_R", fail BLOB, PRIMARY KEY(rhash)); */
errmsg = db_exec(ctx, dstate, "UPDATE pay SET msatoshis=%"PRIu64", ids=x'%s', htlc_peer=x'%s', htlc_id=%"PRIu64", r=NULL, fail=NULL WHERE rhash=x'%s';",
msatoshis,
pubkeys_to_hex(ctx, dstate->secpctx, ids),
pubkey_to_hexstr(ctx, dstate->secpctx, htlc->peer->id),
htlc->id,
tal_hexstr(ctx, rhash, sizeof(*rhash)));
if (errmsg)
log_broken(dstate->base_log, "%s:%s", __func__, errmsg);
tal_free(ctx);
return !errmsg;
}
bool db_complete_pay_command(struct lightningd_state *dstate,
const struct htlc *htlc)
{
const char *errmsg, *ctx = tal(dstate, char);
log_debug(dstate->base_log, "%s", __func__);
log_add_struct(dstate->base_log, "(%s)", struct sha256, &htlc->rhash);
assert(dstate->db->in_transaction);
/* CREATE TABLE pay (rhash "SQL_RHASH", msatoshis INT, ids BLOB, htlc_peer "SQL_PUBKEY", htlc_id INT, r "SQL_R", fail BLOB, PRIMARY KEY(rhash)); */
if (htlc->r)
errmsg = db_exec(ctx, dstate,
"UPDATE pay SET r=x'%s', htlc_peer=NULL WHERE rhash=x'%s';",
tal_hexstr(ctx, htlc->r, sizeof(*htlc->r)),
tal_hexstr(ctx, &htlc->rhash, sizeof(htlc->rhash)));
else
errmsg = db_exec(ctx, dstate,
"UPDATE pay SET fail=x'%s', htlc_peer=NULL WHERE rhash=x'%s';",
tal_hexstr(ctx, htlc->fail, tal_count(htlc->fail)),
tal_hexstr(ctx, &htlc->rhash, sizeof(htlc->rhash)));
if (errmsg)
log_broken(dstate->base_log, "%s:%s", __func__, errmsg);
tal_free(ctx);
return !errmsg;
}

12
daemon/db.h

@ -27,6 +27,16 @@ bool db_set_our_closing_script(struct peer *peer);
bool db_set_their_closing_script(struct peer *peer);
bool db_update_our_closing(struct peer *peer);
bool db_update_their_closing(struct peer *peer);
bool db_new_pay_command(struct lightningd_state *dstate,
const struct sha256 *rhash,
const struct pubkey *ids,
u64 msatoshis,
const struct htlc *htlc);
bool db_replace_pay_command(struct lightningd_state *dstate,
const struct sha256 *rhash,
const struct pubkey *ids,
u64 msatoshis,
const struct htlc *htlc);
/* FIXME: save error handling until db_commit_transaction for calls
* which have to be inside transaction anyway. */
@ -36,6 +46,8 @@ bool db_new_htlc(struct peer *peer, const struct htlc *htlc);
bool db_new_feechange(struct peer *peer, const struct feechange *feechange);
bool db_update_htlc_state(struct peer *peer, const struct htlc *htlc,
enum htlc_state oldstate);
bool db_complete_pay_command(struct lightningd_state *state,
const struct htlc *htlc);
bool db_update_feechange_state(struct peer *peer,
const struct feechange *f,
enum htlc_state oldstate);

103
daemon/pay.c

@ -1,4 +1,5 @@
#include "chaintopology.h"
#include "db.h"
#include "failure.h"
#include "jsonrpc.h"
#include "lightningd.h"
@ -15,15 +16,14 @@
struct pay_command {
struct list_node list;
struct sha256 rhash;
u64 msatoshis;
struct pubkey *ids;
u64 msatoshi;
const struct pubkey *ids;
/* Set if this is in progress. */
struct htlc *htlc;
/* Preimage if this succeeded. */
struct rval *rval;
const struct rval *rval;
struct command *cmd;
};
static void json_pay_success(struct command *cmd, const struct rval *rval)
{
struct json_result *response;
@ -101,6 +101,9 @@ void complete_pay_command(struct lightningd_state *dstate,
list_for_each(&dstate->pay_commands, i, list) {
if (i->htlc == htlc) {
FailInfo *f = NULL;
db_complete_pay_command(dstate, htlc);
if (htlc->r)
i->rval = tal_dup(i, struct rval, htlc->r);
else {
@ -119,7 +122,7 @@ void complete_pay_command(struct lightningd_state *dstate,
}
}
/* Can happen if RPC connection goes away. */
/* Can happen with testing low-level commands. */
log_unusual(dstate->base_log, "No command for HTLC %"PRIu64" %s",
htlc->id, htlc->r ? "fulfill" : "fail");
}
@ -151,6 +154,35 @@ static struct pay_command *find_pay_command(struct lightningd_state *dstate,
return NULL;
}
/* For database restore. */
bool pay_add(struct lightningd_state *dstate,
const struct sha256 *rhash,
u64 msatoshi,
const struct pubkey *ids,
struct htlc *htlc,
const u8 *fail UNNEEDED,
const struct rval *r)
{
struct pay_command *pc;
if (find_pay_command(dstate, rhash))
return false;
pc = tal(dstate, struct pay_command);
pc->rhash = *rhash;
pc->msatoshi = msatoshi;
pc->ids = tal_dup_arr(pc, struct pubkey, ids, tal_count(ids), 0);
pc->htlc = htlc;
if (r)
pc->rval = tal_dup(pc, struct rval, r);
else
pc->rval = NULL;
pc->cmd = NULL;
list_add_tail(&dstate->pay_commands, &pc->list);
return true;
}
static void json_add_route(struct json_result *response,
secp256k1_context *secpctx,
const struct pubkey *id,
@ -158,7 +190,7 @@ static void json_add_route(struct json_result *response,
{
json_object_start(response, NULL);
json_add_pubkey(response, secpctx, "id", id);
json_add_u64(response, "msatoshis", amount);
json_add_u64(response, "msatoshi", amount);
json_add_num(response, "delay", delay);
json_object_end(response);
}
@ -167,10 +199,10 @@ static void json_getroute(struct command *cmd,
const char *buffer, const jsmntok_t *params)
{
struct pubkey id;
jsmntok_t *idtok, *msatoshistok;
jsmntok_t *idtok, *msatoshitok;
struct json_result *response;
int i;
u64 msatoshis;
u64 msatoshi;
s64 fee;
struct node_connection **route;
struct peer *peer;
@ -179,9 +211,9 @@ static void json_getroute(struct command *cmd,
if (!json_get_params(buffer, params,
"id", &idtok,
"msatoshis", &msatoshistok,
"msatoshi", &msatoshitok,
NULL)) {
command_fail(cmd, "Need id and msatoshis");
command_fail(cmd, "Need id and msatoshi");
return;
}
@ -192,14 +224,14 @@ static void json_getroute(struct command *cmd,
return;
}
if (!json_tok_u64(buffer, msatoshistok, &msatoshis)) {
if (!json_tok_u64(buffer, msatoshitok, &msatoshi)) {
command_fail(cmd, "'%.*s' is not a valid number",
(int)(msatoshistok->end - msatoshistok->start),
buffer + msatoshistok->start);
(int)(msatoshitok->end - msatoshitok->start),
buffer + msatoshitok->start);
return;
}
peer = find_route(cmd->dstate, &id, msatoshis, &fee, &route);
peer = find_route(cmd->dstate, &id, msatoshi, &fee, &route);
if (!peer) {
command_fail(cmd, "no route found");
return;
@ -208,7 +240,7 @@ static void json_getroute(struct command *cmd,
/* Fees, delays need to be calculated backwards along route. */
amounts = tal_arr(cmd, u64, tal_count(route)+1);
delays = tal_arr(cmd, unsigned int, tal_count(route)+1);
total_amount = msatoshis;
total_amount = msatoshi;
total_delay = 0;
for (i = tal_count(route) - 1; i >= 0; i--) {
@ -244,8 +276,8 @@ static void json_getroute(struct command *cmd,
const struct json_command getroute_command = {
"getroute",
json_getroute,
"Return route for {msatoshis} to {id}",
"Returns a {route} array of {id} {msatoshis} {delay}: msatoshis and delay (in blocks) is cumulative."
"Return route for {msatoshi} to {id}",
"Returns a {route} array of {id} {msatoshi} {delay}: msatoshi and delay (in blocks) is cumulative."
};
static void json_sendpay(struct command *cmd,
@ -260,6 +292,7 @@ static void json_sendpay(struct command *cmd,
struct sha256 rhash;
struct peer *peer;
struct pay_command *pc;
bool replacing = false;
const u8 *onion;
enum fail_error error_code;
const char *err;
@ -302,18 +335,18 @@ static void json_sendpay(struct command *cmd,
buffer + t->start);
return;
}
amttok = json_get_member(buffer, t, "msatoshis");
amttok = json_get_member(buffer, t, "msatoshi");
idtok = json_get_member(buffer, t, "id");
delaytok = json_get_member(buffer, t, "delay");
if (!amttok || !idtok || !delaytok) {
command_fail(cmd, "route %zu needs msatoshis/id/delay",
command_fail(cmd, "route %zu needs msatoshi/id/delay",
n_hops);
return;
}
tal_resize(&amounts, n_hops+1);
if (!json_tok_u64(buffer, amttok, &amounts[n_hops])) {
command_fail(cmd, "route %zu invalid msatoshis", n_hops);
command_fail(cmd, "route %zu invalid msatoshi", n_hops);
return;
}
tal_resize(&ids, n_hops+1);
@ -339,6 +372,7 @@ static void json_sendpay(struct command *cmd,
pc = find_pay_command(cmd->dstate, &rhash);
if (pc) {
replacing = true;
log_debug(cmd->dstate->base_log, "json_sendpay: found previous");
if (pc->htlc) {
log_add(cmd->dstate->base_log, "... still in progress");
@ -349,10 +383,10 @@ static void json_sendpay(struct command *cmd,
size_t old_nhops = tal_count(pc->ids);
log_add(cmd->dstate->base_log, "... succeeded");
/* Must match successful payment parameters. */
if (pc->msatoshis != amounts[n_hops-1]) {
if (pc->msatoshi != amounts[n_hops-1]) {
command_fail(cmd,
"already succeeded with amount %"
PRIu64, pc->msatoshis);
PRIu64, pc->msatoshi);
return;
}
if (!structeq(&pc->ids[old_nhops-1], &ids[n_hops-1])) {
@ -389,7 +423,7 @@ static void json_sendpay(struct command *cmd,
pc->rhash = rhash;
pc->rval = NULL;
pc->ids = tal_steal(pc, ids);
pc->msatoshis = amounts[n_hops-1];
pc->msatoshi = amounts[n_hops-1];
/* Expiry for HTLCs is absolute. And add one to give some margin. */
err = command_htlc_add(peer, amounts[0],
@ -398,9 +432,32 @@ static void json_sendpay(struct command *cmd,
onion, &error_code, &pc->htlc);
if (err) {
command_fail(cmd, "could not add htlc: %u: %s", error_code, err);
tal_free(pc);
return;
}
if (replacing) {
if (!db_replace_pay_command(cmd->dstate, &pc->rhash,
pc->ids, pc->msatoshi,
pc->htlc)) {
command_fail(cmd, "database error");
/* We could reconnect, but db error is *bad*. */
peer_fail(peer, __func__);
tal_free(pc);
return;
}
} else {
if (!db_new_pay_command(cmd->dstate, &pc->rhash,
pc->ids, pc->msatoshi,
pc->htlc)) {
command_fail(cmd, "database error");
/* We could reconnect, but db error is *bad*. */
peer_fail(peer, __func__);
tal_free(pc);
return;
}
}
/* Wait until we get response. */
list_add_tail(&cmd->dstate->pay_commands, &pc->list);
tal_add_destructor(cmd, remove_cmd_from_pc);

8
daemon/pay.h

@ -8,4 +8,12 @@ struct htlc;
void complete_pay_command(struct lightningd_state *dstate,
const struct htlc *htlc);
bool pay_add(struct lightningd_state *dstate,
const struct sha256 *rhash,
u64 msatoshi,
const struct pubkey *ids,
struct htlc *htlc,
const u8 *fail,
const struct rval *r);
#endif /* LIGHTNING_DAEMON_PAY_H */

5
daemon/test/test.sh

@ -1010,9 +1010,6 @@ if [ ! -n "$MANUALCOMMIT" ]; then
lcli1 add-route $ID2 $ID3 546000 10 36 36
RHASH5=`lcli3 accept-payment $HTLC_AMOUNT | sed 's/.*"\([0-9a-f]*\)".*/\1/'`
# FIXME: We don't save payments in db yet!
DO_RECONNECT=""
# Get route.
ROUTE=`lcli1 getroute $ID3 $HTLC_AMOUNT`
ROUTE=`echo $ROUTE | sed 's/^{ "route" : \(.*\) }$/\1/'`
@ -1061,8 +1058,6 @@ if [ ! -n "$MANUALCOMMIT" ]; then
echo "Pay to node3 didn't fail instantly second time" >&2
exit 1
fi
DO_RECONNECT=$RECONNECT
fi
lcli1 close $ID2

Loading…
Cancel
Save