@@ -45,3 +45,139 @@ impl<K: Ord> Merge for ChangeSet<K> {
4545 self . network . is_none ( ) && self . descriptors . is_empty ( )
4646 }
4747}
48+
49+ #[ cfg( feature = "rusqlite" ) ]
50+ use chain:: {
51+ rusqlite:: { self , OptionalExtension } ,
52+ Impl ,
53+ } ;
54+
55+ #[ cfg( feature = "rusqlite" ) ]
56+ use crate :: CanBePersisted ;
57+
58+ #[ cfg( feature = "rusqlite" ) ]
59+ impl < K > ChangeSet < K >
60+ where
61+ K : Ord + Clone + CanBePersisted ,
62+ {
63+ /// Schema name for `KeyRing`
64+ pub const SCHEMA_NAME : & str = "bdk_keyring" ;
65+ /// Name of table storing the network
66+ pub const NETWORK_TABLE_NAME : & str = "bdk_network" ;
67+ /// Name of table storing the descriptors
68+ pub const DESCRIPTORS_TABLE_NAME : & str = "bdk_descriptor" ;
69+
70+ /// Returns the v0 sqlite schema for [`ChangeSet`]
71+ pub fn schema_v0 ( ) -> alloc:: string:: String {
72+ format ! (
73+ "CREATE TABLE {} ( \
74+ id INTEGER PRIMARY KEY NOT NULL, \
75+ network TEXT NOT NULL \
76+ ) STRICT ; \
77+ CREATE TABLE {} ( \
78+ keychain TEXT PRIMARY KEY NOT NULL, \
79+ descriptor TEXT UNIQUE NOT NULL, \
80+ is_default INTEGER NOT NULL CHECK ( is_default IN (0,1) ) \
81+ ) STRICT ;",
82+ Self :: NETWORK_TABLE_NAME ,
83+ Self :: DESCRIPTORS_TABLE_NAME
84+ )
85+ }
86+
87+ /// Initialize sqlite tables
88+ pub fn init_sqlite_tables ( db_tx : & rusqlite:: Transaction ) -> rusqlite:: Result < ( ) > {
89+ bdk_chain:: rusqlite_impl:: migrate_schema ( db_tx, Self :: SCHEMA_NAME , & [ & Self :: schema_v0 ( ) ] ) ?;
90+ Ok ( ( ) )
91+ }
92+
93+ /// Construct the `KeyRing` from persistence
94+ ///
95+ /// Remember to call [`Self::init_sqlite_tables`] beforehand.
96+ pub fn from_sqlite ( db_tx : & rusqlite:: Transaction ) -> rusqlite:: Result < Self > {
97+ let mut changeset = Self :: default ( ) ;
98+ let mut network_stmt = db_tx. prepare ( & format ! (
99+ "SELECT network FROM {} WHERE id = 0" ,
100+ Self :: NETWORK_TABLE_NAME ,
101+ ) ) ?;
102+ let row = network_stmt
103+ . query_row ( [ ] , |row| row. get :: < _ , Impl < bitcoin:: Network > > ( "network" ) )
104+ . optional ( ) ?;
105+
106+ if let Some ( Impl ( network) ) = row {
107+ changeset. network = Some ( network) ;
108+ }
109+
110+ let mut descriptor_stmt = db_tx. prepare ( & format ! (
111+ "SELECT keychain, descriptor, is_default FROM {}" ,
112+ Self :: DESCRIPTORS_TABLE_NAME
113+ ) ) ?;
114+
115+ let rows = descriptor_stmt. query_map ( [ ] , |row| {
116+ Ok ( (
117+ row. get :: < _ , K :: Persistable > ( "keychain" ) ?,
118+ row. get :: < _ , Impl < Descriptor < DescriptorPublicKey > > > ( "descriptor" ) ?,
119+ row. get :: < _ , u8 > ( "is_default" ) ?,
120+ ) )
121+ } ) ?;
122+
123+ for row in rows {
124+ let ( keychain_persisted, Impl ( descriptor) , is_default) = row?;
125+ let keychain = K :: from_persistable ( keychain_persisted) ;
126+ changeset. descriptors . insert ( keychain. clone ( ) , descriptor) ;
127+
128+ if is_default == 1 {
129+ changeset. default_keychain = Some ( keychain) ;
130+ }
131+ }
132+
133+ Ok ( changeset)
134+ }
135+
136+ /// Persist the `KeyRing`
137+ ///
138+ /// Remember to call [`Self::init_sqlite_tables`] beforehand.
139+ pub fn persist_to_sqlite ( & self , db_tx : & rusqlite:: Transaction ) -> rusqlite:: Result < ( ) > {
140+ use rusqlite:: named_params;
141+ let mut network_stmt = db_tx. prepare_cached ( & format ! (
142+ "INSERT OR IGNORE INTO {}(id, network) VALUES(:id, :network)" ,
143+ Self :: NETWORK_TABLE_NAME
144+ ) ) ?;
145+
146+ if let Some ( network) = self . network {
147+ network_stmt. execute ( named_params ! {
148+ ":id" : 0 ,
149+ ":network" : Impl ( network)
150+ } ) ?;
151+ }
152+
153+ let mut descriptor_stmt = db_tx. prepare_cached ( & format ! (
154+ "INSERT OR IGNORE INTO {}(keychain, descriptor, is_default) VALUES(:keychain, :desc, :is_default)" , Self :: DESCRIPTORS_TABLE_NAME
155+ ) ) ?;
156+
157+ for ( keychain, desc) in & self . descriptors {
158+ descriptor_stmt. execute ( named_params ! {
159+ ":keychain" : keychain. clone( ) . to_persistable( ) ,
160+ ":desc" : Impl ( desc. clone( ) ) ,
161+ ":is_default" : 0 ,
162+ } ) ?;
163+ }
164+
165+ let mut remove_old_default_stmt = db_tx. prepare_cached ( & format ! (
166+ "UPDATE {} SET is_default = 0 WHERE is_default = 1" ,
167+ Self :: DESCRIPTORS_TABLE_NAME ,
168+ ) ) ?;
169+
170+ let mut add_default_stmt = db_tx. prepare_cached ( & format ! (
171+ "UPDATE {} SET is_default = 1 WHERE keychain = :keychain" ,
172+ Self :: DESCRIPTORS_TABLE_NAME ,
173+ ) ) ?;
174+
175+ if let Some ( keychain) = & self . default_keychain {
176+ remove_old_default_stmt. execute ( ( ) ) ?;
177+ add_default_stmt
178+ . execute ( named_params ! { ":keychain" : keychain. clone( ) . to_persistable( ) , } ) ?;
179+ }
180+
181+ Ok ( ( ) )
182+ }
183+ }
0 commit comments