Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/floresta-watch-only/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ bitcoin = { workspace = true, features = ["serde"] }
kv = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true, features = ["alloc"] }
thiserror = "2.0"
tracing = { workspace = true }

# Local dependencies
Expand Down
70 changes: 35 additions & 35 deletions crates/floresta-watch-only/src/kv_database.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
// SPDX-License-Identifier: MIT OR Apache-2.0

use core::error::Error;
use core::fmt;
use core::fmt::Display;
use core::fmt::Formatter;

use bitcoin::consensus::deserialize;
use bitcoin::consensus::encode::Error as EncodingError;
use bitcoin::consensus::serialize;
use bitcoin::hashes::Hash;
use bitcoin::Txid;
use floresta_common::impl_error_from;
use floresta_common::prelude::*;
use kv::Bucket;
use kv::Config;
use kv::Store;
use tracing::error;

use super::AddressCacheDatabase;
use super::Stats;
Expand All @@ -31,32 +26,30 @@ impl KvDatabase {
Ok(KvDatabase(store, bucket))
}
}
#[derive(Debug)]

/// Errors returned when reading or writing the on-disk watch-only address database.
#[derive(Debug, thiserror::Error)]
pub enum KvDatabaseError {
KvError(kv::Error),
SerdeJsonError(serde_json::Error),
/// Underlying key-value store error.
#[error("KvError: {0}")]
KvError(#[from] kv::Error),
/// JSON serialization or deserialization failed.
#[error(transparent)]
SerdeJsonError(#[from] serde_json::Error),
/// Required wallet metadata has not been written yet.
#[error("Wallet not initialized")]
WalletNotInitialized,
DeserializeError(EncodingError),
/// Bitcoin consensus encoding error while reading stored data.
#[error(transparent)]
DeserializeError(#[from] EncodingError),
/// A requested transaction is not stored in the database.
#[error("Transaction not found")]
TransactionNotFound,
}
impl_error_from!(KvDatabaseError, serde_json::Error, SerdeJsonError);
impl_error_from!(KvDatabaseError, kv::Error, KvError);
impl_error_from!(KvDatabaseError, EncodingError, DeserializeError);

impl Display for KvDatabaseError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
KvDatabaseError::KvError(e) => write!(f, "KvError: {e}"),
KvDatabaseError::SerdeJsonError(e) => write!(f, "SerdeJsonError: {e}"),
KvDatabaseError::WalletNotInitialized => write!(f, "WalletNotInitialized"),
KvDatabaseError::DeserializeError(e) => write!(f, "DeserializeError: {e}"),
KvDatabaseError::TransactionNotFound => write!(f, "TransactionNotFound"),
}
}
/// A stored transaction id key had an unexpected byte length.
#[error("Stored transaction id key has invalid length")]
InvalidTxidKey,
}

impl Error for KvDatabaseError {}

type Result<T> = floresta_common::prelude::Result<T, KvDatabaseError>;

impl AddressCacheDatabase for KvDatabase {
Expand All @@ -69,20 +62,26 @@ impl AddressCacheDatabase for KvDatabase {
if *"height" == key || *"desc" == key {
continue;
}
let value: Vec<u8> = item.value().unwrap();
let value: Vec<u8> = item.value()?;
let value = serde_json::from_slice(&value)?;
addresses.push(value);
}
Ok(addresses)
}
fn save(&self, address: &super::CachedAddress) {
let key = address.script_hash.to_string();
let value = serde_json::to_vec(&address).expect("Invalid object serialization");
let Ok(value) = serde_json::to_vec(&address) else {
error!("Could not serialize address {key} for cache persistence");
return;
};

self.1
.set(&key, &value)
.expect("Fatal: Database isn't working");
self.1.flush().expect("Could not write to disk");
if let Err(e) = self.1.set(&key, &value) {
error!("Failed to persist address entry {key}: {e}");
return;
}
if let Err(e) = self.1.flush() {
error!("Failed to flush address cache to disk for key {key}: {e}");
}
}
fn update(&self, address: &super::CachedAddress) {
self.save(address);
Expand All @@ -104,7 +103,7 @@ impl AddressCacheDatabase for KvDatabase {
let mut descs = self.descs_get()?;
descs.push(String::from(descriptor));
self.1
.set(&String::from("desc"), &serde_json::to_vec(&descs).unwrap())?;
.set(&String::from("desc"), &serde_json::to_vec(&descs)?)?;
self.1.flush()?;

Ok(())
Expand Down Expand Up @@ -164,7 +163,8 @@ impl AddressCacheDatabase for KvDatabase {
for item in store.iter() {
let item = item?;
let key = item.key::<&[u8]>()?;
transactions.push(Txid::from_slice(key).unwrap());
let txid = Txid::from_slice(key).map_err(|_| KvDatabaseError::InvalidTxidKey)?;
transactions.push(txid);
}
Ok(transactions)
}
Expand Down
Loading