Browse Source

gossipd: append timestamps & checksums to reply_channel_range if asked (EXPERIMENTAL)

In fact, we always generate them, we only send them if asked.  And we set
the flags to 0 if not --enable-experimental-features, so we never send in
that case.

Generating checksums involves pulling the channel_update from the
gossip_store, which is suboptimal: there's a FIXME to store the
checksum in memory.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
pull/2938/head
Rusty Russell 5 years ago
parent
commit
8abd850d3c
  1. 222
      gossipd/gossipd.c

222
gossipd/gossipd.c

@ -1,5 +1,3 @@
#include <bitcoin/chainparams.h>
#include <ccan/array_size/array_size.h>
/*~ Welcome to the gossip daemon: keeper of maps!
*
* This is the last "global" daemon; it has three purposes.
@ -12,11 +10,14 @@
* The gossip protocol itself is fairly simple, but has some twists which
* add complexity to this daemon.
*/
#include <bitcoin/chainparams.h>
#include <ccan/array_size/array_size.h>
#include <ccan/asort/asort.h>
#include <ccan/bitmap/bitmap.h>
#include <ccan/build_assert/build_assert.h>
#include <ccan/cast/cast.h>
#include <ccan/container_of/container_of.h>
#include <ccan/crc32c/crc32c.h>
#include <ccan/crypto/hkdf_sha256/hkdf_sha256.h>
#include <ccan/crypto/siphash24/siphash24.h>
#include <ccan/endian/endian.h>
@ -81,6 +82,41 @@ static u32 max_encoding_bytes = -1U;
static bool suppress_gossip = false;
#endif
#if EXPERIMENTAL_FEATURES == 0
/* We want these definitions for convenience, even if we never encode/decode
* them when not EXPERIMENTAL_FEATURES */
struct tlv_reply_channel_range_tlvs_timestamps_tlv {
u8 encoding_type;
u8 *encoded_timestamps;
};
struct tlv_reply_channel_range_tlvs_checksums_tlv {
u8 encoding_type;
u8 *encoded_checksums;
};
struct channel_update_timestamps {
u32 timestamp_node_id_1;
u32 timestamp_node_id_2;
};
struct channel_update_checksums {
u32 checksum_node_id_1;
u32 checksum_node_id_2;
};
static void towire_channel_update_checksums(u8 **p,
const struct channel_update_checksums *channel_update_checksums)
{
abort();
}
static void towire_channel_update_timestamps(u8 **p,
const struct channel_update_timestamps *channel_update_timestamps)
{
abort();
}
#endif
/*~ The core daemon structure: */
struct daemon {
/* Who am I? Helps us find ourself in the routing map. */
@ -273,6 +309,26 @@ static void encoding_add_short_channel_id(u8 **encoded,
towire_short_channel_id(encoded, scid);
}
/* Marshal a single channel_update_timestamps */
static void encoding_add_timestamps(u8 **encoded,
const struct channel_update_timestamps *ts)
{
towire_channel_update_timestamps(encoded, ts);
}
/* Marshal a single channel_update_checksums */
static void encoding_add_checksums(u8 **encoded,
const struct channel_update_checksums *csums)
{
towire_channel_update_checksums(encoded, csums);
}
/* Marshal a single query flag (we don't query, so not currently used) */
static UNNEEDED void encoding_add_query_flag(u8 **encoded, bigsize_t flag)
{
towire_bigsize(encoded, flag);
}
/* Greg Maxwell asked me privately about using zlib for communicating a set,
* and suggested that we'd be better off using Golomb-Rice coding a-la BIP
* 158. However, naively using Rice encoding isn't a win: we have to get
@ -867,7 +923,9 @@ void update_peers_broadcast_index(struct list_head *peers, u32 offset)
* a given range of blocks; each one indicates the range of blocks it covers. */
static void reply_channel_range(struct peer *peer,
u32 first_blocknum, u32 number_of_blocks,
const u8 *encoded)
const u8 *encoded_scids,
struct tlv_reply_channel_range_tlvs_timestamps_tlv *timestamps,
struct tlv_reply_channel_range_tlvs_checksums_tlv *checksums)
{
/* BOLT #7:
*
@ -885,21 +943,92 @@ static void reply_channel_range(struct peer *peer,
* - SHOULD set `complete` to 1.
*/
#if EXPERIMENTAL_FEATURES
struct tlv_reply_channel_range_tlvs *tlvs
= tlv_reply_channel_range_tlvs_new(tmpctx);
tlvs->timestamps_tlv = timestamps;
tlvs->checksums_tlv = checksums;
u8 *msg = towire_reply_channel_range(NULL,
&peer->daemon->chain_hash,
first_blocknum,
number_of_blocks,
1, encoded, NULL);
1, encoded_scids, tlvs);
#else
u8 *msg = towire_reply_channel_range(NULL,
&peer->daemon->chain_hash,
first_blocknum,
number_of_blocks,
1, encoded);
1, encoded_scids);
#endif
queue_peer_msg(peer, take(msg));
}
/* BOLT-61a1365a45cc8b463ddbbe3429d350f8eac787dd #7:
*
* `query_option_flags` is a bitfield represented as a minimally-encoded varint.
* Bits have the following meaning:
*
* | Bit Position | Meaning |
* | ------------- | ----------------------- |
* | 0 | Sender wants timestamps |
* | 1 | Sender wants checksums |
*/
enum query_option_flags {
QUERY_ADD_TIMESTAMPS = 0x1,
QUERY_ADD_CHECKSUMS = 0x2,
};
/* BOLT-61a1365a45cc8b463ddbbe3429d350f8eac787dd #7:
*
* The checksum of a `channel_update` is the CRC32C checksum as specified in
* [RFC3720](https://tools.ietf.org/html/rfc3720#appendix-B.4) of this
* `channel_update` without its `signature` and `timestamp` fields.
*/
static u32 crc32_of_update(const u8 *channel_update)
{
u32 sum;
/* BOLT #7:
*
* 1. type: 258 (`channel_update`)
* 2. data:
* * [`signature`:`signature`]
* * [`chain_hash`:`chain_hash`]
* * [`short_channel_id`:`short_channel_id`]
* * [`u32`:`timestamp`]
*...
*/
/* We already checked it's valid before accepting */
assert(tal_count(channel_update) > 64 + 32 + 8 + 4);
sum = crc32c(0, channel_update + 64, 32 + 8);
sum = crc32c(sum, channel_update + 64 + 32 + 8 + 4,
tal_count(channel_update) - (64 + 32 + 8 + 4));
return sum;
}
static void get_checksum_and_timestamp(struct routing_state *rstate,
const struct chan *chan,
int direction,
u32 *tstamp, u32 *csum)
{
const struct half_chan *hc = &chan->half[direction];
if (!is_chan_public(chan) || !is_halfchan_defined(hc)) {
*tstamp = *csum = 0;
} else {
const u8 *update = gossip_store_get(tmpctx, rstate->gs,
hc->bcast.index);
*tstamp = hc->bcast.timestamp;
*csum = crc32_of_update(update);
}
}
/* FIXME: This assumes that the tlv type encodes into 1 byte! */
static size_t tlv_len(const tal_t *msg)
{
return 1 + bigsize_len(tal_count(msg)) + tal_count(msg);
}
/*~ When we need to send an array of channels, it might go over our 64k packet
* size. If it doesn't, we recurse, splitting in two, etc. Each message
* indicates what blocks it contains, so the recipient knows when we're
@ -910,10 +1039,13 @@ static void reply_channel_range(struct peer *peer,
*/
static bool queue_channel_ranges(struct peer *peer,
u32 first_blocknum, u32 number_of_blocks,
u32 tail_blocks)
u32 tail_blocks,
enum query_option_flags query_option_flags)
{
struct routing_state *rstate = peer->daemon->rstate;
u8 *encoded_scids = encoding_start(tmpctx);
struct tlv_reply_channel_range_tlvs_timestamps_tlv *tstamps;
struct tlv_reply_channel_range_tlvs_checksums_tlv *csums;
struct short_channel_id scid;
bool scid_ok;
@ -930,6 +1062,21 @@ static bool queue_channel_ranges(struct peer *peer,
*/
const size_t reply_overhead = 32 + 4 + 4 + 1 + 2;
const size_t max_encoded_bytes = 65535 - 2 - reply_overhead;
size_t extension_bytes;
if (query_option_flags & QUERY_ADD_TIMESTAMPS) {
tstamps = tal(tmpctx,
struct tlv_reply_channel_range_tlvs_timestamps_tlv);
tstamps->encoded_timestamps = encoding_start(tstamps);
} else
tstamps = NULL;
if (query_option_flags & QUERY_ADD_CHECKSUMS) {
csums = tal(tmpctx,
struct tlv_reply_channel_range_tlvs_checksums_tlv);
csums->encoded_checksums = encoding_start(csums);
} else
csums = NULL;
/* Avoid underflow: we don't use block 0 anyway */
if (first_blocknum == 0)
@ -945,23 +1092,67 @@ static bool queue_channel_ranges(struct peer *peer,
* works because each short_channel_id is basically a 64-bit unsigned
* integer.
*
* First we iteraate and gather all the short channel ids. */
* First we iterate and gather all the short channel ids. */
while (uintmap_after(&rstate->chanmap, &scid.u64)) {
struct chan *chan;
struct channel_update_timestamps ts;
struct channel_update_checksums cs;
u32 blocknum = short_channel_id_blocknum(&scid);
if (blocknum >= first_blocknum + number_of_blocks)
break;
encoding_add_short_channel_id(&encoded_scids, &scid);
/* FIXME: Store csum in header. */
chan = get_channel(rstate, &scid);
get_checksum_and_timestamp(rstate, chan, 0,
&ts.timestamp_node_id_1,
&cs.checksum_node_id_1);
get_checksum_and_timestamp(rstate, chan, 1,
&ts.timestamp_node_id_2,
&cs.checksum_node_id_2);
if (csums)
encoding_add_checksums(&csums->encoded_checksums, &cs);
if (tstamps)
encoding_add_timestamps(&tstamps->encoded_timestamps,
&ts);
}
extension_bytes = 0;
/* If either of these can't fit in max_encoded_bytes by itself,
* it's over. */
if (csums) {
if (!encoding_end_external_type(&csums->encoded_checksums,
&csums->encoding_type,
max_encoded_bytes))
goto wont_fit;
/* 1 byte for encoding_type, too */
extension_bytes += 1 + tlv_len(csums->encoded_checksums);
}
if (tstamps) {
if (!encoding_end_external_type(&tstamps->encoded_timestamps,
&tstamps->encoding_type,
max_encoded_bytes))
goto wont_fit;
/* 1 byte for encoding_type, too */
extension_bytes += 1 + tlv_len(tstamps->encoded_timestamps);
}
/* If we can encode that, fine: send it */
if (encoding_end_prepend_type(&encoded_scids, max_encoded_bytes)) {
if (extension_bytes <= max_encoded_bytes
&& encoding_end_prepend_type(&encoded_scids,
max_encoded_bytes - extension_bytes)) {
reply_channel_range(peer, first_blocknum,
number_of_blocks + tail_blocks,
encoded_scids);
encoded_scids,
tstamps, csums);
return true;
}
wont_fit:
/* It wouldn't all fit: divide in half */
/* We assume we can always send one block! */
if (number_of_blocks <= 1) {
@ -976,10 +1167,11 @@ static bool queue_channel_ranges(struct peer *peer,
first_blocknum + number_of_blocks / 2,
number_of_blocks - number_of_blocks / 2,
tail_blocks);
return queue_channel_ranges(peer, first_blocknum, number_of_blocks / 2, 0)
return queue_channel_ranges(peer, first_blocknum, number_of_blocks / 2,
0, query_option_flags)
&& queue_channel_ranges(peer, first_blocknum + number_of_blocks / 2,
number_of_blocks - number_of_blocks / 2,
tail_blocks);
tail_blocks, query_option_flags);
}
/*~ The peer can ask for all channels is a series of blocks. We reply with one
@ -990,6 +1182,7 @@ static u8 *handle_query_channel_range(struct peer *peer, const u8 *msg)
struct bitcoin_blkid chain_hash;
u32 first_blocknum, number_of_blocks, tail_blocks;
struct short_channel_id last_scid;
enum query_option_flags query_option_flags;
#if EXPERIMENTAL_FEATURES
struct tlv_query_channel_range_tlvs *tlvs
@ -1002,6 +1195,10 @@ static u8 *handle_query_channel_range(struct peer *peer, const u8 *msg)
"Bad query_channel_range w/tlvs %s",
tal_hex(tmpctx, msg));
}
if (tlvs->query_option)
query_option_flags = tlvs->query_option->query_option_flags;
else
query_option_flags = 0;
#else
if (!fromwire_query_channel_range(msg, &chain_hash,
&first_blocknum, &number_of_blocks)) {
@ -1009,6 +1206,7 @@ static u8 *handle_query_channel_range(struct peer *peer, const u8 *msg)
"Bad query_channel_range %s",
tal_hex(tmpctx, msg));
}
query_option_flags = 0;
#endif
/* FIXME: if they ask for the wrong chain, we should not ignore it,
@ -1039,7 +1237,7 @@ static u8 *handle_query_channel_range(struct peer *peer, const u8 *msg)
tail_blocks = 0;
if (!queue_channel_ranges(peer, first_blocknum, number_of_blocks,
tail_blocks))
tail_blocks, query_option_flags))
return towire_errorfmt(peer, NULL,
"Invalid query_channel_range %u+%u",
first_blocknum, number_of_blocks + tail_blocks);

Loading…
Cancel
Save