Skip to content

Commit e41bd44

Browse files
committed
Add policy: null signature for failed CHECK(MULTI)SIG
1 parent 5a4f6d7 commit e41bd44

File tree

8 files changed

+55
-2
lines changed

8 files changed

+55
-2
lines changed

src/policy/policy.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY
4343
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS |
4444
SCRIPT_VERIFY_CLEANSTACK |
4545
SCRIPT_VERIFY_MINIMALIF |
46+
SCRIPT_VERIFY_NULLFAIL |
4647
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY |
4748
SCRIPT_VERIFY_CHECKSEQUENCEVERIFY |
4849
SCRIPT_VERIFY_LOW_S |

src/script/interpreter.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,9 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
885885
}
886886
bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion);
887887

888+
if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && vchSig.size())
889+
return set_error(serror, SCRIPT_ERR_SIG_NULLFAIL);
890+
888891
popstack(stack);
889892
popstack(stack);
890893
stack.push_back(fSuccess ? vchTrue : vchFalse);
@@ -914,6 +917,9 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
914917
if (nOpCount > MAX_OPS_PER_SCRIPT)
915918
return set_error(serror, SCRIPT_ERR_OP_COUNT);
916919
int ikey = ++i;
920+
// ikey2 is the position of last non-signature item in the stack. Top stack item = 1.
921+
// With SCRIPT_VERIFY_NULLFAIL, this is used for cleanup if operation fails.
922+
int ikey2 = nKeysCount + 2;
917923
i += nKeysCount;
918924
if ((int)stack.size() < i)
919925
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
@@ -970,8 +976,14 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
970976
}
971977

972978
// Clean up stack of actual arguments
973-
while (i-- > 1)
979+
while (i-- > 1) {
980+
// If the operation failed, we require that all signatures must be empty vector
981+
if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && !ikey2 && stacktop(-1).size())
982+
return set_error(serror, SCRIPT_ERR_SIG_NULLFAIL);
983+
if (ikey2 > 0)
984+
ikey2--;
974985
popstack(stack);
986+
}
975987

976988
// A bug causes CHECKMULTISIG to consume one extra argument
977989
// whose contents were not checked in any way.

src/script/interpreter.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ enum
9898
// Segwit script only: Require the argument of OP_IF/NOTIF to be exactly 0x01 or empty vector
9999
//
100100
SCRIPT_VERIFY_MINIMALIF = (1U << 13),
101+
102+
// Signature(s) must be empty vector if an CHECK(MULTI)SIG operation failed
103+
//
104+
SCRIPT_VERIFY_NULLFAIL = (1U << 14),
101105
};
102106

103107
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);

src/script/script_error.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ const char* ScriptErrorString(const ScriptError serror)
6565
return "Dummy CHECKMULTISIG argument must be zero";
6666
case SCRIPT_ERR_MINIMALIF:
6767
return "OP_IF/NOTIF argument must be minimal";
68+
case SCRIPT_ERR_SIG_NULLFAIL:
69+
return "Signature must be zero for failed CHECK(MULTI)SIG operation";
6870
case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS:
6971
return "NOPx reserved for soft-fork upgrades";
7072
case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM:

src/script/script_error.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ typedef enum ScriptError_t
4949
SCRIPT_ERR_PUBKEYTYPE,
5050
SCRIPT_ERR_CLEANSTACK,
5151
SCRIPT_ERR_MINIMALIF,
52+
SCRIPT_ERR_SIG_NULLFAIL,
5253

5354
/* softfork safeness */
5455
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS,

src/test/data/script_tests.json

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1491,6 +1491,27 @@
14911491
"OK",
14921492
"BIP66 example 4, with DERSIG"
14931493
],
1494+
[
1495+
"0x09 0x300602010102010101",
1496+
"0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT",
1497+
"DERSIG",
1498+
"OK",
1499+
"BIP66 example 4, with DERSIG, non-null DER-compliant signature"
1500+
],
1501+
[
1502+
"0",
1503+
"0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT",
1504+
"DERSIG,NULLFAIL",
1505+
"OK",
1506+
"BIP66 example 4, with DERSIG and NULLFAIL"
1507+
],
1508+
[
1509+
"0x09 0x300602010102010101",
1510+
"0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT",
1511+
"DERSIG,NULLFAIL",
1512+
"NULLFAIL",
1513+
"BIP66 example 4, with DERSIG and NULLFAIL, non-null DER-compliant signature"
1514+
],
14941515
[
14951516
"1",
14961517
"0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG",
@@ -2208,5 +2229,15 @@
22082229
[["645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS", "UNBALANCED_CONDITIONAL"],
22092230
[["645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS,MINIMALIF", "UNBALANCED_CONDITIONAL"],
22102231

2211-
["The End"]
2232+
["NULLFAIL should cover all signatures and signatures only"],
2233+
["0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG", "OK", "BIP66 and NULLFAIL-compliant"],
2234+
["0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL", "OK", "BIP66 and NULLFAIL-compliant"],
2235+
["1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL", "OK", "BIP66 and NULLFAIL-compliant, not NULLDUMMY-compliant"],
2236+
["1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL,NULLDUMMY", "SIG_NULLDUMMY", "BIP66 and NULLFAIL-compliant, not NULLDUMMY-compliant"],
2237+
["0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0x09 0x300602010102010101", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG", "OK", "BIP66-compliant but not NULLFAIL-compliant"],
2238+
["0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0x09 0x300602010102010101", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL", "NULLFAIL", "BIP66-compliant but not NULLFAIL-compliant"],
2239+
["0 0x09 0x300602010102010101 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG", "OK", "BIP66-compliant but not NULLFAIL-compliant"],
2240+
["0 0x09 0x300602010102010101 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL", "NULLFAIL", "BIP66-compliant but not NULLFAIL-compliant"],
2241+
2242+
["The End"]
22122243
]

src/test/script_tests.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ static ScriptErrorDesc script_errors[]={
9090
{SCRIPT_ERR_PUBKEYTYPE, "PUBKEYTYPE"},
9191
{SCRIPT_ERR_CLEANSTACK, "CLEANSTACK"},
9292
{SCRIPT_ERR_MINIMALIF, "MINIMALIF"},
93+
{SCRIPT_ERR_SIG_NULLFAIL, "NULLFAIL"},
9394
{SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS, "DISCOURAGE_UPGRADABLE_NOPS"},
9495
{SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM, "DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"},
9596
{SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH, "WITNESS_PROGRAM_WRONG_LENGTH"},

src/test/transaction_tests.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ static std::map<string, unsigned int> mapFlagNames = boost::assign::map_list_of
5151
(string("DISCOURAGE_UPGRADABLE_NOPS"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
5252
(string("CLEANSTACK"), (unsigned int)SCRIPT_VERIFY_CLEANSTACK)
5353
(string("MINIMALIF"), (unsigned int)SCRIPT_VERIFY_MINIMALIF)
54+
(string("NULLFAIL"), (unsigned int)SCRIPT_VERIFY_NULLFAIL)
5455
(string("CHECKLOCKTIMEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)
5556
(string("CHECKSEQUENCEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKSEQUENCEVERIFY)
5657
(string("WITNESS"), (unsigned int)SCRIPT_VERIFY_WITNESS)

0 commit comments

Comments
 (0)