let mut to_check_later = vec![];
for (i, output) in tx.output.iter().enumerate() {
// this output is ours, we have a path to derive it
- if let Some((script_type, path)) =
+ if let Some((script_type, child)) =
database.get_path_from_script_pubkey(&output.script_pubkey)?
{
debug!("{} output #{} is mine, adding utxo", txid, i);
}
// derive as many change addrs as external addresses that we've seen
- if script_type == ScriptType::Internal
- && u32::from(path.as_ref()[0]) > *change_max_deriv
- {
- *change_max_deriv = u32::from(path.as_ref()[0]);
+ if script_type == ScriptType::Internal && child > *change_max_deriv {
+ *change_max_deriv = child;
}
}
}
-use std::convert::{From, TryInto};
+use std::convert::TryInto;
use sled::{Batch, Tree};
use bitcoin::consensus::encode::{deserialize, serialize};
use bitcoin::hash_types::Txid;
-use bitcoin::util::bip32::{ChildNumber, DerivationPath};
use bitcoin::{OutPoint, Script, Transaction};
use crate::database::memory::MapKey;
macro_rules! impl_batch_operations {
( { $($after_insert:tt)* }, $process_delete:ident ) => {
- fn set_script_pubkey<P: AsRef<[ChildNumber]>>(&mut self, script: &Script, script_type: ScriptType, path: &P) -> Result<(), Error> {
- let deriv_path = DerivationPath::from(path.as_ref());
- let key = MapKey::Path((Some(script_type), Some(&deriv_path))).as_map_key();
+ fn set_script_pubkey(&mut self, script: &Script, script_type: ScriptType, path: u32) -> Result<(), Error> {
+ let key = MapKey::Path((Some(script_type), Some(path))).as_map_key();
self.insert(key, serialize(script))$($after_insert)*;
let key = MapKey::Script(Some(script)).as_map_key();
let value = json!({
"t": script_type,
- "p": deriv_path,
+ "p": path,
});
self.insert(key, serde_json::to_vec(&value)?)$($after_insert)*;
Ok(())
}
- fn del_script_pubkey_from_path<P: AsRef<[ChildNumber]>>(&mut self, script_type: ScriptType, path: &P) -> Result<Option<Script>, Error> {
- let deriv_path = DerivationPath::from(path.as_ref());
- let key = MapKey::Path((Some(script_type), Some(&deriv_path))).as_map_key();
+ fn del_script_pubkey_from_path(&mut self, script_type: ScriptType, path: u32) -> Result<Option<Script>, Error> {
+ let key = MapKey::Path((Some(script_type), Some(path))).as_map_key();
let res = self.remove(key);
let res = $process_delete!(res);
Ok(res.map_or(Ok(None), |x| Some(deserialize(&x)).transpose())?)
}
- fn del_path_from_script_pubkey(&mut self, script: &Script) -> Result<Option<(ScriptType, DerivationPath)>, Error> {
+ fn del_path_from_script_pubkey(&mut self, script: &Script) -> Result<Option<(ScriptType, u32)>, Error> {
let key = MapKey::Script(Some(script)).as_map_key();
let res = self.remove(key);
let res = $process_delete!(res);
.collect()
}
- fn get_script_pubkey_from_path<P: AsRef<[ChildNumber]>>(
+ fn get_script_pubkey_from_path(
&self,
script_type: ScriptType,
- path: &P,
+ path: u32,
) -> Result<Option<Script>, Error> {
- let deriv_path = DerivationPath::from(path.as_ref());
- let key = MapKey::Path((Some(script_type), Some(&deriv_path))).as_map_key();
+ let key = MapKey::Path((Some(script_type), Some(path))).as_map_key();
Ok(self.get(key)?.map(|b| deserialize(&b)).transpose()?)
}
fn get_path_from_script_pubkey(
&self,
script: &Script,
- ) -> Result<Option<(ScriptType, DerivationPath)>, Error> {
+ ) -> Result<Option<(ScriptType, u32)>, Error> {
let key = MapKey::Script(Some(script)).as_map_key();
self.get(key)?
.map(|b| -> Result<_, Error> {
let script = Script::from(
Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
);
- let path = DerivationPath::from_str("m/0/1/2/3").unwrap();
+ let path = 42;
let script_type = ScriptType::External;
- tree.set_script_pubkey(&script, script_type, &path).unwrap();
+ tree.set_script_pubkey(&script, script_type, path).unwrap();
assert_eq!(
- tree.get_script_pubkey_from_path(script_type, &path)
- .unwrap(),
+ tree.get_script_pubkey_from_path(script_type, path).unwrap(),
Some(script.clone())
);
assert_eq!(
let script = Script::from(
Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
);
- let path = DerivationPath::from_str("m/0/1/2/3").unwrap();
+ let path = 42;
let script_type = ScriptType::External;
- batch
- .set_script_pubkey(&script, script_type, &path)
- .unwrap();
+ batch.set_script_pubkey(&script, script_type, path).unwrap();
assert_eq!(
- tree.get_script_pubkey_from_path(script_type, &path)
- .unwrap(),
+ tree.get_script_pubkey_from_path(script_type, path).unwrap(),
None
);
assert_eq!(tree.get_path_from_script_pubkey(&script).unwrap(), None);
tree.commit_batch(batch).unwrap();
assert_eq!(
- tree.get_script_pubkey_from_path(script_type, &path)
- .unwrap(),
+ tree.get_script_pubkey_from_path(script_type, path).unwrap(),
Some(script.clone())
);
assert_eq!(
let script = Script::from(
Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
);
- let path = DerivationPath::from_str("m/0/1/2/3").unwrap();
+ let path = 42;
let script_type = ScriptType::External;
- tree.set_script_pubkey(&script, script_type, &path).unwrap();
+ tree.set_script_pubkey(&script, script_type, path).unwrap();
assert_eq!(tree.iter_script_pubkeys(None).unwrap().len(), 1);
}
let script = Script::from(
Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
);
- let path = DerivationPath::from_str("m/0/1/2/3").unwrap();
+ let path = 42;
let script_type = ScriptType::External;
- tree.set_script_pubkey(&script, script_type, &path).unwrap();
+ tree.set_script_pubkey(&script, script_type, path).unwrap();
assert_eq!(tree.iter_script_pubkeys(None).unwrap().len(), 1);
- tree.del_script_pubkey_from_path(script_type, &path)
- .unwrap();
+ tree.del_script_pubkey_from_path(script_type, path).unwrap();
assert_eq!(tree.iter_script_pubkeys(None).unwrap().len(), 0);
}
use bitcoin::consensus::encode::{deserialize, serialize};
use bitcoin::hash_types::Txid;
-use bitcoin::util::bip32::{ChildNumber, DerivationPath};
use bitcoin::{OutPoint, Script, Transaction};
use crate::database::{BatchDatabase, BatchOperations, Database};
// descriptor checksum d{i,e} -> vec<u8>
pub(crate) enum MapKey<'a> {
- Path((Option<ScriptType>, Option<&'a DerivationPath>)),
+ Path((Option<ScriptType>, Option<u32>)),
Script(Option<&'a Script>),
UTXO(Option<&'a OutPoint>),
RawTx(Option<&'a Txid>),
fn serialize_content(&self) -> Vec<u8> {
match self {
- MapKey::Path((_, Some(path))) => {
- let mut res = vec![];
- for val in *path {
- res.extend(&u32::from(*val).to_be_bytes());
- }
- res
- }
+ MapKey::Path((_, Some(child))) => u32::from(*child).to_be_bytes().to_vec(),
MapKey::Script(Some(s)) => serialize(*s),
MapKey::UTXO(Some(s)) => serialize(*s),
MapKey::RawTx(Some(s)) => serialize(*s),
}
impl BatchOperations for MemoryDatabase {
- fn set_script_pubkey<P: AsRef<[ChildNumber]>>(
+ fn set_script_pubkey(
&mut self,
script: &Script,
script_type: ScriptType,
- path: &P,
+ path: u32,
) -> Result<(), Error> {
- let deriv_path = DerivationPath::from(path.as_ref());
- let key = MapKey::Path((Some(script_type), Some(&deriv_path))).as_map_key();
+ let key = MapKey::Path((Some(script_type), Some(path))).as_map_key();
self.map.insert(key, Box::new(script.clone()));
let key = MapKey::Script(Some(script)).as_map_key();
let value = json!({
"t": script_type,
- "p": deriv_path,
+ "p": path,
});
self.map.insert(key, Box::new(value));
Ok(())
}
- fn del_script_pubkey_from_path<P: AsRef<[ChildNumber]>>(
+ fn del_script_pubkey_from_path(
&mut self,
script_type: ScriptType,
- path: &P,
+ path: u32,
) -> Result<Option<Script>, Error> {
- let deriv_path = DerivationPath::from(path.as_ref());
- let key = MapKey::Path((Some(script_type), Some(&deriv_path))).as_map_key();
+ let key = MapKey::Path((Some(script_type), Some(path))).as_map_key();
let res = self.map.remove(&key);
self.deleted_keys.push(key);
fn del_path_from_script_pubkey(
&mut self,
script: &Script,
- ) -> Result<Option<(ScriptType, DerivationPath)>, Error> {
+ ) -> Result<Option<(ScriptType, u32)>, Error> {
let key = MapKey::Script(Some(script)).as_map_key();
let res = self.map.remove(&key);
self.deleted_keys.push(key);
.collect()
}
- fn get_script_pubkey_from_path<P: AsRef<[ChildNumber]>>(
+ fn get_script_pubkey_from_path(
&self,
script_type: ScriptType,
- path: &P,
+ path: u32,
) -> Result<Option<Script>, Error> {
- let deriv_path = DerivationPath::from(path.as_ref());
- let key = MapKey::Path((Some(script_type), Some(&deriv_path))).as_map_key();
+ let key = MapKey::Path((Some(script_type), Some(path))).as_map_key();
Ok(self
.map
.get(&key)
fn get_path_from_script_pubkey(
&self,
script: &Script,
- ) -> Result<Option<(ScriptType, DerivationPath)>, Error> {
+ ) -> Result<Option<(ScriptType, u32)>, Error> {
let key = MapKey::Script(Some(script)).as_map_key();
Ok(self.map.get(&key).map(|b| {
let mut val: serde_json::Value = b.downcast_ref().cloned().unwrap();
#[cfg(test)]
mod test {
use std::str::FromStr;
- use std::sync::{Arc, Condvar, Mutex, Once};
- use std::time::{SystemTime, UNIX_EPOCH};
use bitcoin::consensus::encode::deserialize;
use bitcoin::hashes::hex::*;
let script = Script::from(
Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
);
- let path = DerivationPath::from_str("m/0/1/2/3").unwrap();
+ let path = 42;
let script_type = ScriptType::External;
- tree.set_script_pubkey(&script, script_type, &path).unwrap();
+ tree.set_script_pubkey(&script, script_type, path).unwrap();
assert_eq!(
- tree.get_script_pubkey_from_path(script_type, &path)
- .unwrap(),
+ tree.get_script_pubkey_from_path(script_type, path).unwrap(),
Some(script.clone())
);
assert_eq!(
let script = Script::from(
Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
);
- let path = DerivationPath::from_str("m/0/1/2/3").unwrap();
+ let path = 42;
let script_type = ScriptType::External;
- batch
- .set_script_pubkey(&script, script_type, &path)
- .unwrap();
+ batch.set_script_pubkey(&script, script_type, path).unwrap();
assert_eq!(
- tree.get_script_pubkey_from_path(script_type, &path)
- .unwrap(),
+ tree.get_script_pubkey_from_path(script_type, path).unwrap(),
None
);
assert_eq!(tree.get_path_from_script_pubkey(&script).unwrap(), None);
tree.commit_batch(batch).unwrap();
assert_eq!(
- tree.get_script_pubkey_from_path(script_type, &path)
- .unwrap(),
+ tree.get_script_pubkey_from_path(script_type, path).unwrap(),
Some(script.clone())
);
assert_eq!(
tree.get_path_from_script_pubkey(&script).unwrap(),
- Some((script_type, path.clone()))
+ Some((script_type, path))
);
}
let script = Script::from(
Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
);
- let path = DerivationPath::from_str("m/0/1/2/3").unwrap();
+ let path = 42;
let script_type = ScriptType::External;
- tree.set_script_pubkey(&script, script_type, &path).unwrap();
+ tree.set_script_pubkey(&script, script_type, path).unwrap();
assert_eq!(tree.iter_script_pubkeys(None).unwrap().len(), 1);
}
let script = Script::from(
Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
);
- let path = DerivationPath::from_str("m/0/1/2/3").unwrap();
+ let path = 42;
let script_type = ScriptType::External;
- tree.set_script_pubkey(&script, script_type, &path).unwrap();
+ tree.set_script_pubkey(&script, script_type, path).unwrap();
assert_eq!(tree.iter_script_pubkeys(None).unwrap().len(), 1);
- tree.del_script_pubkey_from_path(script_type, &path)
- .unwrap();
+ tree.del_script_pubkey_from_path(script_type, path).unwrap();
assert_eq!(tree.iter_script_pubkeys(None).unwrap().len(), 0);
}
let script = Script::from(
Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
);
- let path = DerivationPath::from_str("m/0/1/2/3").unwrap();
+ let path = 42;
let script_type = ScriptType::External;
- tree.set_script_pubkey(&script, script_type, &path).unwrap();
+ tree.set_script_pubkey(&script, script_type, path).unwrap();
assert_eq!(tree.iter_script_pubkeys(None).unwrap().len(), 1);
let mut batch = tree.begin_batch();
batch
- .del_script_pubkey_from_path(script_type, &path)
+ .del_script_pubkey_from_path(script_type, path)
.unwrap();
assert_eq!(tree.iter_script_pubkeys(None).unwrap().len(), 1);
- tree.commit_batch(batch);
+ tree.commit_batch(batch).unwrap();
assert_eq!(tree.iter_script_pubkeys(None).unwrap().len(), 0);
}
use bitcoin::hash_types::Txid;
-use bitcoin::util::bip32::{ChildNumber, DerivationPath};
use bitcoin::{OutPoint, Script, Transaction, TxOut};
use crate::error::Error;
pub mod memory;
pub trait BatchOperations {
- fn set_script_pubkey<P: AsRef<[ChildNumber]>>(
+ fn set_script_pubkey(
&mut self,
script: &Script,
script_type: ScriptType,
- path: &P,
+ child: u32,
) -> Result<(), Error>;
fn set_utxo(&mut self, utxo: &UTXO) -> Result<(), Error>;
fn set_raw_tx(&mut self, transaction: &Transaction) -> Result<(), Error>;
fn set_tx(&mut self, transaction: &TransactionDetails) -> Result<(), Error>;
fn set_last_index(&mut self, script_type: ScriptType, value: u32) -> Result<(), Error>;
- fn del_script_pubkey_from_path<P: AsRef<[ChildNumber]>>(
+ fn del_script_pubkey_from_path(
&mut self,
script_type: ScriptType,
- path: &P,
+ child: u32,
) -> Result<Option<Script>, Error>;
fn del_path_from_script_pubkey(
&mut self,
script: &Script,
- ) -> Result<Option<(ScriptType, DerivationPath)>, Error>;
+ ) -> Result<Option<(ScriptType, u32)>, Error>;
fn del_utxo(&mut self, outpoint: &OutPoint) -> Result<Option<UTXO>, Error>;
fn del_raw_tx(&mut self, txid: &Txid) -> Result<Option<Transaction>, Error>;
fn del_tx(
fn iter_raw_txs(&self) -> Result<Vec<Transaction>, Error>;
fn iter_txs(&self, include_raw: bool) -> Result<Vec<TransactionDetails>, Error>;
- fn get_script_pubkey_from_path<P: AsRef<[ChildNumber]>>(
+ fn get_script_pubkey_from_path(
&self,
script_type: ScriptType,
- path: &P,
+ child: u32,
) -> Result<Option<Script>, Error>;
fn get_path_from_script_pubkey(
&self,
script: &Script,
- ) -> Result<Option<(ScriptType, DerivationPath)>, Error>;
+ ) -> Result<Option<(ScriptType, u32)>, Error>;
fn get_utxo(&self, outpoint: &OutPoint) -> Result<Option<UTXO>, Error>;
fn get_raw_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error>;
fn get_tx(&self, txid: &Txid, include_raw: bool) -> Result<Option<TransactionDetails>, Error>;
use bitcoin::blockdata::opcodes;
use bitcoin::blockdata::script::Builder;
use bitcoin::consensus::encode::serialize;
-use bitcoin::util::bip32::{ChildNumber, DerivationPath};
use bitcoin::util::psbt::PartiallySignedTransaction as PSBT;
use bitcoin::{
Address, Network, OutPoint, PublicKey, Script, SigHashType, Transaction, TxIn, TxOut, Txid,
let mut psbt = PSBT::from_unsigned_tx(tx)?;
// add metadata for the inputs
- for ((psbt_input, (script_type, path)), input) in psbt
+ for ((psbt_input, (script_type, child)), input) in psbt
.inputs
.iter_mut()
.zip(paths.into_iter())
.zip(psbt.global.unsigned_tx.input.iter())
{
- let path: Vec<ChildNumber> = path.into();
- let index = match path.last() {
- Some(ChildNumber::Normal { index }) => *index,
- Some(ChildNumber::Hardened { index }) => *index,
- None => 0,
- };
-
let desc = self.get_descriptor_for(script_type);
- psbt_input.hd_keypaths = desc.get_hd_keypaths(index).unwrap();
- let derived_descriptor = desc.derive(index).unwrap();
+ psbt_input.hd_keypaths = desc.get_hd_keypaths(child).unwrap();
+ let derived_descriptor = desc.derive(child).unwrap();
// TODO: figure out what do redeem_script and witness_script mean
psbt_input.redeem_script = derived_descriptor.psbt_redeem_script();
.iter_mut()
.zip(psbt.global.unsigned_tx.output.iter())
{
- if let Some((script_type, derivation_path)) = self
+ if let Some((script_type, child)) = self
.database
.borrow()
.get_path_from_script_pubkey(&tx_output.script_pubkey)?
{
- let derivation_path: Vec<ChildNumber> = derivation_path.into();
- let index = match derivation_path.last() {
- Some(ChildNumber::Normal { index }) => *index,
- Some(ChildNumber::Hardened { index }) => *index,
- None => 0,
- };
-
let desc = self.get_descriptor_for(script_type);
- psbt_output.hd_keypaths = desc.get_hd_keypaths(index)?;
+ psbt_output.hd_keypaths = desc.get_hd_keypaths(child)?;
}
}
outgoing: u64,
input_witness_weight: usize,
mut fee_val: f32,
- ) -> Result<(Vec<TxIn>, Vec<(ScriptType, DerivationPath)>, u64, f32), Error> {
+ ) -> Result<(Vec<TxIn>, Vec<(ScriptType, u32)>, u64, f32), Error> {
let mut answer = Vec::new();
- let mut paths = Vec::new();
+ let mut deriv_indexes = Vec::new();
let calc_fee_bytes = |wu| (wu as f32) * fee_rate / 4.0;
debug!(
answer.push(new_in);
selected_amount += utxo.txout.value;
- let path = self
+ let child = self
.database
.borrow()
.get_path_from_script_pubkey(&utxo.txout.script_pubkey)?
.unwrap(); // TODO: remove unrwap
- paths.push(path);
+ deriv_indexes.push(child);
}
- Ok((answer, paths, selected_amount, fee_val))
+ Ok((answer, deriv_indexes, selected_amount, fee_val))
}
fn add_hd_keypaths(&self, psbt: &mut PSBT) -> Result<(), Error> {
debug!("found descriptor path {:?}", option_path);
- let (script_type, path) = match option_path {
+ let (script_type, child) = match option_path {
None => continue,
- Some((script_type, path)) => (script_type, path),
- };
-
- // TODO: this is duplicated code
- let index = match path.into_iter().last() {
- Some(ChildNumber::Normal { index }) => *index,
- Some(ChildNumber::Hardened { index }) => *index,
- None => 0,
+ Some((script_type, child)) => (script_type, child),
};
// merge hd_keypaths
let desc = self.get_descriptor_for(script_type);
- let mut hd_keypaths = desc.get_hd_keypaths(index)?;
+ let mut hd_keypaths = desc.get_hd_keypaths(child)?;
psbt_input.hd_keypaths.append(&mut hd_keypaths);
}
}
// TODO:
// let batch_query_size = batch_query_size.unwrap_or(20);
- let path = DerivationPath::from(vec![ChildNumber::Normal { index: max_address }]);
let last_addr = self
.database
.borrow()
- .get_script_pubkey_from_path(ScriptType::External, &path)?;
+ .get_script_pubkey_from_path(ScriptType::External, max_address)?;
// cache a few of our addresses
if last_addr.is_none() {
for i in 0..=max_address {
let derived = self.descriptor.derive(i).unwrap();
- let full_path = DerivationPath::from(vec![ChildNumber::Normal { index: i }]);
address_batch.set_script_pubkey(
&derived.script_pubkey(),
ScriptType::External,
- &full_path,
+ i,
)?;
}
if self.change_descriptor.is_some() {
for i in 0..=max_address {
let derived = self.change_descriptor.as_ref().unwrap().derive(i).unwrap();
- let full_path = DerivationPath::from(vec![ChildNumber::Normal { index: i }]);
address_batch.set_script_pubkey(
&derived.script_pubkey(),
ScriptType::Internal,
- &full_path,
+ i,
)?;
}
}