Write more docs

This commit is contained in:
Alekos Filini
2020-09-04 11:44:49 +02:00
parent 6b9c363937
commit 7065c1fed6
14 changed files with 341 additions and 17 deletions

View File

@@ -22,6 +22,11 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//! Descriptor checksum
//!
//! This module contains a re-implementation of the function used by Bitcoin Core to calculate the
//! checksum of a descriptor
use std::iter::FromIterator;
use crate::descriptor::Error;
@@ -51,6 +56,7 @@ fn poly_mod(mut c: u64, val: u64) -> u64 {
c
}
/// Compute the checksum of a descriptor
pub fn get_checksum(desc: &str) -> Result<String, Error> {
let mut c = 1;
let mut cls = 0;

View File

@@ -22,6 +22,9 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//! Descriptor errors
/// Errors related to the parsing and usage of descriptors
#[derive(Debug)]
pub enum Error {
InternalError,

View File

@@ -22,6 +22,11 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//! Descriptors
//!
//! This module contains generic utilities to work with descriptors, plus some re-exported types
//! from [`miniscript`].
use std::collections::{BTreeMap, HashMap};
use std::fmt;
use std::sync::Arc;
@@ -46,9 +51,17 @@ use self::error::Error;
pub use self::policy::Policy;
use crate::wallet::signer::SignersContainer;
/// Alias for a [`Descriptor`] that can contain extended keys using [`DescriptorPublicKey`]
pub type ExtendedDescriptor = Descriptor<DescriptorPublicKey>;
/// Alias for the type of maps that represent derivation paths in a [`psbt::Input`] or
/// [`psbt::Output`]
///
/// [`psbt::Input`]: bitcoin::util::psbt::Input
/// [`psbt::Output`]: bitcoin::util::psbt::Output
pub type HDKeyPaths = BTreeMap<PublicKey, (Fingerprint, DerivationPath)>;
/// Trait implemented on [`Descriptor`]s to add a method to extract the spending [`policy`]
pub trait ExtractPolicy {
fn extract_policy(
&self,
@@ -56,7 +69,7 @@ pub trait ExtractPolicy {
) -> Result<Option<Policy>, Error>;
}
pub trait XKeyUtils {
pub(crate) trait XKeyUtils {
fn full_path(&self, append: &[ChildNumber]) -> DerivationPath;
fn root_fingerprint(&self) -> Fingerprint;
}
@@ -91,7 +104,7 @@ impl<K: InnerXKey> XKeyUtils for DescriptorXKey<K> {
}
}
pub trait DescriptorMeta: Sized {
pub(crate) trait DescriptorMeta: Sized {
fn is_witness(&self) -> bool;
fn get_hd_keypaths(&self, index: u32) -> Result<HDKeyPaths, Error>;
fn is_fixed(&self) -> bool;
@@ -100,7 +113,7 @@ pub trait DescriptorMeta: Sized {
-> Option<Self>;
}
pub trait DescriptorScripts {
pub(crate) trait DescriptorScripts {
fn psbt_redeem_script(&self) -> Option<Script>;
fn psbt_witness_script(&self) -> Option<Script>;
}

View File

@@ -22,6 +22,27 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//! Descriptor policy
//!
//! This module implements the logic to extract and represent the spending policies of a descriptor
//! in a more human-readable format.
//!
//! ## Example
//!
//! ```
//! # use std::sync::Arc;
//! # use magical_bitcoin_wallet::descriptor::*;
//! let desc = "wsh(and_v(v:pk(cV3oCth6zxZ1UVsHLnGothsWNsaoxRhC6aeNi5VbSdFpwUkgkEci),or_d(pk(cVMTy7uebJgvFaSBwcgvwk8qn8xSLc97dKow4MBetjrrahZoimm2),older(12960))))";
//!
//! let (extended_desc, key_map) = ExtendedDescriptor::parse_secret(desc)?;
//! println!("{:?}", extended_desc);
//!
//! let signers = Arc::new(key_map.into());
//! let policy = extended_desc.extract_policy(signers)?;
//! println!("policy: {}", serde_json::to_string(&policy)?);
//! # Ok::<(), magical_bitcoin_wallet::Error>(())
//! ```
use std::cmp::max;
use std::collections::{BTreeMap, HashSet, VecDeque};
use std::fmt;
@@ -47,6 +68,7 @@ use super::checksum::get_checksum;
use super::error::Error;
use super::XKeyUtils;
/// Raw public key or extended key fingerprint
#[derive(Debug, Clone, Default, Serialize)]
pub struct PKOrF {
#[serde(skip_serializing_if = "Option::is_none")]
@@ -79,6 +101,7 @@ impl PKOrF {
}
}
/// An item that need to be satisfied
#[derive(Debug, Clone, Serialize)]
#[serde(tag = "type", rename_all = "UPPERCASE")]
pub enum SatisfiableItem {
@@ -208,30 +231,44 @@ where
map.end()
}
/// Represent if and how much a policy item is satisfied by the wallet's descriptor
#[derive(Debug, Clone, Serialize)]
#[serde(tag = "type", rename_all = "UPPERCASE")]
pub enum Satisfaction {
/// Only a partial satisfaction of some kind of threshold policy
Partial {
/// Total number of items
n: usize,
/// Threshold
m: usize,
/// The items that can be satisfied by the descriptor
items: Vec<usize>,
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
/// Extra conditions that also need to be satisfied
conditions: ConditionMap,
},
/// Can reach the threshold of some kind of threshold policy
PartialComplete {
/// Total number of items
n: usize,
/// Threshold
m: usize,
/// The items that can be satisfied by the descriptor
items: Vec<usize>,
#[serde(
serialize_with = "serialize_folded_cond_map",
skip_serializing_if = "BTreeMap::is_empty"
)]
/// Extra conditions that also need to be satisfied
conditions: FoldedConditionMap,
},
/// Can satisfy the policy item
Complete {
/// Extra conditions that also need to be satisfied
condition: Condition,
},
/// Cannot satisfy or contribute to the policy item
None,
}
@@ -363,6 +400,7 @@ impl From<bool> for Satisfaction {
}
}
/// Descriptor spending policy
#[derive(Debug, Clone, Serialize)]
pub struct Policy {
id: String,
@@ -373,6 +411,7 @@ pub struct Policy {
contribution: Satisfaction,
}
/// An extra condition that must be satisfied but that is out of control of the user
#[derive(Hash, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Serialize)]
pub struct Condition {
#[serde(skip_serializing_if = "Option::is_none")]
@@ -413,6 +452,7 @@ impl Condition {
}
}
/// Errors that can happen while extracting and manipulating policies
#[derive(Debug)]
pub enum PolicyError {
NotEnoughItemsSelected(String),
@@ -519,11 +559,19 @@ impl Policy {
Ok(Some(policy))
}
/// Return whether or not a specific path in the policy tree is required to unambiguously
/// create a transaction
///
/// What this means is that for some spending policies the user should select which paths in
/// the tree it intends to satisfy while signing, because the transaction must be created differently based
/// on that.
pub fn requires_path(&self) -> bool {
self.get_requirements(&BTreeMap::new()).is_err()
self.get_condition(&BTreeMap::new()).is_err()
}
pub fn get_requirements(
/// Return the conditions that are set by the spending policy for a given path in the
/// policy tree
pub fn get_condition(
&self,
path: &BTreeMap<String, Vec<usize>>,
) -> Result<Condition, PolicyError> {
@@ -544,7 +592,7 @@ impl Policy {
SatisfiableItem::Thresh { items, threshold } => {
let mapped_req = items
.iter()
.map(|i| i.get_requirements(path))
.map(|i| i.get_condition(path))
.collect::<Result<Vec<_>, _>>()?;
// if all the requirements are null we don't care about `selected` because there