script: match multisigs with up to MAX_PUBKEYS_PER_MULTISIG keys

We were previously ruling out 17-20 pubkeys multisig, while they are
only invalid under P2SH context.
This makes multisigs with up to 20 keys be detected as valid by the
solver. This is however *not* a policy change as it would only apply
to bare multisigs, which are already limited to 3 pubkeys.

Note that this does not change the sigOpCount calculation (as it would
break consensus). Therefore 1-16 keys multisigs are counted as 1-16 sigops
and 17-20 keys multisigs are counted as 20 sigops.

Signed-off-by: Antoine Poinsot <darosior@protonmail.com>
This commit is contained in:
Antoine Poinsot
2021-01-05 13:32:41 +01:00
parent ac219dcbcc
commit 9fc68faf35
4 changed files with 72 additions and 8 deletions

View File

@@ -88,21 +88,53 @@ static constexpr bool IsSmallInteger(opcodetype opcode)
return opcode >= OP_1 && opcode <= OP_16;
}
static bool MatchMultisig(const CScript& script, unsigned int& required, std::vector<valtype>& pubkeys)
static constexpr bool IsPushdataOp(opcodetype opcode)
{
return opcode > OP_FALSE && opcode <= OP_PUSHDATA4;
}
static constexpr bool IsValidMultisigKeyCount(int n_keys)
{
return n_keys > 0 && n_keys <= MAX_PUBKEYS_PER_MULTISIG;
}
static bool GetMultisigKeyCount(opcodetype opcode, valtype data, int& count)
{
if (IsSmallInteger(opcode)) {
count = CScript::DecodeOP_N(opcode);
return IsValidMultisigKeyCount(count);
}
if (IsPushdataOp(opcode)) {
if (!CheckMinimalPush(data, opcode)) return false;
try {
count = CScriptNum(data, /* fRequireMinimal = */ true).getint();
return IsValidMultisigKeyCount(count);
} catch (const scriptnum_error&) {
return false;
}
}
return false;
}
static bool MatchMultisig(const CScript& script, int& required_sigs, std::vector<valtype>& pubkeys)
{
opcodetype opcode;
valtype data;
int num_keys;
CScript::const_iterator it = script.begin();
if (script.size() < 1 || script.back() != OP_CHECKMULTISIG) return false;
if (!script.GetOp(it, opcode, data) || !IsSmallInteger(opcode)) return false;
required = CScript::DecodeOP_N(opcode);
if (!script.GetOp(it, opcode, data) || !GetMultisigKeyCount(opcode, data, required_sigs)) return false;
while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) {
pubkeys.emplace_back(std::move(data));
}
if (!IsSmallInteger(opcode)) return false;
unsigned int keys = CScript::DecodeOP_N(opcode);
if (pubkeys.size() != keys || keys < required) return false;
if (!GetMultisigKeyCount(opcode, data, num_keys)) return false;
if (pubkeys.size() != static_cast<unsigned long>(num_keys) || num_keys < required_sigs) return false;
return (it + 1 == script.end());
}
@@ -163,7 +195,7 @@ TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned c
return TxoutType::PUBKEYHASH;
}
unsigned int required;
int required;
std::vector<std::vector<unsigned char>> keys;
if (MatchMultisig(scriptPubKey, required, keys)) {
vSolutionsRet.push_back({static_cast<unsigned char>(required)}); // safe as required is in range 1..16