@@ -246,7 +246,7 @@ struct wallet *wallet_new(struct lightningd *ld, struct timers *timers)
246246}
247247
248248/* Get id for move_accounts; create if necessary */
249- u64 move_accounts_id (struct db * db , const char * name )
249+ static u64 move_accounts_id (struct db * db , const char * name , bool create )
250250{
251251 struct db_stmt * stmt ;
252252 u64 ret ;
@@ -268,6 +268,9 @@ u64 move_accounts_id(struct db *db, const char *name)
268268 }
269269 tal_free (stmt );
270270
271+ if (!create )
272+ return 0 ;
273+
271274 /* Does not exist, so create */
272275 stmt = db_prepare_v2 (db ,
273276 SQL ("INSERT INTO move_accounts (name) VALUES (?)" ));
@@ -3047,7 +3050,7 @@ void wallet_channel_close(struct wallet *w,
30473050 /* Update all accouting records to use channel_id string, instead of
30483051 * referring to dbid. This is robust if we delete in future, and saves
30493052 * a lookup in the load path. */
3050- new_move_id = move_accounts_id (w -> db , fmt_channel_id (tmpctx , & chan -> cid ));
3053+ new_move_id = move_accounts_id (w -> db , fmt_channel_id (tmpctx , & chan -> cid ), true );
30513054 stmt = db_prepare_v2 (w -> db , SQL ("UPDATE chain_moves "
30523055 "SET account_channel_id=?,"
30533056 " account_nonchannel_id=? "
@@ -6959,7 +6962,7 @@ void db_bind_mvt_account_id(struct db_stmt *stmt,
69596962 db_bind_null (stmt );
69606963 } else {
69616964 db_bind_null (stmt );
6962- db_bind_u64 (stmt , move_accounts_id (db , account -> alt_account ));
6965+ db_bind_u64 (stmt , move_accounts_id (db , account -> alt_account , true ));
69636966 }
69646967}
69656968
@@ -7085,73 +7088,124 @@ static u64 insert_chain_mvt(struct lightningd *ld,
70857088 return id ;
70867089}
70877090
7088- void wallet_save_chain_mvt (struct lightningd * ld ,
7089- const struct chain_coin_mvt * chain_mvt )
7091+ static struct mvt_tags db_col_mvt_tags (struct db_stmt * stmt ,
7092+ const char * colname )
7093+ {
7094+ struct mvt_tags tags ;
7095+ tags .bits = db_col_u64 (stmt , colname );
7096+ assert (mvt_tags_valid (tags ));
7097+ return tags ;
7098+ }
7099+
7100+ static bool find_duplicate_chain_move (struct db * db ,
7101+ const char * nonchannel_acctname ,
7102+ /* 0 if none */
7103+ u64 channel_dbid ,
7104+ const struct bitcoin_outpoint * outpoint ,
7105+ /* optional */
7106+ const struct bitcoin_txid * spending_txid ,
7107+ struct mvt_tags tags ,
7108+ u64 id_ceiling )
70907109{
70917110 struct db_stmt * stmt ;
7092- u64 id ;
7111+ u64 nonchannel_id ;
70937112
7094- /* On restart, we do chain replay. For this (and other
7095- * reorgs) we need to de-duplicate here. The other db tables
7096- * do this by deleting old entries on reorg, but we never
7097- * delete. */
7098- if (chain_mvt -> account .channel ) {
7099- stmt = db_prepare_v2 (ld -> wallet -> db ,
7100- SQL ("SELECT"
7101- " cm.spending_txid, cm.tag_bitmap, cm.id"
7102- " FROM chain_moves cm"
7103- " WHERE "
7104- " account_channel_id = ?"
7105- " AND utxo = ?" ));
7106- db_bind_u64 (stmt , chain_mvt -> account .channel -> dbid );
7107- } else {
7108- stmt = db_prepare_v2 (ld -> wallet -> db ,
7109- SQL ("SELECT"
7110- " cm.spending_txid, cm.tag_bitmap, cm.id"
7111- " FROM chain_moves cm"
7112- " JOIN move_accounts ma ON cm.account_nonchannel_id = ma.id"
7113- " WHERE"
7114- " ma.name = ?"
7115- " AND utxo = ?" ));
7116- db_bind_text (stmt , chain_mvt -> account .alt_account );
7117- }
7118- db_bind_outpoint (stmt , & chain_mvt -> outpoint );
7113+ nonchannel_id = move_accounts_id (db , nonchannel_acctname , false);
7114+
7115+ stmt = db_prepare_v2 (db ,
7116+ SQL ("SELECT"
7117+ " spending_txid, tag_bitmap, account_channel_id, account_nonchannel_id"
7118+ " FROM chain_moves"
7119+ " WHERE "
7120+ " utxo = ?"
7121+ " AND "
7122+ " id < ?" ));
7123+ db_bind_outpoint (stmt , outpoint );
7124+ db_bind_u64 (stmt , id_ceiling );
71197125 db_query_prepared (stmt );
71207126
71217127 /* Check that spending_txid and primary_tag match. We could
71227128 * probably just match on spending_txid, but this is robust. */
71237129 while (db_step (stmt )) {
7124- struct mvt_tags tags ;
7130+ struct mvt_tags these_tags ;
7131+ u64 this_nonchannel_id , this_channel_id ;
7132+ bool have_spending_txid ;
71257133
71267134 /* Access this now so it never complains we don't */
7127- tags .bits = db_col_u64 (stmt , "cm.tag_bitmap" );
7128- id = db_col_u64 (stmt , "cm.id" );
7135+ these_tags = db_col_mvt_tags (stmt , "tag_bitmap" );
7136+ have_spending_txid = !db_col_is_null (stmt , "spending_txid" );
7137+ this_nonchannel_id = db_col_is_null (stmt , "account_nonchannel_id" ) ? 0 : db_col_u64 (stmt , "account_nonchannel_id" );
7138+ this_channel_id = db_col_is_null (stmt , "account_channel_id" ) ? 0 : db_col_u64 (stmt , "account_channel_id" );
7139+
7140+ /* Either nonchannel id, or channel id must match */
7141+ if (nonchannel_id != this_nonchannel_id
7142+ && channel_dbid != this_channel_id ) {
7143+ continue ;
7144+ }
71297145
71307146 /* spending_txid must match */
7131- if (chain_mvt -> spending_txid ) {
7147+ if (spending_txid ) {
71327148 struct bitcoin_txid txid ;
7133- if (db_col_is_null ( stmt , "cm.spending_txid" ) )
7149+ if (! have_spending_txid )
71347150 continue ;
7135- db_col_txid (stmt , "cm. spending_txid" , & txid );
7151+ db_col_txid (stmt , "spending_txid" , & txid );
71367152 /* This would only happen for reorgs */
7137- if (!bitcoin_txid_eq (& txid , chain_mvt -> spending_txid ))
7153+ if (!bitcoin_txid_eq (& txid , spending_txid ))
71387154 continue ;
71397155 } else {
7140- if (! db_col_is_null ( stmt , "cm.spending_txid" ) )
7156+ if (have_spending_txid )
71417157 continue ;
71427158 }
71437159
71447160 /* Tags must match */
7145- if (primary_mvt_tag (tags ) != primary_mvt_tag (chain_mvt -> tags ))
7161+ if (primary_mvt_tag (these_tags ) != primary_mvt_tag (tags ))
71467162 continue ;
71477163
7148- /* It's a duplicate. Don't re-add. */
7164+ /* It's a duplicate. */
71497165 tal_free (stmt );
7150- goto out ;
7166+ return true ;
71517167 }
71527168 tal_free (stmt );
7169+ return false;
7170+ }
7171+
7172+ static bool is_duplicate (struct db * db , const struct chain_coin_mvt * chain_mvt )
7173+ {
7174+ const char * nonchannel_acctname ;
7175+ u64 channel_dbid ;
7176+
7177+ /* If we migrated in account_migration.c, it will use the
7178+ * nonchannel id! But we don't worry about adding a
7179+ * non-channel which conflicts with a channel. */
7180+ if (chain_mvt -> account .channel ) {
7181+ nonchannel_acctname = fmt_channel_id (tmpctx , & chain_mvt -> account .channel -> cid );
7182+ channel_dbid = chain_mvt -> account .channel -> dbid ;
7183+ } else {
7184+ nonchannel_acctname = chain_mvt -> account .alt_account ;
7185+ channel_dbid = 0 ;
7186+ }
71537187
7154- id = insert_chain_mvt (ld , ld -> wallet -> db , chain_mvt ),
7188+ return find_duplicate_chain_move (db , nonchannel_acctname ,
7189+ channel_dbid ,
7190+ & chain_mvt -> outpoint ,
7191+ chain_mvt -> spending_txid ,
7192+ chain_mvt -> tags ,
7193+ INT64_MAX );
7194+ }
7195+
7196+ void wallet_save_chain_mvt (struct lightningd * ld ,
7197+ const struct chain_coin_mvt * chain_mvt )
7198+ {
7199+ u64 id ;
7200+
7201+ /* On restart, we do chain replay. For this (and other
7202+ * reorgs) we need to de-duplicate here. The other db tables
7203+ * do this by deleting old entries on reorg, but we never
7204+ * delete. */
7205+ if (is_duplicate (ld -> wallet -> db , chain_mvt ))
7206+ goto out ;
7207+
7208+ id = insert_chain_mvt (ld , ld -> wallet -> db , chain_mvt );
71557209 notify_chain_mvt (ld , chain_mvt , id );
71567210out :
71577211 if (taken (chain_mvt ))
@@ -7190,15 +7244,6 @@ static void db_col_credit_or_debit(struct db_stmt *stmt,
71907244 }
71917245}
71927246
7193- static struct mvt_tags db_col_mvt_tags (struct db_stmt * stmt ,
7194- const char * colname )
7195- {
7196- struct mvt_tags tags ;
7197- tags .bits = db_col_u64 (stmt , colname );
7198- assert (mvt_tags_valid (tags ));
7199- return tags ;
7200- }
7201-
72027247struct chain_coin_mvt * wallet_chain_move_extract (const tal_t * ctx ,
72037248 struct db_stmt * stmt ,
72047249 struct lightningd * ld ,
0 commit comments