Compare commits

...

1 Commits

Author SHA1 Message Date
Alekos Filini
9c4e5b4d25
[wip] Multiparty descriptor creation 2020-05-19 22:29:04 +02:00
8 changed files with 504 additions and 88 deletions

View File

@ -28,6 +28,7 @@ electrum = ["electrum-client"]
esplora = ["reqwest", "futures"]
key-value-db = ["sled"]
cli-utils = ["clap"]
multiparty = []
[dev-dependencies]
tokio = { version = "0.2", features = ["macros"] }
@ -50,6 +51,10 @@ 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"

96
examples/multiparty.rs Normal file
View File

@ -0,0 +1,96 @@
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

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

View File

@ -1,3 +1,6 @@
use std::fmt;
use std::str::FromStr;
use bitcoin::secp256k1::{All, Secp256k1};
use bitcoin::{PrivateKey, PublicKey};
@ -9,34 +12,39 @@ use super::error::Error;
use super::extended_key::DerivationIndex;
use super::DescriptorExtendedKey;
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;
#[derive(Debug, Clone)]
pub struct KeyAlias {
alias: String,
has_secret: bool,
}
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 KeyAlias {
pub(crate) fn new_boxed(alias: &str, has_secret: bool) -> Box<dyn Key> {
Box::new(KeyAlias {
alias: alias.into(),
has_secret,
})
}
}
impl Key for PublicKey {
fn fingerprint(&self, _secp: &Secp256k1<All>) -> Option<Fingerprint> {
None
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)));
}
fn as_public_key(
&self,
_secp: &Secp256k1<All>,
_index: Option<u32>,
) -> Result<PublicKey, Error> {
Ok(PublicKey::clone(self))
return Err(Error::KeyParsingError(string.to_string()));
}
pub trait Key: std::fmt::Debug + std::fmt::Display {
fn as_public_key(&self, secp: &Secp256k1<All>, index: Option<u32>) -> Result<PublicKey, Error>;
fn is_fixed(&self) -> bool;
fn alias(&self) -> Option<&str> {
None
}
fn as_secret_key(&self) -> Option<PrivateKey> {
@ -51,16 +59,50 @@ impl Key for PublicKey {
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 Key for PrivateKey {
fn fingerprint(&self, _secp: &Secp256k1<All>) -> Option<Fingerprint> {
None
impl RealKey for PublicKey {
fn into_key(&self) -> Box<dyn Key> {
Box::new(self.clone())
}
}
impl Key for PrivateKey {
fn as_public_key(
&self,
secp: &Secp256k1<All>,
@ -73,18 +115,15 @@ 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> {
@ -99,7 +138,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 Key>, Error> {
fn public(&self, secp: &Secp256k1<All>) -> Result<Box<dyn RealKey>, Error> {
if self.final_index == DerivationIndex::Hardened {
return Err(Error::HardenedDerivationOnXpub);
}
@ -163,10 +202,6 @@ impl Key for DescriptorExtendedKey {
}))
}
fn as_secret_key(&self) -> Option<PrivateKey> {
None
}
fn xprv(&self) -> Option<ExtendedPrivKey> {
self.secret
}
@ -179,3 +214,67 @@ 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;
mod keys;
pub 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::Key;
use self::keys::{parse_key, DummyKey, Key, RealKey};
trait MiniscriptExtractPolicy {
pub(crate) trait MiniscriptExtractPolicy {
fn extract_policy(
&self,
lookup_map: &BTreeMap<String, Box<dyn Key>>,
@ -40,31 +40,6 @@ 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>;
@ -112,13 +87,13 @@ where
#[derive(Debug, Serialize, Deserialize)]
pub struct ExtendedDescriptor {
#[serde(flatten)]
internal: StringDescriptor,
pub(crate) internal: StringDescriptor,
#[serde(skip)]
keys: BTreeMap<String, Box<dyn Key>>,
pub(crate) keys: BTreeMap<String, Box<dyn RealKey>>,
#[serde(skip)]
ctx: Secp256k1<All>,
pub(crate) ctx: Secp256k1<All>,
}
impl fmt::Display for ExtendedDescriptor {
@ -144,30 +119,18 @@ 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 Key>>> = RefCell::new(BTreeMap::new());
let keys: RefCell<BTreeMap<String, Box<dyn RealKey>>> = RefCell::new(BTreeMap::new());
let translatefpk = |string: &String| -> Result<_, Error> {
let (key, parsed) = Self::parse_string(string)?;
let (key, parsed) = parse_key(string)?;
keys.borrow_mut().insert(key, parsed);
Ok(DummyKey::default())
};
let translatefpkh = |string: &String| -> Result<_, Error> {
let (key, parsed) = Self::parse_string(string)?;
let (key, parsed) = parse_key(string)?;
keys.borrow_mut().insert(key, parsed);
Ok(DummyKey::default())
@ -329,7 +292,7 @@ impl ExtendedDescriptor {
}
pub fn as_public_version(&self) -> Result<ExtendedDescriptor, Error> {
let keys: RefCell<BTreeMap<String, Box<dyn Key>>> = RefCell::new(BTreeMap::new());
let keys: RefCell<BTreeMap<String, Box<dyn RealKey>>> = RefCell::new(BTreeMap::new());
let translatefpk = |string: &String| -> Result<_, Error> {
let public = self.keys.get(string).unwrap().public(&self.ctx)?;
@ -360,7 +323,13 @@ impl ExtendedDescriptor {
impl ExtractPolicy for ExtendedDescriptor {
fn extract_policy(&self) -> Result<Option<Policy>, Error> {
self.internal.extract_policy(&self.keys)
self.internal.extract_policy(
&self
.keys
.iter()
.map(|(k, v)| (k.into(), v.into_key()))
.collect(),
)
}
}

View File

@ -27,19 +27,27 @@ 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();
let pubkey = k.as_public_key(&secp, None).unwrap();
if let Some(fing) = k.fingerprint(&secp) {
if let Some(alias) = k.alias() {
PKOrF {
alias: Some(alias.into()),
..Default::default()
}
} else 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,6 +33,8 @@ 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;

231
src/multiparty/mod.rs Normal file
View File

@ -0,0 +1,231 @@
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()
}
}