Skip to content

Commit 59fe45c

Browse files
committed
Allow miniscripts non-cannonical values
1 parent 54d5efb commit 59fe45c

File tree

2 files changed

+206
-26
lines changed

2 files changed

+206
-26
lines changed

src/lib.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -469,10 +469,6 @@ pub enum Error {
469469
MultiAt(String),
470470
/// Name of a fragment contained `@` but we were not parsing an OR
471471
AtOutsideOr(String),
472-
/// Fragment was an `and_v(_, true)` which should be written as `t:`
473-
NonCanonicalTrue,
474-
/// Fragment was an `or_i(_, false)` or `or_i(false,_)` which should be written as `u:` or `l:`
475-
NonCanonicalFalse,
476472
/// Encountered a `l:0` which is syntactically equal to `u:0` except stupid
477473
LikelyFalse,
478474
/// Encountered a wrapping character that we don't recognize
@@ -595,10 +591,6 @@ impl fmt::Display for Error {
595591
Error::MultiColon(ref s) => write!(f, "«{}» has multiple instances of «:»", s),
596592
Error::MultiAt(ref s) => write!(f, "«{}» has multiple instances of «@»", s),
597593
Error::AtOutsideOr(ref s) => write!(f, "«{}» contains «@» in non-or() context", s),
598-
Error::NonCanonicalTrue => f.write_str("Use «t:X» rather than «and_v(X,true())»"),
599-
Error::NonCanonicalFalse => {
600-
f.write_str("Use «u:X» «l:X» rather than «or_i(X,false)» «or_i(false,X)»")
601-
}
602594
Error::LikelyFalse => write!(f, "0 is not very likely (use «u:0»)"),
603595
Error::UnknownWrapper(ch) => write!(f, "unknown wrapper «{}:»", ch),
604596
Error::NonTopLevel(ref s) => write!(f, "non-T miniscript: {}", s),

src/miniscript/astelem.rs

Lines changed: 206 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,7 @@ where
417417
}
418418
}
419419

