2020-08-31 11:26:36 +02:00
// Magical Bitcoin Library
// Written in 2020 by
// Alekos Filini <alekos.filini@gmail.com>
//
// Copyright (c) 2020 Magical Bitcoin
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
2020-02-17 14:22:53 +01:00
use std ::cmp ::max ;
use std ::collections ::{ BTreeMap , HashSet , VecDeque } ;
2020-08-12 12:51:50 +02:00
use std ::sync ::Arc ;
2020-02-07 23:22:28 +01:00
2020-02-17 14:22:53 +01:00
use serde ::ser ::SerializeMap ;
use serde ::{ Serialize , Serializer } ;
2020-02-07 23:22:28 +01:00
use bitcoin ::hashes ::* ;
use bitcoin ::util ::bip32 ::Fingerprint ;
use bitcoin ::PublicKey ;
2020-08-12 12:51:50 +02:00
use miniscript ::descriptor ::DescriptorPublicKey ;
use miniscript ::{ Descriptor , Miniscript , MiniscriptKey , ScriptContext , Terminal } ;
2020-02-07 23:22:28 +01:00
2020-02-17 14:22:53 +01:00
#[ allow(unused_imports) ]
use log ::{ debug , error , info , trace } ;
2020-02-07 23:22:28 +01:00
2020-08-12 12:51:50 +02:00
use crate ::descriptor ::ExtractPolicy ;
use crate ::wallet ::signer ::{ SignerId , SignersContainer } ;
2020-05-16 16:48:31 +02:00
use super ::checksum ::get_checksum ;
2020-02-17 14:22:53 +01:00
use super ::error ::Error ;
2020-08-12 12:51:50 +02:00
use super ::XKeyUtils ;
2020-02-17 14:22:53 +01:00
#[ derive(Debug, Clone, Default, Serialize) ]
2020-02-07 23:22:28 +01:00
pub struct PKOrF {
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pubkey : Option < PublicKey > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2020-02-17 14:22:53 +01:00
pubkey_hash : Option < hash160 ::Hash > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2020-02-07 23:22:28 +01:00
fingerprint : Option < Fingerprint > ,
}
impl PKOrF {
2020-08-12 12:51:50 +02:00
fn from_key ( k : & DescriptorPublicKey ) -> Self {
match k {
DescriptorPublicKey ::PubKey ( pubkey ) = > PKOrF {
pubkey : Some ( * pubkey ) ,
2020-02-17 14:22:53 +01:00
.. Default ::default ( )
2020-08-12 12:51:50 +02:00
} ,
DescriptorPublicKey ::XPub ( xpub ) = > PKOrF {
fingerprint : Some ( xpub . root_fingerprint ( ) ) ,
2020-02-17 14:22:53 +01:00
.. Default ::default ( )
2020-08-12 12:51:50 +02:00
} ,
}
}
fn from_key_hash ( k : hash160 ::Hash ) -> Self {
PKOrF {
pubkey_hash : Some ( k ) ,
.. Default ::default ( )
2020-02-07 23:22:28 +01:00
}
}
}
2020-02-17 14:22:53 +01:00
#[ derive(Debug, Clone, Serialize) ]
2020-02-07 23:22:28 +01:00
#[ serde(tag = " type " , rename_all = " UPPERCASE " ) ]
pub enum SatisfiableItem {
// Leaves
Signature ( PKOrF ) ,
2020-02-17 14:22:53 +01:00
SignatureKey ( PKOrF ) ,
2020-02-07 23:22:28 +01:00
SHA256Preimage {
hash : sha256 ::Hash ,
} ,
HASH256Preimage {
hash : sha256d ::Hash ,
} ,
RIPEMD160Preimage {
hash : ripemd160 ::Hash ,
} ,
HASH160Preimage {
hash : hash160 ::Hash ,
} ,
AbsoluteTimelock {
2020-02-15 21:27:51 +01:00
value : u32 ,
2020-02-07 23:22:28 +01:00
} ,
RelativeTimelock {
2020-02-15 21:27:51 +01:00
value : u32 ,
2020-02-07 23:22:28 +01:00
} ,
// Complex item
Thresh {
items : Vec < Policy > ,
threshold : usize ,
} ,
Multisig {
keys : Vec < PKOrF > ,
threshold : usize ,
} ,
}
impl SatisfiableItem {
pub fn is_leaf ( & self ) -> bool {
match self {
SatisfiableItem ::Thresh {
items : _ ,
threshold : _ ,
} = > false ,
_ = > true ,
}
}
2020-05-16 16:48:31 +02:00
pub fn id ( & self ) -> String {
get_checksum ( & serde_json ::to_string ( self ) . expect ( " Failed to serialize a SatisfiableItem " ) )
. expect ( " Failed to compute a SatisfiableItem id " )
}
2020-02-17 14:22:53 +01:00
}
2020-02-15 21:27:51 +01:00
2020-02-17 14:22:53 +01:00
fn combinations ( vec : & Vec < usize > , size : usize ) -> Vec < Vec < usize > > {
assert! ( vec . len ( ) > = size ) ;
let mut answer = Vec ::new ( ) ;
let mut queue = VecDeque ::new ( ) ;
for ( index , val ) in vec . iter ( ) . enumerate ( ) {
let mut new_vec = Vec ::with_capacity ( size ) ;
new_vec . push ( * val ) ;
queue . push_back ( ( index , new_vec ) ) ;
2020-02-15 21:27:51 +01:00
}
2020-02-17 14:22:53 +01:00
while let Some ( ( index , vals ) ) = queue . pop_front ( ) {
if vals . len ( ) > = size {
answer . push ( vals ) ;
} else {
for ( new_index , val ) in vec . iter ( ) . skip ( index + 1 ) . enumerate ( ) {
let mut cloned = vals . clone ( ) ;
cloned . push ( * val ) ;
queue . push_front ( ( new_index , cloned ) ) ;
}
}
}
answer
2020-02-07 23:22:28 +01:00
}
2020-02-17 14:22:53 +01:00
fn mix < T : Clone > ( vec : Vec < Vec < T > > ) -> Vec < Vec < T > > {
if vec . is_empty ( ) | | vec . iter ( ) . any ( Vec ::is_empty ) {
return vec! [ ] ;
}
let mut answer = Vec ::new ( ) ;
let size = vec . len ( ) ;
let mut queue = VecDeque ::new ( ) ;
for i in & vec [ 0 ] {
let mut new_vec = Vec ::with_capacity ( size ) ;
new_vec . push ( i . clone ( ) ) ;
queue . push_back ( new_vec ) ;
}
while let Some ( vals ) = queue . pop_front ( ) {
if vals . len ( ) > = size {
answer . push ( vals ) ;
} else {
let level = vals . len ( ) ;
for i in & vec [ level ] {
let mut cloned = vals . clone ( ) ;
cloned . push ( i . clone ( ) ) ;
queue . push_front ( cloned ) ;
}
}
}
answer
}
pub type ConditionMap = BTreeMap < usize , HashSet < Condition > > ;
pub type FoldedConditionMap = BTreeMap < Vec < usize > , HashSet < Condition > > ;
fn serialize_folded_cond_map < S > (
input_map : & FoldedConditionMap ,
serializer : S ,
) -> Result < S ::Ok , S ::Error >
where
S : Serializer ,
{
let mut map = serializer . serialize_map ( Some ( input_map . len ( ) ) ) ? ;
for ( k , v ) in input_map {
let k_string = format! ( " {:?} " , k ) ;
map . serialize_entry ( & k_string , v ) ? ;
}
map . end ( )
}
#[ derive(Debug, Clone, Serialize) ]
2020-02-15 21:27:51 +01:00
#[ serde(tag = " type " , rename_all = " UPPERCASE " ) ]
pub enum Satisfaction {
Partial {
2020-02-17 14:22:53 +01:00
n : usize ,
2020-02-15 21:27:51 +01:00
m : usize ,
2020-02-17 14:22:53 +01:00
items : Vec < usize > ,
#[ serde(skip_serializing_if = " BTreeMap::is_empty " ) ]
conditions : ConditionMap ,
} ,
PartialComplete {
2020-02-15 21:27:51 +01:00
n : usize ,
2020-02-17 14:22:53 +01:00
m : usize ,
items : Vec < usize > ,
#[ serde(
serialize_with = " serialize_folded_cond_map " ,
skip_serializing_if = " BTreeMap::is_empty "
) ]
conditions : FoldedConditionMap ,
} ,
Complete {
condition : Condition ,
2020-02-15 21:27:51 +01:00
} ,
None ,
}
impl Satisfaction {
2020-02-17 14:22:53 +01:00
pub fn is_leaf ( & self ) -> bool {
match self {
Satisfaction ::None | Satisfaction ::Complete { .. } = > true ,
Satisfaction ::PartialComplete { .. } | Satisfaction ::Partial { .. } = > false ,
2020-02-15 21:27:51 +01:00
}
}
2020-02-17 14:22:53 +01:00
// add `inner` as one of self's partial items. this only makes sense on partials
fn add ( & mut self , inner : & Satisfaction , inner_index : usize ) -> Result < ( ) , PolicyError > {
match self {
Satisfaction ::None | Satisfaction ::Complete { .. } = > Err ( PolicyError ::AddOnLeaf ) ,
Satisfaction ::PartialComplete { .. } = > Err ( PolicyError ::AddOnPartialComplete ) ,
Satisfaction ::Partial {
n ,
ref mut conditions ,
ref mut items ,
..
} = > {
if inner_index > = * n | | items . contains ( & inner_index ) {
return Err ( PolicyError ::IndexOutOfRange ( inner_index ) ) ;
}
match inner {
// not relevant if not completed yet
Satisfaction ::None | Satisfaction ::Partial { .. } = > return Ok ( ( ) ) ,
Satisfaction ::Complete { condition } = > {
items . push ( inner_index ) ;
conditions . insert ( inner_index , vec! [ * condition ] . into_iter ( ) . collect ( ) ) ;
}
Satisfaction ::PartialComplete {
conditions : other_conditions ,
..
} = > {
items . push ( inner_index ) ;
let conditions_set = other_conditions
. values ( )
. fold ( HashSet ::new ( ) , | set , i | set . union ( & i ) . cloned ( ) . collect ( ) ) ;
conditions . insert ( inner_index , conditions_set ) ;
}
}
2020-02-15 21:27:51 +01:00
2020-02-17 14:22:53 +01:00
Ok ( ( ) )
}
}
2020-02-15 21:27:51 +01:00
}
2020-02-17 14:22:53 +01:00
fn finalize ( & mut self ) -> Result < ( ) , PolicyError > {
// if partial try to bump it to a partialcomplete
if let Satisfaction ::Partial {
n ,
m ,
items ,
conditions ,
} = self
{
if items . len ( ) > = * m {
let mut map = BTreeMap ::new ( ) ;
let indexes = combinations ( items , * m ) ;
// `indexes` at this point is a Vec<Vec<usize>>, with the "n choose k" of items (m of n)
indexes
. into_iter ( )
// .inspect(|x| println!("--- orig --- {:?}", x))
// we map each of the combinations of elements into a tuple of ([choosen items], [conditions]). unfortunately, those items have potentially more than one
// condition (think about ORs), so we also use `mix` to expand those, i.e. [[0], [1, 2]] becomes [[0, 1], [0, 2]]. This is necessary to make sure that we
// consider every possibile options and check whether or not they are compatible.
. map ( | i_vec | {
mix ( i_vec
. iter ( )
. map ( | i | {
conditions
. get ( i )
. and_then ( | set | Some ( set . clone ( ) . into_iter ( ) . collect ( ) ) )
. unwrap_or ( vec! [ ] )
} )
. collect ( ) )
. into_iter ( )
. map ( | x | ( i_vec . clone ( ) , x ) )
. collect ::< Vec < ( Vec < usize > , Vec < Condition > ) > > ( )
} )
// .inspect(|x: &Vec<(Vec<usize>, Vec<Condition>)>| println!("fetch {:?}", x))
// since the previous step can turn one item of the iterator into multiple ones, we call flatten to expand them out
. flatten ( )
// .inspect(|x| println!("flat {:?}", x))
// try to fold all the conditions for this specific combination of indexes/options. if they are not compatibile, try_fold will be Err
. map ( | ( key , val ) | {
(
key ,
val . into_iter ( )
. try_fold ( Condition ::default ( ) , | acc , v | acc . merge ( & v ) ) ,
)
} )
// .inspect(|x| println!("try_fold {:?}", x))
// filter out all the incompatible combinations
. filter ( | ( _ , val ) | val . is_ok ( ) )
// .inspect(|x| println!("filter {:?}", x))
// push them into the map
. for_each ( | ( key , val ) | {
map . entry ( key )
. or_insert_with ( HashSet ::new )
. insert ( val . unwrap ( ) ) ;
} ) ;
// TODO: if the map is empty, the conditions are not compatible, return an error?
* self = Satisfaction ::PartialComplete {
n : * n ,
m : * m ,
items : items . clone ( ) ,
conditions : map ,
} ;
2020-02-15 21:27:51 +01:00
}
2020-02-17 14:22:53 +01:00
}
Ok ( ( ) )
}
}
impl From < bool > for Satisfaction {
fn from ( other : bool ) -> Self {
if other {
Satisfaction ::Complete {
condition : Default ::default ( ) ,
2020-02-15 21:27:51 +01:00
}
2020-02-17 14:22:53 +01:00
} else {
Satisfaction ::None
2020-02-15 21:27:51 +01:00
}
}
2020-02-07 23:22:28 +01:00
}
2020-02-17 14:22:53 +01:00
#[ derive(Debug, Clone, Serialize) ]
2020-02-07 23:22:28 +01:00
pub struct Policy {
2020-05-16 16:48:31 +02:00
id : String ,
2020-02-07 23:22:28 +01:00
#[ serde(flatten) ]
item : SatisfiableItem ,
2020-02-15 21:27:51 +01:00
satisfaction : Satisfaction ,
contribution : Satisfaction ,
2020-02-07 23:22:28 +01:00
}
2020-02-17 14:22:53 +01:00
#[ derive(Hash, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Serialize) ]
pub struct Condition {
2020-02-15 21:27:51 +01:00
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2020-02-07 23:22:28 +01:00
pub csv : Option < u32 > ,
2020-02-15 21:27:51 +01:00
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2020-02-07 23:22:28 +01:00
pub timelock : Option < u32 > ,
}
2020-02-17 14:22:53 +01:00
impl Condition {
fn merge_timelock ( a : u32 , b : u32 ) -> Result < u32 , PolicyError > {
const BLOCKS_TIMELOCK_THRESHOLD : u32 = 500000000 ;
if ( a < BLOCKS_TIMELOCK_THRESHOLD ) ! = ( b < BLOCKS_TIMELOCK_THRESHOLD ) {
Err ( PolicyError ::MixedTimelockUnits )
} else {
Ok ( max ( a , b ) )
2020-02-07 23:22:28 +01:00
}
2020-02-17 14:22:53 +01:00
}
2020-02-07 23:22:28 +01:00
2020-02-17 14:22:53 +01:00
fn merge ( mut self , other : & Condition ) -> Result < Self , PolicyError > {
2020-02-07 23:22:28 +01:00
match ( self . csv , other . csv ) {
2020-02-17 14:22:53 +01:00
( Some ( a ) , Some ( b ) ) = > self . csv = Some ( Self ::merge_timelock ( a , b ) ? ) ,
( None , any ) = > self . csv = any ,
_ = > { }
}
2020-02-07 23:22:28 +01:00
match ( self . timelock , other . timelock ) {
2020-02-17 14:22:53 +01:00
( Some ( a ) , Some ( b ) ) = > self . timelock = Some ( Self ::merge_timelock ( a , b ) ? ) ,
( None , any ) = > self . timelock = any ,
_ = > { }
}
2020-02-07 23:22:28 +01:00
2020-02-17 14:22:53 +01:00
Ok ( self )
2020-02-07 23:22:28 +01:00
}
pub fn is_null ( & self ) -> bool {
self . csv . is_none ( ) & & self . timelock . is_none ( )
}
}
#[ derive(Debug) ]
pub enum PolicyError {
2020-05-16 16:48:31 +02:00
NotEnoughItemsSelected ( String ) ,
TooManyItemsSelected ( String ) ,
2020-02-17 14:22:53 +01:00
IndexOutOfRange ( usize ) ,
AddOnLeaf ,
AddOnPartialComplete ,
MixedTimelockUnits ,
IncompatibleConditions ,
2020-02-07 23:22:28 +01:00
}
impl Policy {
pub fn new ( item : SatisfiableItem ) -> Self {
Policy {
2020-05-16 16:48:31 +02:00
id : item . id ( ) ,
2020-02-07 23:22:28 +01:00
item ,
2020-02-15 21:27:51 +01:00
satisfaction : Satisfaction ::None ,
contribution : Satisfaction ::None ,
2020-02-07 23:22:28 +01:00
}
}
2020-02-17 14:22:53 +01:00
pub fn make_and ( a : Option < Policy > , b : Option < Policy > ) -> Result < Option < Policy > , PolicyError > {
2020-02-07 23:22:28 +01:00
match ( a , b ) {
2020-02-17 14:22:53 +01:00
( None , None ) = > Ok ( None ) ,
( Some ( x ) , None ) | ( None , Some ( x ) ) = > Ok ( Some ( x ) ) ,
2020-02-15 21:27:51 +01:00
( Some ( a ) , Some ( b ) ) = > Self ::make_thresh ( vec! [ a , b ] , 2 ) ,
2020-02-07 23:22:28 +01:00
}
}
2020-02-17 14:22:53 +01:00
pub fn make_or ( a : Option < Policy > , b : Option < Policy > ) -> Result < Option < Policy > , PolicyError > {
2020-02-07 23:22:28 +01:00
match ( a , b ) {
2020-02-17 14:22:53 +01:00
( None , None ) = > Ok ( None ) ,
( Some ( x ) , None ) | ( None , Some ( x ) ) = > Ok ( Some ( x ) ) ,
2020-02-15 21:27:51 +01:00
( Some ( a ) , Some ( b ) ) = > Self ::make_thresh ( vec! [ a , b ] , 1 ) ,
2020-02-07 23:22:28 +01:00
}
}
2020-02-17 14:22:53 +01:00
pub fn make_thresh (
items : Vec < Policy > ,
threshold : usize ,
) -> Result < Option < Policy > , PolicyError > {
2020-02-07 23:22:28 +01:00
if threshold = = 0 {
2020-02-17 14:22:53 +01:00
return Ok ( None ) ;
2020-02-07 23:22:28 +01:00
}
2020-02-17 14:22:53 +01:00
let mut contribution = Satisfaction ::Partial {
n : items . len ( ) ,
m : threshold ,
items : vec ! [ ] ,
conditions : Default ::default ( ) ,
} ;
for ( index , item ) in items . iter ( ) . enumerate ( ) {
contribution . add ( & item . contribution , index ) ? ;
}
contribution . finalize ( ) ? ;
2020-02-15 21:27:51 +01:00
let mut policy : Policy = SatisfiableItem ::Thresh { items , threshold } . into ( ) ;
policy . contribution = contribution ;
2020-02-17 14:22:53 +01:00
Ok ( Some ( policy ) )
2020-02-07 23:22:28 +01:00
}
2020-02-17 14:22:53 +01:00
fn make_multisig (
2020-08-12 12:51:50 +02:00
keys : & Vec < DescriptorPublicKey > ,
signers : Arc < SignersContainer < DescriptorPublicKey > > ,
2020-02-17 14:22:53 +01:00
threshold : usize ,
) -> Result < Option < Policy > , PolicyError > {
if threshold = = 0 {
return Ok ( None ) ;
}
2020-08-12 12:51:50 +02:00
let parsed_keys = keys . iter ( ) . map ( | k | PKOrF ::from_key ( k ) ) . collect ( ) ;
2020-02-17 14:22:53 +01:00
let mut contribution = Satisfaction ::Partial {
n : keys . len ( ) ,
m : threshold ,
items : vec ! [ ] ,
conditions : Default ::default ( ) ,
} ;
for ( index , key ) in keys . iter ( ) . enumerate ( ) {
2020-08-12 12:51:50 +02:00
if let Some ( _ ) = signers . find ( signer_id ( key ) ) {
contribution . add (
& Satisfaction ::Complete {
condition : Default ::default ( ) ,
} ,
index ,
) ? ;
}
2020-02-17 14:22:53 +01:00
}
contribution . finalize ( ) ? ;
2020-02-15 21:27:51 +01:00
let mut policy : Policy = SatisfiableItem ::Multisig {
keys : parsed_keys ,
threshold ,
}
. into ( ) ;
2020-02-17 14:22:53 +01:00
policy . contribution = contribution ;
Ok ( Some ( policy ) )
2020-02-15 21:27:51 +01:00
}
2020-02-07 23:22:28 +01:00
pub fn requires_path ( & self ) -> bool {
2020-05-16 16:48:31 +02:00
self . get_requirements ( & BTreeMap ::new ( ) ) . is_err ( )
2020-02-07 23:22:28 +01:00
}
2020-05-16 16:48:31 +02:00
pub fn get_requirements (
2020-02-07 23:22:28 +01:00
& self ,
2020-05-16 16:48:31 +02:00
path : & BTreeMap < String , Vec < usize > > ,
2020-02-17 14:22:53 +01:00
) -> Result < Condition , PolicyError > {
2020-02-07 23:22:28 +01:00
// if items.len() == threshold, selected can be omitted and we take all of them by default
let default = match & self . item {
SatisfiableItem ::Thresh { items , threshold } if items . len ( ) = = * threshold = > {
( 0 .. * threshold ) . into_iter ( ) . collect ( )
}
_ = > vec! [ ] ,
} ;
2020-05-16 16:48:31 +02:00
let selected = match path . get ( & self . id ) {
2020-02-07 23:22:28 +01:00
_ if ! default . is_empty ( ) = > & default ,
Some ( arr ) = > arr ,
_ = > & default ,
} ;
match & self . item {
SatisfiableItem ::Thresh { items , threshold } = > {
let mapped_req = items
. iter ( )
2020-05-16 16:48:31 +02:00
. map ( | i | i . get_requirements ( path ) )
2020-02-07 23:22:28 +01:00
. collect ::< Result < Vec < _ > , _ > > ( ) ? ;
// if all the requirements are null we don't care about `selected` because there
// are no requirements
2020-02-17 14:22:53 +01:00
if mapped_req . iter ( ) . all ( Condition ::is_null ) {
return Ok ( Condition ::default ( ) ) ;
2020-02-07 23:22:28 +01:00
}
// if we have something, make sure we have enough items. note that the user can set
// an empty value for this step in case of n-of-n, because `selected` is set to all
// the elements above
if selected . len ( ) < * threshold {
2020-05-16 16:48:31 +02:00
return Err ( PolicyError ::NotEnoughItemsSelected ( self . id . clone ( ) ) ) ;
} else if selected . len ( ) > * threshold {
return Err ( PolicyError ::TooManyItemsSelected ( self . id . clone ( ) ) ) ;
2020-02-07 23:22:28 +01:00
}
// check the selected items, see if there are conflicting requirements
2020-02-17 14:22:53 +01:00
let mut requirements = Condition ::default ( ) ;
2020-02-07 23:22:28 +01:00
for item_index in selected {
2020-02-17 14:22:53 +01:00
requirements = requirements . merge (
2020-02-07 23:22:28 +01:00
mapped_req
. get ( * item_index )
2020-02-17 14:22:53 +01:00
. ok_or ( PolicyError ::IndexOutOfRange ( * item_index ) ) ? ,
2020-02-07 23:22:28 +01:00
) ? ;
}
Ok ( requirements )
}
2020-05-16 16:48:31 +02:00
_ if ! selected . is_empty ( ) = > Err ( PolicyError ::TooManyItemsSelected ( self . id . clone ( ) ) ) ,
2020-02-17 14:22:53 +01:00
SatisfiableItem ::AbsoluteTimelock { value } = > Ok ( Condition {
2020-02-07 23:22:28 +01:00
csv : None ,
2020-02-15 21:27:51 +01:00
timelock : Some ( * value ) ,
2020-02-07 23:22:28 +01:00
} ) ,
2020-02-17 14:22:53 +01:00
SatisfiableItem ::RelativeTimelock { value } = > Ok ( Condition {
2020-02-15 21:27:51 +01:00
csv : Some ( * value ) ,
2020-02-07 23:22:28 +01:00
timelock : None ,
} ) ,
2020-02-17 14:22:53 +01:00
_ = > Ok ( Condition ::default ( ) ) ,
2020-02-07 23:22:28 +01:00
}
}
}
impl From < SatisfiableItem > for Policy {
fn from ( other : SatisfiableItem ) -> Self {
Self ::new ( other )
}
}
2020-08-12 12:51:50 +02:00
fn signer_id ( key : & DescriptorPublicKey ) -> SignerId < DescriptorPublicKey > {
match key {
DescriptorPublicKey ::PubKey ( pubkey ) = > pubkey . to_pubkeyhash ( ) . into ( ) ,
DescriptorPublicKey ::XPub ( xpub ) = > xpub . root_fingerprint ( ) . into ( ) ,
}
}
2020-02-15 21:27:51 +01:00
2020-08-12 12:51:50 +02:00
fn signature (
key : & DescriptorPublicKey ,
signers : Arc < SignersContainer < DescriptorPublicKey > > ,
) -> Policy {
let mut policy : Policy = SatisfiableItem ::Signature ( PKOrF ::from_key ( key ) ) . into ( ) ;
policy . contribution = if signers . find ( signer_id ( key ) ) . is_some ( ) {
Satisfaction ::Complete {
condition : Default ::default ( ) ,
}
} else {
Satisfaction ::None
} ;
policy
2020-02-07 23:22:28 +01:00
}
2020-08-12 12:51:50 +02:00
fn signature_key (
key_hash : & < DescriptorPublicKey as MiniscriptKey > ::Hash ,
signers : Arc < SignersContainer < DescriptorPublicKey > > ,
) -> Policy {
let mut policy : Policy = SatisfiableItem ::Signature ( PKOrF ::from_key_hash ( * key_hash ) ) . into ( ) ;
2020-02-07 23:22:28 +01:00
2020-08-12 12:51:50 +02:00
if let Some ( _ ) = signers . find ( SignerId ::PkHash ( * key_hash ) ) {
policy . contribution = Satisfaction ::Complete {
condition : Default ::default ( ) ,
2020-02-07 23:22:28 +01:00
}
2020-08-12 12:51:50 +02:00
}
2020-02-15 21:27:51 +01:00
2020-08-12 12:51:50 +02:00
policy
2020-02-07 23:22:28 +01:00
}
2020-08-12 12:51:50 +02:00
impl < Ctx : ScriptContext > ExtractPolicy for Miniscript < DescriptorPublicKey , Ctx > {
2020-02-17 14:22:53 +01:00
fn extract_policy (
& self ,
2020-08-12 12:51:50 +02:00
signers : Arc < SignersContainer < DescriptorPublicKey > > ,
2020-02-17 14:22:53 +01:00
) -> Result < Option < Policy > , Error > {
Ok ( match & self . node {
2020-02-07 23:22:28 +01:00
// Leaves
Terminal ::True | Terminal ::False = > None ,
2020-08-12 12:51:50 +02:00
Terminal ::PkK ( pubkey ) = > Some ( signature ( pubkey , Arc ::clone ( & signers ) ) ) ,
Terminal ::PkH ( pubkey_hash ) = > Some ( signature_key ( pubkey_hash , Arc ::clone ( & signers ) ) ) ,
2020-02-15 21:27:51 +01:00
Terminal ::After ( value ) = > {
let mut policy : Policy = SatisfiableItem ::AbsoluteTimelock { value : * value } . into ( ) ;
policy . contribution = Satisfaction ::Complete {
2020-02-17 14:22:53 +01:00
condition : Condition {
2020-02-15 21:27:51 +01:00
timelock : Some ( * value ) ,
2020-02-17 14:22:53 +01:00
csv : None ,
2020-02-15 21:27:51 +01:00
} ,
} ;
Some ( policy )
2020-02-07 23:22:28 +01:00
}
2020-02-15 21:27:51 +01:00
Terminal ::Older ( value ) = > {
let mut policy : Policy = SatisfiableItem ::RelativeTimelock { value : * value } . into ( ) ;
policy . contribution = Satisfaction ::Complete {
2020-02-17 14:22:53 +01:00
condition : Condition {
2020-02-15 21:27:51 +01:00
timelock : None ,
2020-02-17 14:22:53 +01:00
csv : Some ( * value ) ,
2020-02-15 21:27:51 +01:00
} ,
} ;
Some ( policy )
2020-02-07 23:22:28 +01:00
}
Terminal ::Sha256 ( hash ) = > Some ( SatisfiableItem ::SHA256Preimage { hash : * hash } . into ( ) ) ,
Terminal ::Hash256 ( hash ) = > {
Some ( SatisfiableItem ::HASH256Preimage { hash : * hash } . into ( ) )
}
Terminal ::Ripemd160 ( hash ) = > {
Some ( SatisfiableItem ::RIPEMD160Preimage { hash : * hash } . into ( ) )
}
Terminal ::Hash160 ( hash ) = > {
Some ( SatisfiableItem ::HASH160Preimage { hash : * hash } . into ( ) )
}
2020-08-12 12:51:50 +02:00
Terminal ::Multi ( k , pks ) = > Policy ::make_multisig ( pks , Arc ::clone ( & signers ) , * k ) ? ,
2020-02-07 23:22:28 +01:00
// Identities
Terminal ::Alt ( inner )
| Terminal ::Swap ( inner )
| Terminal ::Check ( inner )
| Terminal ::DupIf ( inner )
| Terminal ::Verify ( inner )
| Terminal ::NonZero ( inner )
2020-08-12 12:51:50 +02:00
| Terminal ::ZeroNotEqual ( inner ) = > inner . extract_policy ( Arc ::clone ( & signers ) ) ? ,
2020-02-07 23:22:28 +01:00
// Complex policies
2020-08-12 12:51:50 +02:00
Terminal ::AndV ( a , b ) | Terminal ::AndB ( a , b ) = > Policy ::make_and (
a . extract_policy ( Arc ::clone ( & signers ) ) ? ,
b . extract_policy ( Arc ::clone ( & signers ) ) ? ,
) ? ,
2020-02-07 23:22:28 +01:00
Terminal ::AndOr ( x , y , z ) = > Policy ::make_or (
2020-08-12 12:51:50 +02:00
Policy ::make_and (
x . extract_policy ( Arc ::clone ( & signers ) ) ? ,
y . extract_policy ( Arc ::clone ( & signers ) ) ? ,
) ? ,
z . extract_policy ( Arc ::clone ( & signers ) ) ? ,
2020-02-17 14:22:53 +01:00
) ? ,
2020-02-07 23:22:28 +01:00
Terminal ::OrB ( a , b )
| Terminal ::OrD ( a , b )
| Terminal ::OrC ( a , b )
2020-08-12 12:51:50 +02:00
| Terminal ::OrI ( a , b ) = > Policy ::make_or (
a . extract_policy ( Arc ::clone ( & signers ) ) ? ,
b . extract_policy ( Arc ::clone ( & signers ) ) ? ,
) ? ,
2020-02-07 23:22:28 +01:00
Terminal ::Thresh ( k , nodes ) = > {
let mut threshold = * k ;
let mapped : Vec < _ > = nodes
. iter ( )
2020-08-12 12:51:50 +02:00
. map ( | n | n . extract_policy ( Arc ::clone ( & signers ) ) )
2020-02-17 14:22:53 +01:00
. collect ::< Result < Vec < _ > , _ > > ( ) ?
. into_iter ( )
. filter_map ( | x | x )
2020-02-07 23:22:28 +01:00
. collect ( ) ;
if mapped . len ( ) < nodes . len ( ) {
threshold = match threshold . checked_sub ( nodes . len ( ) - mapped . len ( ) ) {
2020-02-17 14:22:53 +01:00
None = > return Ok ( None ) ,
2020-02-07 23:22:28 +01:00
Some ( x ) = > x ,
} ;
}
2020-02-17 14:22:53 +01:00
Policy ::make_thresh ( mapped , threshold ) ?
2020-02-07 23:22:28 +01:00
}
2020-02-17 14:22:53 +01:00
} )
2020-02-07 23:22:28 +01:00
}
}
2020-08-12 12:51:50 +02:00
impl ExtractPolicy for Descriptor < DescriptorPublicKey > {
2020-02-17 14:22:53 +01:00
fn extract_policy (
& self ,
2020-08-12 12:51:50 +02:00
signers : Arc < SignersContainer < DescriptorPublicKey > > ,
2020-02-17 14:22:53 +01:00
) -> Result < Option < Policy > , Error > {
2020-02-07 23:22:28 +01:00
match self {
Descriptor ::Pk ( pubkey )
| Descriptor ::Pkh ( pubkey )
| Descriptor ::Wpkh ( pubkey )
2020-08-12 12:51:50 +02:00
| Descriptor ::ShWpkh ( pubkey ) = > Ok ( Some ( signature ( pubkey , signers ) ) ) ,
Descriptor ::Bare ( inner ) | Descriptor ::Sh ( inner ) = > Ok ( inner . extract_policy ( signers ) ? ) ,
Descriptor ::Wsh ( inner ) | Descriptor ::ShWsh ( inner ) = > Ok ( inner . extract_policy ( signers ) ? ) ,
2020-02-07 23:22:28 +01:00
}
}
}