Skip to content

Commit 58a0e57

Browse files
committed
db: record whether a coin was received on a change address
So we know what descriptor to use when spending it.
1 parent 9b04a55 commit 58a0e57

File tree

6 files changed

+36
-11
lines changed

6 files changed

+36
-11
lines changed

src/bitcoin/poller/looper.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ fn update_coins(
3535
// Start by fetching newly received coins.
3636
let mut received = Vec::new();
3737
for utxo in bit.received_coins(previous_tip, descs) {
38-
if let Some(derivation_index) = db_conn.derivation_index_by_address(&utxo.address) {
38+
if let Some((derivation_index, is_change)) =
39+
db_conn.derivation_index_by_address(&utxo.address)
40+
{
3941
if !curr_coins.contains_key(&utxo.outpoint) {
4042
let UTxO {
4143
outpoint, amount, ..
@@ -44,6 +46,7 @@ fn update_coins(
4446
outpoint,
4547
amount,
4648
derivation_index,
49+
is_change,
4750
block_height: None,
4851
block_time: None,
4952
spend_txid: None,

src/commands/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,7 @@ mod tests {
627627
block_time: None,
628628
amount: bitcoin::Amount::from_sat(100_000),
629629
derivation_index: bip32::ChildNumber::from(13),
630+
is_change: false,
630631
spend_txid: None,
631632
spend_block: None,
632633
}]);
@@ -721,6 +722,7 @@ mod tests {
721722
block_time: None,
722723
amount: bitcoin::Amount::from_sat(100_000),
723724
derivation_index: bip32::ChildNumber::from(13),
725+
is_change: false,
724726
spend_txid: None,
725727
spend_block: None,
726728
},
@@ -730,6 +732,7 @@ mod tests {
730732
block_time: None,
731733
amount: bitcoin::Amount::from_sat(115_680),
732734
derivation_index: bip32::ChildNumber::from(34),
735+
is_change: false,
733736
spend_txid: None,
734737
spend_block: None,
735738
},

src/database/mod.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,11 @@ pub trait DatabaseConnection {
4949

5050
fn increment_derivation_index(&mut self, secp: &secp256k1::Secp256k1<secp256k1::VerifyOnly>);
5151

52+
/// Get the derivation index for this address, as well as whether this address is change.
5253
fn derivation_index_by_address(
5354
&mut self,
5455
address: &bitcoin::Address,
55-
) -> Option<bip32::ChildNumber>;
56+
) -> Option<(bip32::ChildNumber, bool)>;
5657

5758
/// Get all our coins, past or present, spent or not.
5859
fn coins(&mut self) -> HashMap<bitcoin::OutPoint, Coin>;
@@ -154,9 +155,9 @@ impl DatabaseConnection for SqliteConn {
154155
fn derivation_index_by_address(
155156
&mut self,
156157
address: &bitcoin::Address,
157-
) -> Option<bip32::ChildNumber> {
158+
) -> Option<(bip32::ChildNumber, bool)> {
158159
self.db_address(address)
159-
.map(|db_addr| db_addr.derivation_index)
160+
.map(|db_addr| (db_addr.derivation_index, address == &db_addr.change_address))
160161
}
161162

162163
fn coins_by_outpoints(
@@ -215,6 +216,7 @@ pub struct Coin {
215216
pub block_time: Option<u32>,
216217
pub amount: bitcoin::Amount,
217218
pub derivation_index: bip32::ChildNumber,
219+
pub is_change: bool,
218220
pub spend_txid: Option<bitcoin::Txid>,
219221
pub spend_block: Option<SpendBlock>,
220222
}
@@ -227,6 +229,7 @@ impl std::convert::From<DbCoin> for Coin {
227229
block_time,
228230
amount,
229231
derivation_index,
232+
is_change,
230233
spend_txid,
231234
spend_block,
232235
..
@@ -237,6 +240,7 @@ impl std::convert::From<DbCoin> for Coin {
237240
block_time,
238241
amount,
239242
derivation_index,
243+
is_change,
240244
spend_txid,
241245
spend_block: spend_block.map(SpendBlock::from),
242246
}

src/database/sqlite/mod.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -288,14 +288,15 @@ impl SqliteConn {
288288
for coin in coins {
289289
let deriv_index: u32 = coin.derivation_index.into();
290290
db_tx.execute(
291-
"INSERT INTO coins (wallet_id, txid, vout, amount_sat, derivation_index) \
292-
VALUES (?1, ?2, ?3, ?4, ?5)",
291+
"INSERT INTO coins (wallet_id, txid, vout, amount_sat, derivation_index, is_change) \
292+
VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
293293
rusqlite::params![
294294
WALLET_ID,
295295
coin.outpoint.txid.to_vec(),
296296
coin.outpoint.vout,
297297
coin.amount.to_sat(),
298298
deriv_index,
299+
coin.is_change,
299300
],
300301
)?;
301302
}
@@ -607,6 +608,7 @@ mod tests {
607608
block_time: None,
608609
amount: bitcoin::Amount::from_sat(98765),
609610
derivation_index: bip32::ChildNumber::from_normal_idx(10).unwrap(),
611+
is_change: false,
610612
spend_txid: None,
611613
spend_block: None,
612614
};
@@ -618,7 +620,7 @@ mod tests {
618620
assert_eq!(coins.len(), 1);
619621
assert_eq!(coins[0].outpoint, coin_a.outpoint);
620622

621-
// Add a second one, we'll get both.
623+
// Add a second one (this one is change), we'll get both.
622624
let coin_b = Coin {
623625
outpoint: bitcoin::OutPoint::from_str(
624626
"61db3e276b095e5b05f1849dd6bfffb4e7e5ec1c4a4210099b98fce01571936f:12",
@@ -628,6 +630,7 @@ mod tests {
628630
block_time: None,
629631
amount: bitcoin::Amount::from_sat(1111),
630632
derivation_index: bip32::ChildNumber::from_normal_idx(103).unwrap(),
633+
is_change: true,
631634
spend_txid: None,
632635
spend_block: None,
633636
};
@@ -802,6 +805,7 @@ mod tests {
802805
block_time: None,
803806
amount: bitcoin::Amount::from_sat(98765),
804807
derivation_index: bip32::ChildNumber::from_normal_idx(10).unwrap(),
808+
is_change: false,
805809
spend_txid: None,
806810
spend_block: None,
807811
},
@@ -814,6 +818,7 @@ mod tests {
814818
block_time: Some(1_111_899),
815819
amount: bitcoin::Amount::from_sat(98765),
816820
derivation_index: bip32::ChildNumber::from_normal_idx(100).unwrap(),
821+
is_change: false,
817822
spend_txid: None,
818823
spend_block: None,
819824
},
@@ -826,6 +831,7 @@ mod tests {
826831
block_time: Some(1_121_899),
827832
amount: bitcoin::Amount::from_sat(98765),
828833
derivation_index: bip32::ChildNumber::from_normal_idx(1000).unwrap(),
834+
is_change: false,
829835
spend_txid: Some(
830836
bitcoin::Txid::from_str(
831837
"0c62a990d20d54429e70859292e82374ba6b1b951a3ab60f26bb65fee5724ff7",
@@ -846,6 +852,7 @@ mod tests {
846852
block_time: Some(1_131_899),
847853
amount: bitcoin::Amount::from_sat(98765),
848854
derivation_index: bip32::ChildNumber::from_normal_idx(10000).unwrap(),
855+
is_change: false,
849856
spend_txid: None,
850857
spend_block: None,
851858
},
@@ -858,6 +865,7 @@ mod tests {
858865
block_time: Some(1_134_899),
859866
amount: bitcoin::Amount::from_sat(98765),
860867
derivation_index: bip32::ChildNumber::from_normal_idx(100000).unwrap(),
868+
is_change: false,
861869
spend_txid: Some(
862870
bitcoin::Txid::from_str(
863871
"7477017f992cdc7ba08acafb77cb3b5bc0f42ac340d3e1e1da0785bdda20d5f6",

src/database/sqlite/schema.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ CREATE TABLE coins (
4444
vout INTEGER NOT NULL,
4545
amount_sat INTEGER NOT NULL,
4646
derivation_index INTEGER NOT NULL,
47+
is_change BOOLEAN NOT NULL CHECK (is_change IN (0,1)),
4748
spend_txid BLOB,
4849
spend_block_height INTEGER,
4950
spend_block_time INTEGER,
@@ -146,6 +147,7 @@ pub struct DbCoin {
146147
pub block_time: Option<u32>,
147148
pub amount: bitcoin::Amount,
148149
pub derivation_index: bip32::ChildNumber,
150+
pub is_change: bool,
149151
pub spend_txid: Option<bitcoin::Txid>,
150152
pub spend_block: Option<DbSpendBlock>,
151153
}
@@ -168,12 +170,13 @@ impl TryFrom<&rusqlite::Row<'_>> for DbCoin {
168170
let amount = bitcoin::Amount::from_sat(amount);
169171
let der_idx: u32 = row.get(7)?;
170172
let derivation_index = bip32::ChildNumber::from(der_idx);
173+
let is_change: bool = row.get(8)?;
171174

172-
let spend_txid: Option<Vec<u8>> = row.get(8)?;
175+
let spend_txid: Option<Vec<u8>> = row.get(9)?;
173176
let spend_txid =
174177
spend_txid.map(|txid| encode::deserialize(&txid).expect("We only store valid txids"));
175-
let spend_height: Option<i32> = row.get(9)?;
176-
let spend_time: Option<u32> = row.get(10)?;
178+
let spend_height: Option<i32> = row.get(10)?;
179+
let spend_time: Option<u32> = row.get(11)?;
177180
assert_eq!(spend_height.is_none(), spend_time.is_none());
178181
let spend_block = spend_height.map(|height| DbSpendBlock {
179182
height,
@@ -188,6 +191,7 @@ impl TryFrom<&rusqlite::Row<'_>> for DbCoin {
188191
block_time,
189192
amount,
190193
derivation_index,
194+
is_change,
191195
spend_txid,
192196
spend_block,
193197
})

src/testutils.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,10 @@ impl DatabaseConnection for DummyDbConn {
187187
}
188188
}
189189

190-
fn derivation_index_by_address(&mut self, _: &bitcoin::Address) -> Option<bip32::ChildNumber> {
190+
fn derivation_index_by_address(
191+
&mut self,
192+
_: &bitcoin::Address,
193+
) -> Option<(bip32::ChildNumber, bool)> {
191194
None
192195
}
193196

0 commit comments

Comments
 (0)