lib: couple block height and time

This commit is contained in:
jp1ac4 2023-04-03 09:23:37 +01:00
parent 943cc16e77
commit 4cb6d2dc69
No known key found for this signature in database
GPG Key ID: A7ACD32423568D7B
6 changed files with 104 additions and 84 deletions

View File

@ -60,8 +60,7 @@ fn update_coins(
amount,
derivation_index,
is_change,
block_height: None,
block_time: None,
block_info: None,
spend_txid: None,
spend_block: None,
};
@ -85,7 +84,7 @@ fn update_coins(
.values()
.chain(received.iter())
.filter_map(|coin| {
if coin.block_height.is_none() {
if coin.block_info.is_none() {
Some(coin.outpoint)
} else {
None

View File

@ -293,7 +293,7 @@ impl DaemonControl {
let Coin {
amount,
outpoint,
block_height,
block_info,
spend_txid,
spend_block,
..
@ -302,6 +302,7 @@ impl DaemonControl {
txid,
height: spend_block.map(|b| b.height),
});
let block_height = block_info.map(|b| b.height);
ListCoinsEntry {
amount,
outpoint,
@ -712,8 +713,8 @@ impl DaemonControl {
.into_iter()
.filter(|(_, c)| {
// We are interested in coins available at the *next* block
c.block_height
.map(|h| current_height + 1 >= h + timelock)
c.block_info
.map(|b| current_height + 1 >= b.height + timelock)
.unwrap_or(false)
});
@ -867,7 +868,11 @@ pub struct CreateRecoveryResult {
#[cfg(test)]
mod tests {
use super::*;
use crate::{bitcoin::Block, database::SpendBlock, testutils::*};
use crate::{
bitcoin::Block,
database::{BlockInfo, SpendBlock},
testutils::*,
};
use bitcoin::{
blockdata::transaction::{TxIn, TxOut},
@ -959,8 +964,7 @@ mod tests {
let mut db_conn = control.db().lock().unwrap().connection();
db_conn.new_unspent_coins(&[Coin {
outpoint: dummy_op,
block_height: None,
block_time: None,
block_info: None,
amount: bitcoin::Amount::from_sat(100_000),
derivation_index: bip32::ChildNumber::from(13),
is_change: false,
@ -1065,8 +1069,7 @@ mod tests {
};
db_conn.new_unspent_coins(&[Coin {
outpoint: dummy_op_dup,
block_height: None,
block_time: None,
block_info: None,
amount: bitcoin::Amount::from_sat(400_000),
derivation_index: bip32::ChildNumber::from(42),
is_change: false,
@ -1112,8 +1115,7 @@ mod tests {
db_conn.new_unspent_coins(&[
Coin {
outpoint: dummy_op_a,
block_height: None,
block_time: None,
block_info: None,
amount: bitcoin::Amount::from_sat(100_000),
derivation_index: bip32::ChildNumber::from(13),
is_change: false,
@ -1122,8 +1124,7 @@ mod tests {
},
Coin {
outpoint: dummy_op_b,
block_height: None,
block_time: None,
block_info: None,
amount: bitcoin::Amount::from_sat(115_680),
derivation_index: bip32::ChildNumber::from(34),
is_change: false,
@ -1294,8 +1295,7 @@ mod tests {
txid: deposit1.txid(),
vout: 0,
},
block_time: Some(1),
block_height: Some(1),
block_info: Some(BlockInfo { height: 1, time: 1 }),
spend_block: Some(SpendBlock { time: 3, height: 3 }),
derivation_index: ChildNumber::from(0),
amount: bitcoin::Amount::from_sat(100_000_000),
@ -1308,8 +1308,7 @@ mod tests {
txid: deposit2.txid(),
vout: 0,
},
block_time: Some(2),
block_height: Some(2),
block_info: Some(BlockInfo { height: 2, time: 2 }),
spend_block: None,
derivation_index: ChildNumber::from(1),
amount: bitcoin::Amount::from_sat(2000),
@ -1319,8 +1318,7 @@ mod tests {
Coin {
is_change: true,
outpoint: OutPoint::new(spend_tx.txid(), 1),
block_time: Some(3),
block_height: Some(3),
block_info: Some(BlockInfo { height: 3, time: 3 }),
spend_block: None,
derivation_index: ChildNumber::from(2),
amount: bitcoin::Amount::from_sat(100_000_000 - 4000 - 1000),
@ -1333,8 +1331,7 @@ mod tests {
txid: deposit3.txid(),
vout: 0,
},
block_time: Some(4),
block_height: Some(4),
block_info: Some(BlockInfo { height: 4, time: 4 }),
spend_block: None,
derivation_index: ChildNumber::from(3),
amount: bitcoin::Amount::from_sat(3000),

View File

@ -6,7 +6,7 @@ pub mod sqlite;
use crate::{
bitcoin::BlockChainTip,
database::sqlite::{
schema::{DbCoin, DbSpendBlock, DbTip},
schema::{DbBlockInfo, DbCoin, DbSpendBlock, DbTip},
SqliteConn, SqliteDb,
},
};
@ -276,11 +276,25 @@ impl From<DbSpendBlock> for SpendBlock {
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BlockInfo {
pub height: i32,
pub time: u32,
}
impl From<DbBlockInfo> for BlockInfo {
fn from(b: DbBlockInfo) -> BlockInfo {
BlockInfo {
height: b.height,
time: b.time,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Coin {
pub outpoint: bitcoin::OutPoint,
pub block_height: Option<i32>,
pub block_time: Option<u32>,
pub block_info: Option<BlockInfo>,
pub amount: bitcoin::Amount,
pub derivation_index: bip32::ChildNumber,
pub is_change: bool,
@ -292,8 +306,7 @@ impl std::convert::From<DbCoin> for Coin {
fn from(db_coin: DbCoin) -> Coin {
let DbCoin {
outpoint,
block_height,
block_time,
block_info,
amount,
derivation_index,
is_change,
@ -303,8 +316,7 @@ impl std::convert::From<DbCoin> for Coin {
} = db_coin;
Coin {
outpoint,
block_height,
block_time,
block_info: block_info.map(BlockInfo::from),
amount,
derivation_index,
is_change,
@ -316,7 +328,7 @@ impl std::convert::From<DbCoin> for Coin {
impl Coin {
pub fn is_confirmed(&self) -> bool {
self.block_height.is_some()
self.block_info.is_some()
}
pub fn is_spent(&self) -> bool {

View File

@ -585,7 +585,7 @@ impl SqliteConn {
#[cfg(test)]
mod tests {
use super::*;
use crate::database::SpendBlock;
use crate::database::{BlockInfo, DbBlockInfo, SpendBlock};
use crate::testutils::*;
use std::{
collections::{HashMap, HashSet},
@ -709,8 +709,7 @@ mod tests {
"6f0dc85a369b44458eba3a1f0ea5b5935d563afb6994f70f5b0094e05be1676c:1",
)
.unwrap(),
block_height: None,
block_time: None,
block_info: None,
amount: bitcoin::Amount::from_sat(98765),
derivation_index: bip32::ChildNumber::from_normal_idx(10).unwrap(),
is_change: false,
@ -742,8 +741,7 @@ mod tests {
"61db3e276b095e5b05f1849dd6bfffb4e7e5ec1c4a4210099b98fce01571936f:12",
)
.unwrap(),
block_height: None,
block_time: None,
block_info: None,
amount: bitcoin::Amount::from_sat(1111),
derivation_index: bip32::ChildNumber::from_normal_idx(103).unwrap(),
is_change: true,
@ -780,10 +778,8 @@ mod tests {
let time = 174500;
conn.confirm_coins(&[(coin_a.outpoint, height, time)]);
let coins = conn.coins(CoinType::All);
assert_eq!(coins[0].block_height, Some(height));
assert_eq!(coins[0].block_time, Some(time));
assert!(coins[1].block_height.is_none());
assert!(coins[1].block_time.is_none());
assert_eq!(coins[0].block_info, Some(DbBlockInfo { height, time }));
assert!(coins[1].block_info.is_none());
// Now if we spend one, it'll be marked as such.
conn.spend_coins(&[(
@ -969,8 +965,7 @@ mod tests {
"6f0dc85a369b44458eba3a1f0ea5b5935d563afb6994f70f5b0094e05be1676c:1",
)
.unwrap(),
block_height: None,
block_time: None,
block_info: None,
amount: bitcoin::Amount::from_sat(98765),
derivation_index: bip32::ChildNumber::from_normal_idx(10).unwrap(),
is_change: false,
@ -982,8 +977,10 @@ mod tests {
"c449539458c60bee6c0d8905ba1dadb20b9187b82045d306a408b894cea492b0:2",
)
.unwrap(),
block_height: Some(101_095),
block_time: Some(1_111_899),
block_info: Some(BlockInfo {
height: 101_095,
time: 1_111_899,
}),
amount: bitcoin::Amount::from_sat(98765),
derivation_index: bip32::ChildNumber::from_normal_idx(100).unwrap(),
is_change: false,
@ -995,8 +992,10 @@ mod tests {
"f0801fd9ca8bca0624c230ab422b2e2c4c8dc995e4e1dbc6412510959cce1e4f:3",
)
.unwrap(),
block_height: Some(101_099),
block_time: Some(1_121_899),
block_info: Some(BlockInfo {
height: 101_099,
time: 1_121_899,
}),
amount: bitcoin::Amount::from_sat(98765),
derivation_index: bip32::ChildNumber::from_normal_idx(1000).unwrap(),
is_change: false,
@ -1016,8 +1015,10 @@ mod tests {
"19f56e65069f0a7a3bfb00c6a7085cc0669e03e91befeca1ee9891c9e737b2fb:4",
)
.unwrap(),
block_height: Some(101_100),
block_time: Some(1_131_899),
block_info: Some(BlockInfo {
height: 101_100,
time: 1_131_899,
}),
amount: bitcoin::Amount::from_sat(98765),
derivation_index: bip32::ChildNumber::from_normal_idx(10000).unwrap(),
is_change: false,
@ -1029,8 +1030,10 @@ mod tests {
"ed6c8f1af9325f84de521e785e7ddfd33dc28c9ada4d687dcd3850100bde54e9:5",
)
.unwrap(),
block_height: Some(101_102),
block_time: Some(1_134_899),
block_info: Some(BlockInfo {
height: 101_102,
time: 1_134_899,
}),
amount: bitcoin::Amount::from_sat(98765),
derivation_index: bip32::ChildNumber::from_normal_idx(100000).unwrap(),
is_change: false,
@ -1050,10 +1053,7 @@ mod tests {
conn.confirm_coins(
&coins
.iter()
.filter_map(|c| {
c.block_height
.map(|b| (c.outpoint, b, c.block_time.unwrap()))
})
.filter_map(|c| c.block_info.map(|b| (c.outpoint, b.height, b.time)))
.collect::<Vec<_>>(),
);
conn.confirm_spend(
@ -1115,13 +1115,11 @@ mod tests {
assert_eq!(db_coins[&coins[2].outpoint], coin);
// The fourth one got its own confirmation info wiped
let mut coin = coins[3];
coin.block_height = None;
coin.block_time = None;
coin.block_info = None;
assert_eq!(db_coins[&coins[3].outpoint], coin);
// The fourth one got both is own confirmation and spend confirmation info wiped
let mut coin = coins[4];
coin.block_height = None;
coin.block_time = None;
coin.block_info = None;
coin.spend_block = None;
assert_eq!(db_coins[&coins[4].outpoint], coin);
}
@ -1180,8 +1178,7 @@ mod tests {
"6f0dc85a369b44458eba3a1f0ea5b5935d563afb6994f70f5b0094e05be1676c:1",
)
.unwrap(),
block_height: None,
block_time: None,
block_info: None,
amount: bitcoin::Amount::from_sat(98765),
derivation_index: bip32::ChildNumber::from_normal_idx(10).unwrap(),
is_change: false,
@ -1193,8 +1190,10 @@ mod tests {
"c449539458c60bee6c0d8905ba1dadb20b9187b82045d306a408b894cea492b0:2",
)
.unwrap(),
block_height: Some(101_095),
block_time: Some(1_121_000),
block_info: Some(BlockInfo {
height: 101_095,
time: 1_121_000,
}),
amount: bitcoin::Amount::from_sat(98765),
derivation_index: bip32::ChildNumber::from_normal_idx(100).unwrap(),
is_change: false,
@ -1206,8 +1205,10 @@ mod tests {
"f0801fd9ca8bca0624c230ab422b2e2c4c8dc995e4e1dbc6412510959cce1e4f:3",
)
.unwrap(),
block_height: Some(101_099),
block_time: Some(1_122_000),
block_info: Some(BlockInfo {
height: 101_099,
time: 1_122_000,
}),
amount: bitcoin::Amount::from_sat(98765),
derivation_index: bip32::ChildNumber::from_normal_idx(1000).unwrap(),
is_change: false,
@ -1227,8 +1228,10 @@ mod tests {
"19f56e65069f0a7a3bfb00c6a7085cc0669e03e91befeca1ee9891c9e737b2fb:4",
)
.unwrap(),
block_height: Some(101_100),
block_time: Some(1_124_000),
block_info: Some(BlockInfo {
height: 101_100,
time: 1_124_000,
}),
amount: bitcoin::Amount::from_sat(98765),
derivation_index: bip32::ChildNumber::from_normal_idx(10000).unwrap(),
is_change: false,
@ -1240,8 +1243,10 @@ mod tests {
"ed6c8f1af9325f84de521e785e7ddfd33dc28c9ada4d687dcd3850100bde54e9:5",
)
.unwrap(),
block_height: Some(101_102),
block_time: Some(1_125_000),
block_info: Some(BlockInfo {
height: 101_102,
time: 1_125_000,
}),
amount: bitcoin::Amount::from_sat(98765),
derivation_index: bip32::ChildNumber::from_normal_idx(100000).unwrap(),
is_change: false,
@ -1261,10 +1266,7 @@ mod tests {
conn.confirm_coins(
&coins
.iter()
.filter_map(|c| {
c.block_height
.map(|b| (c.outpoint, b, c.block_time.unwrap()))
})
.filter_map(|c| c.block_info.map(|b| (c.outpoint, b.height, b.time)))
.collect::<Vec<_>>(),
);
conn.confirm_spend(

View File

@ -153,13 +153,18 @@ pub struct DbSpendBlock {
pub time: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DbBlockInfo {
pub height: i32,
pub time: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DbCoin {
pub id: i64,
pub wallet_id: i64,
pub outpoint: bitcoin::OutPoint,
pub block_height: Option<i32>,
pub block_time: Option<u32>,
pub block_info: Option<DbBlockInfo>,
pub amount: bitcoin::Amount,
pub derivation_index: bip32::ChildNumber,
pub is_change: bool,
@ -174,8 +179,13 @@ impl TryFrom<&rusqlite::Row<'_>> for DbCoin {
let id = row.get(0)?;
let wallet_id = row.get(1)?;
let block_height = row.get(2)?;
let block_time = row.get(3)?;
let block_height: Option<i32> = row.get(2)?;
let block_time: Option<u32> = row.get(3)?;
assert_eq!(block_height.is_none(), block_time.is_none());
let block_info = block_height.map(|height| DbBlockInfo {
height,
time: block_time.expect("Must be there if height is"),
});
let txid: Vec<u8> = row.get(4)?;
let txid: bitcoin::Txid = encode::deserialize(&txid).expect("We only store valid txids");
let vout = row.get(5)?;
@ -202,8 +212,7 @@ impl TryFrom<&rusqlite::Row<'_>> for DbCoin {
id,
wallet_id,
outpoint,
block_height,
block_time,
block_info,
amount,
derivation_index,
is_change,

View File

@ -1,7 +1,7 @@
use crate::{
bitcoin::{BitcoinInterface, Block, BlockChainTip, UTxO},
config::{BitcoinConfig, Config},
database::{Coin, CoinType, DatabaseConnection, DatabaseInterface, SpendBlock},
database::{BlockInfo, Coin, CoinType, DatabaseConnection, DatabaseInterface, SpendBlock},
descriptors, DaemonHandle,
};
@ -233,10 +233,11 @@ impl DatabaseConnection for DummyDatabase {
for (op, height, time) in outpoints {
let mut db = self.db.write().unwrap();
let coin = &mut db.coins.get_mut(op).unwrap();
assert!(coin.block_height.is_none());
assert!(coin.block_time.is_none());
coin.block_height = Some(*height);
coin.block_time = Some(*time);
assert!(coin.block_info.is_none());
coin.block_info = Some(BlockInfo {
height: *height,
time: *time,
});
}
}
@ -335,7 +336,7 @@ impl DatabaseConnection for DummyDatabase {
// Get txid and block time of every transactions that happened between start and end
// timestamps.
for coin in coins.values() {
if let Some(time) = coin.block_time {
if let Some(time) = coin.block_info.map(|b| b.time) {
if time >= start && time <= end {
let row = (coin.outpoint.txid, time);
if !txids_and_time.contains(&row) {