commands: add an 'updated_at' field to listspendtxs entries

This commit is contained in:
Antoine Poinsot 2023-03-29 16:28:25 +02:00
parent 6b666e75c0
commit 104c6e1a09
No known key found for this signature in database
GPG Key ID: E13FC145CD3F4304
7 changed files with 34 additions and 24 deletions

View File

@ -174,6 +174,7 @@ This command does not take any parameter for now.
| Field | Type | Description |
| -------------- | ----------------- | ----------------------------------------------------------------------- |
| `psbt` | string | Base64-encoded PSBT of the Spend transaction. |
| `updated_at` | int or null | UNIX timestamp of the last time this PSBT was updated. |
### `delspendtx`

View File

@ -563,7 +563,7 @@ impl DaemonControl {
let spend_txs = db_conn
.list_spend()
.into_iter()
.map(|psbt| ListSpendEntry { psbt })
.map(|(psbt, updated_at)| ListSpendEntry { psbt, updated_at })
.collect();
ListSpendResult { spend_txs }
}
@ -840,6 +840,7 @@ pub struct CreateSpendResult {
pub struct ListSpendEntry {
#[serde(serialize_with = "ser_base64", deserialize_with = "deser_base64")]
pub psbt: Psbt,
pub updated_at: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]

View File

@ -112,8 +112,8 @@ pub trait DatabaseConnection {
/// Insert a new Spend transaction or replace an existing one.
fn store_spend(&mut self, psbt: &Psbt);
/// List all existing Spend transactions.
fn list_spend(&mut self) -> Vec<Psbt>;
/// List all existing Spend transactions, along with an optional last update timestamp.
fn list_spend(&mut self) -> Vec<(Psbt, Option<u32>)>;
/// Delete a Spend transaction from database.
fn delete_spend(&mut self, txid: &bitcoin::Txid);
@ -241,10 +241,10 @@ impl DatabaseConnection for SqliteConn {
self.store_spend(psbt)
}
fn list_spend(&mut self) -> Vec<Psbt> {
fn list_spend(&mut self) -> Vec<(Psbt, Option<u32>)> {
self.list_spend()
.into_iter()
.map(|db_spend| db_spend.psbt)
.map(|db_spend| (db_spend.psbt, db_spend.updated_at))
.collect()
}

View File

@ -15,8 +15,8 @@ use crate::{
sqlite::{
schema::{DbAddress, DbCoin, DbSpendTransaction, DbTip, DbWallet, SCHEMA},
utils::{
create_fresh_db, db_exec, db_query, db_tx_query, db_version, maybe_apply_migration,
LOOK_AHEAD_LIMIT,
create_fresh_db, curr_timestamp, db_exec, db_query, db_tx_query, db_version,
maybe_apply_migration, LOOK_AHEAD_LIMIT,
},
},
Coin, CoinType,
@ -500,9 +500,9 @@ impl SqliteConn {
db_exec(&mut self.conn, |db_tx| {
db_tx.execute(
"INSERT into spend_transactions (psbt, txid) VALUES (?1, ?2) \
"INSERT into spend_transactions (psbt, txid, updated_at) VALUES (?1, ?2, ?3) \
ON CONFLICT DO UPDATE SET psbt=excluded.psbt",
rusqlite::params![psbt, txid],
rusqlite::params![psbt, txid, curr_timestamp()],
)?;
Ok(())
})
@ -1463,12 +1463,11 @@ CREATE TABLE spend_transactions (
.find(|db_spend| db_spend.psbt == first_psbt)
.unwrap();
assert!(first_spend.updated_at.is_none());
// TODO: update once we update store_spend() to take a timestamp.
let second_spend = db_spends
.iter()
.find(|db_spend| db_spend.psbt == second_psbt)
.unwrap();
assert!(second_spend.updated_at.is_none());
assert!(second_spend.updated_at.is_some());
}
fs::remove_dir_all(tmp_dir).unwrap();

View File

@ -50,11 +50,14 @@ where
.collect::<rusqlite::Result<Vec<T>>>()
}
// Sqlite supports up to i64, thus rusqlite prevents us from inserting u64's.
// We use this to panic rather than inserting a truncated integer into the database (as we'd have
// done by using `n as u32`).
fn timestamp_to_u32(n: u64) -> u32 {
n.try_into()
/// The current time as the number of seconds since the UNIX epoch, truncated to u32 since SQLite
/// only supports i64 integers.
pub fn curr_timestamp() -> u32 {
time::SystemTime::now()
.duration_since(time::UNIX_EPOCH)
.expect("System clock went backward the epoch?")
.as_secs()
.try_into()
.expect("Is this the year 2106 yet? Misconfigured system clock.")
}
@ -87,10 +90,7 @@ pub fn create_fresh_db(
) -> Result<(), SqliteDbError> {
create_db_file(db_path)?;
let timestamp = time::SystemTime::now()
.duration_since(time::UNIX_EPOCH)
.map(|dur| timestamp_to_u32(dur.as_secs()))
.expect("System clock went backward the epoch?");
let timestamp = curr_timestamp();
// Fill the initial addresses. On a fresh database, the deposit_derivation_index is
// necessarily 0.

View File

@ -120,7 +120,7 @@ struct DummyDbState {
change_index: bip32::ChildNumber,
curr_tip: Option<BlockChainTip>,
coins: HashMap<bitcoin::OutPoint, Coin>,
spend_txs: HashMap<bitcoin::Txid, Psbt>,
spend_txs: HashMap<bitcoin::Txid, (Psbt, Option<u32>)>,
}
pub struct DummyDatabase {
@ -293,14 +293,20 @@ impl DatabaseConnection for DummyDatabase {
.write()
.unwrap()
.spend_txs
.insert(txid, psbt.clone());
.insert(txid, (psbt.clone(), None));
}
fn spend_tx(&mut self, txid: &bitcoin::Txid) -> Option<Psbt> {
self.db.read().unwrap().spend_txs.get(txid).cloned()
self.db
.read()
.unwrap()
.spend_txs
.get(txid)
.cloned()
.map(|x| x.0)
}
fn list_spend(&mut self) -> Vec<Psbt> {
fn list_spend(&mut self) -> Vec<(Psbt, Option<u32>)> {
self.db
.read()
.unwrap()

View File

@ -159,6 +159,7 @@ def test_list_spend(lianad, bitcoind):
assert "psbt" in res_b
# Store them both in DB.
time_before_update = int(time.time())
assert len(lianad.rpc.listspendtxs()["spend_txs"]) == 0
lianad.rpc.updatespend(res["psbt"])
lianad.rpc.updatespend(res_b["psbt"])
@ -168,7 +169,9 @@ def test_list_spend(lianad, bitcoind):
list_res = lianad.rpc.listspendtxs()["spend_txs"]
assert len(list_res) == 2
first_psbt = next(entry for entry in list_res if entry["psbt"] == res["psbt"])
assert time_before_update <= first_psbt["updated_at"] <= int(time.time())
second_psbt = next(entry for entry in list_res if entry["psbt"] == res_b["psbt"])
assert time_before_update <= second_psbt["updated_at"] <= int(time.time())
# If we delete the first one, we'll get only the second one.
first_psbt = PSBT.from_base64(res["psbt"])