Compare commits

..

2 Commits

Author SHA1 Message Date
Alekos Filini
ea62337f0d [database] Replace DerivationPaths with single u32s 2020-06-30 15:21:14 +02:00
Alekos Filini
2fb104824a [wallet] Add hd_keypaths to outputs 2020-06-30 14:01:38 +02:00
13 changed files with 214 additions and 669 deletions

View File

@@ -28,7 +28,6 @@ electrum = ["electrum-client"]
esplora = ["reqwest", "futures"]
key-value-db = ["sled"]
cli-utils = ["clap"]
multiparty = []
[dev-dependencies]
tokio = { version = "0.2", features = ["macros"] }
@@ -51,10 +50,6 @@ name = "miniscriptc"
path = "examples/compiler.rs"
required-features = ["compiler"]
[[example]]
name = "multiparty"
required-features = ["multiparty","compiler"]
# Provide a more user-friendly alias for the REPL
[[example]]
name = "magic"

View File

@@ -1,96 +0,0 @@
extern crate bitcoin;
extern crate clap;
extern crate log;
extern crate magical_bitcoin_wallet;
extern crate miniscript;
extern crate rand;
extern crate serde_json;
extern crate sled;
use std::str::FromStr;
use log::info;
use clap::{App, Arg};
use bitcoin::PublicKey;
use miniscript::policy::Concrete;
use miniscript::Descriptor;
use magical_bitcoin_wallet::multiparty::{Coordinator, Participant, Peer};
fn main() {
env_logger::init_from_env(
env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"),
);
let matches = App::new("Multiparty Tools")
.arg(
Arg::with_name("POLICY")
.help("Sets the spending policy to compile")
.required(true)
.index(1),
)
.arg(
Arg::with_name("TYPE")
.help("Sets the script type used to embed the compiled policy")
.required(true)
.index(2)
.possible_values(&["sh", "wsh", "sh-wsh"]),
)
.get_matches();
let policy_str = matches.value_of("POLICY").unwrap();
info!("Compiling policy: {}", policy_str);
let policy = Concrete::<String>::from_str(&policy_str).unwrap();
let compiled = policy.compile().unwrap();
let descriptor = match matches.value_of("TYPE").unwrap() {
"sh" => Descriptor::Sh(compiled),
"wsh" => Descriptor::Wsh(compiled),
"sh-wsh" => Descriptor::ShWsh(compiled),
_ => panic!("Invalid type"),
};
info!("Descriptor: {}", descriptor);
let mut coordinator: Participant<Coordinator> = Participant::new(descriptor).unwrap();
/*let policy = coordinator.policy_for(vec![]).unwrap();
info!(
"Policy:\n{}",
serde_json::to_string_pretty(&policy).unwrap()
);*/
let missing_keys = coordinator.missing_keys();
info!("Missing keys: {:?}", missing_keys);
let pk =
PublicKey::from_str("02c65413e56b343a0a31c18d506f1502a17fc64dfbcef6bfb00d1c0d6229bb6f61")
.unwrap();
coordinator.add_key("Alice", pk.into()).unwrap();
coordinator.add_key("Carol", pk.into()).unwrap();
let for_bob = coordinator.descriptor_for("Bob").unwrap();
info!("Descriptor for Bob: {}", for_bob);
let mut bob_peer: Participant<Peer> = Participant::new(for_bob).unwrap();
info!(
"Bob's policy: {}",
serde_json::to_string(&bob_peer.policy().unwrap().unwrap()).unwrap()
);
bob_peer.use_key(pk.into()).unwrap();
info!("Bob's my_key: {}", bob_peer.my_key().unwrap());
coordinator.add_key("Bob", pk.into()).unwrap();
info!("Coordinator completed: {}", coordinator.completed());
let coord_map = coordinator.get_map().unwrap();
let finalized = coordinator.finalize().unwrap();
info!("Coordinator final: {}", finalized);
let bob_finalized = bob_peer.apply_map(coord_map).unwrap();
info!("Bob final: {}", bob_finalized);
}

View File

@@ -226,7 +226,7 @@ pub trait ElectrumLikeSync {
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);
@@ -242,10 +242,8 @@ pub trait ElectrumLikeSync {
}
// 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;
}
}
}

