diff --git a/Cargo.toml b/Cargo.toml index 5137c7f2..a4f61b37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/examples/multiparty.rs b/examples/multiparty.rs new file mode 100644 index 00000000..93b935c2 --- /dev/null +++ b/examples/multiparty.rs @@ -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::::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 = 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 = 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); +} diff --git a/src/descriptor/error.rs b/src/descriptor/error.rs index 7a0d076e..4f570daa 100644 --- a/src/descriptor/error.rs +++ b/src/descriptor/error.rs @@ -6,6 +6,12 @@ pub enum Error { MalformedInput, KeyParsingError(String), + AliasAsPublicKey, + KeyHasSecret, + Incomplete, + MissingAlias(String), + InvalidAlias(String), + Policy(crate::descriptor::policy::PolicyError), InputIndexDoesntExist, diff --git a/src/descriptor/keys.rs b/src/descriptor/keys.rs index 7ff028d9..71564953 100644 --- a/src/descriptor/keys.rs +++ b/src/descriptor/keys.rs @@ -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) -> Option; - fn as_public_key(&self, secp: &Secp256k1, index: Option) -> Result; - fn as_secret_key(&self) -> Option; - fn xprv(&self) -> Option; - fn full_path(&self, index: u32) -> Option; - 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) -> Result, Error> { - Ok(Box::new(self.as_public_key(secp, None)?)) +impl KeyAlias { + pub(crate) fn new_boxed(alias: &str, has_secret: bool) -> Box { + Box::new(KeyAlias { + alias: alias.into(), + has_secret, + }) } } -impl Key for PublicKey { - fn fingerprint(&self, _secp: &Secp256k1) -> Option { - None +pub(crate) fn parse_key(string: &str) -> Result<(String, Box), 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, - _index: Option, - ) -> Result { - 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, index: Option) -> Result; + fn is_fixed(&self) -> bool; + + fn alias(&self) -> Option<&str> { + None } fn as_secret_key(&self) -> Option { @@ -51,16 +59,50 @@ impl Key for PublicKey { None } + fn fingerprint(&self, _secp: &Secp256k1) -> Option { + None + } + + fn has_secret(&self) -> bool { + self.xprv().is_some() || self.as_secret_key().is_some() + } + + fn public(&self, secp: &Secp256k1) -> Result, Error> { + Ok(Box::new(self.as_public_key(secp, None)?)) + } +} + +pub trait RealKey: Key { + fn into_key(&self) -> Box; +} + +impl From for Box { + fn from(key: T) -> Self { + Box::new(key) + } +} + +impl Key for PublicKey { + fn as_public_key( + &self, + _secp: &Secp256k1, + _index: Option, + ) -> Result { + Ok(PublicKey::clone(self)) + } + fn is_fixed(&self) -> bool { true } } -impl Key for PrivateKey { - fn fingerprint(&self, _secp: &Secp256k1) -> Option { - None +impl RealKey for PublicKey { + fn into_key(&self) -> Box { + Box::new(self.clone()) } +} +impl Key for PrivateKey { fn as_public_key( &self, secp: &Secp256k1, @@ -73,18 +115,15 @@ impl Key for PrivateKey { Some(PrivateKey::clone(self)) } - fn xprv(&self) -> Option { - None - } - - fn full_path(&self, _index: u32) -> Option { - None - } - fn is_fixed(&self) -> bool { true } } +impl RealKey for PrivateKey { + fn into_key(&self) -> Box { + Box::new(self.clone()) + } +} impl Key for DescriptorExtendedKey { fn fingerprint(&self, secp: &Secp256k1) -> Option { @@ -99,7 +138,7 @@ impl Key for DescriptorExtendedKey { Ok(self.derive_xpub(secp, index.unwrap_or(0))?.public_key) } - fn public(&self, secp: &Secp256k1) -> Result, Error> { + fn public(&self, secp: &Secp256k1) -> Result, 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 { - None - } - fn xprv(&self) -> Option { self.secret } @@ -179,3 +214,67 @@ impl Key for DescriptorExtendedKey { self.final_index == DerivationIndex::Fixed } } +impl RealKey for DescriptorExtendedKey { + fn into_key(&self) -> Box { + 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, + _index: Option, + ) -> Result { + 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) -> Result, 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 { + Ok(DummyKey::default()) + } +} + +impl miniscript::MiniscriptKey for DummyKey { + type Hash = DummyKey; + + fn to_pubkeyhash(&self) -> DummyKey { + DummyKey::default() + } +} diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs index 6f142a83..492f2c15 100644 --- a/src/descriptor/mod.rs +++ b/src/descriptor/mod.rs @@ -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>, @@ -40,31 +40,6 @@ pub trait ExtractPolicy { fn extract_policy(&self) -> Result, 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 { - Ok(DummyKey::default()) - } -} - -impl miniscript::MiniscriptKey for DummyKey { - type Hash = DummyKey; - - fn to_pubkeyhash(&self) -> DummyKey { - DummyKey::default() - } -} - pub type DerivedDescriptor = Descriptor; pub type StringDescriptor = Descriptor; @@ -112,13 +87,13 @@ where #[derive(Debug, Serialize, Deserialize)] pub struct ExtendedDescriptor { #[serde(flatten)] - internal: StringDescriptor, + pub(crate) internal: StringDescriptor, #[serde(skip)] - keys: BTreeMap>, + pub(crate) keys: BTreeMap>, #[serde(skip)] - ctx: Secp256k1, + pub(crate) ctx: Secp256k1, } impl fmt::Display for ExtendedDescriptor { @@ -144,30 +119,18 @@ impl std::convert::AsRef for ExtendedDescriptor { } impl ExtendedDescriptor { - fn parse_string(string: &str) -> Result<(String, Box), 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 { let ctx = Secp256k1::gen_new(); - let keys: RefCell>> = RefCell::new(BTreeMap::new()); + let keys: RefCell>> = 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 { - let keys: RefCell>> = RefCell::new(BTreeMap::new()); + let keys: RefCell>> = 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, Error> { - self.internal.extract_policy(&self.keys) + self.internal.extract_policy( + &self + .keys + .iter() + .map(|(k, v)| (k.into(), v.into_key())) + .collect(), + ) } } diff --git a/src/descriptor/policy.rs b/src/descriptor/policy.rs index 4e10390b..58289bdc 100644 --- a/src/descriptor/policy.rs +++ b/src/descriptor/policy.rs @@ -27,19 +27,27 @@ pub struct PKOrF { pubkey_hash: Option, #[serde(skip_serializing_if = "Option::is_none")] fingerprint: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + alias: Option, } impl PKOrF { fn from_key(k: &Box) -> 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() diff --git a/src/lib.rs b/src/lib.rs index 8cd97373..dad1f89d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/src/multiparty/mod.rs b/src/multiparty/mod.rs new file mode 100644 index 00000000..4429f6f1 --- /dev/null +++ b/src/multiparty/mod.rs @@ -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 { + descriptor: StringDescriptor, + parsed_keys: BTreeMap>, + received_keys: BTreeMap>, + + _data: T, +} + +impl Participant { + pub fn new(sd: StringDescriptor) -> Result { + 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> { + let keys: RefCell>> = 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, 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 { + 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 { + pub fn descriptor(&self) -> &StringDescriptor { + &self.descriptor + } + + pub fn add_key(&mut self, alias: &str, key: Box) -> 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 { + 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, Error> { + if !self.completed() { + return Err(Error::Incomplete); + } + + Ok(self + .received_keys + .iter() + .map(|(k, v)| (k.into(), format!("{}", v))) + .collect()) + } +} + +impl Participant { + pub fn policy(&self) -> Result, Error> { + self.policy_for(vec!["[PEER]"]) + } + + pub fn use_key(&mut self, key: Box) -> 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> { + self.received_keys.get("[PEER]".into()) + } + + pub fn apply_map(mut self, map: BTreeMap) -> Result { + let mut parsed_map: BTreeMap<_, _> = map + .into_iter() + .map(|(k, v)| -> Result<_, Error> { + let (_, parsed) = parse_key(&v)?; + Ok((k, parsed)) + }) + .collect::>()?; + + self.received_keys.append(&mut parsed_map); + + self.finalize() + } +}