1212#include <common/hsm_encryption.h>
1313#include <common/json_param.h>
1414#include <common/json_stream.h>
15+ #include <common/memleak.h>
1516#include <common/scb_wiregen.h>
1617#include <errno.h>
1718#include <fcntl.h>
2728/* VERSION is the current version of the data encrypted in the file */
2829#define VERSION ((u64)1)
2930
31+ struct peer_backup {
32+ struct node_id peer ;
33+ /* Empty if it's a placeholder */
34+ const u8 * data ;
35+ };
36+
37+ static const struct node_id * peer_backup_keyof (const struct peer_backup * pb )
38+ {
39+ return & pb -> peer ;
40+ }
41+
42+ static bool peer_backup_eq_node_id (const struct peer_backup * pb ,
43+ const struct node_id * id )
44+ {
45+ return node_id_eq (& pb -> peer , id );
46+ }
47+
48+ HTABLE_DEFINE_NODUPS_TYPE (struct peer_backup ,
49+ peer_backup_keyof ,
50+ node_id_hash ,
51+ peer_backup_eq_node_id ,
52+ backup_map );
53+
3054struct chanbackup {
3155 bool peer_backup ;
3256 /* Global secret object to keep the derived encryption key for the SCB */
3357 struct secret secret ;
58+
59+ /* Cache of backups for each peer we know about */
60+ struct backup_map * backups ;
3461};
3562
3663static struct chanbackup * chanbackup (struct plugin * plugin )
3764{
3865 return plugin_get_data (plugin , struct chanbackup );
3966}
4067
68+ /* Must not already exist in map! */
69+ static struct peer_backup * add_to_backup_map (struct chanbackup * cb ,
70+ const struct node_id * peer ,
71+ const u8 * data TAKES )
72+ {
73+ struct peer_backup * pb = tal (cb -> backups , struct peer_backup );
74+ pb -> peer = * peer ;
75+ pb -> data = tal_dup_talarr (pb , u8 , data );
76+ backup_map_add (cb -> backups , pb );
77+ return pb ;
78+ }
79+
4180/* Helper to fetch out SCB from the RPC call */
4281static bool json_to_scb_chan (const char * buffer ,
4382 const jsmntok_t * tok ,
@@ -585,19 +624,25 @@ static struct command_result *after_staticbackup(struct command *cmd,
585624 return send_outreq (req );
586625}
587626
588- static struct command_result * datastore_create_done (struct command * cmd ,
589- const char * method ,
590- const char * buf ,
591- const jsmntok_t * result ,
592- void * unused )
627+ /* Write to the datastore */
628+ static struct command_result * commit_peer_backup (struct command * cmd ,
629+ const struct peer_backup * pb )
593630{
594- return notification_or_hook_done (cmd );
631+ return jsonrpc_set_datastore_binary (cmd ,
632+ tal_fmt (cmd ,
633+ "chanbackup/peers/%s" ,
634+ fmt_node_id (tmpctx ,
635+ & pb -> peer )),
636+ pb -> data ,
637+ "create-or-replace" ,
638+ NULL , NULL , NULL );
595639}
596640
597641static struct command_result * json_state_changed (struct command * cmd ,
598642 const char * buf ,
599643 const jsmntok_t * params )
600644{
645+ struct chanbackup * cb = chanbackup (cmd -> plugin );
601646 const jsmntok_t * notiftok = json_get_member (buf ,
602647 params ,
603648 "channel_state_changed" ),
@@ -628,18 +673,13 @@ static struct command_result *json_state_changed(struct command *cmd,
628673 json_tok_full_len (notiftok ),
629674 json_tok_full (buf , notiftok ));
630675
631- /* Might already exist, that's OK, just don't overwrite! */
632- return jsonrpc_set_datastore_binary (cmd ,
633- tal_fmt (cmd ,
634- "chanbackup/peers/%s" ,
635- fmt_node_id (tmpctx ,
636- & node_id )),
637- /* Empty record */
638- tal_arr (tmpctx , u8 , 0 ),
639- "must-create" ,
640- datastore_create_done ,
641- datastore_create_done ,
642- NULL );
676+ /* Create a placeholder if necessary */
677+ if (!backup_map_get (cb -> backups , & node_id )) {
678+ struct peer_backup * pb
679+ = add_to_backup_map (cb , & node_id ,
680+ take (tal_arr (NULL , u8 , 0 )));
681+ return commit_peer_backup (cmd , pb );
682+ }
643683 }
644684
645685 return notification_or_hook_done (cmd );
@@ -754,6 +794,8 @@ static struct command_result *handle_your_peer_storage(struct command *cmd,
754794 }
755795
756796 if (fromwire_peer_storage (cmd , payload , & payload_deserialise )) {
797+ struct peer_backup * pb ;
798+
757799 /* BOLT #1:
758800 * The receiver of `peer_storage`:
759801 * - If it offered `option_provide_storage`:
@@ -772,16 +814,13 @@ static struct command_result *handle_your_peer_storage(struct command *cmd,
772814 * update per minute.
773815 * - MUST replace the old `blob` with the latest received.
774816 */
775- return jsonrpc_set_datastore_binary (cmd ,
776- tal_fmt (cmd ,
777- "chanbackup/peers/%s" ,
778- fmt_node_id (tmpctx ,
779- & node_id )),
780- payload_deserialise ,
781- "must-replace" ,
782- datastore_success ,
783- datastore_failed ,
784- "Saving chanbackup/peers/" );
817+ pb = backup_map_get (cb -> backups , & node_id );
818+ if (!pb )
819+ return command_hook_success (cmd );
820+
821+ tal_free (pb -> data );
822+ pb -> data = tal_steal (pb , payload_deserialise );
823+ return commit_peer_backup (cmd , pb );
785824 } else if (fromwire_peer_storage_retrieval (cmd , payload , & payload_deserialise )) {
786825 plugin_log (cmd -> plugin , LOG_DBG ,
787826 "Received peer_storage from peer." );
@@ -951,6 +990,64 @@ static struct command_result *on_commitment_revocation(struct command *cmd,
951990 return send_outreq (req );
952991}
953992
993+ static void setup_backup_map (struct command * init_cmd ,
994+ struct chanbackup * cb )
995+ {
996+ struct json_out * params = json_out_new (init_cmd );
997+ const jsmntok_t * result ;
998+ const char * buf ;
999+ const jsmntok_t * datastore , * t ;
1000+ size_t i , total = 0 ;
1001+
1002+ cb -> backups = tal (cb , struct backup_map );
1003+ backup_map_init (cb -> backups );
1004+
1005+ json_out_start (params , NULL , '{' );
1006+ json_out_start (params , "key" , '[' );
1007+ json_out_addstr (params , NULL , "chanbackup" );
1008+ json_out_addstr (params , NULL , "peers" );
1009+ json_out_end (params , ']' );
1010+ json_out_end (params , '}' );
1011+
1012+ result = jsonrpc_request_sync (tmpctx , init_cmd ,
1013+ "listdatastore" ,
1014+ take (params ), & buf );
1015+
1016+ datastore = json_get_member (buf , result , "datastore" );
1017+ json_for_each_arr (i , t , datastore ) {
1018+ const jsmntok_t * keytok , * hextok ;
1019+ struct node_id peer ;
1020+ u8 * data ;
1021+
1022+ /* Key is an array, first two elements are chanbackup, peers */
1023+ keytok = json_get_member (buf , t , "key" ) + 3 ;
1024+ hextok = json_get_member (buf , t , "hex" );
1025+ /* In case someone creates a subdir? */
1026+ if (!hextok )
1027+ continue ;
1028+ if (!json_to_node_id (buf , keytok , & peer ))
1029+ plugin_err (init_cmd -> plugin ,
1030+ "Could not parse datastore id '%.*s'" ,
1031+ json_tok_full_len (keytok ),
1032+ json_tok_full (buf , keytok ));
1033+ data = json_tok_bin_from_hex (NULL , buf , hextok );
1034+ /* Only count non-empty ones. */
1035+ if (tal_bytelen (data ) != 0 )
1036+ total ++ ;
1037+ add_to_backup_map (cb , & peer , take (data ));
1038+ }
1039+ if (total )
1040+ plugin_log (init_cmd -> plugin , LOG_INFORM ,
1041+ "Loaded %zu stored backups for peers" , total );
1042+ }
1043+
1044+ static void chanbackup_mark_mem (struct plugin * plugin ,
1045+ struct htable * memtable )
1046+ {
1047+ const struct chanbackup * cb = chanbackup (plugin );
1048+ memleak_scan_htable (memtable , & cb -> backups -> raw );
1049+ }
1050+
9541051static const char * init (struct command * init_cmd ,
9551052 const char * buf UNUSED ,
9561053 const jsmntok_t * config UNUSED )
@@ -979,6 +1076,7 @@ static const char *init(struct command *init_cmd,
9791076 tal_bytelen (info_hex )))),
9801077 "{secret:%}" , JSON_SCAN (json_to_secret , & cb -> secret ));
9811078
1079+ setup_backup_map (init_cmd , cb );
9821080 plugin_set_data (init_cmd -> plugin , cb );
9831081 plugin_log (init_cmd -> plugin , LOG_DBG , "Chanbackup Initialised!" );
9841082
@@ -987,6 +1085,8 @@ static const char *init(struct command *init_cmd,
9871085
9881086 maybe_create_new_scb (init_cmd -> plugin , scb_chan );
9891087
1088+ plugin_set_memleak_handler (init_cmd -> plugin ,
1089+ chanbackup_mark_mem );
9901090 return NULL ;
9911091}
9921092
0 commit comments