Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 13 additions & 41 deletions src/descriptor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ use miniscript::{decode::Terminal, Legacy, Miniscript, Segwitv0};
use policy;
use push_opcode_size;
use script_num_size;
use util::witness_to_scriptsig;
use Bare;
use Error;
use MiniscriptKey;
Expand Down Expand Up @@ -974,24 +975,9 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
Pk: ToPublicKey<ToPkCtx>,
S: Satisfier<ToPkCtx, Pk>,
{
fn witness_to_scriptsig(witness: &[Vec<u8>]) -> Script {
let mut b = script::Builder::new();
for wit in witness {
if let Ok(n) = script::read_scriptint(wit) {
b = b.push_int(n);
} else {
b = b.push_slice(wit);
}
}
b.into_script()
}

match *self {
Descriptor::Bare(ref d) => {
let wit = match d.satisfy(satisfier, to_pk_ctx) {
Some(wit) => wit,
None => return Err(Error::CouldNotSatisfy),
};
let wit = d.satisfy(satisfier, to_pk_ctx)?;
let script_sig = witness_to_scriptsig(&wit);
let witness = vec![];
Ok((witness, script_sig))
Expand Down Expand Up @@ -1055,20 +1041,14 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
}
}
Descriptor::Sh(ref d) => {
let mut script_witness = match d.satisfy(satisfier, to_pk_ctx) {
Some(wit) => wit,
None => return Err(Error::CouldNotSatisfy),
};
let mut script_witness = d.satisfy(satisfier, to_pk_ctx)?;
script_witness.push(d.encode(to_pk_ctx).into_bytes());
let script_sig = witness_to_scriptsig(&script_witness);
let witness = vec![];
Ok((witness, script_sig))
}
Descriptor::Wsh(ref d) => {
let mut witness = match d.satisfy(satisfier, to_pk_ctx) {
Some(wit) => wit,
None => return Err(Error::CouldNotSatisfy),
};
let mut witness = d.satisfy(satisfier, to_pk_ctx)?;
witness.push(d.encode(to_pk_ctx).into_bytes());
let script_sig = Script::new();
Ok((witness, script_sig))
Expand All @@ -1079,28 +1059,19 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
.push_slice(&witness_script.to_v0_p2wsh()[..])
.into_script();

let mut witness = match d.satisfy(satisfier, to_pk_ctx) {
Some(wit) => wit,
None => return Err(Error::CouldNotSatisfy),
};
let mut witness = d.satisfy(satisfier, to_pk_ctx)?;
witness.push(witness_script.into_bytes());
Ok((witness, script_sig))
}
Descriptor::ShSortedMulti(ref smv) => {
let mut script_witness = match smv.satisfy(satisfier, to_pk_ctx) {
Some(wit) => wit,
None => return Err(Error::CouldNotSatisfy),
};
let mut script_witness = smv.satisfy(satisfier, to_pk_ctx)?;
script_witness.push(smv.encode(to_pk_ctx).into_bytes());
let script_sig = witness_to_scriptsig(&script_witness);
let witness = vec![];
Ok((witness, script_sig))
}
Descriptor::WshSortedMulti(ref smv) => {
let mut witness = match smv.satisfy(satisfier, to_pk_ctx) {
Some(wit) => wit,
None => return Err(Error::CouldNotSatisfy),
};
let mut witness = smv.satisfy(satisfier, to_pk_ctx)?;
witness.push(smv.encode(to_pk_ctx).into_bytes());
let script_sig = Script::new();
Ok((witness, script_sig))
Expand All @@ -1111,10 +1082,7 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
.push_slice(&witness_script.to_v0_p2wsh()[..])
.into_script();

let mut witness = match smv.satisfy(satisfier, to_pk_ctx) {
Some(wit) => wit,
None => return Err(Error::CouldNotSatisfy),
};
let mut witness = smv.satisfy(satisfier, to_pk_ctx)?;
witness.push(witness_script.into_bytes());
Ok((witness, script_sig))
}
Expand Down Expand Up @@ -1556,7 +1524,11 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {

/// Attempt to produce a satisfying witness for the
/// witness script represented by the parse tree
pub fn satisfy<ToPkCtx, S>(&self, satisfier: S, to_pk_ctx: ToPkCtx) -> Option<Vec<Vec<u8>>>
pub fn satisfy<ToPkCtx, S>(
&self,
satisfier: S,
to_pk_ctx: ToPkCtx,
) -> Result<Vec<Vec<u8>>, Error>
where
ToPkCtx: Copy,
Pk: ToPublicKey<ToPkCtx>,
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ pub mod miniscript;
pub mod policy;
pub mod psbt;

mod util;

use std::str::FromStr;
use std::{error, fmt, hash, str};

Expand Down
35 changes: 35 additions & 0 deletions src/miniscript/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use miniscript::types::extra_props::{
MAX_STANDARD_P2WSH_SCRIPT_SIZE, MAX_STANDARD_P2WSH_STACK_ITEMS,
};
use std::fmt;
use util::{witness_size, witness_to_scriptsig};
use Error;
use {Miniscript, MiniscriptKey, Terminal};
/// Error for Script Context
Expand Down Expand Up @@ -111,6 +112,18 @@ pub trait ScriptContext:
_frag: &Terminal<Pk, Ctx>,
) -> Result<(), ScriptContextError>;

/// Check whether the given satisfaction is valid under the ScriptContext
/// For example, segwit satisfactions may fail if the witness len is more
/// 3600 or number of stack elements are more than 100.
fn check_witness<Pk: MiniscriptKey, Ctx: ScriptContext>(
_witness: &[Vec<u8>],
) -> Result<(), ScriptContextError> {
// Only really need to do this for segwitv0 and legacy
// Bare is already restrcited by standardness rules
// and would reach these limits.
Ok(())
}

/// Depending on script context, the size of a satifaction witness may slightly differ.
fn max_satisfaction_size<Pk: MiniscriptKey, Ctx: ScriptContext>(
ms: &Miniscript<Pk, Ctx>,
Expand Down Expand Up @@ -241,6 +254,17 @@ impl ScriptContext for Legacy {
}
}

fn check_witness<Pk: MiniscriptKey, Ctx: ScriptContext>(
witness: &[Vec<u8>],
) -> Result<(), ScriptContextError> {
// In future, we could avoid by having a function to count only
// len of script instead of converting it.
if witness_to_scriptsig(witness).len() > MAX_SCRIPTSIG_SIZE {
return Err(ScriptContextError::MaxScriptSigSizeExceeded);
}
Ok(())
}

fn check_global_consensus_validity<Pk: MiniscriptKey, Ctx: ScriptContext>(
ms: &Miniscript<Pk, Ctx>,
) -> Result<(), ScriptContextError> {
Expand Down Expand Up @@ -294,6 +318,17 @@ impl ScriptContext for Segwitv0 {
Ok(())
}

fn check_witness<Pk: MiniscriptKey, Ctx: ScriptContext>(
witness: &[Vec<u8>],
) -> Result<(), ScriptContextError> {
if witness_size(witness) > MAX_STANDARD_P2WSH_SCRIPT_SIZE {
return Err(ScriptContextError::MaxScriptSigSizeExceeded);
Comment on lines +324 to +325
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm shouldn't it return a MaxWitnessSizeExceeded Error instead ?

} else if witness.len() > MAX_STANDARD_P2WSH_STACK_ITEMS {
return Err(ScriptContextError::MaxWitnessItemssExceeded);
}
Ok(())
}

fn check_global_consensus_validity<Pk: MiniscriptKey, Ctx: ScriptContext>(
ms: &Miniscript<Pk, Ctx>,
) -> Result<(), ScriptContextError> {
Expand Down
22 changes: 16 additions & 6 deletions src/miniscript/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,15 +296,20 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
&self,
satisfier: S,
to_pk_ctx: ToPkCtx,
) -> Option<Vec<Vec<u8>>>
) -> Result<Vec<Vec<u8>>, Error>
where
Pk: ToPublicKey<ToPkCtx>,
{
match satisfy::Satisfaction::satisfy(&self.node, &satisfier, self.ty.mall.safe, to_pk_ctx)
.stack
{
satisfy::Witness::Stack(stack) => Some(stack),
satisfy::Witness::Unavailable | satisfy::Witness::Impossible => None,
satisfy::Witness::Stack(stack) => {
Ctx::check_witness::<Pk, Ctx>(&stack)?;
Ok(stack)
}
satisfy::Witness::Unavailable | satisfy::Witness::Impossible => {
Err(Error::CouldNotSatisfy)
}
}
}

Expand All @@ -314,7 +319,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
&self,
satisfier: S,
to_pk_ctx: ToPkCtx,
) -> Option<Vec<Vec<u8>>>
) -> Result<Vec<Vec<u8>>, Error>
where
Pk: ToPublicKey<ToPkCtx>,
{
Expand All @@ -326,8 +331,13 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
)
.stack
{
satisfy::Witness::Stack(stack) => Some(stack),
satisfy::Witness::Unavailable | satisfy::Witness::Impossible => None,
satisfy::Witness::Stack(stack) => {
Ctx::check_witness::<Pk, Ctx>(&stack)?;
Ok(stack)
}
satisfy::Witness::Unavailable | satisfy::Witness::Impossible => {
Err(Error::CouldNotSatisfy)
}
}
}
}
Expand Down
10 changes: 1 addition & 9 deletions src/miniscript/satisfy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use {MiniscriptKey, ToPublicKey};
use miniscript::types::extra_props::{
HEIGHT_TIME_THRESHOLD, SEQUENCE_LOCKTIME_DISABLE_FLAG, SEQUENCE_LOCKTIME_TYPE_FLAG,
};
use util::witness_size;
use Error;
use Miniscript;
use NullCtx;
Expand Down Expand Up @@ -415,15 +416,6 @@ impl PartialOrd for Witness {
}
}