View File

@@ -1,10 +1,9 @@
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;
@@ -14,15 +13,14 @@ use crate::types::*;
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)*;
@@ -70,16 +68,15 @@ macro_rules! impl_batch_operations {
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);
@@ -245,20 +242,19 @@ impl Database for Tree {
.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> {
@@ -415,14 +411,13 @@ mod test {
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!(
@@ -439,16 +434,13 @@ mod test {
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);
@@ -456,8 +448,7 @@ mod test {
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!(
@@ -473,10 +464,10 @@ mod test {
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);
}
@@ -488,14 +479,13 @@ mod test {
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);
}

View File

@@ -3,7 +3,6 @@ use std::ops::Bound::{Excluded, Included};
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};
@@ -19,7 +18,7 @@ use crate::types::*;
// 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>),
@@ -49,13 +48,7 @@ impl MapKey<'_> {
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),
@@ -99,20 +92,19 @@ impl MemoryDatabase {
}
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));
@@ -154,13 +146,12 @@ impl BatchOperations for MemoryDatabase {
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);
@@ -169,7 +160,7 @@ impl BatchOperations for MemoryDatabase {
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);
@@ -313,13 +304,12 @@ impl Database for MemoryDatabase {
.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)
@@ -329,7 +319,7 @@ impl Database for MemoryDatabase {
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();
@@ -410,8 +400,6 @@ impl BatchDatabase for MemoryDatabase {
#[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::*;
@@ -431,14 +419,13 @@ mod test {
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!(
@@ -455,16 +442,13 @@ mod test {
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);
@@ -472,13 +456,12 @@ mod test {
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))
);
}
@@ -489,10 +472,10 @@ mod test {
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);
}
@@ -504,14 +487,13 @@ mod test {
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);
}
@@ -522,20 +504,20 @@ mod test {
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);
}

View File

