Skip to content

Commit c8cd42b

Browse files
authored
Merge pull request #189 from sanket1729/satisfy_fail
Fail satisfy when resource limits exceed
2 parents 28820d6 + 70b987f commit c8cd42b

File tree

7 files changed

+94
-60
lines changed

7 files changed

+94
-60
lines changed

src/descriptor/mod.rs

Lines changed: 13 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ use miniscript::{decode::Terminal, Legacy, Miniscript, Segwitv0};
4545
use policy;
4646
use push_opcode_size;
4747
use script_num_size;
48+
use util::witness_to_scriptsig;
4849
use Bare;
4950
use Error;
5051
use MiniscriptKey;
@@ -974,24 +975,9 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
974975
Pk: ToPublicKey<ToPkCtx>,
975976
S: Satisfier<ToPkCtx, Pk>,
976977
{
977-
fn witness_to_scriptsig(witness: &[Vec<u8>]) -> Script {
978-
let mut b = script::Builder::new();
979-
for wit in witness {
980-
if let Ok(n) = script::read_scriptint(wit) {
981-
b = b.push_int(n);
982-
} else {
983-
b = b.push_slice(wit);
984-
}
985-
}
986-
b.into_script()
987-
}
988-
989978
match *self {
990979
Descriptor::Bare(ref d) => {
991-
let wit = match d.satisfy(satisfier, to_pk_ctx) {
992-
Some(wit) => wit,
993-
None => return Err(Error::CouldNotSatisfy),
994-
};
980+
let wit = d.satisfy(satisfier, to_pk_ctx)?;
995981
let script_sig = witness_to_scriptsig(&wit);
996982
let witness = vec![];
997983
Ok((witness, script_sig))
@@ -1055,20 +1041,14 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
10551041
}
10561042
}
10571043
Descriptor::Sh(ref d) => {
1058-
let mut script_witness = match d.satisfy(satisfier, to_pk_ctx) {
1059-
Some(wit) => wit,
1060-
None => return Err(Error::CouldNotSatisfy),
1061-
};
1044+
let mut script_witness = d.satisfy(satisfier, to_pk_ctx)?;
10621045
script_witness.push(d.encode(to_pk_ctx).into_bytes());
10631046
let script_sig = witness_to_scriptsig(&script_witness);
10641047
let witness = vec![];
10651048
Ok((witness, script_sig))
10661049
}
10671050
Descriptor::Wsh(ref d) => {
1068-
let mut witness = match d.satisfy(satisfier, to_pk_ctx) {
1069-
Some(wit) => wit,
1070-
None => return Err(Error::CouldNotSatisfy),
1071-
};
1051+
let mut witness = d.satisfy(satisfier, to_pk_ctx)?;
10721052
witness.push(d.encode(to_pk_ctx).into_bytes());
10731053
let script_sig = Script::new();
10741054
Ok((witness, script_sig))
@@ -1079,28 +1059,19 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
10791059
.push_slice(&witness_script.to_v0_p2wsh()[..])
10801060
.into_script();
10811061

1082-
let mut witness = match d.satisfy(satisfier, to_pk_ctx) {
1083-
Some(wit) => wit,
1084-
None => return Err(Error::CouldNotSatisfy),
1085-
};
1062+
let mut witness = d.satisfy(satisfier, to_pk_ctx)?;
10861063
witness.push(witness_script.into_bytes());
10871064
Ok((witness, script_sig))
10881065
}
10891066
Descriptor::ShSortedMulti(ref smv) => {
1090-
let mut script_witness = match smv.satisfy(satisfier, to_pk_ctx) {
1091-
Some(wit) => wit,
1092-
None => return Err(Error::CouldNotSatisfy),
1093-
};
1067+
let mut script_witness = smv.satisfy(satisfier, to_pk_ctx)?;
10941068
script_witness.push(smv.encode(to_pk_ctx).into_bytes());
10951069
let script_sig = witness_to_scriptsig(&script_witness);
10961070
let witness = vec![];
10971071
Ok((witness, script_sig))
10981072
}
10991073
Descriptor::WshSortedMulti(ref smv) => {
1100-
let mut witness = match smv.satisfy(satisfier, to_pk_ctx) {
1101-
Some(wit) => wit,
1102-
None => return Err(Error::CouldNotSatisfy),
1103-
};
1074+
let mut witness = smv.satisfy(satisfier, to_pk_ctx)?;
11041075
witness.push(smv.encode(to_pk_ctx).into_bytes());
11051076
let script_sig = Script::new();
11061077
Ok((witness, script_sig))
@@ -1111,10 +1082,7 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
11111082
.push_slice(&witness_script.to_v0_p2wsh()[..])
11121083
.into_script();
11131084

1114-
let mut witness = match smv.satisfy(satisfier, to_pk_ctx) {
1115-
Some(wit) => wit,
1116-
None => return Err(Error::CouldNotSatisfy),
1117-
};
1085+
let mut witness = smv.satisfy(satisfier, to_pk_ctx)?;
11181086
witness.push(witness_script.into_bytes());
11191087
Ok((witness, script_sig))
11201088
}
@@ -1556,7 +1524,11 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
15561524

15571525
/// Attempt to produce a satisfying witness for the
15581526
/// witness script represented by the parse tree
1559-
pub fn satisfy<ToPkCtx, S>(&self, satisfier: S, to_pk_ctx: ToPkCtx) -> Option<Vec<Vec<u8>>>
1527+
pub fn satisfy<ToPkCtx, S>(
1528+
&self,
1529+
satisfier: S,
1530+
to_pk_ctx: ToPkCtx,
1531+
) -> Result<Vec<Vec<u8>>, Error>
15601532
where
15611533
ToPkCtx: Copy,
15621534
Pk: ToPublicKey<ToPkCtx>,

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ pub mod miniscript;
120120
pub mod policy;
121121
pub mod psbt;
122122

123+
mod util;
124+
123125
use std::str::FromStr;
124126
use std::{error, fmt, hash, str};
125127

src/miniscript/context.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use miniscript::types::extra_props::{
1818
MAX_STANDARD_P2WSH_SCRIPT_SIZE, MAX_STANDARD_P2WSH_STACK_ITEMS,
1919
};
2020
use std::fmt;
21+
use util::{witness_size, witness_to_scriptsig};
2122
use Error;
2223
use {Miniscript, MiniscriptKey, Terminal};
2324
/// Error for Script Context
@@ -111,6 +112,18 @@ pub trait ScriptContext:
111112
_frag: &Terminal<Pk, Ctx>,
112113
) -> Result<(), ScriptContextError>;
113114