420+
#[cfg(not(fuzzing))]
420421
impl<Pk, Ctx> expression::FromTree for Terminal<Pk, Ctx>
421422
where
422423
Pk: MiniscriptKey + str::FromStr,
@@ -503,15 +504,7 @@ where
503504
}),
504505
("1", 0) => Ok(Terminal::True),
505506
("0", 0) => Ok(Terminal::False),
506-
("and_v", 2) => {
507-
let expr = expression::binary(top, Terminal::AndV)?;
508-
if let Terminal::AndV(_, ref right) = expr {
509-
if let Terminal::True = right.node {
510-
return Err(Error::NonCanonicalTrue);
511-
}
512-
}
513-
Ok(expr)
514-
}
507+
("and_v", 2) => expression::binary(top, Terminal::AndV),
515508
("and_b", 2) => expression::binary(top, Terminal::AndB),
516509
("and_n", 2) => Ok(Terminal::AndOr(
517510
expression::FromTree::from_tree(&top.args[0])?,
@@ -526,15 +519,7 @@ where
526519
("or_b", 2) => expression::binary(top, Terminal::OrB),
527520
("or_d", 2) => expression::binary(top, Terminal::OrD),
528521
("or_c", 2) => expression::binary(top, Terminal::OrC),
529-
("or_i", 2) => {
530-
let expr = expression::binary(top, Terminal::OrI)?;
531-
if let Terminal::OrI(ref left, ref right) = expr {
532-
if left.node == Terminal::False || right.node == Terminal::False {
533-
return Err(Error::NonCanonicalFalse);
534-
}
535-
}
536-
Ok(expr)
537-
}
522+
("or_i", 2) => expression::binary(top, Terminal::OrI),
538523
("thresh", n) => {
539524
if n == 0 {
540525
return Err(errstr("no arguments given"));
@@ -815,3 +800,206 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
815800
}
816801
}
817802
}
803+
804+
#[cfg(fuzzing)]
805+
impl<Pk, Ctx> expression::FromTree for Terminal<Pk, Ctx>
806+
where
807+
Pk: MiniscriptKey + str::FromStr,
808+
Pk::Hash: str::FromStr,
809+
Ctx: ScriptContext,
810+
<Pk as str::FromStr>::Err: ToString,
811+
<<Pk as MiniscriptKey>::Hash as str::FromStr>::Err: ToString,
812+
{
813+
fn from_tree(top: &expression::Tree) -> Result<Terminal<Pk, Ctx>, Error> {
814+
let mut aliased_wrap;
815+
let frag_name;
816+
let frag_wrap;
817+
let mut name_split = top.name.split(':');
818+
match (name_split.next(), name_split.next(), name_split.next()) {
819+
(None, _, _) => {
820+
frag_name = "";
821+
frag_wrap = "";
822+
}
823+
(Some(name), None, _) => {
824+
if name == "pk" {
825+
frag_name = "pk_k";
826+
frag_wrap = "c";
827+
} else if name == "pkh" {
828+
frag_name = "pk_h";
829+
frag_wrap = "c";
830+
} else {
831+
frag_name = name;
832+
frag_wrap = "";
833+
}
834+
}
835+
(Some(wrap), Some(name), None) => {
836+
if wrap.is_empty() {
837+
return Err(Error::Unexpected(top.name.to_owned()));
838+
}
839+
if name == "pk" {
840+
frag_name = "pk_k";
841+
aliased_wrap = wrap.to_owned();
842+
aliased_wrap.push_str("c");
843+
frag_wrap = &aliased_wrap;
844+
} else if name == "pkh" {
845+
frag_name = "pk_h";
846+
aliased_wrap = wrap.to_owned();
847+
aliased_wrap.push_str("c");
848+
frag_wrap = &aliased_wrap;
849+
} else {
850+
frag_name = name;
851+
frag_wrap = wrap;
852+
}
853+
}
854+
(Some(_), Some(_), Some(_)) => {
855+
return Err(Error::MultiColon(top.name.to_owned()));
856+
}
857+
}
858+
let mut unwrapped = match (frag_name, top.args.len()) {
859+
("pk_k", 1) => {
860+
expression::terminal(&top.args[0], |x| Pk::from_str(x).map(Terminal::PkK))
861+
}
862+
("pk_h", 1) => {
863+
expression::terminal(&top.args[0], |x| Pk::Hash::from_str(x).map(Terminal::PkH))
864+
}
865+
("after", 1) => expression::terminal(&top.args[0], |x| {
866+
expression::parse_num(x).map(Terminal::After)
867+
}),
868+
("older", 1) => expression::terminal(&top.args[0], |x| {
869+
expression::parse_num(x).map(Terminal::Older)
870+
}),
871+
("sha256", 1) => expression::terminal(&top.args[0], |x| {
872+
sha256::Hash::from_hex(x).map(Terminal::Sha256)
873+
}),
874+
("hash256", 1) => expression::terminal(&top.args[0], |x| {
875+
sha256d::Hash::from_hex(x)
876+
.map(|x| x.into_inner())
877+
.map(|mut x| {
878+
x.reverse();
879+
x
880+
})
881+
.map(|x| Terminal::Hash256(sha256d::Hash::from_inner(x)))
882+
}),
883+
("ripemd160", 1) => expression::terminal(&top.args[0], |x| {
884+
ripemd160::Hash::from_hex(x).map(Terminal::Ripemd160)
885+
}),
886+
("hash160", 1) => expression::terminal(&top.args[0], |x| {
887+
hash160::Hash::from_hex(x).map(Terminal::Hash160)
888+
}),
889+
("1", 0) => Ok(Terminal::True),
890+
("0", 0) => Ok(Terminal::False),
891+
("and_v", 2) => {
892+
let expr = expression::binary(top, Terminal::AndV)?;
893+
if let Terminal::AndV(_, ref right) = expr {
894+
if let Terminal::True = right.node {
895+
return Err(Error::Unexpected(String::from("NonCannonicalTrue")));
896+
}
897+
}
898+
Ok(expr)
899+
}
900+
("and_b", 2) => expression::binary(top, Terminal::AndB),
901+
("and_n", 2) => Ok(Terminal::AndOr(
902+
expression::FromTree::from_tree(&top.args[0])?,
903+
expression::FromTree::from_tree(&top.args[1])?,
904+
Arc::new(Miniscript::from_ast(Terminal::False)?),
905+
)),
906+
("andor", 3) => Ok(Terminal::AndOr(
907+
expression::FromTree::from_tree(&top.args[0])?,
908+
expression::FromTree::from_tree(&top.args[1])?,
909+
expression::FromTree::from_tree(&top.args[2])?,
910+
)),
911+
("or_b", 2) => expression::binary(top, Terminal::OrB),
912+
("or_d", 2) => expression::binary(top, Terminal::OrD),
913+
("or_c", 2) => expression::binary(top, Terminal::OrC),
914+
("or_i", 2) => {
915+
let expr = expression::binary(top, Terminal::OrI)?;
916+
if let Terminal::OrI(ref left, ref right) = expr {
917+
if left.node == Terminal::False || right.node == Terminal::False {
918+
return Err(Error::Unexpected(String::from("NonCannonicalFalse")));
919+
}
920+
}
921+
Ok(expr)
922+
}
923+
("thresh", n) => {
924+
if n == 0 {
925+
return Err(errstr("no arguments given"));
926+
}
927+
let k = expression::terminal(&top.args[0], expression::parse_num)? as usize;
928+
if k > n - 1 {
929+
return Err(errstr("higher threshold than there are subexpressions"));
930+
}
931+
if n == 1 {
932+
return Err(errstr("empty thresholds not allowed in descriptors"));
933+
}
934+
935+
let subs: Result<Vec<Arc<Miniscript<Pk, Ctx>>>, _> = top.args[1..]
936+
.iter()
937+
.map(|sub| expression::FromTree::from_tree(sub))
938+
.collect();
939+
940+
Ok(Terminal::Thresh(k, subs?))
941+
}
942+
("multi", n) => {
943+
if n == 0 {
944+
return Err(errstr("no arguments given"));
945+
}
946+
let k = expression::terminal(&top.args[0], expression::parse_num)? as usize;
947+
if k > n - 1 {
948+
return Err(errstr("higher threshold than there were keys in multi"));
949+
}
950+
951+
let pks: Result<Vec<Pk>, _> = top.args[1..]
952+
.iter()
953+
.map(|sub| expression::terminal(sub, Pk::from_str))
954+
.collect();
955+
956+
pks.map(|pks| Terminal::Multi(k, pks))
957+
}
958+
_ => Err(Error::Unexpected(format!(
959+
"{}({} args) while parsing Miniscript",
960+
top.name,
961+
top.args.len(),
962+
))),
963+
}?;
964+
for ch in frag_wrap.chars().rev() {
965+
// Check whether the wrapper is valid under the current context
966+
let ms = Miniscript::from_ast(unwrapped)?;
967+
Ctx::check_global_validity(&ms)?;
968+
match ch {
969+
'a' => unwrapped = Terminal::Alt(Arc::new(ms)),
970+
's' => unwrapped = Terminal::Swap(Arc::new(ms)),
971+
'c' => unwrapped = Terminal::Check(Arc::new(ms)),
972+
'd' => unwrapped = Terminal::DupIf(Arc::new(ms)),
973+
'v' => unwrapped = Terminal::Verify(Arc::new(ms)),
974+
'j' => unwrapped = Terminal::NonZero(Arc::new(ms)),
975+
'n' => unwrapped = Terminal::ZeroNotEqual(Arc::new(ms)),
976+
't' => {
977+
unwrapped = Terminal::AndV(
978+
Arc::new(ms),
979+
Arc::new(Miniscript::from_ast(Terminal::True)?),
980+
)
981+
}
982+
'u' => {
983+
unwrapped = Terminal::OrI(
984+
Arc::new(ms),
985+
Arc::new(Miniscript::from_ast(Terminal::False)?),
986+
)
987+
}
988+
'l' => {
989+
if ms.node == Terminal::False {
990+
return Err(Error::LikelyFalse);
991+
}
992+
unwrapped = Terminal::OrI(
993+
Arc::new(Miniscript::from_ast(Terminal::False)?),
994+
Arc::new(ms),
995+
)
996+
}
997+
x => return Err(Error::UnknownWrapper(x)),
998+
}
999+
}
1000+
// Check whether the unwrapped miniscript is valid under the current context
1001+
let ms = Miniscript::from_ast(unwrapped)?;
1002+
Ctx::check_global_validity(&ms)?;
1003+
Ok(ms.node)
1004+
}
1005+
}

0 commit comments

Comments
 (0)