@@ -39,7 +39,7 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
3939BC_PUSH_WARNING (NO_ARRAY_INDEXING)
4040
4141// static
42- // TODO: would be inlined but machine is a circular include.
42+ // This would be inlined but machine is a circular include.
4343// *****************************************************************************
4444// CONSENSUS: BIP34 requires coinbase input script to begin with one byte
4545// that indicates height size. This is inconsistent with an extreme future
@@ -54,6 +54,40 @@ bool script::is_coinbase_pattern(const operations& ops, size_t height) NOEXCEPT
5454 && ops[0 ].data () == chunk::from_integer (to_signed (height));
5555}
5656
57+ // static
58+ // This would be constexpr but machine is a circular include.
59+ // op_check_multisig is limited to 20 signatures (max_script_public_keys).
60+ bool script::is_pay_multisig_pattern (const operations& ops) NOEXCEPT
61+ {
62+ if (ops.size () < 4u || ops.back ().code () != opcode::checkmultisig)
63+ return false ;
64+
65+ const auto signatures = [](const operation& op) NOEXCEPT
66+ {
67+ using namespace machine ::number;
68+ if (op.is_positive ())
69+ return operation::opcode_to_positive (op.code ());
70+
71+ int32_t count{};
72+ if (op.is_payload () && integer<4 >::from_chunk (count, op.data ()) &&
73+ !is_limited (count, one, max_script_public_keys))
74+ return narrow_sign_cast<uint8_t >(count);
75+
76+ return 0_u8;
77+ };
78+
79+ const auto m = signatures (ops.front ());
80+ const auto n = signatures (ops[ops.size () - 2u ]);
81+ if (is_zero (m) || is_zero (n) || (m > n) || (n != ops.size () - 3u ))
82+ return false ;
83+
84+ for (auto op = std::next (ops.begin ()); op != std::prev (ops.end (), 2 ); ++op)
85+ if (!is_public_key (op->data ()))
86+ return false ;
87+
88+ return true ;
89+ }
90+
5791// Constructors.
5892// ----------------------------------------------------------------------------
5993
@@ -131,8 +165,8 @@ script::script(reader& source, bool prefix) NOEXCEPT
131165 assign_data (source, prefix);
132166}
133167
134- script::script (const std::string_view& mnemonic) NOEXCEPT
135- : script(from_string(mnemonic))
168+ script::script (const std::string_view& mnemonic, bool bitcoind ) NOEXCEPT
169+ : script(from_string(mnemonic, bitcoind ))
136170{
137171}
138172
@@ -286,8 +320,11 @@ void script::assign_data(reader& source, bool prefix) NOEXCEPT
286320}
287321
288322// static/private
289- script script::from_string (const std::string_view& mnemonic) NOEXCEPT
323+ script script::from_string (const std::string_view& mnemonic,
324+ bool /* bitcoind */ ) NOEXCEPT
290325{
326+ // TODO: incorporate bitcoind option.
327+
291328 constexpr auto valid = true ;
292329 auto easier = false ;
293330 auto failer = false ;
@@ -303,8 +340,6 @@ script script::from_string(const std::string_view& mnemonic) NOEXCEPT
303340
304341 operations ops{};
305342 ops.reserve (tokens.size ());
306-
307- // Create an op list from the split tokens.
308343 for (const auto & token: tokens)
309344 {
310345 ops.emplace_back (std::string_view{ token });
@@ -351,14 +386,16 @@ void script::to_data(writer& sink, bool prefix) const NOEXCEPT
351386 op->to_data (sink);
352387}
353388
354- std::string script::to_string (uint32_t active_flags) const NOEXCEPT
389+ std::string script::to_string (uint32_t active_flags,
390+ bool /* bitcoind */ ) const NOEXCEPT
355391{
392+ // TODO: incorporate bitcoind option.
393+
356394 auto first = true ;
357395 std::ostringstream text{};
358-
359- // Throwing stream aborts.
360396 for (const auto & op: ops ())
361397 {
398+ // Throwing stream aborts.
362399 text << (first ? " " : " " ) << op.to_string (active_flags);
363400 first = false ;
364401 }
0 commit comments