Skip to content

Commit 3107be9

Browse files
authored
Merge pull request #28 from buffrr/wallet-updates
Wallet updates (+ breaking changes)
2 parents 13139f0 + fd59b34 commit 3107be9

File tree

9 files changed

+312
-545
lines changed

9 files changed

+312
-545
lines changed

node/src/bin/space-cli.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use spaced::{
2525
store::Sha256,
2626
wallets::AddressKind,
2727
};
28+
use wallet::export::WalletExport;
2829

2930
#[derive(Parser, Debug)]
3031
#[command(version, about, long_about = None)]
@@ -443,11 +444,12 @@ async fn handle_commands(
443444
Commands::ImportWallet { path } => {
444445
let content =
445446
fs::read_to_string(path).map_err(|e| ClientError::Custom(e.to_string()))?;
446-
cli.client.wallet_import(&content).await?;
447+
let wallet: WalletExport = serde_json::from_str(&content)?;
448+
cli.client.wallet_import(wallet).await?;
447449
}
448450
Commands::ExportWallet { name } => {
449451
let result = cli.client.wallet_export(&name).await?;
450-
println!("{}", result);
452+
println!("{}", serde_json::to_string_pretty(&result).expect("result"));
451453
}
452454
Commands::GetWalletInfo { name } => {
453455
let result = cli.client.wallet_get_info(&name).await?;

node/src/rpc.rs

Lines changed: 46 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ use bdk::{
1111
DerivableKey, ExtendedKey, GeneratableKey, GeneratedKey,
1212
},
1313
miniscript::Tap,
14-
template::Bip84,
15-
KeychainKind, LocalOutput,
14+
KeychainKind,
1615
};
1716
use jsonrpsee::{core::async_trait, proc_macros::rpc, server::Server, types::ErrorObjectOwned};
1817
use log::info;
@@ -35,16 +34,18 @@ use tokio::{
3534
task::JoinSet,
3635
};
3736
use wallet::{
38-
bdk_wallet as bdk, bitcoin::hashes::Hash, derivation::SpaceDerivation, DoubleUtxo,
39-
SpacesWallet, WalletConfig, WalletExport, WalletInfo,
37+
bdk_wallet as bdk, bdk_wallet::template::Bip86, bitcoin::hashes::Hash, export::WalletExport,
38+
DoubleUtxo, SpacesWallet, WalletConfig, WalletDescriptors, WalletInfo,
4039
};
4140

4241
use crate::{
4342
config::ExtendedNetwork,
4443
node::BlockMeta,
4544
source::BitcoinRpc,
4645
store::{ChainState, LiveSnapshot},
47-
wallets::{AddressKind, JointBalance, RpcWallet, TxResponse, WalletCommand, WalletResponse},
46+
wallets::{
47+
AddressKind, Balance, RpcWallet, TxResponse, WalletCommand, WalletOutput, WalletResponse,
48+
},
4849
};
4950

5051
pub(crate) type Responder<T> = oneshot::Sender<T>;
@@ -129,13 +130,13 @@ pub trait Rpc {
129130
async fn wallet_load(&self, name: &str) -> Result<(), ErrorObjectOwned>;
130131

131132
#[method(name = "walletimport")]
132-
async fn wallet_import(&self, content: &str) -> Result<(), ErrorObjectOwned>;
133+
async fn wallet_import(&self, wallet: WalletExport) -> Result<(), ErrorObjectOwned>;
133134

134135
#[method(name = "walletgetinfo")]
135136
async fn wallet_get_info(&self, name: &str) -> Result<WalletInfo, ErrorObjectOwned>;
136137

137138
#[method(name = "walletexport")]
138-
async fn wallet_export(&self, name: &str) -> Result<String, ErrorObjectOwned>;
139+
async fn wallet_export(&self, name: &str) -> Result<WalletExport, ErrorObjectOwned>;
139140

140141
#[method(name = "walletcreate")]
141142
async fn wallet_create(&self, name: &str) -> Result<(), ErrorObjectOwned>;
@@ -164,11 +165,13 @@ pub trait Rpc {
164165

165166
#[method(name = "walletlistspaces")]
166167
async fn wallet_list_spaces(&self, wallet: &str)
167-
-> Result<Vec<FullSpaceOut>, ErrorObjectOwned>;
168+
-> Result<Vec<WalletOutput>, ErrorObjectOwned>;
168169

169170
#[method(name = "walletlistunspent")]
170-
async fn wallet_list_unspent(&self, wallet: &str)
171-
-> Result<Vec<LocalOutput>, ErrorObjectOwned>;
171+
async fn wallet_list_unspent(
172+
&self,
173+
wallet: &str,
174+
) -> Result<Vec<WalletOutput>, ErrorObjectOwned>;
172175

173176
#[method(name = "walletlistauctionoutputs")]
174177
async fn wallet_list_auction_outputs(
@@ -177,7 +180,7 @@ pub trait Rpc {
177180
) -> Result<Vec<DoubleUtxo>, ErrorObjectOwned>;
178181

179182
#[method(name = "walletgetbalance")]
180-
async fn wallet_get_balance(&self, wallet: &str) -> Result<JointBalance, ErrorObjectOwned>;
183+
async fn wallet_get_balance(&self, wallet: &str) -> Result<Balance, ErrorObjectOwned>;
181184
}
182185

183186
#[derive(Clone, Serialize, Deserialize)]
@@ -283,10 +286,8 @@ impl WalletManager {
283286
pub async fn import_wallet(
284287
&self,
285288
client: &reqwest::Client,
286-
content: &str,
289+
wallet: WalletExport,
287290
) -> anyhow::Result<()> {
288-
let wallet = WalletExport::from_str(content)?;
289-
290291
let wallet_path = self.data_dir.join(&wallet.label);
291292
if wallet_path.exists() {
292293
return Err(anyhow!(format!(
@@ -304,12 +305,14 @@ impl WalletManager {
304305
Ok(())
305306
}
306307

307-
pub async fn export_wallet(&self, name: &str) -> anyhow::Result<String> {
308+
pub async fn export_wallet(&self, name: &str) -> anyhow::Result<WalletExport> {
308309
let wallet_dir = self.data_dir.join(name);
309310
if !wallet_dir.exists() {
310311
return Err(anyhow!("Wallet does not exist"));
311312
}
312-
Ok(fs::read_to_string(wallet_dir.join("wallet.json"))?)
313+
let wallet = fs::read_to_string(wallet_dir.join("wallet.json"))?;
314+
let export: WalletExport = serde_json::from_str(&wallet)?;
315+
Ok(export)
313316
}
314317

315318
pub async fn create_wallet(&self, client: &reqwest::Client, name: &str) -> anyhow::Result<()> {
@@ -351,16 +354,10 @@ impl WalletManager {
351354
let (network, _) = self.fallback_network();
352355
let xpriv = Self::descriptor_from_mnemonic(network, &mnemonic.to_string())?;
353356

354-
let coins_descriptors = Self::default_coin_descriptors(xpriv);
355-
let space_descriptors = Self::default_spaces_descriptors(xpriv);
356-
357-
let export = WalletExport::from_descriptors(
358-
name,
359-
start_block.height,
360-
network,
361-
coins_descriptors.0,
362-
space_descriptors.0,
363-
)?;
357+
let (external, internal) = Self::default_descriptors(xpriv);
358+
let tmp = bdk::wallet::Wallet::new_or_load(external, internal, None, network)?;
359+
let export =
360+
WalletExport::export_wallet(&tmp, &name, start_block.height).map_err(|e| anyhow!(e))?;
364361

365362
Ok(export)
366363
}
@@ -403,54 +400,10 @@ impl WalletManager {
403400
(network, genesis_hash)
404401
}
405402

406-
// TODO: remove in the next update
407-
pub async fn migrate_legacy_v0_0_1_wallet(
408-
&self,
409-
name: String,
410-
wallet_dir: PathBuf,
411-
) -> anyhow::Result<bool> {
412-
let legacy_secret = wallet_dir
413-
.join("insecure_secret")
414-
.to_str()
415-
.unwrap()
416-
.replace("/testnet/wallets/", "/test/wallets/");
417-
let legacy_secret = std::path::PathBuf::from(legacy_secret);
418-
419-
if !legacy_secret.exists() {
420-
return Ok(false);
421-
}
422-
423-
let mnemonic = fs::read_to_string(legacy_secret)?.trim().to_string();
424-
let start_block = match self.network {
425-
ExtendedNetwork::Testnet => ChainAnchor::TESTNET(),
426-
ExtendedNetwork::Testnet4 => ChainAnchor::TESTNET4(),
427-
ExtendedNetwork::Regtest => ChainAnchor::REGTEST(),
428-
_ => panic!("could not migrate legacy wallet: unsupported network"),
429-
};
430-
431-
self.setup_new_wallet(
432-
name,
433-
mnemonic,
434-
BlockId {
435-
height: start_block.height,
436-
hash: start_block.hash,
437-
},
438-
)?;
439-
440-
Ok(true)
441-
}
442-
443403
pub async fn load_wallet(&self, client: &reqwest::Client, name: &str) -> anyhow::Result<()> {
444404
let wallet_dir = self.data_dir.join(name);
445405
if !wallet_dir.exists() {
446-
if self
447-
.migrate_legacy_v0_0_1_wallet(name.to_string(), wallet_dir.clone())
448-
.await?
449-
{
450-
info!("Migrated legacy wallet {}", name);
451-
} else {
452-
return Err(anyhow!("Wallet does not exist"));
453-
}
406+
return Err(anyhow!("Wallet does not exist"));
454407
}
455408

456409
let file = fs::File::open(wallet_dir.join("wallet.json"))?;
@@ -459,20 +412,23 @@ impl WalletManager {
459412
let export: WalletExport = serde_json::from_reader(file)?;
460413

461414
let mut wallet = SpacesWallet::new(WalletConfig {
462-
start_block: export.block_height,
415+
start_block: export.blockheight,
463416
data_dir: wallet_dir,
464417
name: name.to_string(),
465418
network,
466419
genesis_hash,
467-
coins_descriptors: export.descriptors(),
468-
space_descriptors: export.space_descriptors(),
420+
space_descriptors: WalletDescriptors {
421+
external: export.descriptor(),
422+
internal: export
423+
.change_descriptor()
424+
.expect("expected a change descriptor"),
425+
},
469426
})?;
470427

471-
let wallet_tip = wallet.coins.local_chain().tip().height();
428+
let wallet_tip = wallet.spaces.local_chain().tip().height();
472429

473-
if wallet_tip < export.block_height {
474-
let block_id = self.get_block_hash(client, export.block_height).await?;
475-
wallet.coins.insert_checkpoint(block_id)?;
430+
if wallet_tip < export.blockheight {
431+
let block_id = self.get_block_hash(client, export.blockheight).await?;
476432
wallet.spaces.insert_checkpoint(block_id)?;
477433
wallet.commit()?;
478434
}
@@ -520,17 +476,10 @@ impl WalletManager {
520476
Ok(xkey.into_xprv(network).expect("xpriv"))
521477
}
522478

523-
fn default_coin_descriptors(x: Xpriv) -> (Bip84<Xpriv>, Bip84<Xpriv>) {
524-
(
525-
Bip84(x, KeychainKind::External),
526-
Bip84(x, KeychainKind::Internal),
527-
)
528-
}
529-
530-
fn default_spaces_descriptors(x: Xpriv) -> (SpaceDerivation<Xpriv>, SpaceDerivation<Xpriv>) {
479+
fn default_descriptors(x: Xpriv) -> (Bip86<Xpriv>, Bip86<Xpriv>) {
531480
(
532-
SpaceDerivation(x, KeychainKind::External),
533-
SpaceDerivation(x, KeychainKind::Internal),
481+
Bip86(x, KeychainKind::External),
482+
Bip86(x, KeychainKind::Internal),
534483
)
535484
}
536485
}
@@ -693,7 +642,7 @@ impl RpcServer for RpcServerImpl {
693642
})
694643
}
695644

696-
async fn wallet_import(&self, content: &str) -> Result<(), ErrorObjectOwned> {
645+
async fn wallet_import(&self, content: WalletExport) -> Result<(), ErrorObjectOwned> {
697646
self.wallet_manager
698647
.import_wallet(&self.client, content)
699648
.await
@@ -710,7 +659,7 @@ impl RpcServer for RpcServerImpl {
710659
.map_err(|error| ErrorObjectOwned::owned(-1, error.to_string(), None::<String>))
711660
}
712661

713-
async fn wallet_export(&self, name: &str) -> Result<String, ErrorObjectOwned> {
662+
async fn wallet_export(&self, name: &str) -> Result<WalletExport, ErrorObjectOwned> {
714663
self.wallet_manager
715664
.export_wallet(name)
716665
.await
@@ -769,7 +718,7 @@ impl RpcServer for RpcServerImpl {
769718
async fn wallet_list_spaces(
770719
&self,
771720
wallet: &str,
772-
) -> Result<Vec<FullSpaceOut>, ErrorObjectOwned> {
721+
) -> Result<Vec<WalletOutput>, ErrorObjectOwned> {
773722
self.wallet(&wallet)
774723
.await?
775724
.send_list_spaces()
@@ -780,7 +729,7 @@ impl RpcServer for RpcServerImpl {
780729
async fn wallet_list_unspent(
781730
&self,
782731
wallet: &str,
783-
) -> Result<Vec<LocalOutput>, ErrorObjectOwned> {
732+
) -> Result<Vec<WalletOutput>, ErrorObjectOwned> {
784733
self.wallet(&wallet)
785734
.await?
786735
.send_list_unspent()
@@ -799,7 +748,7 @@ impl RpcServer for RpcServerImpl {
799748
.map_err(|error| ErrorObjectOwned::owned(-1, error.to_string(), None::<String>))
800749
}
801750

802-
async fn wallet_get_balance(&self, wallet: &str) -> Result<JointBalance, ErrorObjectOwned> {
751+
async fn wallet_get_balance(&self, wallet: &str) -> Result<Balance, ErrorObjectOwned> {
803752
self.wallet(&wallet)
804753
.await?
805754
.send_get_balance()
@@ -825,11 +774,10 @@ impl AsyncChainState {
825774
.await
826775
.map_err(|e| anyhow!("Could not retrieve tx ({})", e))?;
827776

828-
let block_hash = BlockHash::from_str(
829-
info.get("blockhash")
830-
.and_then(|t| t.as_str())
831-
.ok_or_else(|| anyhow!("Could not retrieve block hash for tx (is it in the mempool?)"))?,
832-
)?;
777+
let block_hash =
778+
BlockHash::from_str(info.get("blockhash").and_then(|t| t.as_str()).ok_or_else(
779+
|| anyhow!("Could not retrieve block hash for tx (is it in the mempool?)"),
780+
)?)?;
833781
let block = Self::get_indexed_block(index, &block_hash, client, rpc, chain_state).await?;
834782

835783
if let Some(block) = block {

0 commit comments

Comments
 (0)