mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 21:35:11 +01:00
sphinx: Add function to add a new v0 hop to a sphinx_path
This is just taking the existing serialization code and repackaging it in a more useful form. Signed-off-by: Christian Decker <decker.christian@gmail.com>
This commit is contained in:
parent
71cf4e1b39
commit
a0a1a1f752
147
common/sphinx.c
147
common/sphinx.c
@ -4,6 +4,7 @@
|
||||
#include <ccan/crypto/ripemd160/ripemd160.h>
|
||||
#include <ccan/crypto/sha256/sha256.h>
|
||||
#include <ccan/mem/mem.h>
|
||||
#include <common/node_id.h>
|
||||
#include <common/sphinx.h>
|
||||
#include <common/utils.h>
|
||||
|
||||
@ -85,6 +86,67 @@ struct sphinx_path *sphinx_path_new_with_key(const tal_t *ctx,
|
||||
return sp;
|
||||
}
|
||||
|
||||
static size_t sphinx_hop_size(const struct sphinx_hop *hop)
|
||||
{
|
||||
size_t size = tal_bytelen(hop->payload), vsize;
|
||||
|
||||
/* There is no point really in trying to serialize something that is
|
||||
* larger than the maximum length we can fit into the payload region
|
||||
* anyway. 3 here is the maximum varint size that we allow. */
|
||||
assert(size < ROUTING_INFO_SIZE - 3 - HMAC_SIZE);
|
||||
|
||||
/* Backwards compatibility: realm 0 is the legacy hop_data format and
|
||||
* always has 65 bytes in size */
|
||||
if (hop->realm == 0x00)
|
||||
return 65;
|
||||
|
||||
/* Since this uses the bigsize serialization format for variable
|
||||
* length integer encodings we need to allocate enough space for
|
||||
* it. Values >= 0xfd are used to signal multi-byte serializations. */
|
||||
if (size < 0xFD)
|
||||
vsize = 1;
|
||||
else
|
||||
vsize = 3;
|
||||
|
||||
/* The hop must accomodate the hop_payload, as well as the varint
|
||||
* describing the length and HMAC. */
|
||||
return vsize + size + HMAC_SIZE;
|
||||
}
|
||||
|
||||
static size_t sphinx_path_payloads_size(const struct sphinx_path *path)
|
||||
{
|
||||
size_t size = 0;
|
||||
for (size_t i=0; i<tal_count(path->hops); i++)
|
||||
size += sphinx_hop_size(&path->hops[i]);
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a raw payload hop to the path.
|
||||
*/
|
||||
static void sphinx_add_raw_hop(struct sphinx_path *path,
|
||||
const struct pubkey *pubkey, u8 realm,
|
||||
const u8 *payload)
|
||||
{
|
||||
struct sphinx_hop sp;
|
||||
sp.payload = payload;
|
||||
sp.realm = realm;
|
||||
sp.pubkey = *pubkey;
|
||||
tal_arr_expand(&path->hops, sp);
|
||||
assert(sphinx_path_payloads_size(path) <= ROUTING_INFO_SIZE);
|
||||
}
|
||||
|
||||
void sphinx_add_v0_hop(struct sphinx_path *path, const struct pubkey *pubkey,
|
||||
const struct short_channel_id *scid,
|
||||
struct amount_msat forward, u32 outgoing_cltv)
|
||||
{
|
||||
u8 *buf = tal_arr(path, u8, 0);
|
||||
towire_short_channel_id(&buf, scid);
|
||||
towire_u64(&buf, forward.millisatoshis); /* Raw: low-level serializer */
|
||||
towire_u32(&buf, outgoing_cltv);
|
||||
sphinx_add_raw_hop(path, pubkey, 0, buf);
|
||||
}
|
||||
|
||||
/* Small helper to append data to a buffer and update the position
|
||||
* into the buffer
|
||||
*/
|
||||
@ -247,28 +309,24 @@ static void compute_blinding_factor(const struct pubkey *key,
|
||||
memcpy(res, &temp, 32);
|
||||
}
|
||||
|
||||
static bool blind_group_element(
|
||||
struct pubkey *blindedelement,
|
||||
const struct pubkey *pubkey,
|
||||
const u8 blind[BLINDING_FACTOR_SIZE])
|
||||
static bool blind_group_element(struct pubkey *blindedelement,
|
||||
const struct pubkey *pubkey,
|
||||
const u8 blind[BLINDING_FACTOR_SIZE])
|
||||
{
|
||||
/* tweak_mul is inplace so copy first. */
|
||||
if (pubkey != blindedelement)
|
||||
*blindedelement = *pubkey;
|
||||
if (secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx, &blindedelement->pubkey, blind) != 1)
|
||||
if (secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx,
|
||||
&blindedelement->pubkey, blind) != 1)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool create_shared_secret(
|
||||
u8 *secret,
|
||||
const struct pubkey *pubkey,
|
||||
const u8 *sessionkey)
|
||||
static bool create_shared_secret(u8 *secret, const struct pubkey *pubkey,
|
||||
const struct secret *session_key)
|
||||
{
|
||||
|
||||
if (secp256k1_ecdh(secp256k1_ctx, secret, &pubkey->pubkey, sessionkey,
|
||||
NULL, NULL)
|
||||
!= 1)
|
||||
if (secp256k1_ecdh(secp256k1_ctx, secret, &pubkey->pubkey,
|
||||
session_key->data, NULL, NULL) != 1)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@ -279,7 +337,7 @@ bool onion_shared_secret(
|
||||
const struct privkey *privkey)
|
||||
{
|
||||
return create_shared_secret(secret, &packet->ephemeralkey,
|
||||
privkey->secret.data);
|
||||
&privkey->secret);
|
||||
}
|
||||
|
||||
static void generate_key_set(const u8 secret[SHARED_SECRET_SIZE],
|
||||
@ -294,19 +352,21 @@ static void generate_key_set(const u8 secret[SHARED_SECRET_SIZE],
|
||||
static struct hop_params *generate_hop_params(
|
||||
const tal_t *ctx,
|
||||
const u8 *sessionkey,
|
||||
struct pubkey path[])
|
||||
struct sphinx_path *path)
|
||||
{
|
||||
int i, j, num_hops = tal_count(path);
|
||||
int i, j, num_hops = tal_count(path->hops);
|
||||
struct pubkey temp;
|
||||
u8 blind[BLINDING_FACTOR_SIZE];
|
||||
struct hop_params *params = tal_arr(ctx, struct hop_params, num_hops);
|
||||
|
||||
/* Initialize the first hop with the raw information */
|
||||
if (secp256k1_ec_pubkey_create(
|
||||
secp256k1_ctx, ¶ms[0].ephemeralkey.pubkey, sessionkey) != 1)
|
||||
if (secp256k1_ec_pubkey_create(secp256k1_ctx,
|
||||
¶ms[0].ephemeralkey.pubkey,
|
||||
path->session_key->data) != 1)
|
||||
return NULL;
|
||||
|
||||
if (!create_shared_secret(params[0].secret, &path[0], sessionkey))
|
||||
if (!create_shared_secret(params[0].secret, &path->hops[0].pubkey,
|
||||
path->session_key))
|
||||
return NULL;
|
||||
|
||||
compute_blinding_factor(
|
||||
@ -327,7 +387,7 @@ static struct hop_params *generate_hop_params(
|
||||
* Order is indifferent, multiplication is commutative.
|
||||
*/
|
||||
memcpy(&blind, sessionkey, 32);
|
||||
temp = path[i];
|
||||
temp = path->hops[i].pubkey;
|
||||
if (!blind_group_element(&temp, &temp, blind))
|
||||
return NULL;
|
||||
for (j = 0; j < i; j++)
|
||||
@ -353,19 +413,6 @@ static struct hop_params *generate_hop_params(
|
||||
return params;
|
||||
}
|
||||
|
||||
static void serialize_hop_data(tal_t *ctx, u8 *dst, const struct hop_data *data)
|
||||
{
|
||||
u8 *buf = tal_arr(ctx, u8, 0);
|
||||
towire_u8(&buf, data->realm);
|
||||
towire_short_channel_id(&buf, &data->channel_id);
|
||||
towire_amount_msat(&buf, data->amt_forward);
|
||||
towire_u32(&buf, data->outgoing_cltv);
|
||||
towire_pad(&buf, 12);
|
||||
towire(&buf, data->hmac, HMAC_SIZE);
|
||||
memcpy(dst, buf, tal_count(buf));
|
||||
tal_free(buf);
|
||||
}
|
||||
|
||||
static void deserialize_hop_data(struct hop_data *data, const u8 *src)
|
||||
{
|
||||
const u8 *cursor = src;
|
||||
@ -378,25 +425,35 @@ static void deserialize_hop_data(struct hop_data *data, const u8 *src)
|
||||
fromwire(&cursor, &max, &data->hmac, HMAC_SIZE);
|
||||
}
|
||||
|
||||
static void sphinx_write_frame(u8 *dest, const struct sphinx_hop *hop)
|
||||
{
|
||||
memset(dest, 0, FRAME_SIZE);
|
||||
dest[0] = hop->realm;
|
||||
memcpy(dest + 1, hop->payload, tal_bytelen(hop->payload));
|
||||
memcpy(dest + FRAME_SIZE - HMAC_SIZE, hop->hmac, HMAC_SIZE);
|
||||
}
|
||||
|
||||
struct onionpacket *create_onionpacket(
|
||||
const tal_t *ctx,
|
||||
struct pubkey *path,
|
||||
struct hop_data hops_data[],
|
||||
const u8 *sessionkey,
|
||||
const u8 *assocdata,
|
||||
const size_t assocdatalen,
|
||||
struct sphinx_path *sp,
|
||||
struct secret **path_secrets
|
||||
)
|
||||
{
|
||||
struct onionpacket *packet = talz(ctx, struct onionpacket);
|
||||
int i, num_hops = tal_count(path);
|
||||
int i, num_hops = tal_count(sp->hops);
|
||||
u8 filler[(num_hops - 1) * FRAME_SIZE];
|
||||
struct keyset keys;
|
||||
u8 nexthmac[HMAC_SIZE];
|
||||
u8 stream[ROUTING_INFO_SIZE];
|
||||
struct hop_params *params = generate_hop_params(ctx, sessionkey, path);
|
||||
struct hop_params *params;
|
||||
struct secret *secrets = tal_arr(ctx, struct secret, num_hops);
|
||||
|
||||
if (sp->session_key == NULL) {
|
||||
sp->session_key = tal(sp, struct secret);
|
||||
randombytes_buf(sp->session_key, sizeof(struct secret));
|
||||
}
|
||||
|
||||
params = generate_hop_params(ctx, sp->session_key->data, sp);
|
||||
if (!params) {
|
||||
tal_free(packet);
|
||||
tal_free(secrets);
|
||||
@ -410,15 +467,15 @@ struct onionpacket *create_onionpacket(
|
||||
"rho", 3, num_hops, params);
|
||||
|
||||
for (i = num_hops - 1; i >= 0; i--) {
|
||||
memcpy(hops_data[i].hmac, nexthmac, HMAC_SIZE);
|
||||
hops_data[i].realm = 0;
|
||||
memcpy(sp->hops[i].hmac, nexthmac, HMAC_SIZE);
|
||||
generate_key_set(params[i].secret, &keys);
|
||||
generate_cipher_stream(stream, keys.rho, ROUTING_INFO_SIZE);
|
||||
|
||||
/* Rightshift mix-header by 2*HMAC_SIZE */
|
||||
/* Rightshift mix-header by FRAME_SIZE */
|
||||
memmove(packet->routinginfo + FRAME_SIZE, packet->routinginfo,
|
||||
ROUTING_INFO_SIZE - FRAME_SIZE);
|
||||
serialize_hop_data(packet, packet->routinginfo, &hops_data[i]);
|
||||
sphinx_write_frame(packet->routinginfo, &sp->hops[i]);
|
||||
|
||||
xorbytes(packet->routinginfo, packet->routinginfo, stream, ROUTING_INFO_SIZE);
|
||||
|
||||
if (i == num_hops - 1) {
|
||||
@ -426,7 +483,7 @@ struct onionpacket *create_onionpacket(
|
||||
memcpy(packet->routinginfo + len, filler, sizeof(filler));
|
||||
}
|
||||
|
||||
compute_packet_hmac(packet, assocdata, assocdatalen, keys.mu,
|
||||
compute_packet_hmac(packet, sp->associated_data, tal_bytelen(sp->associated_data), keys.mu,
|
||||
nexthmac);
|
||||
}
|
||||
memcpy(packet->mac, nexthmac, sizeof(nexthmac));
|
||||
|
@ -110,11 +110,7 @@ struct route_step {
|
||||
*/
|
||||
struct onionpacket *create_onionpacket(
|
||||
const tal_t * ctx,
|
||||
struct pubkey path[],
|
||||
struct hop_data hops_data[],
|
||||
const u8 * sessionkey,
|
||||
const u8 *assocdata,
|
||||
const size_t assocdatalen,
|
||||
struct sphinx_path *sp,
|
||||
struct secret **path_secrets
|
||||
);
|
||||
|
||||
@ -231,4 +227,10 @@ struct sphinx_path *sphinx_path_new_with_key(const tal_t *ctx,
|
||||
const u8 *associated_data,
|
||||
const struct secret *session_key);
|
||||
|
||||
/**
|
||||
* Add a V0 (Realm 0) single frame hop to the path.
|
||||
*/
|
||||
void sphinx_add_v0_hop(struct sphinx_path *path, const struct pubkey *pubkey,
|
||||
const struct short_channel_id *scid,
|
||||
struct amount_msat forward, u32 outgoing_cltv);
|
||||
#endif /* LIGHTNING_COMMON_SPHINX_H */
|
||||
|
@ -56,6 +56,9 @@ void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED)
|
||||
/* Generated stub for towire_u32 */
|
||||
void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED)
|
||||
{ fprintf(stderr, "towire_u32 called!\n"); abort(); }
|
||||
/* Generated stub for towire_u64 */
|
||||
void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED)
|
||||
{ fprintf(stderr, "towire_u64 called!\n"); abort(); }
|
||||
/* Generated stub for towire_u8 */
|
||||
void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED)
|
||||
{ fprintf(stderr, "towire_u8 called!\n"); abort(); }
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <ccan/read_write_all/read_write_all.h>
|
||||
#include <ccan/short_types/short_types.h>
|
||||
#include <ccan/str/hex/hex.h>
|
||||
#include <common/amount.h>
|
||||
#include <common/sphinx.h>
|
||||
#include <common/utils.h>
|
||||
#include <err.h>
|
||||
@ -19,11 +20,15 @@ static void do_generate(int argc, char **argv,
|
||||
int num_hops = argc - 1;
|
||||
struct pubkey *path = tal_arr(ctx, struct pubkey, num_hops);
|
||||
u8 privkeys[argc - 1][32];
|
||||
u8 sessionkey[32];
|
||||
struct secret session_key;
|
||||
struct hop_data hops_data[num_hops];
|
||||
struct secret *shared_secrets;
|
||||
struct sphinx_path *sp;
|
||||
|
||||
memset(&sessionkey, 'A', sizeof(sessionkey));
|
||||
assocdata = tal_arr(ctx, u8, 32);
|
||||
memset(&session_key, 'A', sizeof(struct secret));
|
||||
|
||||
sp = sphinx_path_new_with_key(ctx, assocdata, &session_key);
|
||||
|
||||
for (int i = 0; i < num_hops; i++) {
|
||||
size_t klen = strcspn(argv[1 + i], "/");
|
||||
@ -33,8 +38,9 @@ static void do_generate(int argc, char **argv,
|
||||
if (secp256k1_ec_pubkey_create(secp256k1_ctx, &path[i].pubkey,
|
||||
privkeys[i]) != 1)
|
||||
errx(1, "Could not decode pubkey");
|
||||
printf("# Node %d pubkey %s\n",
|
||||
i, secp256k1_pubkey_to_hexstr(ctx, &path[i].pubkey));
|
||||
fprintf(stderr, "Node %d pubkey %s\n", i,
|
||||
secp256k1_pubkey_to_hexstr(ctx, &path[i].pubkey));
|
||||
|
||||
memset(&hops_data[i], 0, sizeof(hops_data[i]));
|
||||
if (argv[1 + i][klen] != '\0') {
|
||||
/* FIXME: Generic realm support, not this hack! */
|
||||
@ -76,11 +82,12 @@ static void do_generate(int argc, char **argv,
|
||||
hops_data[i].amt_forward.millisatoshis = i; /* Raw: test code */
|
||||
hops_data[i].outgoing_cltv = i;
|
||||
}
|
||||
fprintf(stderr, "Hopdata %d: %s\n", i, tal_hexstr(NULL, &hops_data[i], sizeof(hops_data[i])));
|
||||
sphinx_add_v0_hop(sp, &path[i], &hops_data[i].channel_id, hops_data[i].amt_forward, i);
|
||||
}
|
||||
|
||||
struct onionpacket *res =
|
||||
create_onionpacket(ctx, path, hops_data, sessionkey,
|
||||
assocdata, ASSOC_DATA_SIZE, &shared_secrets);
|
||||
create_onionpacket(ctx, sp, &shared_secrets);
|
||||
|
||||
u8 *serialized = serialize_onionpacket(ctx, res);
|
||||
if (!serialized)
|
||||
|
@ -590,37 +590,36 @@ send_payment(struct lightningd *ld,
|
||||
enum onion_type failcode;
|
||||
size_t i, n_hops = tal_count(route);
|
||||
struct hop_data *hop_data = tal_arr(tmpctx, struct hop_data, n_hops);
|
||||
struct pubkey *path = tal_arr(tmpctx, struct pubkey, n_hops);
|
||||
struct node_id *ids = tal_arr(tmpctx, struct node_id, n_hops);
|
||||
struct wallet_payment *payment = NULL;
|
||||
struct htlc_out *hout;
|
||||
struct short_channel_id *channels;
|
||||
struct routing_failure *fail;
|
||||
struct channel *channel;
|
||||
struct sphinx_path *path;
|
||||
struct short_channel_id finalscid;
|
||||
struct pubkey pubkey;
|
||||
bool ret;
|
||||
|
||||
/* Expiry for HTLCs is absolute. And add one to give some margin. */
|
||||
base_expiry = get_block_height(ld->topology) + 1;
|
||||
|
||||
/* Extract IDs for each hop: create_onionpacket wants array of *keys*,
|
||||
* and wallet wants continuous array of node_ids */
|
||||
for (i = 0; i < n_hops; i++) {
|
||||
path = sphinx_path_new(tmpctx, rhash->u.u8);
|
||||
/* Extract IDs for each hop: create_onionpacket wants array. */
|
||||
for (i = 0; i < n_hops; i++)
|
||||
ids[i] = route[i].nodeid;
|
||||
/* JSON parsing checked these were valid, so Shouldn't Happen */
|
||||
if (!pubkey_from_node_id(&path[i], &ids[i])) {
|
||||
return command_fail(cmd, PAY_UNSPECIFIED_ERROR,
|
||||
"Invalid nodeid %s",
|
||||
type_to_string(tmpctx,
|
||||
struct node_id,
|
||||
&ids[i]));
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy hop_data[n] from route[n+1] (ie. where it goes next) */
|
||||
for (i = 0; i < n_hops - 1; i++) {
|
||||
ret = pubkey_from_node_id(&pubkey, &ids[i]);
|
||||
assert(ret);
|
||||
hop_data[i].realm = 0;
|
||||
hop_data[i].channel_id = route[i+1].channel_id;
|
||||
hop_data[i].amt_forward = route[i+1].amount;
|
||||
hop_data[i].outgoing_cltv = base_expiry + route[i+1].delay;
|
||||
sphinx_add_v0_hop(path, &pubkey, &route[i + 1].channel_id,
|
||||
route[i + 1].amount,
|
||||
base_expiry + route[i + 1].delay);
|
||||
}
|
||||
|
||||
/* And finally set the final hop to the special values in
|
||||
@ -630,6 +629,13 @@ send_payment(struct lightningd *ld,
|
||||
memset(&hop_data[i].channel_id, 0, sizeof(struct short_channel_id));
|
||||
hop_data[i].amt_forward = route[i].amount;
|
||||
|
||||
memset(&finalscid, 0, sizeof(struct short_channel_id));
|
||||
ret = pubkey_from_node_id(&pubkey, &ids[i]);
|
||||
assert(ret);
|
||||
sphinx_add_v0_hop(path, &pubkey, &finalscid,
|
||||
route[i].amount,
|
||||
base_expiry + route[i].delay);
|
||||
|
||||
/* Now, do we already have a payment? */
|
||||
payment = wallet_payment_by_hash(tmpctx, ld->wallet, rhash);
|
||||
if (payment) {
|
||||
@ -679,8 +685,7 @@ send_payment(struct lightningd *ld,
|
||||
randombytes_buf(&sessionkey, sizeof(sessionkey));
|
||||
|
||||
/* Onion will carry us from first peer onwards. */
|
||||
packet = create_onionpacket(tmpctx, path, hop_data, sessionkey, rhash->u.u8,
|
||||
sizeof(struct sha256), &path_secrets);
|
||||
packet = create_onionpacket(tmpctx, path, &path_secrets);
|
||||
onion = serialize_onionpacket(tmpctx, packet);
|
||||
|
||||
log_info(ld->log, "Sending %s over %zu hops to deliver %s",
|
||||
|
Loading…
Reference in New Issue
Block a user