Compare commits
1 Commits
frost
...
multiparty
Author | SHA1 | Date | |
---|---|---|---|
|
9c4e5b4d25 |
@ -28,6 +28,7 @@ electrum = ["electrum-client"]
|
|||||||
esplora = ["reqwest", "futures"]
|
esplora = ["reqwest", "futures"]
|
||||||
key-value-db = ["sled"]
|
key-value-db = ["sled"]
|
||||||
cli-utils = ["clap"]
|
cli-utils = ["clap"]
|
||||||
|
multiparty = []
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = { version = "0.2", features = ["macros"] }
|
tokio = { version = "0.2", features = ["macros"] }
|
||||||
@ -50,6 +51,10 @@ name = "miniscriptc"
|
|||||||
path = "examples/compiler.rs"
|
path = "examples/compiler.rs"
|
||||||
required-features = ["compiler"]
|
required-features = ["compiler"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "multiparty"
|
||||||
|
required-features = ["multiparty","compiler"]
|
||||||
|
|
||||||
# Provide a more user-friendly alias for the REPL
|
# Provide a more user-friendly alias for the REPL
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "magic"
|
name = "magic"
|
||||||
|
96
examples/multiparty.rs
Normal file
96
examples/multiparty.rs
Normal 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);
|
||||||
|
}
|
@ -6,6 +6,12 @@ pub enum Error {
|
|||||||
MalformedInput,
|
MalformedInput,
|
||||||
KeyParsingError(String),
|
KeyParsingError(String),
|
||||||
|
|
||||||
|
AliasAsPublicKey,
|
||||||
|
KeyHasSecret,
|
||||||
|
Incomplete,
|
||||||
|
MissingAlias(String),
|
||||||
|
InvalidAlias(String),
|
||||||
|
|
||||||
Policy(crate::descriptor::policy::PolicyError),
|
Policy(crate::descriptor::policy::PolicyError),
|
||||||
|
|
||||||
InputIndexDoesntExist,
|
InputIndexDoesntExist,
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
use std::fmt;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use bitcoin::secp256k1::{All, Secp256k1};
|
use bitcoin::secp256k1::{All, Secp256k1};
|
||||||
use bitcoin::{PrivateKey, PublicKey};
|
use bitcoin::{PrivateKey, PublicKey};
|
||||||
|
|
||||||
@ -9,36 +12,41 @@ use super::error::Error;
|
|||||||
use super::extended_key::DerivationIndex;
|
use super::extended_key::DerivationIndex;
|
||||||
use super::DescriptorExtendedKey;
|
use super::DescriptorExtendedKey;
|
||||||
|
|
||||||
pub(super) trait Key: std::fmt::Debug + std::fmt::Display {
|
#[derive(Debug, Clone)]
|
||||||
fn fingerprint(&self, secp: &Secp256k1<All>) -> Option<Fingerprint>;
|
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 {
|
||||||
fn as_public_key(&self, secp: &Secp256k1<All>, index: Option<u32>) -> Result<PublicKey, Error>;
|
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 is_fixed(&self) -> bool;
|
||||||
|
|
||||||
fn has_secret(&self) -> bool {
|
fn alias(&self) -> Option<&str> {
|
||||||
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
|
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> {
|
fn as_secret_key(&self) -> Option<PrivateKey> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -51,16 +59,50 @@ impl Key for PublicKey {
|
|||||||
None
|
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 {
|
fn is_fixed(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Key for PrivateKey {
|
impl RealKey for PublicKey {
|
||||||
fn fingerprint(&self, _secp: &Secp256k1<All>) -> Option<Fingerprint> {
|
fn into_key(&self) -> Box<dyn Key> {
|
||||||
None
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Key for PrivateKey {
|
||||||
fn as_public_key(
|
fn as_public_key(
|
||||||
&self,
|
&self,
|
||||||
secp: &Secp256k1<All>,
|
secp: &Secp256k1<All>,
|
||||||
@ -73,18 +115,15 @@ impl Key for PrivateKey {
|
|||||||
Some(PrivateKey::clone(self))
|
Some(PrivateKey::clone(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn xprv(&self) -> Option<ExtendedPrivKey> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn full_path(&self, _index: u32) -> Option<DerivationPath> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_fixed(&self) -> bool {
|
fn is_fixed(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl RealKey for PrivateKey {
|
||||||
|
fn into_key(&self) -> Box<dyn Key> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Key for DescriptorExtendedKey {
|
impl Key for DescriptorExtendedKey {
|
||||||
fn fingerprint(&self, secp: &Secp256k1<All>) -> Option<Fingerprint> {
|
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)
|
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 {
|
if self.final_index == DerivationIndex::Hardened {
|
||||||
return Err(Error::HardenedDerivationOnXpub);
|
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> {
|
fn xprv(&self) -> Option<ExtendedPrivKey> {
|
||||||
self.secret
|
self.secret
|
||||||
}
|
}
|
||||||
@ -179,3 +214,67 @@ impl Key for DescriptorExtendedKey {
|
|||||||
self.final_index == DerivationIndex::Fixed
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -19,7 +19,7 @@ use crate::psbt::utils::PSBTUtils;
|
|||||||
pub mod checksum;
|
pub mod checksum;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod extended_key;
|
pub mod extended_key;
|
||||||
mod keys;
|
pub mod keys;
|
||||||
pub mod policy;
|
pub mod policy;
|
||||||
|
|
||||||
pub use self::checksum::get_checksum;
|
pub use self::checksum::get_checksum;
|
||||||
@ -27,9 +27,9 @@ use self::error::Error;
|
|||||||
pub use self::extended_key::{DerivationIndex, DescriptorExtendedKey};
|
pub use self::extended_key::{DerivationIndex, DescriptorExtendedKey};
|
||||||
pub use self::policy::Policy;
|
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(
|
fn extract_policy(
|
||||||
&self,
|
&self,
|
||||||
lookup_map: &BTreeMap<String, Box<dyn Key>>,
|
lookup_map: &BTreeMap<String, Box<dyn Key>>,
|
||||||
@ -40,31 +40,6 @@ pub trait ExtractPolicy {
|
|||||||
fn extract_policy(&self) -> Result<Option<Policy>, Error>;
|
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 DerivedDescriptor = Descriptor<PublicKey>;
|
||||||
pub type StringDescriptor = Descriptor<String>;
|
pub type StringDescriptor = Descriptor<String>;
|
||||||
|
|
||||||
@ -112,13 +87,13 @@ where
|
|||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct ExtendedDescriptor {
|
pub struct ExtendedDescriptor {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
internal: StringDescriptor,
|
pub(crate) internal: StringDescriptor,
|
||||||
|
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
keys: BTreeMap<String, Box<dyn Key>>,
|
pub(crate) keys: BTreeMap<String, Box<dyn RealKey>>,
|
||||||
|
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
ctx: Secp256k1<All>,
|
pub(crate) ctx: Secp256k1<All>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ExtendedDescriptor {
|
impl fmt::Display for ExtendedDescriptor {
|
||||||
@ -144,30 +119,18 @@ impl std::convert::AsRef<StringDescriptor> for ExtendedDescriptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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> {
|
fn new(sd: StringDescriptor) -> Result<Self, Error> {
|
||||||
let ctx = Secp256k1::gen_new();
|
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 translatefpk = |string: &String| -> Result<_, Error> {
|
||||||
let (key, parsed) = Self::parse_string(string)?;
|
let (key, parsed) = parse_key(string)?;
|
||||||
keys.borrow_mut().insert(key, parsed);
|
keys.borrow_mut().insert(key, parsed);
|
||||||
|
|
||||||
Ok(DummyKey::default())
|
Ok(DummyKey::default())
|
||||||
};
|
};
|
||||||
let translatefpkh = |string: &String| -> Result<_, Error> {
|
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);
|
keys.borrow_mut().insert(key, parsed);
|
||||||
|
|
||||||
Ok(DummyKey::default())
|
Ok(DummyKey::default())
|
||||||
@ -329,7 +292,7 @@ impl ExtendedDescriptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_public_version(&self) -> Result<ExtendedDescriptor, Error> {
|
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 translatefpk = |string: &String| -> Result<_, Error> {
|
||||||
let public = self.keys.get(string).unwrap().public(&self.ctx)?;
|
let public = self.keys.get(string).unwrap().public(&self.ctx)?;
|
||||||
@ -360,7 +323,13 @@ impl ExtendedDescriptor {
|
|||||||
|
|
||||||
impl ExtractPolicy for ExtendedDescriptor {
|
impl ExtractPolicy for ExtendedDescriptor {
|
||||||
fn extract_policy(&self) -> Result<Option<Policy>, Error> {
|
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(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,19 +27,27 @@ pub struct PKOrF {
|
|||||||
pubkey_hash: Option<hash160::Hash>,
|
pubkey_hash: Option<hash160::Hash>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
fingerprint: Option<Fingerprint>,
|
fingerprint: Option<Fingerprint>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
alias: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PKOrF {
|
impl PKOrF {
|
||||||
fn from_key(k: &Box<dyn Key>) -> Self {
|
fn from_key(k: &Box<dyn Key>) -> Self {
|
||||||
let secp = Secp256k1::gen_new();
|
let secp = Secp256k1::gen_new();
|
||||||
|
|
||||||
let pubkey = k.as_public_key(&secp, None).unwrap();
|
if let Some(alias) = k.alias() {
|
||||||
if let Some(fing) = k.fingerprint(&secp) {
|
PKOrF {
|
||||||
|
alias: Some(alias.into()),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
} else if let Some(fing) = k.fingerprint(&secp) {
|
||||||
PKOrF {
|
PKOrF {
|
||||||
fingerprint: Some(fing),
|
fingerprint: Some(fing),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
let pubkey = k.as_public_key(&secp, None).unwrap();
|
||||||
PKOrF {
|
PKOrF {
|
||||||
pubkey: Some(pubkey),
|
pubkey: Some(pubkey),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -33,6 +33,8 @@ pub mod error;
|
|||||||
pub mod blockchain;
|
pub mod blockchain;
|
||||||
pub mod database;
|
pub mod database;
|
||||||
pub mod descriptor;
|
pub mod descriptor;
|
||||||
|
#[cfg(feature = "multiparty")]
|
||||||
|
pub mod multiparty;
|
||||||
pub mod psbt;
|
pub mod psbt;
|
||||||
pub mod signer;
|
pub mod signer;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
231
src/multiparty/mod.rs
Normal file
231
src/multiparty/mod.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user