Skip to content

Commit d718299

Browse files
committed
Merge #1256: cherry-pick feat(wallet)!: add NonEmptyDatabase variant to NewError
a1d34af feat(wallet)!: add `NonEmptyDatabase` variant to `NewError` (志宇) Pull request description: ### Description `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`). ### Notes to the reviewers This is cherry-picked from #1172 so that we can add it to the alpha.3 release. ### Changelog notice Change - Return `NonEmptyDatabase` error when constructing a wallet with `Wallet::new` if the file already contains data (in which case, the caller should use `load` or `new_or_load`). ### Checklists #### All Submissions: * [x] I've signed all my commits * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md) * [x] I ran `cargo fmt` and `cargo clippy` before committing #### New Features: * [x] I've added tests for the new feature * [x] I've added docs for the new feature ACKs for top commit: evanlinjin: ACK a1d34af Tree-SHA512: 7c20171fa3d7dee5b1ac24f8a808781dbb0be0034951005e1e87acdf023123c01161e225b47b6d4484865889778c39549a3780f641227ddc0f84d1577d69f40a
2 parents 9cc0332 + a1d34af commit d718299

File tree

2 files changed

+25
-2
lines changed

2 files changed

+25
-2
lines changed

crates/bdk/src/wallet/mod.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ impl Wallet {
237237
network: Network,
238238
) -> Result<Self, DescriptorError> {
239239
Self::new(descriptor, change_descriptor, (), network).map_err(|e| match e {
240+
NewError::NonEmptyDatabase => unreachable!("mock-database cannot have data"),
240241
NewError::Descriptor(e) => e,
241242
NewError::Write(_) => unreachable!("mock-write must always succeed"),
242243
})
@@ -251,6 +252,7 @@ impl Wallet {
251252
) -> Result<Self, crate::descriptor::DescriptorError> {
252253
Self::new_with_genesis_hash(descriptor, change_descriptor, (), network, genesis_hash)
253254
.map_err(|e| match e {
255+
NewError::NonEmptyDatabase => unreachable!("mock-database cannot have data"),
254256
NewError::Descriptor(e) => e,
255257
NewError::Write(_) => unreachable!("mock-write must always succeed"),
256258
})
@@ -288,6 +290,8 @@ where
288290
/// [`new_with_genesis_hash`]: Wallet::new_with_genesis_hash
289291
#[derive(Debug)]
290292
pub enum NewError<W> {
293+
/// Database already has data.
294+
NonEmptyDatabase,
291295
/// There was problem with the passed-in descriptor(s).
292296
Descriptor(crate::descriptor::DescriptorError),
293297
/// We were unable to write the wallet's data to the persistence backend.
@@ -300,6 +304,10 @@ where
300304
{
301305
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
302306
match self {
307+
NewError::NonEmptyDatabase => write!(
308+
f,
309+
"database already has data - use `load` or `new_or_load` methods instead"
310+
),
303311
NewError::Descriptor(e) => e.fmt(f),
304312
NewError::Write(e) => e.fmt(f),
305313
}
@@ -446,13 +454,18 @@ impl<D> Wallet<D> {
446454
pub fn new_with_genesis_hash<E: IntoWalletDescriptor>(
447455
descriptor: E,
448456
change_descriptor: Option<E>,
449-
db: D,
457+
mut db: D,
450458
network: Network,
451459
genesis_hash: BlockHash,
452460
) -> Result<Self, NewError<D::WriteError>>
453461
where
454462
D: PersistBackend<ChangeSet>,
455463
{
464+
if let Ok(changeset) = db.load_from_persistence() {
465+
if changeset.is_some() {
466+
return Err(NewError::NonEmptyDatabase);
467+
}
468+
}
456469
let secp = Secp256k1::new();
457470
let (chain, chain_changeset) = LocalChain::from_genesis_hash(genesis_hash);
458471
let mut index = KeychainTxOutIndex::<KeychainKind>::default();
@@ -615,6 +628,9 @@ impl<D> Wallet<D> {
615628
genesis_hash,
616629
)
617630
.map_err(|e| match e {
631+
NewError::NonEmptyDatabase => {
632+
unreachable!("database is already checked to have no data")
633+
}
618634
NewError::Descriptor(e) => NewOrLoadError::Descriptor(e),
619635
NewError::Write(e) => NewOrLoadError::Write(e),
620636
}),

crates/bdk/tests/wallet.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use bdk::signer::{SignOptions, SignerError};
77
use bdk::wallet::coin_selection::{self, LargestFirstCoinSelection};
88
use bdk::wallet::error::CreateTxError;
99
use bdk::wallet::tx_builder::AddForeignUtxoError;
10-
use bdk::wallet::AddressIndex::*;
1110
use bdk::wallet::{AddressIndex, AddressInfo, Balance, Wallet};
11+
use bdk::wallet::{AddressIndex::*, NewError};
1212
use bdk::{FeeRate, KeychainKind};
1313
use bdk_chain::COINBASE_MATURITY;
1414
use bdk_chain::{BlockId, ConfirmationTime};
@@ -92,6 +92,13 @@ fn load_recovers_wallet() {
9292
wallet_spk_index.last_revealed_indices()
9393
);
9494
}
95+
96+
// `new` can only be called on empty db
97+
{
98+
let db = bdk_file_store::Store::open(DB_MAGIC, &file_path).expect("must recover db");
99+
let result = Wallet::new(get_test_tr_single_sig_xprv(), None, db, Network::Testnet);
100+
assert!(matches!(result, Err(NewError::NonEmptyDatabase)));
101+
}
95102
}
96103

97104
#[test]

0 commit comments

Comments
 (0)