115+
/// Check whether the given satisfaction is valid under the ScriptContext
116+
/// For example, segwit satisfactions may fail if the witness len is more
117+
/// 3600 or number of stack elements are more than 100.
118+
fn check_witness<Pk: MiniscriptKey, Ctx: ScriptContext>(
119+
_witness: &[Vec<u8>],
120+
) -> Result<(), ScriptContextError> {
121+
// Only really need to do this for segwitv0 and legacy
122+
// Bare is already restrcited by standardness rules
123+
// and would reach these limits.
124+
Ok(())
125+
}
126+
114127
/// Depending on script context, the size of a satifaction witness may slightly differ.
115128
fn max_satisfaction_size<Pk: MiniscriptKey, Ctx: ScriptContext>(
116129
ms: &Miniscript<Pk, Ctx>,
@@ -241,6 +254,17 @@ impl ScriptContext for Legacy {
241254
}
242255
}
243256

257+
fn check_witness<Pk: MiniscriptKey, Ctx: ScriptContext>(
258+
witness: &[Vec<u8>],
259+
) -> Result<(), ScriptContextError> {
260+
// In future, we could avoid by having a function to count only
261+
// len of script instead of converting it.
262+
if witness_to_scriptsig(witness).len() > MAX_SCRIPTSIG_SIZE {
263+
return Err(ScriptContextError::MaxScriptSigSizeExceeded);
264+
}
265+
Ok(())
266+
}
267+
244268
fn check_global_consensus_validity<Pk: MiniscriptKey, Ctx: ScriptContext>(
245269
ms: &Miniscript<Pk, Ctx>,
246270
) -> Result<(), ScriptContextError> {
@@ -294,6 +318,17 @@ impl ScriptContext for Segwitv0 {
294318
Ok(())
295319
}
296320

321+
fn check_witness<Pk: MiniscriptKey, Ctx: ScriptContext>(
322+
witness: &[Vec<u8>],
323+
) -> Result<(), ScriptContextError> {
324+
if witness_size(witness) > MAX_STANDARD_P2WSH_SCRIPT_SIZE {
325+
return Err(ScriptContextError::MaxScriptSigSizeExceeded);
326+
} else if witness.len() > MAX_STANDARD_P2WSH_STACK_ITEMS {
327+
return Err(ScriptContextError::MaxWitnessItemssExceeded);
328+
}
329+
Ok(())
330+
}
331+
297332
fn check_global_consensus_validity<Pk: MiniscriptKey, Ctx: ScriptContext>(
298333
ms: &Miniscript<Pk, Ctx>,
299334
) -> Result<(), ScriptContextError> {

src/miniscript/mod.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -296,15 +296,20 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
296296
&self,
297297
satisfier: S,
298298
to_pk_ctx: ToPkCtx,
299-
) -> Option<Vec<Vec<u8>>>
299+
) -> Result<Vec<Vec<u8>>, Error>
300300
where
301301
Pk: ToPublicKey<ToPkCtx>,
302302
{
303303
match satisfy::Satisfaction::satisfy(&self.node, &satisfier, self.ty.mall.safe, to_pk_ctx)
304304
.stack
305305
{
306-
satisfy::Witness::Stack(stack) => Some(stack),
307-
satisfy::Witness::Unavailable | satisfy::Witness::Impossible => None,
306+
satisfy::Witness::Stack(stack) => {
307+
Ctx::check_witness::<Pk, Ctx>(&stack)?;
308+
Ok(stack)
309+
}
310+
satisfy::Witness::Unavailable | satisfy::Witness::Impossible => {
311+
Err(Error::CouldNotSatisfy)
312+
}
308313
}
309314
}
310315

