From 9b04a551474b2cd5ed793e42832454635d964495 Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Mon, 24 Oct 2022 10:01:33 +0200 Subject: [PATCH] db: store derivation index also for addresses from the change desc This doubles the storage required but there is no way around it if we want the poller to detect those coins without grinding. --- src/database/sqlite/mod.rs | 39 ++++++++++++++++++++++++++++------- src/database/sqlite/schema.rs | 20 ++++++++++++------ src/database/sqlite/utils.rs | 12 +++++++---- 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/src/database/sqlite/mod.rs b/src/database/sqlite/mod.rs index 3b672df4..50c92f4d 100644 --- a/src/database/sqlite/mod.rs +++ b/src/database/sqlite/mod.rs @@ -238,17 +238,22 @@ impl SqliteConn { // Update the address to derivation index mapping. // TODO: have this as a helper in descriptors.rs let next_la_index = next_index + LOOK_AHEAD_LIMIT - 1; - let next_la_address = db_wallet + let next_receive_address = db_wallet .main_descriptor .receive_descriptor() .derive(next_la_index.into(), secp) .address(network); - db_tx - .execute( - "INSERT INTO addresses (address, derivation_index) VALUES (?1, ?2)", - rusqlite::params![next_la_address.to_string(), next_la_index], - ) - .map(|_| ()) + let next_change_address = db_wallet + .main_descriptor + .change_descriptor() + .derive(next_la_index.into(), secp) + .address(network); + db_tx.execute( + "INSERT INTO addresses (receive_address, change_address, derivation_index) VALUES (?1, ?2, ?3)", + rusqlite::params![next_receive_address.to_string(), next_change_address.to_string(), next_la_index], + )?; + + Ok(()) }) .expect("Database must be available") } @@ -363,7 +368,7 @@ impl SqliteConn { pub fn db_address(&mut self, address: &bitcoin::Address) -> Option { db_query( &mut self.conn, - "SELECT * FROM addresses WHERE address = ?1", + "SELECT * FROM addresses WHERE receive_address = ?1 OR change_address = ?1", rusqlite::params![address.to_string()], |row| row.try_into(), ) @@ -721,6 +726,15 @@ mod tests { let db_addr = conn.db_address(&addr).unwrap(); assert_eq!(db_addr.derivation_index, 0.into()); + // And also for the change address + let addr = options + .main_descriptor + .change_descriptor() + .derive(0.into(), &secp) + .address(options.bitcoind_network); + let db_addr = conn.db_address(&addr).unwrap(); + assert_eq!(db_addr.derivation_index, 0.into()); + // There is the index for the 199th index (look-ahead limit) let addr = options .main_descriptor @@ -742,6 +756,15 @@ mod tests { conn.increment_derivation_index(&secp); let db_addr = conn.db_address(&addr).unwrap(); assert_eq!(db_addr.derivation_index, 200.into()); + + // Same for the change descriptor. + let addr = options + .main_descriptor + .change_descriptor() + .derive(200.into(), &secp) + .address(options.bitcoind_network); + let db_addr = conn.db_address(&addr).unwrap(); + assert_eq!(db_addr.derivation_index, 200.into()); } fs::remove_dir_all(&tmp_dir).unwrap(); diff --git a/src/database/sqlite/schema.rs b/src/database/sqlite/schema.rs index 99975e48..87c55bb5 100644 --- a/src/database/sqlite/schema.rs +++ b/src/database/sqlite/schema.rs @@ -57,7 +57,8 @@ CREATE TABLE coins ( * we can get the derivation index from the parent descriptor from bitcoind. */ CREATE TABLE addresses ( - address TEXT NOT NULL UNIQUE, + receive_address TEXT NOT NULL UNIQUE, + change_address TEXT NOT NULL UNIQUE, derivation_index INTEGER NOT NULL UNIQUE ); @@ -195,7 +196,8 @@ impl TryFrom<&rusqlite::Row<'_>> for DbCoin { #[derive(Debug, Clone, PartialEq, Eq)] pub struct DbAddress { - pub address: bitcoin::Address, + pub receive_address: bitcoin::Address, + pub change_address: bitcoin::Address, pub derivation_index: bip32::ChildNumber, } @@ -203,15 +205,21 @@ impl TryFrom<&rusqlite::Row<'_>> for DbAddress { type Error = rusqlite::Error; fn try_from(row: &rusqlite::Row) -> Result { - let address: String = row.get(0)?; - let address = bitcoin::Address::from_str(&address).expect("We only store valid addresses"); + let receive_address: String = row.get(0)?; + let receive_address = + bitcoin::Address::from_str(&receive_address).expect("We only store valid addresses"); - let derivation_index: u32 = row.get(1)?; + let change_address: String = row.get(1)?; + let change_address = + bitcoin::Address::from_str(&change_address).expect("We only store valid addresses"); + + let derivation_index: u32 = row.get(2)?; let derivation_index = bip32::ChildNumber::from(derivation_index); assert!(derivation_index.is_normal()); Ok(DbAddress { - address, + receive_address, + change_address, derivation_index, }) } diff --git a/src/database/sqlite/utils.rs b/src/database/sqlite/utils.rs index 43a3102c..82d09d84 100644 --- a/src/database/sqlite/utils.rs +++ b/src/database/sqlite/utils.rs @@ -95,15 +95,19 @@ pub fn create_fresh_db( // necessarily 0. let mut query = String::with_capacity(100 * LOOK_AHEAD_LIMIT as usize); for index in 0..LOOK_AHEAD_LIMIT { - // TODO: have this as a helper in descriptors.rs - let address = options + let receive_address = options .main_descriptor .receive_descriptor() .derive(index.into(), secp) .address(options.bitcoind_network); + let change_address = options + .main_descriptor + .change_descriptor() + .derive(index.into(), secp) + .address(options.bitcoind_network); query += &format!( - "INSERT INTO addresses (address, derivation_index) VALUES (\"{}\", {});\n", - address, index + "INSERT INTO addresses (receive_address, change_address, derivation_index) VALUES (\"{}\", \"{}\", {});\n", + receive_address, change_address, index ); }