fn varint_len(n: usize) -> usize {
bitcoin::VarInt(n as u64).len()
}

// Helper function to calculate witness size
fn witness_size(wit: &[Vec<u8>]) -> usize {
wit.iter().map(Vec::len).sum::<usize>() + varint_len(wit.len())
}

impl Ord for Witness {
fn cmp(&self, other: &Self) -> cmp::Ordering {
match (self, other) {
Expand Down
8 changes: 4 additions & 4 deletions src/policy/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1388,15 +1388,15 @@ mod tests {
right_sat.insert(keys[i].to_pubkeyhash(), (keys[i], bitcoinsig));
}

assert!(ms.satisfy(no_sat, NullCtx).is_none());
assert!(ms.satisfy(&left_sat, NullCtx).is_some());
assert!(ms.satisfy(no_sat, NullCtx).is_err());
assert!(ms.satisfy(&left_sat, NullCtx).is_ok());
assert!(ms
.satisfy((&right_sat, satisfy::Older(10001)), NullCtx)
.is_some());
.is_ok());
//timelock not met
assert!(ms
.satisfy((&right_sat, satisfy::Older(9999)), NullCtx)
.is_none());
.is_err());

assert_eq!(
ms.satisfy((left_sat, satisfy::Older(9999)), NullCtx)
Expand Down
23 changes: 23 additions & 0 deletions src/util.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use bitcoin;
use bitcoin::blockdata::script;
use bitcoin::Script;
pub(crate) fn varint_len(n: usize) -> usize {
bitcoin::VarInt(n as u64).len()
}

// Helper function to calculate witness size
pub(crate) fn witness_size(wit: &[Vec<u8>]) -> usize {
wit.iter().map(Vec::len).sum::<usize>() + varint_len(wit.len())
}

pub(crate) fn witness_to_scriptsig(witness: &[Vec<u8>]) -> Script {
let mut b = script::Builder::new();
for wit in witness {
if let Ok(n) = script::read_scriptint(wit) {
b = b.push_int(n);
} else {
b = b.push_slice(wit);
}
}
b.into_script()
}