@@ -314,7 +319,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
314319
&self,
315320
satisfier: S,
316321
to_pk_ctx: ToPkCtx,
317-
) -> Option<Vec<Vec<u8>>>
322+
) -> Result<Vec<Vec<u8>>, Error>
318323
where
319324
Pk: ToPublicKey<ToPkCtx>,
320325
{
@@ -326,8 +331,13 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
326331
)
327332
.stack
328333
{
329-
satisfy::Witness::Stack(stack) => Some(stack),
330-
satisfy::Witness::Unavailable | satisfy::Witness::Impossible => None,
334+
satisfy::Witness::Stack(stack) => {
335+
Ctx::check_witness::<Pk, Ctx>(&stack)?;
336+
Ok(stack)
337+
}
338+
satisfy::Witness::Unavailable | satisfy::Witness::Impossible => {
339+
Err(Error::CouldNotSatisfy)
340+
}
331341
}
332342
}
333343
}

src/miniscript/satisfy.rs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use {MiniscriptKey, ToPublicKey};
2929
use miniscript::types::extra_props::{
3030
HEIGHT_TIME_THRESHOLD, SEQUENCE_LOCKTIME_DISABLE_FLAG, SEQUENCE_LOCKTIME_TYPE_FLAG,
3131
};
32+
use util::witness_size;
3233
use Error;
3334
use Miniscript;
3435
use NullCtx;
@@ -415,15 +416,6 @@ impl PartialOrd for Witness {
415416
}
416417
}
417418

418-
fn varint_len(n: usize) -> usize {
419-
bitcoin::VarInt(n as u64).len()
420-
}
421-
422-
// Helper function to calculate witness size
423-
fn witness_size(wit: &[Vec<u8>]) -> usize {
424-
wit.iter().map(Vec::len).sum::<usize>() + varint_len(wit.len())
425-
}
426-
427419
impl Ord for Witness {
428420
fn cmp(&self, other: &Self) -> cmp::Ordering {
429421
match (self, other) {

src/policy/compiler.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1388,15 +1388,15 @@ mod tests {
13881388
right_sat.insert(keys[i].to_pubkeyhash(), (keys[i], bitcoinsig));
13891389
}
13901390

1391-
assert!(ms.satisfy(no_sat, NullCtx).is_none());
1392-
assert!(ms.satisfy(&left_sat, NullCtx).is_some());
1391+
assert!(ms.satisfy(no_sat, NullCtx).is_err());
1392+
assert!(ms.satisfy(&left_sat, NullCtx).is_ok());
13931393
assert!(ms
13941394
.satisfy((&right_sat, satisfy::Older(10001)), NullCtx)
1395-
.is_some());
1395+
.is_ok());
13961396
//timelock not met
13971397
assert!(ms
13981398
.satisfy((&right_sat, satisfy::Older(9999)), NullCtx)
1399-
.is_none());
1399+
.is_err());
14001400

14011401
assert_eq!(
14021402
ms.satisfy((left_sat, satisfy::Older(9999)), NullCtx)

src/util.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use bitcoin;
2+
use bitcoin::blockdata::script;
3+
use bitcoin::Script;
4+
pub(crate) fn varint_len(n: usize) -> usize {
5+
bitcoin::VarInt(n as u64).len()
6+
}
7+
8+
// Helper function to calculate witness size
9+
pub(crate) fn witness_size(wit: &[Vec<u8>]) -> usize {
10+
wit.iter().map(Vec::len).sum::<usize>() + varint_len(wit.len())
11+
}
12+
13+
pub(crate) fn witness_to_scriptsig(witness: &[Vec<u8>]) -> Script {
14+
let mut b = script::Builder::new();
15+
for wit in witness {
16+
if let Ok(n) = script::read_scriptint(wit) {
17+
b = b.push_int(n);
18+
} else {
19+
b = b.push_slice(wit);
20+
}
21+
}
22+
b.into_script()
23+
}

0 commit comments

Comments
 (0)