From 5846360948a2725a0401b4ad5b9fb9c217f44dca Mon Sep 17 00:00:00 2001 From: Samuel Dobson Date: Mon, 20 Sep 2021 13:17:50 +1200 Subject: [PATCH] Non-recursive decode review fixups --- bitcoin/script/miniscript.h | 301 +++++++++++++++++++----------------- 1 file changed, 155 insertions(+), 146 deletions(-) diff --git a/bitcoin/script/miniscript.h b/bitcoin/script/miniscript.h index 2ba4d92..fd75394 100644 --- a/bitcoin/script/miniscript.h +++ b/bitcoin/script/miniscript.h @@ -1015,30 +1015,71 @@ bool DecomposeScript(const CScript& script, std::vector>& in, int64_t& k); enum class DecodeContext { - Expression, - WExpression, - Swap, - MaybeAndV, - Alt, - Check, - DupIf, - Verify, - NonZero, - ZeroNotEqual, - AndV, - AndB, - AndOr, - OrB, - OrC, - OrD, - ThreshW, - ThreshE, - // could be or_d, or_c, or_i, d:, n: - EndIf, - // could be or_d, or_c - EndIfNotIf, - // could be or_i or andor - EndIfElse, + /** A single expression of type B, K, or V. Specifically, this can't be an + * and_v or an expression of type W (a: and s: wrappers). */ + SINGLE_BKV_EXPR, + /** Potentially multiple SINGLE_BKV_EXPRs as children of (potentially multiple) + * and_v expressions. */ + BKV_EXPR, + /* An expression of type W (a: or s: wrappers). */ + W_EXPR, + + /** SWAP expects the next element to be OP_SWAP (inside a W-type expression that + * didn't end with FROMALTSTACK), and wraps the top of the constructed stack + * with s: */ + SWAP, + /** ALT expects the next element to be TOALTSTACK (we must have already read a + * FROMALTSTACK earlier), and wraps the top of the constructed stack with a: */ + ALT, + /* CHECK wraps the top constructed node with c: */ + CHECK, + /* DUP_IF wraps the top constructed node with d: */ + DUP_IF, + /* VERIFY wraps the top constructed node with v: */ + VERIFY, + /* NON_ZERO wraps the top constructed node with j: */ + NON_ZERO, + /* ZERO_NOTEQUAL wraps the top constructed node with n: */ + ZERO_NOTEQUAL, + + /** MAYBE_AND_V will check if the next part of the script could be a valid + * miniscript sub-expression, and if so it will push AND_V and SINGLE_BKV_EXPR + * to decode it and construct the and_v node. This is recursive, to deal with + * multiple and_v nodes inside each other. */ + MAYBE_AND_V, + /* AND_V will construct an and_v node from the last two constructed nodes. */ + AND_V, + /* AND_B will construct an and_b node from the last two constructed nodes. */ + AND_B, + /* ANDOR will construct an andor node from the last three constructed nodes. */ + ANDOR, + /* OR_B will construct an or_b node from the last two constructed nodes. */ + OR_B, + /* OR_C will construct an or_c node from the last two constructed nodes. */ + OR_C, + /* OR_D will construct an or_d node from the last two constructed nodes. */ + OR_D, + + /** In a thresh expression, all sub-expressions other than the first are W-type, + * and end in OP_ADD. THRESH_W will check for this OP_ADD and either push a W_EXPR + * or a SINGLE_BKV_EXPR and jump to THRESH_E accordingly. */ + THRESH_W, + /** THRESH_E constructs a thresh node from the appropriate number of constructed + * children. */ + THRESH_E, + + /** ENDIF signals that we are inside some sort of OP_IF structure, which could be + * or_d, or_c, or_i, andor, d:, or j: wrapper, depending on what follows. We read + * a BKV_EXPR and then deal with the next opcode case-by-case. */ + ENDIF, + /** If, inside an ENDIF context, we find an OP_NOTIF before finding an OP_ELSE, + * we could either be in an or_d or an or_c node. We then check for IFDUP to + * distinguish these cases. */ + ENDIF_NOTIF, + /** If, inside an ENDIF context, we find an OP_ELSE, then we could be in either an + * or_i or an andor node. Read the next BKV_EXPR and find either an OP_IF or an + * OP_NOTIF. */ + ENDIF_ELSE, }; template @@ -1047,29 +1088,25 @@ inline NodeRef DecodeScript(I& in, I last, const Ctx& ctx) { std::vector> to_parse; std::vector> constructed; - // There may be implicit and_v expressions [X] [Y] - to_parse.emplace_back(DecodeContext::MaybeAndV, -1, -1); - // This is the top level, so we assume it is a B-type expression - to_parse.emplace_back(DecodeContext::Expression, -1, -1); + // This is the top level, so we assume the type is B + // (in particular, disallowing top level W expressions) + to_parse.emplace_back(DecodeContext::BKV_EXPR, -1, -1); while (!to_parse.empty()) { // Get the current context we are decoding within - DecodeContext cur_context = std::get<0>(to_parse.back()); - int64_t n = std::get<1>(to_parse.back()); - int64_t k = std::get<2>(to_parse.back()); + auto [cur_context, n, k] = to_parse.back(); to_parse.pop_back(); switch(cur_context) { - case DecodeContext::Expression: { + case DecodeContext::SINGLE_BKV_EXPR: { if (in >= last) return {}; // Constants if (in[0].first == OP_1) { ++in; constructed.push_back(MakeNodeRef(NodeType::JUST_1)); - } - else if (in[0].first == OP_0) { + } else if (in[0].first == OP_0) { ++in; constructed.push_back(MakeNodeRef(NodeType::JUST_0)); } @@ -1079,8 +1116,7 @@ inline NodeRef DecodeScript(I& in, I last, const Ctx& ctx) { if (!ctx.FromPKBytes(in[0].second.begin(), in[0].second.end(), key)) return {}; ++in; constructed.push_back(MakeNodeRef(NodeType::PK_K, Vector(std::move(key)))); - } - else if (last - in >= 5 && in[0].first == OP_VERIFY && in[1].first == OP_EQUAL && in[3].first == OP_HASH160 && in[4].first == OP_DUP && in[2].second.size() == 20) { + } else if (last - in >= 5 && in[0].first == OP_VERIFY && in[1].first == OP_EQUAL && in[3].first == OP_HASH160 && in[4].first == OP_DUP && in[2].second.size() == 20) { Key key; if (!ctx.FromPKHBytes(in[2].second.begin(), in[2].second.end(), key)) return {}; in += 5; @@ -1091,8 +1127,7 @@ inline NodeRef DecodeScript(I& in, I last, const Ctx& ctx) { in += 2; if (k < 1 || k > 0x7FFFFFFFL) return {}; constructed.push_back(MakeNodeRef(NodeType::OLDER, k)); - } - else if (last - in >= 2 && in[0].first == OP_CHECKLOCKTIMEVERIFY && ParseScriptNumber(in[1], k)) { + } else if (last - in >= 2 && in[0].first == OP_CHECKLOCKTIMEVERIFY && ParseScriptNumber(in[1], k)) { in += 2; if (k < 1 || k > 0x7FFFFFFFL) return {}; constructed.push_back(MakeNodeRef(NodeType::AFTER, k)); @@ -1101,16 +1136,13 @@ inline NodeRef DecodeScript(I& in, I last, const Ctx& ctx) { else if (last - in >= 7 && in[0].first == OP_EQUAL && in[1].second.size() == 32 && in[2].first == OP_SHA256 && in[3].first == OP_VERIFY && in[4].first == OP_EQUAL && ParseScriptNumber(in[5], k) && k == 32 && in[6].first == OP_SIZE) { constructed.push_back(MakeNodeRef(NodeType::SHA256, in[1].second)); in += 7; - } - else if (last - in >= 7 && in[0].first == OP_EQUAL && in[1].second.size() == 20 && in[2].first == OP_RIPEMD160 && in[3].first == OP_VERIFY && in[4].first == OP_EQUAL && ParseScriptNumber(in[5], k) && k == 32 && in[6].first == OP_SIZE) { + } else if (last - in >= 7 && in[0].first == OP_EQUAL && in[1].second.size() == 20 && in[2].first == OP_RIPEMD160 && in[3].first == OP_VERIFY && in[4].first == OP_EQUAL && ParseScriptNumber(in[5], k) && k == 32 && in[6].first == OP_SIZE) { constructed.push_back(MakeNodeRef(NodeType::RIPEMD160, in[1].second)); in += 7; - } - else if (last - in >= 7 && in[0].first == OP_EQUAL && in[1].second.size() == 32 && in[2].first == OP_HASH256 && in[3].first == OP_VERIFY && in[4].first == OP_EQUAL && ParseScriptNumber(in[5], k) && k == 32 && in[6].first == OP_SIZE) { + } else if (last - in >= 7 && in[0].first == OP_EQUAL && in[1].second.size() == 32 && in[2].first == OP_HASH256 && in[3].first == OP_VERIFY && in[4].first == OP_EQUAL && ParseScriptNumber(in[5], k) && k == 32 && in[6].first == OP_SIZE) { constructed.push_back(MakeNodeRef(NodeType::HASH256, in[1].second)); in += 7; - } - else if (last - in >= 7 && in[0].first == OP_EQUAL && in[1].second.size() == 20 && in[2].first == OP_HASH160 && in[3].first == OP_VERIFY && in[4].first == OP_EQUAL && ParseScriptNumber(in[5], k) && k == 32 && in[6].first == OP_SIZE) { + } else if (last - in >= 7 && in[0].first == OP_EQUAL && in[1].second.size() == 20 && in[2].first == OP_HASH160 && in[3].first == OP_VERIFY && in[4].first == OP_EQUAL && ParseScriptNumber(in[5], k) && k == 32 && in[6].first == OP_SIZE) { constructed.push_back(MakeNodeRef(NodeType::HASH160, in[1].second)); in += 7; } @@ -1135,189 +1167,168 @@ inline NodeRef DecodeScript(I& in, I last, const Ctx& ctx) { // c: wrapper else if (in[0].first == OP_CHECKSIG) { ++in; - to_parse.emplace_back(DecodeContext::Check, -1, -1); - to_parse.emplace_back(DecodeContext::Expression, -1, -1); + to_parse.emplace_back(DecodeContext::CHECK, -1, -1); + to_parse.emplace_back(DecodeContext::SINGLE_BKV_EXPR, -1, -1); } // v: wrapper else if (in[0].first == OP_VERIFY) { ++in; - to_parse.emplace_back(DecodeContext::Verify, -1, -1); - to_parse.emplace_back(DecodeContext::Expression, -1, -1); + to_parse.emplace_back(DecodeContext::VERIFY, -1, -1); + to_parse.emplace_back(DecodeContext::SINGLE_BKV_EXPR, -1, -1); } // n: wrapper else if (in[0].first == OP_0NOTEQUAL) { ++in; - to_parse.emplace_back(DecodeContext::ZeroNotEqual, -1, -1); - to_parse.emplace_back(DecodeContext::Expression, -1, -1); + to_parse.emplace_back(DecodeContext::ZERO_NOTEQUAL, -1, -1); + to_parse.emplace_back(DecodeContext::SINGLE_BKV_EXPR, -1, -1); } // Thresh else if (last - in >= 3 && in[0].first == OP_EQUAL && ParseScriptNumber(in[1], k)) { if (k < 1) return {}; in += 2; - to_parse.emplace_back(DecodeContext::ThreshW, 0, k); + to_parse.emplace_back(DecodeContext::THRESH_W, 0, k); } // OP_ENDIF can be WRAP_J, WRAP_D, ANDOR, OR_C, OR_D, or OR_I else if (in[0].first == OP_ENDIF) { ++in; - to_parse.emplace_back(DecodeContext::EndIf, -1, -1); - to_parse.emplace_back(DecodeContext::MaybeAndV, -1, -1); - to_parse.emplace_back(DecodeContext::Expression, -1, -1); + to_parse.emplace_back(DecodeContext::ENDIF, -1, -1); + to_parse.emplace_back(DecodeContext::BKV_EXPR, -1, -1); } // and_b else if (in[0].first == OP_BOOLAND) { ++in; - to_parse.emplace_back(DecodeContext::AndB, -1, -1); - to_parse.emplace_back(DecodeContext::Expression, -1, -1); - to_parse.emplace_back(DecodeContext::WExpression, -1, -1); + to_parse.emplace_back(DecodeContext::AND_B, -1, -1); + to_parse.emplace_back(DecodeContext::SINGLE_BKV_EXPR, -1, -1); + to_parse.emplace_back(DecodeContext::W_EXPR, -1, -1); } // or_b else if (in[0].first == OP_BOOLOR) { ++in; - to_parse.emplace_back(DecodeContext::OrB, -1, -1); - to_parse.emplace_back(DecodeContext::Expression, -1, -1); - to_parse.emplace_back(DecodeContext::WExpression, -1, -1); - } - else { + to_parse.emplace_back(DecodeContext::OR_B, -1, -1); + to_parse.emplace_back(DecodeContext::SINGLE_BKV_EXPR, -1, -1); + to_parse.emplace_back(DecodeContext::W_EXPR, -1, -1); + } else { // Unrecognised expression return {}; } break; } - case DecodeContext::WExpression: { + case DecodeContext::BKV_EXPR: { + to_parse.emplace_back(DecodeContext::MAYBE_AND_V, -1, -1); + to_parse.emplace_back(DecodeContext::SINGLE_BKV_EXPR, -1, -1); + break; + } + case DecodeContext::W_EXPR: { // a: wrapper if (in >= last) return {}; if (in[0].first == OP_FROMALTSTACK) { ++in; - to_parse.emplace_back(DecodeContext::Alt, -1, -1); + to_parse.emplace_back(DecodeContext::ALT, -1, -1); } else { - to_parse.emplace_back(DecodeContext::Swap, -1, -1); + to_parse.emplace_back(DecodeContext::SWAP, -1, -1); } - to_parse.emplace_back(DecodeContext::MaybeAndV, -1, -1); - to_parse.emplace_back(DecodeContext::Expression, -1, -1); + to_parse.emplace_back(DecodeContext::BKV_EXPR, -1, -1); break; } - case DecodeContext::MaybeAndV: { + case DecodeContext::MAYBE_AND_V: { // If we reach a potential AND_V top-level, check if the next part of the script could be another AND_V child + // These op-codes cannot end any well-formed miniscript so cannot be used in an and_v node. if (in < last && in[0].first != OP_IF && in[0].first != OP_ELSE && in[0].first != OP_NOTIF && in[0].first != OP_TOALTSTACK && in[0].first != OP_SWAP) { - to_parse.emplace_back(DecodeContext::AndV, -1, -1); - to_parse.emplace_back(DecodeContext::Expression, -1, -1); + to_parse.emplace_back(DecodeContext::AND_V, -1, -1); + // BKV_EXPR can contain more AND_V nodes + to_parse.emplace_back(DecodeContext::BKV_EXPR, -1, -1); } break; } - case DecodeContext::Swap: { + case DecodeContext::SWAP: { if (in >= last || in[0].first != OP_SWAP) return {}; ++in; - NodeRef node = MakeNodeRef(NodeType::WRAP_S, Vector(std::move(constructed.back()))); - constructed.pop_back(); - constructed.push_back(std::move(node)); + constructed.back() = MakeNodeRef(NodeType::WRAP_S, Vector(std::move(constructed.back()))); break; } - case DecodeContext::Alt: { + case DecodeContext::ALT: { if (in >= last || in[0].first != OP_TOALTSTACK) return {}; ++in; - NodeRef node = MakeNodeRef(NodeType::WRAP_A, Vector(std::move(constructed.back()))); - constructed.pop_back(); - constructed.push_back(std::move(node)); + constructed.back() = MakeNodeRef(NodeType::WRAP_A, Vector(std::move(constructed.back()))); break; } - case DecodeContext::Check: { - NodeRef node = MakeNodeRef(NodeType::WRAP_C, Vector(std::move(constructed.back()))); - constructed.pop_back(); - constructed.push_back(std::move(node)); + case DecodeContext::CHECK: { + constructed.back() = MakeNodeRef(NodeType::WRAP_C, Vector(std::move(constructed.back()))); break; } - case DecodeContext::DupIf: { - NodeRef node = MakeNodeRef(NodeType::WRAP_D, Vector(std::move(constructed.back()))); - constructed.pop_back(); - constructed.push_back(std::move(node)); + case DecodeContext::DUP_IF: { + constructed.back() = MakeNodeRef(NodeType::WRAP_D, Vector(std::move(constructed.back()))); break; } - case DecodeContext::Verify: { - NodeRef node = MakeNodeRef(NodeType::WRAP_V, Vector(std::move(constructed.back()))); - constructed.pop_back(); - constructed.push_back(std::move(node)); + case DecodeContext::VERIFY: { + constructed.back() = MakeNodeRef(NodeType::WRAP_V, Vector(std::move(constructed.back()))); break; } - case DecodeContext::NonZero: { - NodeRef node = MakeNodeRef(NodeType::WRAP_J, Vector(std::move(constructed.back()))); - constructed.pop_back(); - constructed.push_back(std::move(node)); + case DecodeContext::NON_ZERO: { + constructed.back() = MakeNodeRef(NodeType::WRAP_J, Vector(std::move(constructed.back()))); break; } - case DecodeContext::ZeroNotEqual: { - NodeRef node = MakeNodeRef(NodeType::WRAP_N, Vector(std::move(constructed.back()))); - constructed.pop_back(); - constructed.push_back(std::move(node)); + case DecodeContext::ZERO_NOTEQUAL: { + constructed.back() = MakeNodeRef(NodeType::WRAP_N, Vector(std::move(constructed.back()))); break; } - case DecodeContext::AndV: { - if (in < last && in[0].first != OP_IF && in[0].first != OP_ELSE && in[0].first != OP_NOTIF && in[0].first != OP_TOALTSTACK && in[0].first != OP_SWAP) { - to_parse.emplace_back(DecodeContext::AndV, -1, -1); - to_parse.emplace_back(DecodeContext::MaybeAndV, -1, -1); - } else { - NodeRef left = std::move(constructed.back()); - constructed.pop_back(); - NodeRef right = std::move(constructed.back()); - constructed.pop_back(); - constructed.push_back(MakeNodeRef(NodeType::AND_V, Vector(std::move(left), std::move(right)))); - } + case DecodeContext::AND_V: { + NodeRef left = std::move(constructed.back()); + constructed.pop_back(); + NodeRef right = std::move(constructed.back()); + constructed.back() = MakeNodeRef(NodeType::AND_V, Vector(std::move(left), std::move(right))); break; } - case DecodeContext::AndB: { + case DecodeContext::AND_B: { NodeRef left = std::move(constructed.back()); constructed.pop_back(); NodeRef right = std::move(constructed.back()); - constructed.pop_back(); - constructed.push_back(MakeNodeRef(NodeType::AND_B, Vector(std::move(left), std::move(right)))); + constructed.back() = MakeNodeRef(NodeType::AND_B, Vector(std::move(left), std::move(right))); break; } - case DecodeContext::OrB: { + case DecodeContext::OR_B: { NodeRef left = std::move(constructed.back()); constructed.pop_back(); NodeRef right = std::move(constructed.back()); - constructed.pop_back(); - constructed.push_back(MakeNodeRef(NodeType::OR_B, Vector(std::move(left), std::move(right)))); + constructed.back() = MakeNodeRef(NodeType::OR_B, Vector(std::move(left), std::move(right))); break; } - case DecodeContext::OrC: { + case DecodeContext::OR_C: { NodeRef left = std::move(constructed.back()); constructed.pop_back(); NodeRef right = std::move(constructed.back()); - constructed.pop_back(); - constructed.push_back(MakeNodeRef(NodeType::OR_C, Vector(std::move(left), std::move(right)))); + constructed.back() = MakeNodeRef(NodeType::OR_C, Vector(std::move(left), std::move(right))); break; } - case DecodeContext::OrD: { + case DecodeContext::OR_D: { NodeRef left = std::move(constructed.back()); constructed.pop_back(); NodeRef right = std::move(constructed.back()); - constructed.pop_back(); - constructed.push_back(MakeNodeRef(NodeType::OR_D, Vector(std::move(left), std::move(right)))); + constructed.back() = MakeNodeRef(NodeType::OR_D, Vector(std::move(left), std::move(right))); break; } - case DecodeContext::AndOr: { + case DecodeContext::ANDOR: { NodeRef left = std::move(constructed.back()); constructed.pop_back(); NodeRef right = std::move(constructed.back()); constructed.pop_back(); NodeRef mid = std::move(constructed.back()); - constructed.pop_back(); - constructed.push_back(MakeNodeRef(NodeType::ANDOR, Vector(std::move(left), std::move(mid), std::move(right)))); + constructed.back() = MakeNodeRef(NodeType::ANDOR, Vector(std::move(left), std::move(mid), std::move(right))); break; } - case DecodeContext::ThreshW: { + case DecodeContext::THRESH_W: { if (in >= last) return {}; if (in[0].first == OP_ADD) { ++in; - to_parse.emplace_back(DecodeContext::ThreshW, n+1, k); - to_parse.emplace_back(DecodeContext::WExpression, -1, -1); + to_parse.emplace_back(DecodeContext::THRESH_W, n+1, k); + to_parse.emplace_back(DecodeContext::W_EXPR, -1, -1); } else { - to_parse.emplace_back(DecodeContext::ThreshE, n+1, k); - to_parse.emplace_back(DecodeContext::Expression, -1, -1); + to_parse.emplace_back(DecodeContext::THRESH_E, n+1, k); + to_parse.emplace_back(DecodeContext::SINGLE_BKV_EXPR, -1, -1); } break; } - case DecodeContext::ThreshE: { + case DecodeContext::THRESH_E: { std::vector> subs; for (int i = 0; i < n; ++i) { NodeRef sub = std::move(constructed.back()); @@ -1327,24 +1338,23 @@ inline NodeRef DecodeScript(I& in, I last, const Ctx& ctx) { constructed.push_back(MakeNodeRef(NodeType::THRESH, std::move(subs), k)); break; } - case DecodeContext::EndIf: { + case DecodeContext::ENDIF: { if (in >= last) return {}; // could be andor or or_i if (in[0].first == OP_ELSE) { ++in; - to_parse.emplace_back(DecodeContext::EndIfElse, -1, -1); - to_parse.emplace_back(DecodeContext::MaybeAndV, -1, -1); - to_parse.emplace_back(DecodeContext::Expression, -1, -1); + to_parse.emplace_back(DecodeContext::ENDIF_ELSE, -1, -1); + to_parse.emplace_back(DecodeContext::BKV_EXPR, -1, -1); } // could be j: or d: wrapper else if (in[0].first == OP_IF) { if (last - in >= 2 && in[1].first == OP_DUP) { in += 2; - to_parse.emplace_back(DecodeContext::DupIf, -1, -1); + to_parse.emplace_back(DecodeContext::DUP_IF, -1, -1); } else if (last - in >= 3 && in[1].first == OP_0NOTEQUAL && in[2].first == OP_SIZE) { in += 3; - to_parse.emplace_back(DecodeContext::NonZero, -1, -1); + to_parse.emplace_back(DecodeContext::NON_ZERO, -1, -1); } else { return {}; @@ -1352,37 +1362,36 @@ inline NodeRef DecodeScript(I& in, I last, const Ctx& ctx) { // could be or_c or or_d } else if (in[0].first == OP_NOTIF) { ++in; - to_parse.emplace_back(DecodeContext::EndIfNotIf, -1, -1); + to_parse.emplace_back(DecodeContext::ENDIF_NOTIF, -1, -1); } else { return {}; } break; } - case DecodeContext::EndIfNotIf: { + case DecodeContext::ENDIF_NOTIF: { if (in >= last) return {}; if (in[0].first == OP_IFDUP) { ++in; - to_parse.emplace_back(DecodeContext::OrD, -1, -1); + to_parse.emplace_back(DecodeContext::OR_D, -1, -1); } else { - to_parse.emplace_back(DecodeContext::OrC, -1, -1); + to_parse.emplace_back(DecodeContext::OR_C, -1, -1); } - to_parse.emplace_back(DecodeContext::Expression, -1, -1); + to_parse.emplace_back(DecodeContext::SINGLE_BKV_EXPR, -1, -1); break; } - case DecodeContext::EndIfElse: { + case DecodeContext::ENDIF_ELSE: { if (in >= last) return {}; if (in[0].first == OP_IF) { ++in; NodeRef left = std::move(constructed.back()); constructed.pop_back(); NodeRef right = std::move(constructed.back()); - constructed.pop_back(); - constructed.push_back(MakeNodeRef(NodeType::OR_I, Vector(std::move(left), std::move(right)))); + constructed.back() = MakeNodeRef(NodeType::OR_I, Vector(std::move(left), std::move(right))); } else if (in[0].first == OP_NOTIF) { ++in; - to_parse.emplace_back(DecodeContext::AndOr, -1, -1); - to_parse.emplace_back(DecodeContext::Expression, -1, -1); + to_parse.emplace_back(DecodeContext::ANDOR, -1, -1); + to_parse.emplace_back(DecodeContext::SINGLE_BKV_EXPR, -1, -1); } else { return {}; }