2021-12-04 12:23:56 +01:00
# include "config.h"
2019-06-03 20:13:25 +02:00
# include <ccan/array_size/array_size.h>
2019-06-11 07:30:54 +02:00
# include <ccan/crc32c/crc32c.h>
2019-06-03 20:15:25 +02:00
# include <ccan/noerr/noerr.h>
2018-03-13 12:08:03 +01:00
# include <ccan/read_write_all/read_write_all.h>
2019-06-20 04:58:52 +02:00
# include <ccan/tal/str/str.h>
2019-05-04 07:53:13 +02:00
# include <common/gossip_store.h>
2018-03-13 12:08:03 +01:00
# include <common/status.h>
2018-04-11 01:03:35 +02:00
# include <errno.h>
2018-03-13 12:08:03 +01:00
# include <fcntl.h>
2021-12-04 12:23:56 +01:00
# include <gossipd/gossip_store.h>
2020-08-25 04:05:45 +02:00
# include <gossipd/gossip_store_wiregen.h>
2024-01-31 06:44:06 +01:00
# include <gossipd/gossipd.h>
# include <gossipd/gossmap_manage.h>
# include <inttypes.h>
2019-10-08 03:11:24 +02:00
# include <sys/stat.h>
2019-06-03 20:13:25 +02:00
# include <sys/uio.h>
2018-03-18 15:54:34 +01:00
# include <unistd.h>
2020-09-07 23:06:50 +02:00
# include <wire/peer_wire.h>
2018-03-13 12:08:03 +01:00
2024-01-31 05:33:11 +01:00
/* Obsolete ZOMBIE bit */
# define GOSSIP_STORE_ZOMBIE_BIT_V13 0x1000U
2018-05-30 16:06:23 +02:00
# define GOSSIP_STORE_TEMP_FILENAME "gossip_store.tmp"
2024-01-31 05:33:11 +01:00
/* We write it as major version 0, minor version 14 */
# define GOSSIP_STORE_VER ((0 << 5) | 14)
2018-03-13 12:08:03 +01:00
struct gossip_store {
2024-01-31 05:26:33 +01:00
/* Back pointer. */
struct daemon * daemon ;
2018-03-20 16:18:18 +01:00
int fd ;
2018-03-23 15:51:17 +01:00
u8 version ;
2018-05-30 14:27:32 +02:00
2019-04-10 09:31:29 +02:00
/* Offset of current EOF */
u64 len ;
2019-10-08 03:11:24 +02:00
/* Timestamp of store when we opened it (0 if we created it) */
u32 timestamp ;
2018-03-13 12:08:03 +01:00
} ;
2018-03-20 14:17:07 +01:00
static void gossip_store_destroy ( struct gossip_store * gs )
{
2018-03-20 16:18:18 +01:00
close ( gs - > fd ) ;
2018-03-20 14:17:07 +01:00
}
2019-09-11 01:53:41 +02:00
# if HAVE_PWRITEV
2021-03-17 02:54:51 +01:00
/* One fewer syscall for the win! */
2019-09-11 01:53:41 +02:00
static ssize_t gossip_pwritev ( int fd , const struct iovec * iov , int iovcnt ,
off_t offset )
{
return pwritev ( fd , iov , iovcnt , offset ) ;
}
# else /* Hello MacOS! */
static ssize_t gossip_pwritev ( int fd , const struct iovec * iov , int iovcnt ,
off_t offset )
{
2021-03-17 02:54:51 +01:00
if ( lseek ( fd , offset , SEEK_SET ) ! = offset )
return - 1 ;
return writev ( fd , iov , iovcnt ) ;
2019-09-11 01:53:41 +02:00
}
# endif /* !HAVE_PWRITEV */
2024-01-31 06:44:14 +01:00
static bool append_msg ( int fd , const u8 * msg , u32 timestamp , u64 * len )
2019-05-04 07:53:12 +02:00
{
2019-06-03 20:17:25 +02:00
struct gossip_hdr hdr ;
2019-05-04 07:53:12 +02:00
u32 msglen ;
2019-06-03 20:13:25 +02:00
struct iovec iov [ 2 ] ;
2019-05-04 07:53:12 +02:00
2021-03-17 02:57:27 +01:00
/* Don't ever overwrite the version header! */
assert ( * len ) ;
2019-05-04 07:53:12 +02:00
msglen = tal_count ( msg ) ;
2023-01-30 07:24:08 +01:00
hdr . len = cpu_to_be16 ( msglen ) ;
hdr . flags = 0 ;
2019-06-03 20:17:25 +02:00
hdr . crc = cpu_to_be32 ( crc32c ( timestamp , msg , msglen ) ) ;
hdr . timestamp = cpu_to_be32 ( timestamp ) ;
2019-05-04 07:53:12 +02:00
2021-03-17 02:54:51 +01:00
/* pwritev makes it more likely to appear at once, plus it's
* exactly what we want . */
2019-06-03 20:17:25 +02:00
iov [ 0 ] . iov_base = & hdr ;
2019-06-03 20:13:25 +02:00
iov [ 0 ] . iov_len = sizeof ( hdr ) ;
iov [ 1 ] . iov_base = ( void * ) msg ;
iov [ 1 ] . iov_len = msglen ;
2019-09-11 01:53:41 +02:00
if ( gossip_pwritev ( fd , iov , ARRAY_SIZE ( iov ) , * len ) ! = sizeof ( hdr ) + msglen )
2019-07-30 03:10:09 +02:00
return false ;
* len + = sizeof ( hdr ) + msglen ;
return true ;
2019-05-04 07:53:12 +02:00
}
2022-09-14 05:50:31 +02:00
/* v9 added the GOSSIP_STORE_LEN_RATELIMIT_BIT.
* v10 removed any remaining non - htlc - max channel_update .
2023-07-19 19:11:30 +02:00
* v11 mandated channel_updates use the htlc_maximum_msat field
* v12 added the zombie flag for expired channel updates
2024-01-31 04:16:16 +01:00
* v13 removed private gossip entries
2024-01-31 05:33:11 +01:00
* v14 removed zombie and spam flags
2022-09-14 05:50:31 +02:00
*/
2020-05-04 02:18:25 +02:00
static bool can_upgrade ( u8 oldversion )
{
2024-01-31 05:33:11 +01:00
return oldversion > = 9 & & oldversion < = 13 ;
2024-01-31 04:16:16 +01:00
}
/* On upgrade, do best effort on private channels: hand them to
* lightningd as if we just receive them , before removing from the
* store */
2024-01-31 05:26:33 +01:00
static void give_lightningd_canned_private_update ( struct daemon * daemon ,
2024-01-31 04:16:16 +01:00
const u8 * msg )
{
u8 * update ;
secp256k1_ecdsa_signature signature ;
struct bitcoin_blkid chain_hash ;
struct short_channel_id short_channel_id ;
u32 timestamp ;
u8 message_flags , channel_flags ;
u16 cltv_expiry_delta ;
struct amount_msat htlc_minimum_msat , htlc_maximum_msat ;
u32 fee_base_msat , fee_proportional_millionths ;
if ( ! fromwire_gossip_store_private_update_obs ( tmpctx , msg , & update ) ) {
status_broken ( " Could not parse private update %s " ,
tal_hex ( tmpctx , msg ) ) ;
return ;
}
if ( ! fromwire_channel_update ( update ,
& signature ,
& chain_hash ,
& short_channel_id ,
& timestamp ,
& message_flags ,
& channel_flags ,
& cltv_expiry_delta ,
& htlc_minimum_msat ,
& fee_base_msat ,
& fee_proportional_millionths ,
& htlc_maximum_msat ) ) {
status_broken ( " Could not parse inner private update %s " ,
tal_hex ( tmpctx , msg ) ) ;
return ;
}
/* From NULL source (i.e. trust us!) */
2024-01-31 05:26:33 +01:00
tell_lightningd_peer_update ( daemon ,
2024-01-31 04:16:16 +01:00
NULL ,
short_channel_id ,
fee_base_msat ,
fee_proportional_millionths ,
cltv_expiry_delta ,
htlc_minimum_msat ,
htlc_maximum_msat ) ;
2020-05-04 02:18:25 +02:00
}
2020-10-20 05:59:30 +02:00
static bool upgrade_field ( u8 oldversion ,
2024-01-31 05:26:33 +01:00
struct daemon * daemon ,
2024-01-31 05:33:11 +01:00
u16 hdr_flags ,
2020-10-20 05:59:30 +02:00
u8 * * msg )
2020-05-04 02:18:25 +02:00
{
2024-01-31 04:16:16 +01:00
int type = fromwire_peektype ( * msg ) ;
2020-05-04 02:18:25 +02:00
assert ( can_upgrade ( oldversion ) ) ;
2022-09-14 05:50:31 +02:00
2024-01-31 04:16:16 +01:00
if ( oldversion < = 10 ) {
2022-09-14 05:50:31 +02:00
/* Remove old channel_update with no htlc_maximum_msat */
2024-01-31 04:16:16 +01:00
if ( type = = WIRE_CHANNEL_UPDATE
2022-09-14 05:50:31 +02:00
& & tal_bytelen ( * msg ) = = 130 ) {
* msg = tal_free ( * msg ) ;
}
}
2024-01-31 04:16:16 +01:00
if ( oldversion < = 12 ) {
/* Remove private entries */
if ( type = = WIRE_GOSSIP_STORE_PRIVATE_CHANNEL_OBS ) {
* msg = tal_free ( * msg ) ;
} else if ( type = = WIRE_GOSSIP_STORE_PRIVATE_UPDATE_OBS ) {
2024-01-31 05:26:33 +01:00
give_lightningd_canned_private_update ( daemon , * msg ) ;
2024-01-31 04:16:16 +01:00
* msg = tal_free ( * msg ) ;
}
}
2024-01-31 05:33:11 +01:00
if ( oldversion < = 13 ) {
/* Discard any zombies */
if ( hdr_flags & GOSSIP_STORE_ZOMBIE_BIT_V13 ) {
* msg = tal_free ( * msg ) ;
}
}
2022-09-14 05:50:31 +02:00
2020-05-04 02:18:25 +02:00
return true ;
}
2024-01-31 07:08:20 +01:00
/* Read gossip store entries, copy non-deleted ones. Check basic
* validity , but this code is written as simply and robustly as
* possible !
*
* Returns fd of new store .
*/
static int gossip_store_compact ( struct daemon * daemon ,
u64 * total_len ,
bool * populated ,
struct chan_dying * * dying )
2019-06-22 02:18:13 +02:00
{
2024-01-31 07:08:20 +01:00
size_t cannounces = 0 , cupdates = 0 , nannounces = 0 , deleted = 0 ;
2019-06-22 02:18:13 +02:00
int old_fd , new_fd ;
2024-01-31 07:08:20 +01:00
u64 old_len , cur_off ;
2019-06-22 02:18:13 +02:00
struct gossip_hdr hdr ;
2022-09-14 05:50:31 +02:00
u8 oldversion , version = GOSSIP_STORE_VER ;
2019-10-08 03:11:24 +02:00
struct stat st ;
2024-01-31 07:08:20 +01:00
bool prev_chan_ann = false ;
struct timeabs start = time_now ( ) ;
const char * bad ;
* populated = false ;
old_len = 0 ;
2019-06-22 02:18:13 +02:00
2024-01-31 07:08:20 +01:00
new_fd = open ( GOSSIP_STORE_TEMP_FILENAME , O_RDWR | O_TRUNC | O_CREAT , 0600 ) ;
if ( new_fd < 0 ) {
status_failed ( STATUS_FAIL_INTERNAL_ERROR ,
" Opening new gossip_store file: %s " ,
strerror ( errno ) ) ;
}
if ( ! write_all ( new_fd , & version , sizeof ( version ) ) ) {
status_failed ( STATUS_FAIL_INTERNAL_ERROR ,
" Writing new gossip_store file: %s " ,
strerror ( errno ) ) ;
}
* total_len = sizeof ( version ) ;
/* RDWR since we add closed marker at end! */
2021-05-22 07:00:22 +02:00
old_fd = open ( GOSSIP_STORE_FILENAME , O_RDWR ) ;
2024-01-31 07:08:20 +01:00
if ( old_fd = = - 1 ) {
if ( errno = = ENOENT )
goto rename_new ;
status_failed ( STATUS_FAIL_INTERNAL_ERROR ,
" Reading gossip_store file: %s " ,
strerror ( errno ) ) ;
} ;
2019-10-08 03:11:24 +02:00
if ( fstat ( old_fd , & st ) ! = 0 ) {
status_broken ( " Could not stat gossip_store: %s " ,
strerror ( errno ) ) ;
2024-01-31 07:08:20 +01:00
goto rename_new ;
2019-06-22 02:18:13 +02:00
}
2020-05-04 02:18:25 +02:00
if ( ! read_all ( old_fd , & oldversion , sizeof ( oldversion ) )
| | ( oldversion ! = version & & ! can_upgrade ( oldversion ) ) ) {
2019-06-22 02:18:13 +02:00
status_broken ( " gossip_store_compact: bad version " ) ;
2024-01-31 07:08:20 +01:00
goto rename_new ;
2019-06-22 02:18:13 +02:00
}
2024-01-31 07:08:20 +01:00
cur_off = old_len = sizeof ( oldversion ) ;
2019-06-22 02:18:13 +02:00
2024-01-31 07:08:20 +01:00
/* Read everything, write non-deleted ones to new_fd. If something goes wrong,
* we end up with truncated store . */
2019-06-22 02:18:13 +02:00
while ( read_all ( old_fd , & hdr , sizeof ( hdr ) ) ) {
size_t msglen ;
u8 * msg ;
2024-01-31 07:08:20 +01:00
/* Partial writes can happen, and we simply truncate */
2023-01-30 07:24:08 +01:00
msglen = be16_to_cpu ( hdr . len ) ;
2019-06-22 02:18:13 +02:00
msg = tal_arr ( NULL , u8 , msglen ) ;
if ( ! read_all ( old_fd , msg , msglen ) ) {
2024-01-31 07:08:20 +01:00
status_unusual ( " gossip_store_compact: store ends early at % " PRIu64 ,
old_len ) ;
2019-06-22 02:18:13 +02:00
tal_free ( msg ) ;
2024-01-31 07:08:20 +01:00
goto rename_new ;
2019-06-22 02:18:13 +02:00
}
2024-01-31 07:08:20 +01:00
cur_off = old_len ;
old_len + = sizeof ( hdr ) + msglen ;
2023-01-30 07:24:08 +01:00
if ( be16_to_cpu ( hdr . flags ) & GOSSIP_STORE_DELETED_BIT ) {
2019-06-22 02:18:13 +02:00
deleted + + ;
tal_free ( msg ) ;
continue ;
}
2022-09-14 05:50:31 +02:00
/* Check checksum (upgrade would overwrite, so do it now) */
if ( be32_to_cpu ( hdr . crc )
! = crc32c ( be32_to_cpu ( hdr . timestamp ) , msg , msglen ) ) {
2024-01-31 07:08:20 +01:00
bad = tal_fmt ( tmpctx , " checksum verification failed? %08x should be %08x " ,
2022-09-14 05:50:31 +02:00
be32_to_cpu ( hdr . crc ) ,
2022-09-14 05:50:31 +02:00
crc32c ( be32_to_cpu ( hdr . timestamp ) , msg , msglen ) ) ;
2024-01-31 07:08:20 +01:00
goto badmsg ;
2022-09-14 05:50:31 +02:00
}
2020-05-04 02:18:25 +02:00
if ( oldversion ! = version ) {
2024-01-31 05:33:11 +01:00
if ( ! upgrade_field ( oldversion , daemon ,
be16_to_cpu ( hdr . flags ) , & msg ) ) {
2020-05-04 02:18:25 +02:00
tal_free ( msg ) ;
2024-01-31 07:08:20 +01:00
bad = " upgrade of store failed " ;
goto badmsg ;
2020-05-04 02:18:25 +02:00
}
2022-09-14 05:50:31 +02:00
/* It can tell us to delete record entirely. */
if ( msg = = NULL ) {
deleted + + ;
continue ;
}
2020-05-04 02:18:25 +02:00
/* Recalc msglen and header */
msglen = tal_bytelen ( msg ) ;
2023-01-30 07:24:08 +01:00
hdr . len = cpu_to_be16 ( msglen ) ;
2020-05-04 02:18:25 +02:00
hdr . crc = cpu_to_be32 ( crc32c ( be32_to_cpu ( hdr . timestamp ) ,
msg , msglen ) ) ;
}
2020-10-20 05:58:06 +02:00
/* Don't write out old tombstones */
if ( fromwire_peektype ( msg ) = = WIRE_GOSSIP_STORE_DELETE_CHAN ) {
deleted + + ;
tal_free ( msg ) ;
continue ;
}
2024-01-31 07:08:20 +01:00
switch ( fromwire_peektype ( msg ) ) {
case WIRE_GOSSIP_STORE_CHANNEL_AMOUNT :
/* Previous channel_announcement may have been deleted */
if ( prev_chan_ann )
cannounces + + ;
prev_chan_ann = false ;
break ;
case WIRE_CHANNEL_ANNOUNCEMENT :
if ( prev_chan_ann ) {
bad = " channel_announcement without amount " ;
goto badmsg ;
}
prev_chan_ann = true ;
break ;
case WIRE_GOSSIP_STORE_CHAN_DYING : {
struct chan_dying cd ;
if ( ! fromwire_gossip_store_chan_dying ( msg ,
& cd . scid ,
& cd . deadline ) ) {
bad = " Bad gossip_store_chan_dying " ;
goto badmsg ;
}
/* By convention, these offsets are *after* header */
cd . gossmap_offset = * total_len + sizeof ( hdr ) ;
tal_arr_expand ( dying , cd ) ;
break ;
}
case WIRE_CHANNEL_UPDATE :
cupdates + + ;
break ;
case WIRE_NODE_ANNOUNCEMENT :
nannounces + + ;
break ;
default :
bad = " Unknown message " ;
goto badmsg ;
}
2019-06-22 02:18:13 +02:00
if ( ! write_all ( new_fd , & hdr , sizeof ( hdr ) )
| | ! write_all ( new_fd , msg , msglen ) ) {
2024-01-31 07:08:20 +01:00
status_failed ( STATUS_FAIL_INTERNAL_ERROR ,
" gossip_store_compact: writing msg len %zu to new store: %s " ,
2019-06-22 02:18:13 +02:00
msglen , strerror ( errno ) ) ;
}
tal_free ( msg ) ;
2024-01-31 07:08:20 +01:00
* total_len + = sizeof ( hdr ) + msglen ;
2019-06-22 02:18:13 +02:00
}
2024-01-31 07:08:20 +01:00
assert ( * total_len = = lseek ( new_fd , 0 , SEEK_END ) ) ;
/* Unlikely, but a channel_announcement without an amount: we just truncate. */
if ( prev_chan_ann ) {
bad = " channel_announcement without amount " ;
goto badmsg ;
2019-06-22 02:18:13 +02:00
}
2024-01-31 07:08:20 +01:00
/* If we have any contents, and the file is less than 1 hour
* old , say " seems good " */
if ( st . st_mtime > time_now ( ) . ts . tv_sec - 3600 & & * total_len > 1 ) {
* populated = true ;
}
rename_new :
2019-06-22 02:18:13 +02:00
if ( rename ( GOSSIP_STORE_TEMP_FILENAME , GOSSIP_STORE_FILENAME ) ! = 0 ) {
2024-01-31 07:08:20 +01:00
status_failed ( STATUS_FAIL_INTERNAL_ERROR ,
" gossip_store_compact: rename failed: %s " ,
2019-06-22 02:18:13 +02:00
strerror ( errno ) ) ;
}
2021-05-22 07:00:22 +02:00
/* Create end marker now new file exists. */
2024-01-31 07:08:20 +01:00
if ( old_fd ! = - 1 ) {
append_msg ( old_fd , towire_gossip_store_ended ( tmpctx , * total_len ) ,
0 , & old_len ) ;
close ( old_fd ) ;
}
2019-06-22 02:18:13 +02:00
2024-01-31 07:08:20 +01:00
status_debug ( " Store compact time: % " PRIu64 " msec " ,
time_to_msec ( time_between ( time_now ( ) , start ) ) ) ;
status_debug ( " gossip_store: Read %zu/%zu/%zu/%zu cannounce/cupdate/nannounce/delete from store in % " PRIu64 " bytes, now % " PRIu64 " bytes (populated=%s) " ,
cannounces , cupdates , nannounces , deleted ,
old_len , * total_len ,
* populated ? " true " : " false " ) ;
return new_fd ;
badmsg :
/* We truncate */
status_broken ( " gossip_store: %s (offset % " PRIu64 " ). Moving to %s.corrupt and truncating " ,
bad , cur_off , GOSSIP_STORE_FILENAME ) ;
rename ( GOSSIP_STORE_FILENAME , GOSSIP_STORE_FILENAME " .corrupt " ) ;
if ( lseek ( new_fd , 0 , SEEK_SET ) ! = 0
| | ! write_all ( new_fd , & version , sizeof ( version ) ) ) {
2019-07-04 01:23:07 +02:00
status_failed ( STATUS_FAIL_INTERNAL_ERROR ,
2024-01-31 07:08:20 +01:00
" Overwriting new gossip_store file: %s " ,
2019-07-04 01:23:07 +02:00
strerror ( errno ) ) ;
2024-01-31 07:08:20 +01:00
}
* total_len = sizeof ( version ) ;
goto rename_new ;
}
2018-04-11 01:03:35 +02:00
2024-01-31 07:08:20 +01:00
struct gossip_store * gossip_store_new ( const tal_t * ctx ,
struct daemon * daemon ,
bool * populated ,
struct chan_dying * * dying )
{
struct gossip_store * gs = tal ( ctx , struct gossip_store ) ;
2018-03-18 15:54:34 +01:00
2024-01-31 07:08:20 +01:00
gs - > daemon = daemon ;
* dying = tal_arr ( ctx , struct chan_dying , 0 ) ;
gs - > fd = gossip_store_compact ( daemon , & gs - > len , populated , dying ) ;
tal_add_destructor ( gs , gossip_store_destroy ) ;
2018-03-13 12:08:03 +01:00
return gs ;
}
2018-05-30 22:18:45 +02:00
2024-01-31 07:08:20 +01:00
int gossip_store_get_fd ( const struct gossip_store * gs )
{
return gs - > fd ;
}
2024-01-31 06:44:14 +01:00
u64 gossip_store_add ( struct gossip_store * gs , const u8 * gossip_msg , u32 timestamp )
2019-04-10 09:31:29 +02:00
{
u64 off = gs - > len ;
2024-01-31 06:44:14 +01:00
if ( ! append_msg ( gs - > fd , gossip_msg , timestamp , & gs - > len ) ) {
2018-05-31 11:43:25 +02:00
status_broken ( " Failed writing to gossip store: %s " ,
strerror ( errno ) ) ;
2019-04-10 09:31:29 +02:00
return 0 ;
2018-05-31 11:43:25 +02:00
}
2024-01-31 06:44:14 +01:00
/* By gossmap convention, offset is *after* hdr */
return off + sizeof ( struct gossip_hdr ) ;
2018-03-25 18:23:10 +02:00
}
2024-01-31 06:44:14 +01:00
/* Offsets are all gossmap-style: *after* hdr! */
2024-01-31 06:44:13 +01:00
static const u8 * gossip_store_get_with_hdr ( const tal_t * ctx ,
struct gossip_store * gs ,
u64 offset ,
struct gossip_hdr * hdr )
2023-07-19 08:35:31 +02:00
{
2024-01-31 06:44:13 +01:00
u32 msglen , checksum ;
u8 * msg ;
2023-07-19 08:35:31 +02:00
2024-01-31 06:44:14 +01:00
if ( offset < = sizeof ( * hdr ) )
2024-01-31 06:44:13 +01:00
status_failed ( STATUS_FAIL_INTERNAL_ERROR ,
" gossip_store: can't access offset % " PRIu64 ,
offset ) ;
2024-01-31 06:44:14 +01:00
if ( pread ( gs - > fd , hdr , sizeof ( * hdr ) , offset - sizeof ( * hdr ) ) ! = sizeof ( * hdr ) ) {
2024-01-31 06:44:13 +01:00
status_failed ( STATUS_FAIL_INTERNAL_ERROR ,
" gossip_store: can't read hdr offset % " PRIu64
" /% " PRIu64 " : %s " ,
2024-01-31 06:44:14 +01:00
offset - sizeof ( * hdr ) , gs - > len , strerror ( errno ) ) ;
2023-07-19 08:35:31 +02:00
}
2024-01-31 06:44:13 +01:00
if ( be16_to_cpu ( hdr - > flags ) & GOSSIP_STORE_DELETED_BIT )
2023-07-19 08:35:31 +02:00
status_failed ( STATUS_FAIL_INTERNAL_ERROR ,
2024-01-31 06:44:13 +01:00
" gossip_store: get delete entry offset % " PRIu64
" /% " PRIu64 " " ,
2024-01-31 06:44:14 +01:00
offset - sizeof ( * hdr ) , gs - > len ) ;
2023-07-19 08:35:31 +02:00
2024-01-31 06:44:13 +01:00
msglen = be16_to_cpu ( hdr - > len ) ;
checksum = be32_to_cpu ( hdr - > crc ) ;
msg = tal_arr ( ctx , u8 , msglen ) ;
2024-01-31 06:44:14 +01:00
if ( pread ( gs - > fd , msg , msglen , offset ) ! = msglen )
2023-07-19 08:35:31 +02:00
status_failed ( STATUS_FAIL_INTERNAL_ERROR ,
2024-01-31 06:44:13 +01:00
" gossip_store: can't read len %u offset % " PRIu64
" /% " PRIu64 , msglen , offset , gs - > len ) ;
if ( checksum ! = crc32c ( be32_to_cpu ( hdr - > timestamp ) , msg , msglen ) )
status_failed ( STATUS_FAIL_INTERNAL_ERROR ,
" gossip_store: bad checksum offset % " PRIu64 " : %s " ,
2024-01-31 06:44:14 +01:00
offset - sizeof ( * hdr ) , tal_hex ( tmpctx , msg ) ) ;
2024-01-31 06:44:13 +01:00
return msg ;
}
2024-02-11 11:31:41 +01:00
/* Populates hdr */
static bool check_msg_type ( struct gossip_store * gs , u64 offset , int flag , int type ,
struct gossip_hdr * hdr )
2024-01-31 06:44:13 +01:00
{
2024-02-11 11:31:41 +01:00
const u8 * msg = gossip_store_get_with_hdr ( tmpctx , gs , offset , hdr ) ;
2024-01-31 06:44:13 +01:00
if ( fromwire_peektype ( msg ) = = type )
return true ;
2024-01-31 06:44:14 +01:00
status_broken ( " asked to flag-%u type %i @% " PRIu64 " but store contains "
2024-01-31 06:44:13 +01:00
" %i (gs->len=% " PRIu64 " ): %s " ,
2024-01-31 06:44:14 +01:00
flag , type , offset , fromwire_peektype ( msg ) ,
2024-01-31 06:44:13 +01:00
gs - > len , tal_hex ( tmpctx , msg ) ) ;
return false ;
2023-07-19 08:35:31 +02:00
}
2024-01-31 06:44:14 +01:00
/* Returns offset of following entry (i.e. after its header). */
u64 gossip_store_set_flag ( struct gossip_store * gs ,
u64 offset , u16 flag , int type )
2018-03-28 12:54:09 +02:00
{
2024-02-11 11:31:41 +01:00
struct gossip_hdr hdr ;
2019-06-14 05:41:39 +02:00
2024-02-11 11:31:41 +01:00
if ( ! check_msg_type ( gs , offset , flag , type , & hdr ) )
2024-01-31 06:44:14 +01:00
return offset ;
2019-06-03 20:09:25 +02:00
2024-02-10 01:14:52 +01:00
if ( be16_to_cpu ( hdr . flags ) & flag ) {
status_broken ( " gossip_store flag-%u @% " PRIu64 " for %u already set! " ,
flag , offset , type ) ;
}
2024-02-11 11:31:41 +01:00
hdr . flags | = cpu_to_be16 ( flag ) ;
if ( pwrite ( gs - > fd , & hdr , sizeof ( hdr ) , offset - sizeof ( hdr ) ) ! = sizeof ( hdr ) )
2024-02-10 01:14:52 +01:00
2019-06-03 20:09:25 +02:00
status_failed ( STATUS_FAIL_INTERNAL_ERROR ,
2024-02-11 11:31:41 +01:00
" Failed writing set flags @% " PRIu64 " : %s " ,
2024-01-31 06:44:14 +01:00
offset , strerror ( errno ) ) ;
2019-06-03 20:22:25 +02:00
2024-02-11 11:31:41 +01:00
return offset + be16_to_cpu ( hdr . len ) + sizeof ( struct gossip_hdr ) ;
}
u16 gossip_store_get_flags ( struct gossip_store * gs ,
u64 offset , int type )
{
struct gossip_hdr hdr ;
if ( ! check_msg_type ( gs , offset , - 1 , type , & hdr ) )
return 0 ;
return be16_to_cpu ( hdr . flags ) ;
}
void gossip_store_clear_flag ( struct gossip_store * gs ,
u64 offset , u16 flag , int type )
{
struct gossip_hdr hdr ;
if ( ! check_msg_type ( gs , offset , flag , type , & hdr ) )
return ;
assert ( be16_to_cpu ( hdr . flags ) & flag ) ;
hdr . flags & = ~ cpu_to_be16 ( flag ) ;
if ( pwrite ( gs - > fd , & hdr , sizeof ( hdr ) , offset - sizeof ( hdr ) ) ! = sizeof ( hdr ) )
status_failed ( STATUS_FAIL_INTERNAL_ERROR ,
" Failed writing clear flags @% " PRIu64 " : %s " ,
offset , strerror ( errno ) ) ;
2019-06-14 05:41:39 +02:00
}
2024-01-31 05:19:33 +01:00
void gossip_store_del ( struct gossip_store * gs ,
u64 offset ,
int type )
{
u32 next_index ;
assert ( offset > sizeof ( struct gossip_hdr ) ) ;
2024-01-31 06:44:14 +01:00
next_index = gossip_store_set_flag ( gs , offset ,
GOSSIP_STORE_DELETED_BIT ,
type ) ;
2024-01-31 05:19:33 +01:00
/* For a channel_announcement, we need to delete amount too */
if ( type = = WIRE_CHANNEL_ANNOUNCEMENT )
2024-01-31 06:44:14 +01:00
gossip_store_set_flag ( gs , next_index ,
GOSSIP_STORE_DELETED_BIT ,
WIRE_GOSSIP_STORE_CHANNEL_AMOUNT ) ;
2024-01-31 05:19:33 +01:00
}
u32 gossip_store_get_timestamp ( struct gossip_store * gs , u64 offset )
2019-04-10 09:31:29 +02:00
{
2019-06-03 20:17:25 +02:00
struct gossip_hdr hdr ;
2024-01-31 05:19:33 +01:00
assert ( offset > sizeof ( struct gossip_hdr ) ) ;
if ( pread ( gs - > fd , & hdr , sizeof ( hdr ) , offset - sizeof ( hdr ) ) ! = sizeof ( hdr ) ) {
status_broken ( " gossip_store overrun during get_timestamp @% " PRIu64
" gs->len: % " PRIu64 , offset , gs - > len ) ;
return 0 ;
}
return be32_to_cpu ( hdr . timestamp ) ;
}
void gossip_store_set_timestamp ( struct gossip_store * gs , u64 offset , u32 timestamp )
{
struct gossip_hdr hdr ;
const u8 * msg ;
2024-01-31 06:44:14 +01:00
msg = gossip_store_get_with_hdr ( tmpctx , gs , offset , & hdr ) ;
2024-01-31 05:19:33 +01:00
/* Change timestamp and crc */
hdr . timestamp = cpu_to_be32 ( timestamp ) ;
hdr . crc = cpu_to_be32 ( crc32c ( timestamp , msg , tal_bytelen ( msg ) ) ) ;
if ( pwrite ( gs - > fd , & hdr , sizeof ( hdr ) , offset - sizeof ( hdr ) ) ! = sizeof ( hdr ) )
status_failed ( STATUS_FAIL_INTERNAL_ERROR ,
" Failed writing header to re-timestamp @% " PRIu64 " : %s " ,
offset , strerror ( errno ) ) ;
}