@@ -1,5 +1,4 @@
use bitcoin::hash_types::Txid;
use bitcoin::util::bip32::{ChildNumber, DerivationPath};
use bitcoin::{OutPoint, Script, Transaction, TxOut};
use crate::error::Error;
@@ -10,26 +9,26 @@ pub mod keyvalue;
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(
@@ -52,15 +51,15 @@ pub trait Database: BatchOperations {
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>;

View File

@@ -6,12 +6,6 @@ pub enum Error {
MalformedInput,
KeyParsingError(String),
AliasAsPublicKey,
KeyHasSecret,
Incomplete,
MissingAlias(String),
InvalidAlias(String),
Policy(crate::descriptor::policy::PolicyError),
InputIndexDoesntExist,

View File

@@ -1,6 +1,3 @@
use std::fmt;
use std::str::FromStr;
use bitcoin::secp256k1::{All, Secp256k1};
use bitcoin::{PrivateKey, PublicKey};
@@ -12,41 +9,36 @@ use super::error::Error;
use super::extended_key::DerivationIndex;
use super::DescriptorExtendedKey;
#[derive(Debug, Clone)]
pub struct KeyAlias {
alias: String,
has_secret: bool,
}
impl KeyAlias {
pub(crate) fn new_boxed(alias: &str, has_secret: bool) -> Box<dyn Key> {
Box::new(KeyAlias {
alias: alias.into(),
has_secret,
})
}
}
pub(crate) fn parse_key(string: &str) -> Result<(String, Box<dyn RealKey>), Error> {
if let Ok(pk) = PublicKey::from_str(string) {
return Ok((string.to_string(), Box::new(pk)));
} else if let Ok(sk) = PrivateKey::from_wif(string) {
return Ok((string.to_string(), Box::new(sk)));
} else if let Ok(ext_key) = DescriptorExtendedKey::from_str(string) {
return Ok((string.to_string(), Box::new(ext_key)));
}
return Err(Error::KeyParsingError(string.to_string()));
}
pub trait Key: std::fmt::Debug + std::fmt::Display {
pub(super) trait Key: std::fmt::Debug + std::fmt::Display {
fn fingerprint(&self, secp: &Secp256k1<All>) -> Option<Fingerprint>;
fn as_public_key(&self, secp: &Secp256k1<All>, index: Option<u32>) -> Result<PublicKey, Error>;
fn as_secret_key(&self) -> Option<PrivateKey>;
fn xprv(&self) -> Option<ExtendedPrivKey>;
fn full_path(&self, index: u32) -> Option<DerivationPath>;
fn is_fixed(&self) -> bool;
fn alias(&self) -> Option<&str> {
fn has_secret(&self) -> bool {
self.xprv().is_some() || self.as_secret_key().is_some()
}
fn public(&self, secp: &Secp256k1<All>) -> Result<Box<dyn Key>, Error> {
Ok(Box::new(self.as_public_key(secp, None)?))
}
}
impl Key for PublicKey {
fn fingerprint(&self, _secp: &Secp256k1<All>) -> Option<Fingerprint> {
None
}
fn as_public_key(
&self,
_secp: &Secp256k1<All>,
_index: Option<u32>,
) -> Result<PublicKey, Error> {
Ok(PublicKey::clone(self))
}
fn as_secret_key(&self) -> Option<PrivateKey> {
None
}
@@ -59,50 +51,16 @@ pub trait Key: std::fmt::Debug + std::fmt::Display {
None
}
fn fingerprint(&self, _secp: &Secp256k1<All>) -> Option<Fingerprint> {
None
}
fn has_secret(&self) -> bool {
self.xprv().is_some() || self.as_secret_key().is_some()
}
fn public(&self, secp: &Secp256k1<All>) -> Result<Box<dyn RealKey>, Error> {
Ok(Box::new(self.as_public_key(secp, None)?))
}
}
pub trait RealKey: Key {
fn into_key(&self) -> Box<dyn Key>;
}
impl<T: RealKey + 'static> From<T> for Box<dyn RealKey> {
fn from(key: T) -> Self {
Box::new(key)
}
}
impl Key for PublicKey {
fn as_public_key(
&self,
_secp: &Secp256k1<All>,
_index: Option<u32>,
) -> Result<PublicKey, Error> {
Ok(PublicKey::clone(self))
}
fn is_fixed(&self) -> bool {
true
}
}
impl RealKey for PublicKey {
fn into_key(&self) -> Box<dyn Key> {
Box::new(self.clone())
}
}
impl Key for PrivateKey {
fn fingerprint(&self, _secp: &Secp256k1<All>) -> Option<Fingerprint> {
None
}
fn as_public_key(
&self,
secp: &Secp256k1<All>,
@@ -115,15 +73,18 @@ impl Key for PrivateKey {
Some(PrivateKey::clone(self))
}
fn xprv(&self) -> Option<ExtendedPrivKey> {
None
}
fn full_path(&self, _index: u32) -> Option<DerivationPath> {
None
}
fn is_fixed(&self) -> bool {
true
}
}
impl RealKey for PrivateKey {
fn into_key(&self) -> Box<dyn Key> {
Box::new(self.clone())
}
}
impl Key for DescriptorExtendedKey {
fn fingerprint(&self, secp: &Secp256k1<All>) -> Option<Fingerprint> {
@@ -138,7 +99,7 @@ impl Key for DescriptorExtendedKey {
Ok(self.derive_xpub(secp, index.unwrap_or(0))?.public_key)
}
fn public(&self, secp: &Secp256k1<All>) -> Result<Box<dyn RealKey>, Error> {
fn public(&self, secp: &Secp256k1<All>) -> Result<Box<dyn Key>, Error> {
if self.final_index == DerivationIndex::Hardened {
return Err(Error::HardenedDerivationOnXpub);
}
@@ -202,6 +163,10 @@ impl Key for DescriptorExtendedKey {
}))
}
fn as_secret_key(&self) -> Option<PrivateKey> {
None
}
fn xprv(&self) -> Option<ExtendedPrivKey> {
self.secret
}
@@ -214,67 +179,3 @@ impl Key for DescriptorExtendedKey {
self.final_index == DerivationIndex::Fixed
}
}
impl RealKey for DescriptorExtendedKey {
fn into_key(&self) -> Box<dyn Key> {
Box::new(self.clone())
}
}
impl std::fmt::Display for KeyAlias {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let flag = if self.has_secret { "#" } else { "" };
write!(f, "{}{}", flag, self.alias)
}
}
impl Key for KeyAlias {
fn as_public_key(
&self,
_secp: &Secp256k1<All>,
_index: Option<u32>,
) -> Result<PublicKey, Error> {
Err(Error::AliasAsPublicKey)
}
fn is_fixed(&self) -> bool {
true
}
fn alias(&self) -> Option<&str> {
Some(self.alias.as_str())
}
fn has_secret(&self) -> bool {
self.has_secret
}
fn public(&self, _secp: &Secp256k1<All>) -> Result<Box<dyn RealKey>, Error> {
Err(Error::AliasAsPublicKey)
}
}
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd, Eq, Ord, Default)]
pub(crate) struct DummyKey();
impl fmt::Display for DummyKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "DummyKey")
}
}
impl std::str::FromStr for DummyKey {
type Err = ();
fn from_str(_: &str) -> Result<Self, Self::Err> {
Ok(DummyKey::default())
}
}
impl miniscript::MiniscriptKey for DummyKey {
type Hash = DummyKey;
fn to_pubkeyhash(&self) -> DummyKey {
DummyKey::default()
}
}

