commands: implement getnewaddress
This commit is contained in:
parent
8309c85e30
commit
2609061a69
@ -4,7 +4,11 @@
|
||||
|
||||
use crate::{DaemonControl, VERSION};
|
||||
|
||||
use miniscript::{bitcoin, descriptor};
|
||||
use miniscript::{
|
||||
bitcoin,
|
||||
descriptor::{self, DescriptorTrait},
|
||||
TranslatePk2,
|
||||
};
|
||||
|
||||
impl DaemonControl {
|
||||
/// Get information about the current state of the daemon
|
||||
@ -19,6 +23,23 @@ impl DaemonControl {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a new deposit address. This will always generate a new deposit address, regardless of
|
||||
/// whether it was actually used.
|
||||
pub fn get_new_address(&self) -> bitcoin::Address {
|
||||
let mut db_conn = self.db.connection();
|
||||
let index = db_conn.derivation_index();
|
||||
// TODO: handle should we wrap around instead of failing?
|
||||
db_conn.update_derivation_index(index.increment().expect("TODO: handle wraparound"));
|
||||
self.config
|
||||
.main_descriptor
|
||||
// TODO: have a descriptor newtype along with a derived descriptor one.
|
||||
.derive(index.into())
|
||||
.translate_pk2(|xpk| xpk.derive_public_key(&self.secp))
|
||||
.expect("All pubkeys were derived, no wildcard.")
|
||||
.address(self.config.bitcoind_config.network)
|
||||
.expect("It's a wsh() descriptor")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
@ -8,6 +8,8 @@ use crate::{
|
||||
database::sqlite::{schema::DbTip, SqliteConn, SqliteDb},
|
||||
};
|
||||
|
||||
use miniscript::bitcoin::util::bip32;
|
||||
|
||||
pub trait DatabaseInterface: Send {
|
||||
fn connection(&self) -> Box<dyn DatabaseConnection>;
|
||||
}
|
||||
@ -24,6 +26,10 @@ pub trait DatabaseConnection {
|
||||
|
||||
/// Update our best chain seen.
|
||||
fn update_tip(&mut self, tip: &BlockChainTip);
|
||||
|
||||
fn derivation_index(&mut self) -> bip32::ChildNumber;
|
||||
|
||||
fn update_derivation_index(&mut self, index: bip32::ChildNumber);
|
||||
}
|
||||
|
||||
impl DatabaseConnection for SqliteConn {
|
||||
@ -41,4 +47,12 @@ impl DatabaseConnection for SqliteConn {
|
||||
fn update_tip(&mut self, tip: &BlockChainTip) {
|
||||
self.update_tip(&tip)
|
||||
}
|
||||
|
||||
fn derivation_index(&mut self) -> bip32::ChildNumber {
|
||||
self.db_wallet().deposit_derivation_index
|
||||
}
|
||||
|
||||
fn update_derivation_index(&mut self, index: bip32::ChildNumber) {
|
||||
self.update_derivation_index(index)
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,7 +19,10 @@ use crate::{
|
||||
|
||||
use std::{convert::TryInto, fmt, io, path};
|
||||
|
||||
use miniscript::{bitcoin, Descriptor, DescriptorPublicKey};
|
||||
use miniscript::{
|
||||
bitcoin::{self, util::bip32},
|
||||
Descriptor, DescriptorPublicKey,
|
||||
};
|
||||
|
||||
const DB_VERSION: i64 = 0;
|
||||
|
||||
@ -196,6 +199,21 @@ impl SqliteConn {
|
||||
})
|
||||
.expect("Database must be available")
|
||||
}
|
||||
|
||||
/// Update the deposit derivation index.
|
||||
pub fn update_derivation_index(&mut self, index: bip32::ChildNumber) {
|
||||
let new_index: u32 = index.into();
|
||||
db_exec(&mut self.conn, |db_tx| {
|
||||
// NOTE: should be updated if we ever have multi-wallet support
|
||||
db_tx
|
||||
.execute(
|
||||
"UPDATE wallets SET deposit_derivation_index = (?1)",
|
||||
rusqlite::params![new_index],
|
||||
)
|
||||
.map(|_| ())
|
||||
})
|
||||
.expect("Database must be available")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
31
src/lib.rs
31
src/lib.rs
@ -22,6 +22,8 @@ use crate::{
|
||||
|
||||
use std::{error, fmt, fs, io, path, sync};
|
||||
|
||||
use miniscript::bitcoin::secp256k1;
|
||||
|
||||
#[cfg(not(test))]
|
||||
use std::{panic, process};
|
||||
// A panic in any thread should stop the main thread, and print the panic.
|
||||
@ -146,6 +148,7 @@ pub struct DaemonControl {
|
||||
config: Config,
|
||||
bitcoin: Box<dyn BitcoinInterface>,
|
||||
db: Box<dyn DatabaseInterface>,
|
||||
secp: secp256k1::Secp256k1<secp256k1::VerifyOnly>,
|
||||
}
|
||||
|
||||
impl DaemonControl {
|
||||
@ -154,10 +157,12 @@ impl DaemonControl {
|
||||
bitcoin: Box<dyn BitcoinInterface>,
|
||||
db: Box<dyn DatabaseInterface>,
|
||||
) -> DaemonControl {
|
||||
let secp = secp256k1::Secp256k1::verification_only();
|
||||
DaemonControl {
|
||||
config,
|
||||
bitcoin,
|
||||
db,
|
||||
secp,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -246,11 +251,7 @@ impl DaemonHandle {
|
||||
);
|
||||
|
||||
// Finally, set up the API.
|
||||
let control = DaemonControl {
|
||||
config,
|
||||
bitcoin: Box::from(bitcoind),
|
||||
db: Box::from(sqlite),
|
||||
};
|
||||
let control = DaemonControl::new(config, Box::from(bitcoind), Box::from(sqlite));
|
||||
|
||||
Ok(Self {
|
||||
control,
|
||||
@ -463,7 +464,7 @@ mod tests {
|
||||
};
|
||||
|
||||
// Create a dummy config with this bitcoind
|
||||
let desc_str = "wsh(andor(pk(03b506a1dbe57b4bf48c95e0c7d417b87dd3b4349d290d2e7e9ba72c912652d80a),older(10000),pk(0295e7f5d12a2061f1fd2286cefec592dff656a19f55f4f01305d6aa56630880ce)))#39x77spy";
|
||||
let desc_str = "wsh(andor(pk(xpub68JJTXc1MWK8KLW4HGLXZBJknja7kDUJuFHnM424LbziEXsfkh1WQCiEjjHw4zLqSUm4rvhgyGkkuRowE9tCJSgt3TQB5J3SKAbZ2SdcKST/*),older(10000),pk(xpub68JJTXc1MWK8PEQozKsRatrUHXKFNkD1Cb1BuQU9Xr5moCv87anqGyXLyUd4KpnDyZgo3gz4aN1r3NiaoweFW8UutBsBbgKHzaD5HkTkifK/*)))#tk6wzexy";
|
||||
let desc = Descriptor::<DescriptorPublicKey>::from_str(desc_str).unwrap();
|
||||
let config = Config {
|
||||
bitcoind_config,
|
||||
@ -479,7 +480,21 @@ mod tests {
|
||||
let config = config.clone();
|
||||
move || {
|
||||
let handle = DaemonHandle::start(config).unwrap();
|
||||
// TODO: avoid scope creep. We should move the bitcoind-specific checks to the
|
||||
// bitcoind module, test the startup with a mocked bitcoind interface, and not test
|
||||
// commands here but in the commands module.
|
||||
let addr = handle.control.get_new_address();
|
||||
let addr2 = handle.control.get_new_address();
|
||||
assert_eq!(
|
||||
addr,
|
||||
bitcoin::Address::from_str(
|
||||
"bc1qdu9dama0pwc6fd9lj4sqzq4f728y5q2ucqyj55mfzfvuxr268zks7yajm3"
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
assert_ne!(addr, addr2);
|
||||
handle.shutdown();
|
||||
addr
|
||||
}
|
||||
});
|
||||
complete_sanity_check(&server);
|
||||
@ -490,11 +505,13 @@ mod tests {
|
||||
complete_wallet_check(&server, &wo_path);
|
||||
complete_desc_check(&server, desc_str);
|
||||
complete_sync_check(&server);
|
||||
daemon_thread.join().unwrap();
|
||||
let addr = daemon_thread.join().unwrap();
|
||||
|
||||
// The datadir is created now, so if we restart it it won't create the wo wallet.
|
||||
let daemon_thread = thread::spawn(move || {
|
||||
let handle = DaemonHandle::start(config).unwrap();
|
||||
// TODO: avoid scope creep. See above comment.
|
||||
assert_ne!(handle.control.get_new_address(), addr);
|
||||
handle.shutdown();
|
||||
});
|
||||
complete_sanity_check(&server);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user