11use bdk_chain:: {
22 indexed_tx_graph, keychain_txout, local_chain, tx_graph, ConfirmationBlockTime , Merge ,
33} ;
4+ use chain:: BlockId ;
45use miniscript:: { Descriptor , DescriptorPublicKey } ;
56use serde:: { Deserialize , Serialize } ;
67
@@ -110,6 +111,9 @@ pub struct ChangeSet {
110111 pub change_descriptor : Option < Descriptor < DescriptorPublicKey > > ,
111112 /// Stores the network type of the transaction data.
112113 pub network : Option < bitcoin:: Network > ,
114+ /// Stores the [`Wallet`]'s birthday, defined as the first
115+ /// [`Block`] with relevant transactions to this [`Wallet`].
116+ pub birthday : Option < BlockId > ,
113117 /// Changes to the [`LocalChain`](local_chain::LocalChain).
114118 pub local_chain : local_chain:: ChangeSet ,
115119 /// Changes to [`TxGraph`](tx_graph::TxGraph).
@@ -145,6 +149,14 @@ impl Merge for ChangeSet {
145149 ) ;
146150 self . network = other. network ;
147151 }
152+ // TODO(@luisschwab): should merging [`ChangeSet`]s with distinct birthdays be possible?
153+ if other. birthday . is_some ( ) {
154+ debug_assert ! (
155+ self . birthday. is_none( ) || self . birthday == other. birthday,
156+ "birthday must never change"
157+ ) ;
158+ self . birthday = other. birthday ;
159+ }
148160
149161 // merge locked outpoints
150162 self . locked_outpoints . merge ( other. locked_outpoints ) ;
@@ -158,6 +170,7 @@ impl Merge for ChangeSet {
158170 self . descriptor . is_none ( )
159171 && self . change_descriptor . is_none ( )
160172 && self . network . is_none ( )
173+ && self . birthday . is_none ( )
161174 && self . local_chain . is_empty ( )
162175 && self . tx_graph . is_empty ( )
163176 && self . indexer . is_empty ( )
@@ -174,7 +187,7 @@ impl ChangeSet {
174187 /// Name of table to store wallet locked outpoints.
175188 pub const WALLET_OUTPOINT_LOCK_TABLE_NAME : & ' static str = "bdk_wallet_locked_outpoints" ;
176189
177- /// Get v0 sqlite [ChangeSet] schema
190+ /// Get v0 sqlite [` ChangeSet` ] schema.
178191 pub fn schema_v0 ( ) -> alloc:: string:: String {
179192 format ! (
180193 "CREATE TABLE {} ( \
@@ -199,12 +212,19 @@ impl ChangeSet {
199212 )
200213 }
201214
215+ pub fn schema_v2 ( ) -> alloc:: string:: String {
216+ format ! (
217+ "ALTER TABLE {} ADD COLUMN birthday TEXT" ,
218+ Self :: WALLET_TABLE_NAME ,
219+ )
220+ }
221+
202222 /// Initialize sqlite tables for wallet tables.
203223 pub fn init_sqlite_tables ( db_tx : & chain:: rusqlite:: Transaction ) -> chain:: rusqlite:: Result < ( ) > {
204224 crate :: rusqlite_impl:: migrate_schema (
205225 db_tx,
206226 Self :: WALLET_SCHEMA_NAME ,
207- & [ & Self :: schema_v0 ( ) , & Self :: schema_v1 ( ) ] ,
227+ & [ & Self :: schema_v0 ( ) , & Self :: schema_v1 ( ) , & Self :: schema_v2 ( ) ] ,
208228 ) ?;
209229
210230 bdk_chain:: local_chain:: ChangeSet :: init_sqlite_tables ( db_tx) ?;
@@ -223,7 +243,7 @@ impl ChangeSet {
223243 let mut changeset = Self :: default ( ) ;
224244
225245 let mut wallet_statement = db_tx. prepare ( & format ! (
226- "SELECT descriptor, change_descriptor, network FROM {}" ,
246+ "SELECT descriptor, change_descriptor, network, birthday FROM {}" ,
227247 Self :: WALLET_TABLE_NAME ,
228248 ) ) ?;
229249 let row = wallet_statement
@@ -234,13 +254,16 @@ impl ChangeSet {
234254 "change_descriptor" ,
235255 ) ?,
236256 row. get :: < _ , Option < Impl < bitcoin:: Network > > > ( "network" ) ?,
257+ // TODO(@luisschwab): merge bdk#2097, publish new bdk_chain version and bump it here for [`BlockId`] impls.
258+ row. get :: < _ , Option < Impl < BlockId > > > ( "birthday" ) ?,
237259 ) )
238260 } )
239261 . optional ( ) ?;
240- if let Some ( ( desc, change_desc, network) ) = row {
262+ if let Some ( ( desc, change_desc, network, birthday ) ) = row {
241263 changeset. descriptor = desc. map ( Impl :: into_inner) ;
242264 changeset. change_descriptor = change_desc. map ( Impl :: into_inner) ;
243265 changeset. network = network. map ( Impl :: into_inner) ;
266+ changeset. birthday = birthday. map ( Impl :: into_inner) ;
244267 }
245268
246269 // Select locked outpoints.
@@ -309,6 +332,17 @@ impl ChangeSet {
309332 } ) ?;
310333 }
311334
335+ let mut birthday_statement = db_tx. prepare_cached ( & format ! (
336+ "INSERT INTO {}(id, birthday) VALUES(:id, :birthday) ON CONFLICT(id) DO UPDATE SET birthday=:birthday" ,
337+ Self :: WALLET_TABLE_NAME ,
338+ ) ) ?;
339+ if let Some ( birthday) = self . birthday {
340+ birthday_statement. execute ( named_params ! {
341+ ":id" : 0 ,
342+ ":birthday" : Impl ( birthday) ,
343+ } ) ?;
344+ }
345+
312346 // Insert or delete locked outpoints.
313347 let mut insert_stmt = db_tx. prepare_cached ( & format ! (
314348 "INSERT OR IGNORE INTO {}(txid, vout) VALUES(:txid, :vout)" ,
0 commit comments