feat(wallet)!: add NonEmptyDatabase variant to NewError

`NewError` is the error type when constructing a wallet with
`Wallet::new`. We want this to return an error when the database already
contains data (in which case, the caller should use `load` or
`new_or_load`).
This commit is contained in:
志宇 2024-01-02 13:03:19 +08:00
parent 9cc03324f4
commit a1d34afa24
No known key found for this signature in database
GPG Key ID: F6345C9837C2BDE8
2 changed files with 25 additions and 2 deletions

View File

@ -237,6 +237,7 @@ impl Wallet {
network: Network,
) -> Result<Self, DescriptorError> {
Self::new(descriptor, change_descriptor, (), network).map_err(|e| match e {
NewError::NonEmptyDatabase => unreachable!("mock-database cannot have data"),
NewError::Descriptor(e) => e,
NewError::Write(_) => unreachable!("mock-write must always succeed"),
})
@ -251,6 +252,7 @@ impl Wallet {
) -> Result<Self, crate::descriptor::DescriptorError> {
Self::new_with_genesis_hash(descriptor, change_descriptor, (), network, genesis_hash)
.map_err(|e| match e {
NewError::NonEmptyDatabase => unreachable!("mock-database cannot have data"),
NewError::Descriptor(e) => e,
NewError::Write(_) => unreachable!("mock-write must always succeed"),
})
@ -288,6 +290,8 @@ where
/// [`new_with_genesis_hash`]: Wallet::new_with_genesis_hash
#[derive(Debug)]
pub enum NewError<W> {
/// Database already has data.
NonEmptyDatabase,
/// There was problem with the passed-in descriptor(s).
Descriptor(crate::descriptor::DescriptorError),
/// We were unable to write the wallet's data to the persistence backend.
@ -300,6 +304,10 @@ where
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NewError::NonEmptyDatabase => write!(
f,
"database already has data - use `load` or `new_or_load` methods instead"
),
NewError::Descriptor(e) => e.fmt(f),
NewError::Write(e) => e.fmt(f),
}
@ -446,13 +454,18 @@ impl<D> Wallet<D> {
pub fn new_with_genesis_hash<E: IntoWalletDescriptor>(
descriptor: E,
change_descriptor: Option<E>,
db: D,
mut db: D,
network: Network,
genesis_hash: BlockHash,
) -> Result<Self, NewError<D::WriteError>>
where
D: PersistBackend<ChangeSet>,
{
if let Ok(changeset) = db.load_from_persistence() {
if changeset.is_some() {
return Err(NewError::NonEmptyDatabase);
}
}
let secp = Secp256k1::new();
let (chain, chain_changeset) = LocalChain::from_genesis_hash(genesis_hash);
let mut index = KeychainTxOutIndex::<KeychainKind>::default();
@ -615,6 +628,9 @@ impl<D> Wallet<D> {
genesis_hash,
)
.map_err(|e| match e {
NewError::NonEmptyDatabase => {
unreachable!("database is already checked to have no data")
}
NewError::Descriptor(e) => NewOrLoadError::Descriptor(e),
NewError::Write(e) => NewOrLoadError::Write(e),
}),

View File

@ -7,8 +7,8 @@ use bdk::signer::{SignOptions, SignerError};
use bdk::wallet::coin_selection::{self, LargestFirstCoinSelection};
use bdk::wallet::error::CreateTxError;
use bdk::wallet::tx_builder::AddForeignUtxoError;
use bdk::wallet::AddressIndex::*;
use bdk::wallet::{AddressIndex, AddressInfo, Balance, Wallet};
use bdk::wallet::{AddressIndex::*, NewError};
use bdk::{FeeRate, KeychainKind};
use bdk_chain::COINBASE_MATURITY;
use bdk_chain::{BlockId, ConfirmationTime};
@ -92,6 +92,13 @@ fn load_recovers_wallet() {
wallet_spk_index.last_revealed_indices()
);
}
// `new` can only be called on empty db
{
let db = bdk_file_store::Store::open(DB_MAGIC, &file_path).expect("must recover db");
let result = Wallet::new(get_test_tr_single_sig_xprv(), None, db, Network::Testnet);
assert!(matches!(result, Err(NewError::NonEmptyDatabase)));
}
}
#[test]