Merge #60: Clippification
7e911b8bb7ccca866db2afadb357615d3712a4cb ci: add Clippy check for both the daemon and the GUI (Antoine Poinsot) 6871407b1a2514c8c3b259a88cd5c267b206761e gui: clippification (Antoine Poinsot) 8b4866158bc2097a14857194d38bfb058678a55b daemon: clippification (Antoine Poinsot) Pull request description: This fixes *all* clippy warning from the whole repository. Some of them are... Well very opinionated to say the least. But to avoid bikeshedding i just applied all of them. Let's start fresh! @edouardparis can you skim through the clippy fixes in the GUI folder? I checked the daemon one already. Fixes #10. ACKs for top commit: edouardparis: ACK 7e911b8bb7ccca866db2afadb357615d3712a4cb Tree-SHA512: acebc86425039511d090b58139e12c149463d7a288d0d2aed3ba75d303aa2034db65f39f7e00436d556e86aa382686acab6699e65afe10ea31238c0a48a0635c
This commit is contained in:
commit
efe2cf634d
6
.github/workflows/main.yml
vendored
6
.github/workflows/main.yml
vendored
@ -10,10 +10,12 @@ jobs:
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
components: rustfmt
|
||||
components: rustfmt, clippy
|
||||
override: true
|
||||
- name: rustfmt
|
||||
run: cargo fmt -- --check
|
||||
- name: clippy
|
||||
run: cargo clippy --all-features --all-targets -- -D warnings
|
||||
|
||||
unit_tests:
|
||||
needs: linter
|
||||
@ -54,6 +56,8 @@ jobs:
|
||||
override: true
|
||||
- name: rustfmt
|
||||
run: cd gui && cargo fmt -- --check
|
||||
- name: clippy
|
||||
run: cargo clippy --all-features --all-targets -- -D warnings
|
||||
|
||||
unit_tests_gui:
|
||||
needs: linter_gui
|
||||
|
||||
1
clippy.toml
Normal file
1
clippy.toml
Normal file
@ -0,0 +1 @@
|
||||
msrv = "1.48"
|
||||
@ -1,15 +1,7 @@
|
||||
use crate::daemon::model::Coin;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Cache {
|
||||
pub blockheight: i32,
|
||||
pub coins: Vec<Coin>,
|
||||
}
|
||||
|
||||
impl Default for Cache {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
blockheight: 0,
|
||||
coins: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,7 +102,7 @@ impl App {
|
||||
self.cache.coins = coins.clone();
|
||||
}
|
||||
Message::BlockHeight(Ok(blockheight)) => {
|
||||
self.cache.blockheight = blockheight.clone();
|
||||
self.cache.blockheight = *blockheight;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
@ -31,7 +31,7 @@ pub struct Home {
|
||||
}
|
||||
|
||||
impl Home {
|
||||
pub fn new(coins: &Vec<Coin>) -> Self {
|
||||
pub fn new(coins: &[Coin]) -> Self {
|
||||
Self {
|
||||
balance: Amount::from_sat(coins.iter().map(|coin| coin.amount.as_sat()).sum()),
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ use crate::ui::component::text::*;
|
||||
|
||||
use super::message::Message;
|
||||
|
||||
pub fn home_view<'a>(balance: &'a bitcoin::Amount) -> Element<'a, Message> {
|
||||
pub fn home_view(balance: &bitcoin::Amount) -> Element<Message> {
|
||||
column()
|
||||
.push(column().padding(40))
|
||||
.push(text(&format!("{} BTC", balance.as_btc())).bold().size(50))
|
||||
|
||||
@ -92,7 +92,7 @@ impl error::Error for Error {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
/// A JSONRPC error object
|
||||
pub struct RpcError {
|
||||
/// The integer identifier of the error
|
||||
|
||||
@ -113,7 +113,7 @@ impl JsonRPCClient {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||
/// A JSONRPC request object
|
||||
pub struct Request<'f, T: Serialize> {
|
||||
/// The name of the RPC call
|
||||
@ -126,7 +126,7 @@ pub struct Request<'f, T: Serialize> {
|
||||
pub jsonrpc: &'f str,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||
/// A JSONRPC response object
|
||||
pub struct Response<T> {
|
||||
/// A result if there is one, or null
|
||||
@ -243,7 +243,7 @@ impl From<Error> for super::DaemonError {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
/// A JSONRPC error object
|
||||
pub struct RpcError {
|
||||
/// The integer identifier of the error
|
||||
|
||||
@ -306,9 +306,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_parse_args() {
|
||||
assert_eq!(true, parse_args(vec!["--meth".into()]).is_err());
|
||||
assert_eq!(true, parse_args(vec!["--datadir".into()]).is_err());
|
||||
assert_eq!(true, parse_args(vec!["--conf".into()]).is_err());
|
||||
assert!(parse_args(vec!["--meth".into()]).is_err());
|
||||
assert!(parse_args(vec!["--datadir".into()]).is_err());
|
||||
assert!(parse_args(vec!["--conf".into()]).is_err());
|
||||
assert_eq!(
|
||||
Some(vec![
|
||||
Arg::DatadirPath(PathBuf::from(".")),
|
||||
@ -316,7 +316,7 @@ mod tests {
|
||||
]),
|
||||
parse_args(
|
||||
"--datadir . --conf hello.toml"
|
||||
.split(" ")
|
||||
.split(' ')
|
||||
.map(|a| a.to_string())
|
||||
.collect()
|
||||
)
|
||||
@ -333,7 +333,7 @@ mod tests {
|
||||
]),
|
||||
parse_args(
|
||||
"--datadir hello --testnet"
|
||||
.split(" ")
|
||||
.split(' ')
|
||||
.map(|a| a.to_string())
|
||||
.collect()
|
||||
)
|
||||
@ -346,7 +346,7 @@ mod tests {
|
||||
]),
|
||||
parse_args(
|
||||
"--testnet --datadir hello"
|
||||
.split(" ")
|
||||
.split(' ')
|
||||
.map(|a| a.to_string())
|
||||
.collect()
|
||||
)
|
||||
|
||||
@ -9,9 +9,11 @@ use std::sync::{
|
||||
};
|
||||
use std::thread;
|
||||
|
||||
type TransportReceiver = Receiver<Result<Value, DaemonError>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DaemonClient {
|
||||
transport: Mutex<(Sender<Value>, Receiver<Result<Value, DaemonError>>)>,
|
||||
transport: Mutex<(Sender<Value>, TransportReceiver)>,
|
||||
}
|
||||
|
||||
impl Client for DaemonClient {
|
||||
|
||||
@ -13,7 +13,7 @@ pub struct Sandbox<S: State> {
|
||||
|
||||
impl<S: State + Send + 'static> Sandbox<S> {
|
||||
pub fn new(state: S) -> Self {
|
||||
return Self { state };
|
||||
Self { state }
|
||||
}
|
||||
|
||||
pub fn state(&self) -> &S {
|
||||
|
||||
@ -183,7 +183,7 @@ impl BitcoinD {
|
||||
.url(&watchonly_url)
|
||||
.map_err(BitcoindError::from)?
|
||||
.timeout(Duration::from_secs(RPC_SOCKET_TIMEOUT))
|
||||
.cookie_auth(cookie_string.clone())
|
||||
.cookie_auth(cookie_string)
|
||||
.build(),
|
||||
);
|
||||
|
||||
@ -249,7 +249,7 @@ impl BitcoinD {
|
||||
let res = resp.result().map_err(BitcoindError::Server)?;
|
||||
log::trace!("Got from bitcoind: {:#?}", res);
|
||||
|
||||
return Ok(res);
|
||||
Ok(res)
|
||||
}
|
||||
Err(e) => Err(BitcoindError::Server(e)),
|
||||
}
|
||||
@ -289,16 +289,14 @@ impl BitcoinD {
|
||||
fn get_bitcoind_version(&self) -> u64 {
|
||||
self.make_node_request("getnetworkinfo", &[])
|
||||
.get("version")
|
||||
.map(Json::as_u64)
|
||||
.flatten()
|
||||
.and_then(Json::as_u64)
|
||||
.expect("Missing or invalid 'version' in 'getnetworkinfo' result?")
|
||||
}
|
||||
|
||||
fn get_network_bip70(&self) -> String {
|
||||
self.make_node_request("getblockchaininfo", &[])
|
||||
.get("chain")
|
||||
.map(Json::as_str)
|
||||
.flatten()
|
||||
.and_then(Json::as_str)
|
||||
.expect("Missing or invalid 'chain' in 'getblockchaininfo' result?")
|
||||
.to_string()
|
||||
}
|
||||
@ -341,7 +339,7 @@ impl BitcoinD {
|
||||
),
|
||||
);
|
||||
|
||||
if let Some(warning) = res.get("warning").map(Json::as_str).flatten() {
|
||||
if let Some(warning) = res.get("warning").and_then(Json::as_str) {
|
||||
if !warning.is_empty() {
|
||||
return Some(warning.to_string());
|
||||
}
|
||||
@ -365,12 +363,9 @@ impl BitcoinD {
|
||||
let all_succeeded = res
|
||||
.as_array()
|
||||
.map(|results| {
|
||||
results.iter().all(|res| {
|
||||
res.get("success")
|
||||
.map(Json::as_bool)
|
||||
.flatten()
|
||||
.unwrap_or(false)
|
||||
})
|
||||
results
|
||||
.iter()
|
||||
.all(|res| res.get("success").and_then(Json::as_bool).unwrap_or(false))
|
||||
})
|
||||
.unwrap_or(false);
|
||||
if all_succeeded {
|
||||
@ -615,7 +610,7 @@ impl BitcoinD {
|
||||
.get("txid")
|
||||
.and_then(Json::as_str)
|
||||
.expect("A valid txid must be present");
|
||||
if visited_txs.contains(&spending_txid) || &spent_txid == spending_txid {
|
||||
if visited_txs.contains(&spending_txid) || spent_txid == spending_txid {
|
||||
continue;
|
||||
} else {
|
||||
visited_txs.insert(spending_txid);
|
||||
@ -729,7 +724,7 @@ impl From<Json> for LSBlockRes {
|
||||
.get("transactions")
|
||||
.and_then(Json::as_array)
|
||||
.expect("Array must be present")
|
||||
.into_iter()
|
||||
.iter()
|
||||
.filter_map(|j| {
|
||||
if j.get("category")
|
||||
.and_then(Json::as_str)
|
||||
@ -770,7 +765,7 @@ impl From<Json> for GetTxRes {
|
||||
.and_then(Json::as_array)
|
||||
.map(|array| {
|
||||
array
|
||||
.into_iter()
|
||||
.iter()
|
||||
.map(|v| {
|
||||
bitcoin::Txid::from_str(v.as_str().expect("wrong json format")).unwrap()
|
||||
})
|
||||
|
||||
@ -130,8 +130,8 @@ impl BitcoinInterface for d::BitcoinD {
|
||||
let mut spent = Vec::with_capacity(outpoints.len());
|
||||
|
||||
for op in outpoints {
|
||||
if self.is_spent(&op) {
|
||||
let spending_txid = if let Some(txid) = self.get_spender_txid(&op) {
|
||||
if self.is_spent(op) {
|
||||
let spending_txid = if let Some(txid) = self.get_spender_txid(op) {
|
||||
txid
|
||||
} else {
|
||||
// TODO: better handling of this edge case.
|
||||
@ -180,7 +180,7 @@ impl BitcoinInterface for d::BitcoinD {
|
||||
let tx: Option<&d::GetTxRes> = match cache.get(txid) {
|
||||
Some(tx) => tx.as_ref(),
|
||||
None => {
|
||||
let tx = self.get_transaction(&txid);
|
||||
let tx = self.get_transaction(txid);
|
||||
txs_to_cache.push((*txid, tx));
|
||||
txs_to_cache.last().unwrap().1.as_ref()
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ fn update_coins(
|
||||
// Start by fetching newly received coins.
|
||||
let curr_coins = db_conn.unspent_coins();
|
||||
let mut received = Vec::new();
|
||||
for utxo in bit.received_coins(&previous_tip) {
|
||||
for utxo in bit.received_coins(previous_tip) {
|
||||
if let Some(derivation_index) = db_conn.derivation_index_by_address(&utxo.address) {
|
||||
if !curr_coins.contains_key(&utxo.outpoint) {
|
||||
let UTxO {
|
||||
@ -119,7 +119,7 @@ fn new_tip(bit: &impl BitcoinInterface, current_tip: &BlockChainTip) -> Option<B
|
||||
|
||||
if bitcoin_tip.height > current_tip.height {
|
||||
// Make sure we are on the same chain.
|
||||
if bit.is_in_chain(¤t_tip) {
|
||||
if bit.is_in_chain(current_tip) {
|
||||
// All good, we just moved forward.
|
||||
return Some(bitcoin_tip);
|
||||
}
|
||||
|
||||
@ -109,7 +109,7 @@ fn sanity_check_psbt(psbt: &Psbt) -> Result<(), CommandError> {
|
||||
value_in += psbtin
|
||||
.witness_utxo
|
||||
.as_ref()
|
||||
.ok_or(CommandError::SanityCheckFailure(psbt.clone()))?
|
||||
.ok_or_else(|| CommandError::SanityCheckFailure(psbt.clone()))?
|
||||
.value;
|
||||
}
|
||||
|
||||
@ -117,17 +117,17 @@ fn sanity_check_psbt(psbt: &Psbt) -> Result<(), CommandError> {
|
||||
let value_out: u64 = tx.output.iter().map(|o| o.value).sum();
|
||||
let abs_fee = value_in
|
||||
.checked_sub(value_out)
|
||||
.ok_or(CommandError::SanityCheckFailure(psbt.clone()))?;
|
||||
.ok_or_else(|| CommandError::SanityCheckFailure(psbt.clone()))?;
|
||||
if abs_fee > MAX_FEE {
|
||||
return Err(CommandError::SanityCheckFailure(psbt.clone()));
|
||||
}
|
||||
|
||||
// Check the feerate isn't insane.
|
||||
let tx_vb: u64 = tx_vbytes(&tx);
|
||||
let tx_vb: u64 = tx_vbytes(tx);
|
||||
let feerate_sats_vb = abs_fee
|
||||
.checked_div(tx_vb)
|
||||
.ok_or(CommandError::SanityCheckFailure(psbt.clone()))?;
|
||||
if feerate_sats_vb > MAX_FEERATE || feerate_sats_vb < 1 {
|
||||
.ok_or_else(|| CommandError::SanityCheckFailure(psbt.clone()))?;
|
||||
if !(1..=MAX_FEERATE).contains(&feerate_sats_vb) {
|
||||
return Err(CommandError::SanityCheckFailure(psbt.clone()));
|
||||
}
|
||||
|
||||
@ -188,7 +188,7 @@ impl DaemonControl {
|
||||
let address = self
|
||||
.config
|
||||
.main_descriptor
|
||||
.derive(index.into(), &self.secp)
|
||||
.derive(index, &self.secp)
|
||||
.address(self.config.bitcoin_config.network);
|
||||
GetAddressResult { address }
|
||||
}
|
||||
@ -320,7 +320,7 @@ impl DaemonControl {
|
||||
// Get the change address to create a dummy change txo.
|
||||
// TODO: decent change management
|
||||
let first_coin = coins
|
||||
.get(&coins_outpoints.get(0).expect("We checked it wasn't empty"))
|
||||
.get(coins_outpoints.get(0).expect("We checked it wasn't empty"))
|
||||
.expect("We checked they were all present");
|
||||
let coin_desc = self.derived_desc(first_coin.derivation_index);
|
||||
let mut change_txo = bitcoin::TxOut {
|
||||
@ -696,13 +696,11 @@ mod tests {
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
let destinations_c: HashMap<bitcoin::Address, u64> = [
|
||||
(dummy_addr_a.clone(), dummy_value_a),
|
||||
(dummy_addr_b.clone(), dummy_value_b),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
let destinations_c: HashMap<bitcoin::Address, u64> =
|
||||
[(dummy_addr_a, dummy_value_a), (dummy_addr_b, dummy_value_b)]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
let mut psbt_a = control
|
||||
.create_spend(&[dummy_op_a], &destinations_a, 1)
|
||||
.unwrap()
|
||||
|
||||
@ -48,11 +48,7 @@ pub fn change_index(psbt: &Psbt) -> Option<usize> {
|
||||
};
|
||||
|
||||
let tx = &psbt.global.unsigned_tx;
|
||||
for i in (0..tx.output.len()).rev() {
|
||||
if &tx.output[i].script_pubkey == first_coin_spk {
|
||||
return Some(i);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
(0..tx.output.len())
|
||||
.rev()
|
||||
.find(|&i| &tx.output[i].script_pubkey == first_coin_spk)
|
||||
}
|
||||
|
||||
@ -113,7 +113,7 @@ pub enum ConfigError {
|
||||
DatadirNotFound,
|
||||
FileNotFound,
|
||||
ReadingFile(String),
|
||||
UnexpectedDescriptor(InheritanceDescriptor),
|
||||
UnexpectedDescriptor(Box<InheritanceDescriptor>),
|
||||
Unexpected(String),
|
||||
}
|
||||
|
||||
@ -187,7 +187,7 @@ impl Config {
|
||||
/// futile duplication.
|
||||
pub fn from_file(custom_path: Option<PathBuf>) -> Result<Config, ConfigError> {
|
||||
let config_file =
|
||||
custom_path.unwrap_or(config_file_path().ok_or_else(|| ConfigError::DatadirNotFound)?);
|
||||
custom_path.unwrap_or(config_file_path().ok_or(ConfigError::DatadirNotFound)?);
|
||||
|
||||
let config = toml::from_slice::<Config>(&std::fs::read(&config_file)?)
|
||||
.map_err(|e| ConfigError::ReadingFile(format!("Parsing configuration file: {}", e)))?;
|
||||
|
||||
@ -61,16 +61,16 @@ pub trait DatabaseConnection {
|
||||
fn list_spending_coins(&mut self) -> HashMap<bitcoin::OutPoint, Coin>;
|
||||
|
||||
/// Store new UTxOs. Coins must not already be in database.
|
||||
fn new_unspent_coins<'a>(&mut self, coins: &[Coin]);
|
||||
fn new_unspent_coins(&mut self, coins: &[Coin]);
|
||||
|
||||
/// Mark a set of coins as being confirmed at a specified height and block time.
|
||||
fn confirm_coins<'a>(&mut self, outpoints: &[(bitcoin::OutPoint, i32, u32)]);
|
||||
fn confirm_coins(&mut self, outpoints: &[(bitcoin::OutPoint, i32, u32)]);
|
||||
|
||||
/// Mark a set of coins as being spent by a specified txid of a pending transaction.
|
||||
fn spend_coins<'a>(&mut self, outpoints: &[(bitcoin::OutPoint, bitcoin::Txid)]);
|
||||
fn spend_coins(&mut self, outpoints: &[(bitcoin::OutPoint, bitcoin::Txid)]);
|
||||
|
||||
/// Mark a set of coins as spent by a specified txid at a specified block time.
|
||||
fn confirm_spend<'a>(&mut self, outpoints: &[(bitcoin::OutPoint, bitcoin::Txid, u32)]);
|
||||
fn confirm_spend(&mut self, outpoints: &[(bitcoin::OutPoint, bitcoin::Txid, u32)]);
|
||||
|
||||
/// Get specific coins from the database.
|
||||
fn coins_by_outpoints(
|
||||
@ -107,7 +107,7 @@ impl DatabaseConnection for SqliteConn {
|
||||
}
|
||||
|
||||
fn update_tip(&mut self, tip: &BlockChainTip) {
|
||||
self.update_tip(&tip)
|
||||
self.update_tip(tip)
|
||||
}
|
||||
|
||||
fn derivation_index(&mut self) -> bip32::ChildNumber {
|
||||
@ -186,7 +186,7 @@ impl DatabaseConnection for SqliteConn {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Coin {
|
||||
pub outpoint: bitcoin::OutPoint,
|
||||
pub block_height: Option<i32>,
|
||||
@ -221,12 +221,6 @@ impl std::convert::From<DbCoin> for Coin {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::hash::Hash for Coin {
|
||||
fn hash<H: std::hash::Hasher>(&self, h: &mut H) {
|
||||
self.outpoint.hash(h)
|
||||
}
|
||||
}
|
||||
|
||||
impl Coin {
|
||||
pub fn is_confirmed(&self) -> bool {
|
||||
self.block_height.is_some()
|
||||
|
||||
@ -36,7 +36,7 @@ pub enum SqliteDbError {
|
||||
FileNotFound(path::PathBuf),
|
||||
UnsupportedVersion(i64),
|
||||
InvalidNetwork(bitcoin::Network),
|
||||
DescriptorMismatch(InheritanceDescriptor),
|
||||
DescriptorMismatch(Box<InheritanceDescriptor>),
|
||||
Rusqlite(rusqlite::Error),
|
||||
}
|
||||
|
||||
@ -102,7 +102,7 @@ impl SqliteDb {
|
||||
log::info!("Created a fresh database at {}.", db_path.display());
|
||||
}
|
||||
if !db_path.exists() {
|
||||
return Err(SqliteDbError::FileNotFound(db_path.to_path_buf()));
|
||||
return Err(SqliteDbError::FileNotFound(db_path));
|
||||
}
|
||||
|
||||
Ok(SqliteDb { db_path })
|
||||
@ -139,7 +139,9 @@ impl SqliteDb {
|
||||
// The config and db descriptors must match!
|
||||
let db_wallet = conn.db_wallet();
|
||||
if &db_wallet.main_descriptor != main_descriptor {
|
||||
return Err(SqliteDbError::DescriptorMismatch(db_wallet.main_descriptor));
|
||||
return Err(SqliteDbError::DescriptorMismatch(
|
||||
db_wallet.main_descriptor.into(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -215,15 +217,13 @@ impl SqliteConn {
|
||||
let network = self.db_tip().network;
|
||||
|
||||
db_exec(&mut self.conn, |db_tx| {
|
||||
let db_wallet: DbWallet = db_tx_query(
|
||||
&db_tx,
|
||||
"SELECT * FROM wallets",
|
||||
rusqlite::params![],
|
||||
|row| row.try_into(),
|
||||
)
|
||||
.expect("Db must not fail")
|
||||
.pop()
|
||||
.expect("There is always a row in the wallet table");
|
||||
let db_wallet: DbWallet =
|
||||
db_tx_query(db_tx, "SELECT * FROM wallets", rusqlite::params![], |row| {
|
||||
row.try_into()
|
||||
})
|
||||
.expect("Db must not fail")
|
||||
.pop()
|
||||
.expect("There is always a row in the wallet table");
|
||||
let next_index: u32 = db_wallet
|
||||
.deposit_derivation_index
|
||||
.increment()
|
||||
@ -240,7 +240,7 @@ impl SqliteConn {
|
||||
let next_la_index = next_index + LOOK_AHEAD_LIMIT - 1;
|
||||
let next_la_address = db_wallet
|
||||
.main_descriptor
|
||||
.derive(next_la_index.into(), &secp)
|
||||
.derive(next_la_index.into(), secp)
|
||||
.address(network);
|
||||
db_tx
|
||||
.execute(
|
||||
@ -470,7 +470,7 @@ mod tests {
|
||||
.iter()
|
||||
.collect();
|
||||
let options = dummy_options();
|
||||
let db = SqliteDb::new(db_path.clone(), Some(options.clone()), &secp).unwrap();
|
||||
let db = SqliteDb::new(db_path, Some(options.clone()), &secp).unwrap();
|
||||
|
||||
(tmp_dir, options, secp, db)
|
||||
}
|
||||
@ -510,7 +510,7 @@ mod tests {
|
||||
let db = SqliteDb::new(db_path.clone(), Some(options.clone()), &secp).unwrap();
|
||||
db.sanity_check(bitcoin::Network::Bitcoin, &options.main_descriptor)
|
||||
.unwrap();
|
||||
let db = SqliteDb::new(db_path.clone(), None, &secp).unwrap();
|
||||
let db = SqliteDb::new(db_path, None, &secp).unwrap();
|
||||
db.sanity_check(bitcoin::Network::Bitcoin, &options.main_descriptor)
|
||||
.unwrap();
|
||||
|
||||
@ -607,14 +607,8 @@ mod tests {
|
||||
assert_eq!(coins[0].outpoint, coin_b.outpoint);
|
||||
let coins = conn.db_coins(&[coin_a.outpoint, coin_b.outpoint]);
|
||||
assert_eq!(coins.len(), 2);
|
||||
assert!(coins
|
||||
.iter()
|
||||
.find(|c| c.outpoint == coin_a.outpoint)
|
||||
.is_some());
|
||||
assert!(coins
|
||||
.iter()
|
||||
.find(|c| c.outpoint == coin_b.outpoint)
|
||||
.is_some());
|
||||
assert!(coins.iter().any(|c| c.outpoint == coin_a.outpoint));
|
||||
assert!(coins.iter().any(|c| c.outpoint == coin_b.outpoint));
|
||||
|
||||
// Now if we confirm one, it'll be marked as such.
|
||||
let height = 174500;
|
||||
|
||||
@ -126,7 +126,7 @@ impl TryFrom<&rusqlite::Row<'_>> for DbWallet {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct DbCoin {
|
||||
pub id: i64,
|
||||
pub wallet_id: i64,
|
||||
@ -139,12 +139,6 @@ pub struct DbCoin {
|
||||
pub spent_at: Option<u32>,
|
||||
}
|
||||
|
||||
impl std::hash::Hash for DbCoin {
|
||||
fn hash<H: std::hash::Hasher>(&self, h: &mut H) {
|
||||
self.outpoint.hash(h)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&rusqlite::Row<'_>> for DbCoin {
|
||||
type Error = rusqlite::Error;
|
||||
|
||||
|
||||
@ -28,13 +28,9 @@ where
|
||||
P::Item: rusqlite::ToSql,
|
||||
F: FnMut(&rusqlite::Row<'_>) -> rusqlite::Result<T>,
|
||||
{
|
||||
// rustc says 'borrowed value does not live long enough'
|
||||
let x = tx
|
||||
.prepare(stmt_str)?
|
||||
tx.prepare(stmt_str)?
|
||||
.query_map(params, f)?
|
||||
.collect::<rusqlite::Result<Vec<T>>>();
|
||||
|
||||
x
|
||||
.collect::<rusqlite::Result<Vec<T>>>()
|
||||
}
|
||||
|
||||
/// Internal helper for queries boilerplate
|
||||
@ -49,13 +45,9 @@ where
|
||||
P::Item: rusqlite::ToSql,
|
||||
F: FnMut(&rusqlite::Row<'_>) -> rusqlite::Result<T>,
|
||||
{
|
||||
// rustc says 'borrowed value does not live long enough'
|
||||
let x = conn
|
||||
.prepare(stmt_str)?
|
||||
conn.prepare(stmt_str)?
|
||||
.query_map(params, f)?
|
||||
.collect::<rusqlite::Result<Vec<T>>>();
|
||||
|
||||
x
|
||||
.collect::<rusqlite::Result<Vec<T>>>()
|
||||
}
|
||||
|
||||
// Sqlite supports up to i64, thus rusqlite prevents us from inserting u64's.
|
||||
|
||||
@ -106,7 +106,7 @@ impl str::FromStr for DerivedPublicKey {
|
||||
return Err(DescCreationError::DerivedKeyParsing);
|
||||
}
|
||||
|
||||
let key = bitcoin::PublicKey::from_str(&key_str)
|
||||
let key = bitcoin::PublicKey::from_str(key_str)
|
||||
.map_err(|_| DescCreationError::DerivedKeyParsing)?;
|
||||
|
||||
Ok(DerivedPublicKey {
|
||||
@ -434,31 +434,31 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn inheritance_descriptor_creation() {
|
||||
let owner_key = descriptor::DescriptorPublicKey::from_str(&"xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/*").unwrap();
|
||||
let heir_key = descriptor::DescriptorPublicKey::from_str(&"xpub688Hn4wScQAAiYJLPg9yH27hUpfZAUnmJejRQBCiwfP5PEDzjWMNW1wChcninxr5gyavFqbbDjdV1aK5USJz8NDVjUy7FRQaaqqXHh5SbXe/*").unwrap();
|
||||
let owner_key = descriptor::DescriptorPublicKey::from_str("xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/*").unwrap();
|
||||
let heir_key = descriptor::DescriptorPublicKey::from_str("xpub688Hn4wScQAAiYJLPg9yH27hUpfZAUnmJejRQBCiwfP5PEDzjWMNW1wChcninxr5gyavFqbbDjdV1aK5USJz8NDVjUy7FRQaaqqXHh5SbXe/*").unwrap();
|
||||
let timelock = 52560;
|
||||
assert_eq!(InheritanceDescriptor::new(owner_key.clone(), heir_key.clone(), timelock).unwrap().to_string(), "wsh(or_d(pk(xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/*),and_v(v:pkh(xpub688Hn4wScQAAiYJLPg9yH27hUpfZAUnmJejRQBCiwfP5PEDzjWMNW1wChcninxr5gyavFqbbDjdV1aK5USJz8NDVjUy7FRQaaqqXHh5SbXe/*),older(52560))))#eeyujkt7");
|
||||
|
||||
// We prevent footguns with timelocks
|
||||
InheritanceDescriptor::new(owner_key.clone(), heir_key.clone(), 0x00_01_0f_00).unwrap_err();
|
||||
InheritanceDescriptor::new(owner_key.clone(), heir_key.clone(), (1 << 31) + 1).unwrap_err();
|
||||
InheritanceDescriptor::new(owner_key.clone(), heir_key.clone(), (1 << 22) + 1).unwrap_err();
|
||||
InheritanceDescriptor::new(owner_key, heir_key, (1 << 22) + 1).unwrap_err();
|
||||
|
||||
let owner_key = descriptor::DescriptorPublicKey::from_str(&"[aabb0011/10/4893]xpub661MyMwAqRbcFG59fiikD8UV762quhruT8K8bdjqy6N2o3LG7yohoCdLg1m2HAY1W6rfBrtauHkBhbfA4AQ3iazaJj5wVPhwgaRCHBW2DBg/*").unwrap();
|
||||
let heir_key = descriptor::DescriptorPublicKey::from_str(&"xpub661MyMwAqRbcFfxf71L4Dx4w5TmyNXrBicTEAM7vLzumxangwATWWgdJPb6xH1JHcJH9S3jNZx3fCnkkB1WyqrqGgavj1rehHcbythmruvZ/24/32/*").unwrap();
|
||||
let owner_key = descriptor::DescriptorPublicKey::from_str("[aabb0011/10/4893]xpub661MyMwAqRbcFG59fiikD8UV762quhruT8K8bdjqy6N2o3LG7yohoCdLg1m2HAY1W6rfBrtauHkBhbfA4AQ3iazaJj5wVPhwgaRCHBW2DBg/*").unwrap();
|
||||
let heir_key = descriptor::DescriptorPublicKey::from_str("xpub661MyMwAqRbcFfxf71L4Dx4w5TmyNXrBicTEAM7vLzumxangwATWWgdJPb6xH1JHcJH9S3jNZx3fCnkkB1WyqrqGgavj1rehHcbythmruvZ/24/32/*").unwrap();
|
||||
let timelock = 57600;
|
||||
assert_eq!(InheritanceDescriptor::new(owner_key.clone(), heir_key, timelock).unwrap().to_string(), "wsh(or_d(pk([aabb0011/10/4893]xpub661MyMwAqRbcFG59fiikD8UV762quhruT8K8bdjqy6N2o3LG7yohoCdLg1m2HAY1W6rfBrtauHkBhbfA4AQ3iazaJj5wVPhwgaRCHBW2DBg/*),and_v(v:pkh(xpub661MyMwAqRbcFfxf71L4Dx4w5TmyNXrBicTEAM7vLzumxangwATWWgdJPb6xH1JHcJH9S3jNZx3fCnkkB1WyqrqGgavj1rehHcbythmruvZ/24/32/*),older(57600))))#8kamh6y8");
|
||||
|
||||
// We can't pass a raw key, an xpub that is not deriveable, or only hardened derivable
|
||||
let heir_key = descriptor::DescriptorPublicKey::from_str(&"xpub661MyMwAqRbcFfxf71L4Dx4w5TmyNXrBicTEAM7vLzumxangwATWWgdJPb6xH1JHcJH9S3jNZx3fCnkkB1WyqrqGgavj1rehHcbythmruvZ/0/354").unwrap();
|
||||
let heir_key = descriptor::DescriptorPublicKey::from_str("xpub661MyMwAqRbcFfxf71L4Dx4w5TmyNXrBicTEAM7vLzumxangwATWWgdJPb6xH1JHcJH9S3jNZx3fCnkkB1WyqrqGgavj1rehHcbythmruvZ/0/354").unwrap();
|
||||
InheritanceDescriptor::new(owner_key.clone(), heir_key, timelock).unwrap_err();
|
||||
let heir_key = descriptor::DescriptorPublicKey::from_str(&"xpub661MyMwAqRbcFfxf71L4Dx4w5TmyNXrBicTEAM7vLzumxangwATWWgdJPb6xH1JHcJH9S3jNZx3fCnkkB1WyqrqGgavj1rehHcbythmruvZ/0/*'").unwrap();
|
||||
let heir_key = descriptor::DescriptorPublicKey::from_str("xpub661MyMwAqRbcFfxf71L4Dx4w5TmyNXrBicTEAM7vLzumxangwATWWgdJPb6xH1JHcJH9S3jNZx3fCnkkB1WyqrqGgavj1rehHcbythmruvZ/0/*'").unwrap();
|
||||
InheritanceDescriptor::new(owner_key.clone(), heir_key, timelock).unwrap_err();
|
||||
let heir_key = descriptor::DescriptorPublicKey::from_str(
|
||||
&"02e24913be26dbcfdf8e8e94870b28725cdae09b448b6c127767bf0154e3a3c8e5",
|
||||
"02e24913be26dbcfdf8e8e94870b28725cdae09b448b6c127767bf0154e3a3c8e5",
|
||||
)
|
||||
.unwrap();
|
||||
InheritanceDescriptor::new(owner_key.clone(), heir_key, timelock).unwrap_err();
|
||||
InheritanceDescriptor::new(owner_key, heir_key, timelock).unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -10,38 +10,38 @@ use miniscript::bitcoin::{self, consensus, util::psbt::PartiallySignedTransactio
|
||||
fn create_spend(control: &DaemonControl, params: Params) -> Result<serde_json::Value, Error> {
|
||||
let outpoints = params
|
||||
.get(0, "outpoints")
|
||||
.ok_or(Error::invalid_params("Missing 'outpoints' parameter."))?
|
||||
.ok_or_else(|| Error::invalid_params("Missing 'outpoints' parameter."))?
|
||||
.as_array()
|
||||
.and_then(|arr| {
|
||||
arr.into_iter()
|
||||
arr.iter()
|
||||
.map(|entry| {
|
||||
entry
|
||||
.as_str()
|
||||
.and_then(|e| bitcoin::OutPoint::from_str(&e).ok())
|
||||
.and_then(|e| bitcoin::OutPoint::from_str(e).ok())
|
||||
})
|
||||
.collect::<Option<Vec<bitcoin::OutPoint>>>()
|
||||
})
|
||||
.ok_or(Error::invalid_params("Invalid 'outpoints' parameter."))?;
|
||||
.ok_or_else(|| Error::invalid_params("Invalid 'outpoints' parameter."))?;
|
||||
let destinations = params
|
||||
.get(1, "destinations")
|
||||
.ok_or(Error::invalid_params("Missing 'destinations' parameter."))?
|
||||
.ok_or_else(|| Error::invalid_params("Missing 'destinations' parameter."))?
|
||||
.as_object()
|
||||
.and_then(|obj| {
|
||||
obj.into_iter()
|
||||
.map(|(k, v)| {
|
||||
let addr = bitcoin::Address::from_str(&k).ok()?;
|
||||
let addr = bitcoin::Address::from_str(k).ok()?;
|
||||
let amount: u64 = v.as_i64()?.try_into().ok()?;
|
||||
Some((addr, amount))
|
||||
})
|
||||
.collect::<Option<HashMap<bitcoin::Address, u64>>>()
|
||||
})
|
||||
.ok_or(Error::invalid_params("Invalid 'destinations' parameter."))?;
|
||||
.ok_or_else(|| Error::invalid_params("Invalid 'destinations' parameter."))?;
|
||||
let feerate: u64 = params
|
||||
.get(2, "feerate")
|
||||
.ok_or(Error::invalid_params("Missing 'feerate' parameter."))?
|
||||
.ok_or_else(|| Error::invalid_params("Missing 'feerate' parameter."))?
|
||||
.as_i64()
|
||||
.and_then(|i| i.try_into().ok())
|
||||
.ok_or(Error::invalid_params("Invalid 'feerate' parameter."))?;
|
||||
.ok_or_else(|| Error::invalid_params("Invalid 'feerate' parameter."))?;
|
||||
|
||||
let res = control.create_spend(&outpoints, &destinations, feerate)?;
|
||||
Ok(serde_json::json!(&res))
|
||||
@ -50,11 +50,11 @@ fn create_spend(control: &DaemonControl, params: Params) -> Result<serde_json::V
|
||||
fn update_spend(control: &DaemonControl, params: Params) -> Result<serde_json::Value, Error> {
|
||||
let psbt: Psbt = params
|
||||
.get(0, "psbt")
|
||||
.ok_or(Error::invalid_params("Missing 'psbt' parameter."))?
|
||||
.ok_or_else(|| Error::invalid_params("Missing 'psbt' parameter."))?
|
||||
.as_str()
|
||||
.and_then(|s| base64::decode(&s).ok())
|
||||
.and_then(|bytes| consensus::deserialize(&bytes).ok())
|
||||
.ok_or(Error::invalid_params("Invalid 'feerate' parameter."))?;
|
||||
.ok_or_else(|| Error::invalid_params("Invalid 'feerate' parameter."))?;
|
||||
control.update_spend(psbt)?;
|
||||
|
||||
Ok(serde_json::json!({}))
|
||||
@ -63,10 +63,10 @@ fn update_spend(control: &DaemonControl, params: Params) -> Result<serde_json::V
|
||||
fn delete_spend(control: &DaemonControl, params: Params) -> Result<serde_json::Value, Error> {
|
||||
let txid = params
|
||||
.get(0, "txid")
|
||||
.ok_or(Error::invalid_params("Missing 'txid' parameter."))?
|
||||
.ok_or_else(|| Error::invalid_params("Missing 'txid' parameter."))?
|
||||
.as_str()
|
||||
.and_then(|s| bitcoin::Txid::from_str(&s).ok())
|
||||
.ok_or(Error::invalid_params("Invalid 'feerate' parameter."))?;
|
||||
.and_then(|s| bitcoin::Txid::from_str(s).ok())
|
||||
.ok_or_else(|| Error::invalid_params("Invalid 'feerate' parameter."))?;
|
||||
control.delete_spend(&txid);
|
||||
|
||||
Ok(serde_json::json!({}))
|
||||
@ -76,15 +76,17 @@ fn delete_spend(control: &DaemonControl, params: Params) -> Result<serde_json::V
|
||||
pub fn handle_request(control: &DaemonControl, req: Request) -> Result<Response, Error> {
|
||||
let result = match req.method.as_str() {
|
||||
"createspend" => {
|
||||
let params = req.params.ok_or(Error::invalid_params(
|
||||
"Missing 'outpoints', 'destinations' and 'feerate' parameters.",
|
||||
))?;
|
||||
let params = req.params.ok_or_else(|| {
|
||||
Error::invalid_params(
|
||||
"Missing 'outpoints', 'destinations' and 'feerate' parameters.",
|
||||
)
|
||||
})?;
|
||||
create_spend(control, params)?
|
||||
}
|
||||
"delspendtx" => {
|
||||
let params = req
|
||||
.params
|
||||
.ok_or(Error::invalid_params("Missing 'txid' parameter."))?;
|
||||
.ok_or_else(|| Error::invalid_params("Missing 'txid' parameter."))?;
|
||||
delete_spend(control, params)?
|
||||
}
|
||||
"getinfo" => serde_json::json!(&control.get_info()),
|
||||
@ -95,7 +97,7 @@ pub fn handle_request(control: &DaemonControl, req: Request) -> Result<Response,
|
||||
"updatespend" => {
|
||||
let params = req
|
||||
.params
|
||||
.ok_or(Error::invalid_params("Missing 'psbt' parameter."))?;
|
||||
.ok_or_else(|| Error::invalid_params("Missing 'psbt' parameter."))?;
|
||||
update_spend(control, params)?
|
||||
}
|
||||
_ => {
|
||||
|
||||
@ -7,7 +7,7 @@ use std::{error, fmt};
|
||||
|
||||
use serde::{self, Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[serde(untagged)]
|
||||
pub enum Params {
|
||||
@ -29,7 +29,7 @@ impl Params {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[serde(untagged)]
|
||||
pub enum ReqId {
|
||||
@ -38,7 +38,7 @@ pub enum ReqId {
|
||||
}
|
||||
|
||||
/// A JSONRPC2 request. See https://www.jsonrpc.org/specification#request_object.
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Request {
|
||||
/// Version. Must be "2.0".
|
||||
@ -52,7 +52,7 @@ pub struct Request {
|
||||
}
|
||||
|
||||
/// JSONRPC2 error codes. See https://www.jsonrpc.org/specification#error_object.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum ErrorCode {
|
||||
/// The method does not exist / is not available.
|
||||
MethodNotFound,
|
||||
@ -64,9 +64,9 @@ pub enum ErrorCode {
|
||||
ServerError(i64),
|
||||
}
|
||||
|
||||
impl Into<i64> for &ErrorCode {
|
||||
fn into(self) -> i64 {
|
||||
match self {
|
||||
impl From<&ErrorCode> for i64 {
|
||||
fn from(code: &ErrorCode) -> i64 {
|
||||
match code {
|
||||
ErrorCode::MethodNotFound => -32601,
|
||||
ErrorCode::InvalidParams => -32602,
|
||||
ErrorCode::InternalError => -32603,
|
||||
@ -105,7 +105,7 @@ impl Serialize for ErrorCode {
|
||||
}
|
||||
|
||||
/// JSONRPC2 error response. See https://www.jsonrpc.org/specification#error_object.
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Error {
|
||||
pub code: ErrorCode,
|
||||
@ -164,7 +164,7 @@ impl From<commands::CommandError> for Error {
|
||||
}
|
||||
|
||||
/// JSONRPC2 response. See https://www.jsonrpc.org/specification#response_object.
|
||||
#[derive(Clone, Debug, PartialEq, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Response {
|
||||
/// Version. Must be "2.0".
|
||||
|
||||
@ -196,7 +196,9 @@ pub fn rpcserver_setup(socket_path: &path::Path) -> Result<net::UnixListener, io
|
||||
// Create the socket with RW permissions only for the user
|
||||
#[cfg(not(test))]
|
||||
let old_umask = unsafe { libc::umask(0o177) };
|
||||
let listener = bind(&socket_path);
|
||||
#[allow(clippy::all)]
|
||||
let listener = bind(socket_path);
|
||||
|
||||
#[cfg(not(test))]
|
||||
unsafe {
|
||||
libc::umask(old_umask);
|
||||
@ -249,7 +251,7 @@ mod tests {
|
||||
fn write_messages(socket_path: &path::Path, messages: &[&[u8]]) {
|
||||
let mut client = net::UnixStream::connect(&socket_path).unwrap();
|
||||
for mess in messages {
|
||||
client.write_all(&mess).unwrap();
|
||||
client.write_all(mess).unwrap();
|
||||
// Simulate throttling, this mimics real conditions and actually triggered a crash.
|
||||
thread::sleep(time::Duration::from_millis(50));
|
||||
}
|
||||
@ -305,7 +307,7 @@ mod tests {
|
||||
let t = read_one_command(&socket_path);
|
||||
let req = br#"{"jsonrpc": "2.0", "id": 0, "method": "test", "params": ["a", 10]}"#;
|
||||
let parsed_req: Request = serde_json::from_slice(req).unwrap();
|
||||
let tmp: Vec<Vec<u8>> = req.into_iter().map(|c| vec![*c]).collect();
|
||||
let tmp: Vec<Vec<u8>> = req.iter().map(|c| vec![*c]).collect();
|
||||
let mut to_send: Vec<&[u8]> = tmp.iter().map(|v| v.as_slice()).collect();
|
||||
to_send.push(b"\n");
|
||||
write_messages(&socket_path, &to_send);
|
||||
|
||||
12
src/lib.rs
12
src/lib.rs
@ -497,7 +497,7 @@ mod tests {
|
||||
}
|
||||
|
||||
// Send them a response to 'listwallets' with the watchonly wallet path
|
||||
fn complete_wallet_check<'a>(server: &net::TcpListener, watchonly_wallet_path: &'a str) {
|
||||
fn complete_wallet_check(server: &net::TcpListener, watchonly_wallet_path: &str) {
|
||||
let net_resp = [
|
||||
"HTTP/1.1 200\n\r\n{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":[\"".as_bytes(),
|
||||
watchonly_wallet_path.as_bytes(),
|
||||
@ -511,7 +511,7 @@ mod tests {
|
||||
}
|
||||
|
||||
// Send them a response to 'listdescriptors' with the main descriptor
|
||||
fn complete_desc_check<'a>(server: &net::TcpListener, desc: &'a str) {
|
||||
fn complete_desc_check(server: &net::TcpListener, desc: &str) {
|
||||
let net_resp = [
|
||||
"HTTP/1.1 200\n\r\n{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"descriptors\":[{\"desc\":\"".as_bytes(),
|
||||
desc.as_bytes(),
|
||||
@ -525,7 +525,7 @@ mod tests {
|
||||
}
|
||||
|
||||
// Send them a response to 'getblockhash' with the genesis block hash
|
||||
fn complete_tip_init<'a>(server: &net::TcpListener) {
|
||||
fn complete_tip_init(server: &net::TcpListener) {
|
||||
let net_resp = [
|
||||
"HTTP/1.1 200\n\r\n{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":\"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f\"}\n".as_bytes(),
|
||||
]
|
||||
@ -537,7 +537,7 @@ mod tests {
|
||||
}
|
||||
|
||||
// Send them a response to 'getblockchaininfo' saying we are far from being synced
|
||||
fn complete_sync_check<'a>(server: &net::TcpListener) {
|
||||
fn complete_sync_check(server: &net::TcpListener) {
|
||||
let net_resp = [
|
||||
"HTTP/1.1 200\n\r\n{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"verificationprogress\":0.1}}\n".as_bytes(),
|
||||
]
|
||||
@ -590,7 +590,7 @@ mod tests {
|
||||
};
|
||||
let bitcoind_config = BitcoindConfig {
|
||||
addr,
|
||||
cookie_path: cookie.clone(),
|
||||
cookie_path: cookie,
|
||||
};
|
||||
|
||||
// Create a dummy config with this bitcoind
|
||||
@ -599,7 +599,7 @@ mod tests {
|
||||
let config = Config {
|
||||
bitcoin_config,
|
||||
bitcoind_config: Some(bitcoind_config),
|
||||
data_dir: Some(data_dir.clone()),
|
||||
data_dir: Some(data_dir),
|
||||
#[cfg(unix)]
|
||||
daemon: false,
|
||||
log_level: log::LevelFilter::Debug,
|
||||
|
||||
@ -82,6 +82,12 @@ impl DummyDb {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DummyDb {
|
||||
fn default() -> DummyDb {
|
||||
DummyDb::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl DatabaseInterface for sync::Arc<sync::RwLock<DummyDb>> {
|
||||
fn connection(&self) -> Box<dyn DatabaseConnection> {
|
||||
Box::new(DummyDbConn { db: self.clone() })
|
||||
@ -121,8 +127,8 @@ impl DatabaseConnection for DummyDbConn {
|
||||
fn list_spending_coins(&mut self) -> HashMap<bitcoin::OutPoint, Coin> {
|
||||
let mut result = HashMap::new();
|
||||
for (k, v) in self.db.read().unwrap().coins.iter() {
|
||||
if !v.spend_txid.is_none() {
|
||||
result.insert(k.clone(), v.clone());
|
||||
if v.spend_txid.is_some() {
|
||||
result.insert(*k, v.clone());
|
||||
}
|
||||
}
|
||||
result
|
||||
@ -163,7 +169,7 @@ impl DatabaseConnection for DummyDbConn {
|
||||
for (op, spend_txid, time) in outpoints {
|
||||
let mut db = self.db.write().unwrap();
|
||||
let spent = &mut db.coins.get_mut(op).unwrap();
|
||||
assert!(!spent.spend_txid.is_none());
|
||||
assert!(spent.spend_txid.is_some());
|
||||
assert!(spent.spent_at.is_none());
|
||||
spent.spend_txid = Some(*spend_txid);
|
||||
spent.spent_at = Some(*time);
|
||||
@ -185,7 +191,7 @@ impl DatabaseConnection for DummyDbConn {
|
||||
.coins
|
||||
.clone()
|
||||
.into_iter()
|
||||
.filter(|(op, _)| outpoints.contains(&op))
|
||||
.filter(|(op, _)| outpoints.contains(op))
|
||||
.collect()
|
||||
}
|
||||
|
||||
@ -260,7 +266,7 @@ impl DummyMinisafe {
|
||||
let config = Config {
|
||||
bitcoin_config,
|
||||
bitcoind_config: None,
|
||||
data_dir: Some(data_dir.clone()),
|
||||
data_dir: Some(data_dir),
|
||||
#[cfg(unix)]
|
||||
daemon: false,
|
||||
log_level: log::LevelFilter::Debug,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user