diff --git a/daemon/routing.c b/daemon/routing.c index e838872a3..b8460504c 100644 --- a/daemon/routing.c +++ b/daemon/routing.c @@ -477,6 +477,25 @@ static bool get_slash_u32(const char **arg, u32 *v) return (endp == *arg); } +bool short_channel_id_from_str(const char *str, size_t strlen, + struct short_channel_id *dst) +{ + u32 blocknum, txnum; + u16 outnum; + int matches; + + char buf[strlen + 1]; + memcpy(buf, str, strlen); + buf[strlen] = 0; + + matches = sscanf(buf, "%u:%u:%hu", &blocknum, &txnum, &outnum); + dst->blocknum = blocknum; + dst->txnum = txnum; + dst->outnum = outnum; + return matches == 3; +} + + /* srcid/dstid/base/var/delay/minblocks */ char *opt_add_route(const char *arg, struct lightningd_state *dstate) { diff --git a/daemon/routing.h b/daemon/routing.h index a4584c2db..40e86e17b 100644 --- a/daemon/routing.h +++ b/daemon/routing.h @@ -180,4 +180,7 @@ struct route_hop *get_route(tal_t *ctx, struct routing_state *rstate, * the direction bit the matching channel should get */ #define get_channel_direction(from, to) (pubkey_cmp(from, to) > 0) +bool short_channel_id_from_str(const char *str, size_t strlen, + struct short_channel_id *dst); + #endif /* LIGHTNING_DAEMON_ROUTING_H */ diff --git a/lightningd/pay.c b/lightningd/pay.c index 0a341b368..e41c7ec82 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -193,9 +193,14 @@ static void json_sendpay(struct command *cmd, end = json_next(routetok); n_hops = 0; ids = tal_arr(cmd, struct pubkey, n_hops); - hoppayloads = tal_arr(cmd, struct hoppayload, 0); + + /* Switching to hop_data in the next commit, and it causes a + * double free in peer_control otherwise */ + hoppayloads = tal_arr(NULL, struct hoppayload, 0); for (t = routetok + 1; t < end; t = json_next(t)) { - const jsmntok_t *amttok, *idtok, *delaytok; + const jsmntok_t *amttok, *idtok, *delaytok, *chantok; + /* Will populate into hop_data in the next commit */ + struct short_channel_id scid; if (t->type != JSMN_OBJECT) { command_fail(cmd, "route %zu '%.*s' is not an object", @@ -207,8 +212,9 @@ static void json_sendpay(struct command *cmd, 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 msatoshi/id/delay", + chantok = json_get_member(buffer, t, "channel"); + if (!amttok || !idtok || !delaytok || !chantok) { + command_fail(cmd, "route %zu needs msatoshi/id/channel/delay", n_hops); return; } @@ -233,6 +239,12 @@ static void json_sendpay(struct command *cmd, tal_resize(&ids, n_hops+1); memset(&ids[n_hops], 0, sizeof(ids[n_hops])); + if (!short_channel_id_from_str(buffer + chantok->start, + chantok->end - chantok->start, + &scid)) { + command_fail(cmd, "route %zu invalid id", n_hops); + return; + } if (!pubkey_from_hexstr(buffer + idtok->start, idtok->end - idtok->start, &ids[n_hops])) { diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index 94d03e8f7..e100953fe 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -218,7 +218,12 @@ class LightningDTests(BaseLightningDTests): rhash = l2.rpc.invoice(amt, 'testpayment2')['rhash'] assert l2.rpc.listinvoice('testpayment2')[0]['complete'] == False - routestep = { 'msatoshi' : amt, 'id' : l2.info['id'], 'delay' : 5} + routestep = { + 'msatoshi' : amt, + 'id' : l2.info['id'], + 'delay' : 5, + 'channel': '1:1:1' + } # Insufficient funds. rs = copy.deepcopy(routestep) @@ -257,7 +262,7 @@ class LightningDTests(BaseLightningDTests): # Overpaying by "only" a factor of 2 succeeds. rhash = l2.rpc.invoice(amt, 'testpayment3')['rhash'] assert l2.rpc.listinvoice('testpayment3')[0]['complete'] == False - routestep = { 'msatoshi' : amt * 2, 'id' : l2.info['id'], 'delay' : 5} + routestep = { 'msatoshi' : amt * 2, 'id' : l2.info['id'], 'delay' : 5, 'channel': '1:1:1'} l1.rpc.sendpay(to_json([routestep]), rhash) assert l2.rpc.listinvoice('testpayment3')[0]['complete'] == True