View File

@@ -19,7 +19,7 @@ use crate::psbt::utils::PSBTUtils;
pub mod checksum;
pub mod error;
pub mod extended_key;
pub mod keys;
mod keys;
pub mod policy;
pub use self::checksum::get_checksum;
@@ -27,9 +27,9 @@ use self::error::Error;
pub use self::extended_key::{DerivationIndex, DescriptorExtendedKey};
pub use self::policy::Policy;
use self::keys::{parse_key, DummyKey, Key, RealKey};
use self::keys::Key;
pub(crate) trait MiniscriptExtractPolicy {
trait MiniscriptExtractPolicy {
fn extract_policy(
&self,
lookup_map: &BTreeMap<String, Box<dyn Key>>,
@@ -40,6 +40,31 @@ pub trait ExtractPolicy {
fn extract_policy(&self) -> Result<Option<Policy>, Error>;
}
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd, Eq, Ord, Default)]
struct DummyKey();
impl fmt::Display for DummyKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "DummyKey")
}
}
impl std::str::FromStr for DummyKey {
type Err = ();
fn from_str(_: &str) -> Result<Self, Self::Err> {
Ok(DummyKey::default())
}
}
impl miniscript::MiniscriptKey for DummyKey {
type Hash = DummyKey;
fn to_pubkeyhash(&self) -> DummyKey {
DummyKey::default()
}
}
pub type DerivedDescriptor = Descriptor<PublicKey>;
pub type StringDescriptor = Descriptor<String>;
@@ -87,13 +112,13 @@ where
#[derive(Debug, Serialize, Deserialize)]
pub struct ExtendedDescriptor {
#[serde(flatten)]
pub(crate) internal: StringDescriptor,
internal: StringDescriptor,
#[serde(skip)]
pub(crate) keys: BTreeMap<String, Box<dyn RealKey>>,
keys: BTreeMap<String, Box<dyn Key>>,
#[serde(skip)]
pub(crate) ctx: Secp256k1<All>,
ctx: Secp256k1<All>,
}
impl fmt::Display for ExtendedDescriptor {
@@ -119,18 +144,30 @@ impl std::convert::AsRef<StringDescriptor> for ExtendedDescriptor {
}
impl ExtendedDescriptor {
fn parse_string(string: &str) -> Result<(String, Box<dyn Key>), Error> {
if let Ok(pk) = PublicKey::from_str(string) {
return Ok((string.to_string(), Box::new(pk)));
} else if let Ok(sk) = PrivateKey::from_wif(string) {
return Ok((string.to_string(), Box::new(sk)));
} else if let Ok(ext_key) = DescriptorExtendedKey::from_str(string) {
return Ok((string.to_string(), Box::new(ext_key)));
}
return Err(Error::KeyParsingError(string.to_string()));
}
fn new(sd: StringDescriptor) -> Result<Self, Error> {
let ctx = Secp256k1::gen_new();
let keys: RefCell<BTreeMap<String, Box<dyn RealKey>>> = RefCell::new(BTreeMap::new());
let keys: RefCell<BTreeMap<String, Box<dyn Key>>> = RefCell::new(BTreeMap::new());
let translatefpk = |string: &String| -> Result<_, Error> {
let (key, parsed) = parse_key(string)?;
let (key, parsed) = Self::parse_string(string)?;
keys.borrow_mut().insert(key, parsed);
Ok(DummyKey::default())
};
let translatefpkh = |string: &String| -> Result<_, Error> {
let (key, parsed) = parse_key(string)?;
let (key, parsed) = Self::parse_string(string)?;
keys.borrow_mut().insert(key, parsed);
Ok(DummyKey::default())
@@ -292,7 +329,7 @@ impl ExtendedDescriptor {
}
pub fn as_public_version(&self) -> Result<ExtendedDescriptor, Error> {
let keys: RefCell<BTreeMap<String, Box<dyn RealKey>>> = RefCell::new(BTreeMap::new());
let keys: RefCell<BTreeMap<String, Box<dyn Key>>> = RefCell::new(BTreeMap::new());
let translatefpk = |string: &String| -> Result<_, Error> {
let public = self.keys.get(string).unwrap().public(&self.ctx)?;
@@ -323,13 +360,7 @@ impl ExtendedDescriptor {
impl ExtractPolicy for ExtendedDescriptor {
fn extract_policy(&self) -> Result<Option<Policy>, Error> {
self.internal.extract_policy(
&self
.keys
.iter()
.map(|(k, v)| (k.into(), v.into_key()))
.collect(),
)
self.internal.extract_policy(&self.keys)
}
}

View File

@@ -27,27 +27,19 @@ pub struct PKOrF {
pubkey_hash: Option<hash160::Hash>,
#[serde(skip_serializing_if = "Option::is_none")]
fingerprint: Option<Fingerprint>,
#[serde(skip_serializing_if = "Option::is_none")]
alias: Option<String>,
}
impl PKOrF {
fn from_key(k: &Box<dyn Key>) -> Self {
let secp = Secp256k1::gen_new();
if let Some(alias) = k.alias() {
PKOrF {
alias: Some(alias.into()),
..Default::default()
}
} else if let Some(fing) = k.fingerprint(&secp) {
let pubkey = k.as_public_key(&secp, None).unwrap();
if let Some(fing) = k.fingerprint(&secp) {
PKOrF {
fingerprint: Some(fing),
..Default::default()
}
} else {
let pubkey = k.as_public_key(&secp, None).unwrap();
PKOrF {
pubkey: Some(pubkey),
..Default::default()

View File

@@ -33,8 +33,6 @@ pub mod error;
pub mod blockchain;
pub mod database;
pub mod descriptor;
#[cfg(feature = "multiparty")]
pub mod multiparty;
pub mod psbt;
pub mod signer;
pub mod types;

View File

@@ -1,231 +0,0 @@
use std::cell::RefCell;
use std::collections::BTreeMap;
use bitcoin::secp256k1::Secp256k1;
use crate::descriptor::error::Error;
use crate::descriptor::keys::{parse_key, DummyKey, Key, KeyAlias, RealKey};
use crate::descriptor::{ExtendedDescriptor, MiniscriptExtractPolicy, Policy, StringDescriptor};
pub trait ParticipantType: Default {
fn validate_aliases(aliases: Vec<&String>) -> Result<(), Error>;
}
#[derive(Default)]
pub struct Coordinator {}
impl ParticipantType for Coordinator {
fn validate_aliases(aliases: Vec<&String>) -> Result<(), Error> {
if aliases.into_iter().any(|a| a == "[PEER]") {
Err(Error::InvalidAlias("[PEER]".into()))
} else {
Ok(())
}
}
}
#[derive(Default)]
pub struct Peer;
impl ParticipantType for Peer {
fn validate_aliases(aliases: Vec<&String>) -> Result<(), Error> {
if !aliases.into_iter().any(|a| a == "[PEER]") {
Err(Error::MissingAlias("[PEER]".into()))
} else {
Ok(())
}
}
}
pub struct Participant<T: ParticipantType> {
descriptor: StringDescriptor,
parsed_keys: BTreeMap<String, Box<dyn Key>>,
received_keys: BTreeMap<String, Box<dyn RealKey>>,
_data: T,
}
impl<T: ParticipantType> Participant<T> {
pub fn new(sd: StringDescriptor) -> Result<Self, Error> {
let parsed_keys = Self::parse_keys(&sd, vec![]);
T::validate_aliases(parsed_keys.keys().collect())?;
Ok(Participant {
descriptor: sd,
parsed_keys,
received_keys: Default::default(),
_data: Default::default(),
})
}
fn parse_keys(
sd: &StringDescriptor,
with_secrets: Vec<&str>,
) -> BTreeMap<String, Box<dyn Key>> {
let keys: RefCell<BTreeMap<String, Box<dyn Key>>> = RefCell::new(BTreeMap::new());
let translatefpk = |string: &String| -> Result<_, Error> {
let (key, parsed) = match parse_key(string) {
Ok((key, parsed)) => (key, parsed.into_key()),
Err(_) => (
string.clone(),
KeyAlias::new_boxed(string.as_str(), with_secrets.contains(&string.as_str())),
),
};
keys.borrow_mut().insert(key, parsed);
Ok(DummyKey::default())
};
let translatefpkh = |string: &String| -> Result<_, Error> {
let (key, parsed) = match parse_key(string) {
Ok((key, parsed)) => (key, parsed.into_key()),
Err(_) => (
string.clone(),
KeyAlias::new_boxed(string.as_str(), with_secrets.contains(&string.as_str())),
),
};
keys.borrow_mut().insert(key, parsed);
Ok(DummyKey::default())
};
sd.translate_pk(translatefpk, translatefpkh).unwrap();
keys.into_inner()
}
pub fn policy_for(&self, with_secrets: Vec<&str>) -> Result<Option<Policy>, Error> {
let keys = Self::parse_keys(&self.descriptor, with_secrets);
self.descriptor.extract_policy(&keys)
}
fn _missing_keys(&self) -> Vec<&String> {
self.parsed_keys
.keys()
.filter(|k| !self.received_keys.contains_key(*k))
.collect()
}
pub fn completed(&self) -> bool {
self._missing_keys().is_empty()
}
pub fn finalize(self) -> Result<ExtendedDescriptor, Error> {
if !self.completed() {
return Err(Error::Incomplete);
}
let translatefpk = |string: &String| -> Result<_, Error> {
Ok(format!(
"{}",
self.received_keys
.get(string)
.expect(&format!("Missing key: `{}`", string))
))
};
let translatefpkh = |string: &String| -> Result<_, Error> {
Ok(format!(
"{}",
self.received_keys
.get(string)
.expect(&format!("Missing key: `{}`", string))
))
};
let internal = self.descriptor.translate_pk(translatefpk, translatefpkh)?;
Ok(ExtendedDescriptor {
internal,
keys: self.received_keys,
ctx: Secp256k1::gen_new(),
})
}
}
impl Participant<Coordinator> {
pub fn descriptor(&self) -> &StringDescriptor {
&self.descriptor
}
pub fn add_key(&mut self, alias: &str, key: Box<dyn RealKey>) -> Result<(), Error> {
// TODO: check network
if key.has_secret() {
return Err(Error::KeyHasSecret);
}
self.received_keys.insert(alias.into(), key);
Ok(())
}
pub fn received_keys(&self) -> Vec<&String> {
self.received_keys.keys().collect()
}
pub fn missing_keys(&self) -> Vec<&String> {
self._missing_keys()
}
pub fn descriptor_for(&self, alias: &str) -> Result<StringDescriptor, Error> {
if !self.parsed_keys.contains_key(alias) {
return Err(Error::MissingAlias(alias.into()));
}
let map_name = |s: &String| {
if s == alias {
"[PEER]".into()
} else {
s.into()
}
};
let translatefpk = |string: &String| -> Result<_, Error> { Ok(map_name(string)) };
let translatefpkh = |string: &String| -> Result<_, Error> { Ok(map_name(string)) };
Ok(self.descriptor.translate_pk(translatefpk, translatefpkh)?)
}
pub fn get_map(&self) -> Result<BTreeMap<String, String>, Error> {
if !self.completed() {
return Err(Error::Incomplete);
}
Ok(self
.received_keys
.iter()
.map(|(k, v)| (k.into(), format!("{}", v)))
.collect())
}
}
impl Participant<Peer> {
pub fn policy(&self) -> Result<Option<Policy>, Error> {
self.policy_for(vec!["[PEER]"])
}
pub fn use_key(&mut self, key: Box<dyn RealKey>) -> Result<(), Error> {
let secp = Secp256k1::gen_new();
self.received_keys
.insert("[PEER]".into(), key.public(&secp)?);
Ok(())
}
pub fn my_key(&mut self) -> Option<&Box<dyn RealKey>> {
self.received_keys.get("[PEER]".into())
}
pub fn apply_map(mut self, map: BTreeMap<String, String>) -> Result<ExtendedDescriptor, Error> {
let mut parsed_map: BTreeMap<_, _> = map
.into_iter()
.map(|(k, v)| -> Result<_, Error> {
let (_, parsed) = parse_key(&v)?;
Ok((k, parsed))
})
.collect::<Result<_, _>>()?;
self.received_keys.append(&mut parsed_map);
self.finalize()
}
}

View File

@@ -7,7 +7,6 @@ use std::time::{Instant, SystemTime, UNIX_EPOCH};
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,
@@ -247,22 +246,15 @@ where
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();
@@ -285,9 +277,20 @@ where
psbt_input.sighash_type = Some(SigHashType::All);
}
// TODO: add metadata for the outputs, like derivation paths for change addrs
/*for psbt_output in psbt.outputs.iter_mut().zip(psbt.global.unsigned_tx.output.iter()) {
}*/
for (psbt_output, tx_output) in psbt
.outputs
.iter_mut()
.zip(psbt.global.unsigned_tx.output.iter())
{
if let Some((script_type, child)) = self
.database
.borrow()
.get_path_from_script_pubkey(&tx_output.script_pubkey)?
{
let desc = self.get_descriptor_for(script_type);
psbt_output.hd_keypaths = desc.get_hd_keypaths(child)?;
}
}
let transaction_details = TransactionDetails {
transaction: None,
@@ -301,47 +304,6 @@ where
Ok((psbt, transaction_details))
}
// TODO: move down to the "internals"
fn add_hd_keypaths(&self, psbt: &mut PSBT) -> Result<(), Error> {
let mut input_utxos = Vec::with_capacity(psbt.inputs.len());
for n in 0..psbt.inputs.len() {
input_utxos.push(psbt.get_utxo_for(n).clone());
}
// try to add hd_keypaths if we've already seen the output
for (psbt_input, out) in psbt.inputs.iter_mut().zip(input_utxos.iter()) {
debug!("searching hd_keypaths for out: {:?}", out);
if let Some(out) = out {
let option_path = self
.database
.borrow()
.get_path_from_script_pubkey(&out.script_pubkey)?;
debug!("found descriptor path {:?}", option_path);
let (script_type, path) = 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,
};
// merge hd_keypaths
let desc = self.get_descriptor_for(script_type);
let mut hd_keypaths = desc.get_hd_keypaths(index)?;
psbt_input.hd_keypaths.append(&mut hd_keypaths);
}
}
Ok(())
}
// TODO: define an enum for signing errors
pub fn sign(&self, mut psbt: PSBT, assume_height: Option<u32>) -> Result<(PSBT, bool), Error> {
// this helps us doing our job later
@@ -661,9 +623,9 @@ where
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!(
@@ -697,15 +659,48 @@ where
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> {
let mut input_utxos = Vec::with_capacity(psbt.inputs.len());
for n in 0..psbt.inputs.len() {
input_utxos.push(psbt.get_utxo_for(n).clone());
}
// try to add hd_keypaths if we've already seen the output
for (psbt_input, out) in psbt.inputs.iter_mut().zip(input_utxos.iter()) {
debug!("searching hd_keypaths for out: {:?}", out);
if let Some(out) = out {
let option_path = self
.database
.borrow()
.get_path_from_script_pubkey(&out.script_pubkey)?;
debug!("found descriptor path {:?}", option_path);
let (script_type, child) = match option_path {
None => continue,
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(child)?;
psbt_input.hd_keypaths.append(&mut hd_keypaths);
}
}
Ok(())
}
}
@@ -775,11 +770,10 @@ where
// 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() {
@@ -789,23 +783,21 @@ where
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,
)?;
}
}