diff --git a/src/script/script_error.h b/src/script/script_error.h --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -63,6 +63,7 @@ /* Schnorr */ SCRIPT_ERR_SIG_BADLENGTH, + SCRIPT_ERR_SIG_NONSCHNORR, /* softfork safeness */ SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS, diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -84,6 +84,8 @@ "operation"; case SCRIPT_ERR_SIG_BADLENGTH: return "Signature cannot be 65 bytes in CHECKMULTISIG"; + case SCRIPT_ERR_SIG_NONSCHNORR: + return "Only Schnorr signatures allowed in this operation"; case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS: return "NOPx reserved for soft-fork upgrades"; case SCRIPT_ERR_PUBKEYTYPE: diff --git a/src/script/sigencoding.h b/src/script/sigencoding.h --- a/src/script/sigencoding.h +++ b/src/script/sigencoding.h @@ -52,6 +52,15 @@ uint32_t flags, ScriptError *serror); +/** + * Check that the signature provided to authentify a transaction is properly + * encoded Schnorr signature (or null). Signatures passed to the new-mode + * OP_CHECKMULTISIG and its verify variant must be checked using this function. + */ +bool CheckTransactionSchnorrSignatureEncoding(const valtype &vchSig, + uint32_t flags, + ScriptError *serror); + /** * Check that a public key is encoded properly. */ diff --git a/src/script/sigencoding.cpp b/src/script/sigencoding.cpp --- a/src/script/sigencoding.cpp +++ b/src/script/sigencoding.cpp @@ -177,6 +177,15 @@ return true; } +static bool CheckRawSchnorrSignatureEncoding(const slicedvaltype &sig, + uint32_t flags, + ScriptError *serror) { + if (IsSchnorrSig(sig)) { + return true; + } + return set_error(serror, SCRIPT_ERR_SIG_NONSCHNORR); +} + static bool CheckRawSignatureEncoding(const slicedvaltype &sig, uint32_t flags, ScriptError *serror) { if (IsSchnorrSig(sig)) { @@ -262,6 +271,18 @@ }); } +bool CheckTransactionSchnorrSignatureEncoding(const valtype &vchSig, + uint32_t flags, + ScriptError *serror) { + return CheckTransactionSignatureEncodingImpl( + vchSig, flags, serror, + [](const slicedvaltype &templateSig, uint32_t templateFlags, + ScriptError *templateSerror) { + return CheckRawSchnorrSignatureEncoding(templateSig, templateFlags, + templateSerror); + }); +} + static bool IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) { switch (vchPubKey.size()) { case 33: diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -84,6 +84,7 @@ {SCRIPT_ERR_MINIMALIF, "MINIMALIF"}, {SCRIPT_ERR_SIG_NULLFAIL, "NULLFAIL"}, {SCRIPT_ERR_SIG_BADLENGTH, "SIG_BADLENGTH"}, + {SCRIPT_ERR_SIG_NONSCHNORR, "SIG_NONSCHNORR"}, {SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS, "DISCOURAGE_UPGRADABLE_NOPS"}, {SCRIPT_ERR_NONCOMPRESSED_PUBKEY, "NONCOMPRESSED_PUBKEY"}, {SCRIPT_ERR_ILLEGAL_FORKID, "ILLEGAL_FORKID"}, diff --git a/src/test/sigencoding_tests.cpp b/src/test/sigencoding_tests.cpp --- a/src/test/sigencoding_tests.cpp +++ b/src/test/sigencoding_tests.cpp @@ -44,6 +44,8 @@ BOOST_CHECK(CheckTransactionSignatureEncoding(validSig, flags, &err)); BOOST_CHECK_EQUAL(!is64, CheckTransactionECDSASignatureEncoding( validSig, flags, &err)); + BOOST_CHECK_EQUAL(is64, CheckTransactionSchnorrSignatureEncoding( + validSig, flags, &err)); // If we have strict encoding, we prevent the use of undefined flags. std::array undefSigHashes{ @@ -65,6 +67,13 @@ BOOST_CHECK_EQUAL(err, is64 ? SCRIPT_ERR_SIG_BADLENGTH : SCRIPT_ERR_SIG_HASHTYPE); } + BOOST_CHECK_EQUAL(CheckTransactionSchnorrSignatureEncoding( + undefSighash, flags, &err), + !(hasStrictEnc || !is64)); + if (!is64 || hasStrictEnc) { + BOOST_CHECK_EQUAL(err, !is64 ? SCRIPT_ERR_SIG_NONSCHNORR + : SCRIPT_ERR_SIG_HASHTYPE); + } } // If we check strict encoding, then invalid forkid is an error. @@ -87,6 +96,15 @@ : hasForkId ? SCRIPT_ERR_MUST_USE_FORKID : SCRIPT_ERR_ILLEGAL_FORKID); } + BOOST_CHECK_EQUAL( + CheckTransactionSchnorrSignatureEncoding(invalidSig, flags, &err), + !(hasStrictEnc || !is64)); + if (!is64 || hasStrictEnc) { + BOOST_CHECK_EQUAL(err, !is64 + ? SCRIPT_ERR_SIG_NONSCHNORR + : hasForkId ? SCRIPT_ERR_MUST_USE_FORKID + : SCRIPT_ERR_ILLEGAL_FORKID); + } } } @@ -188,6 +206,7 @@ BOOST_CHECK(CheckDataSignatureEncoding({}, flags, &err)); BOOST_CHECK(CheckTransactionSignatureEncoding({}, flags, &err)); BOOST_CHECK(CheckTransactionECDSASignatureEncoding({}, flags, &err)); + BOOST_CHECK(CheckTransactionSchnorrSignatureEncoding({}, flags, &err)); // 64-byte signatures are valid as long as the hashtype is correct. CheckSignatureEncodingWithSigHashType(Zero64, flags); @@ -416,12 +435,16 @@ BOOST_CHECK( !CheckTransactionECDSASignatureEncoding(DER65_hb, flags, &err)); BOOST_CHECK_EQUAL(err, SCRIPT_ERR_SIG_BADLENGTH); + BOOST_CHECK( + CheckTransactionSchnorrSignatureEncoding(DER65_hb, flags, &err)); BOOST_CHECK(CheckDataSignatureEncoding(Zero64, flags, &err)); BOOST_CHECK(CheckTransactionSignatureEncoding(Zero65_hb, flags, &err)); BOOST_CHECK( !CheckTransactionECDSASignatureEncoding(Zero65_hb, flags, &err)); BOOST_CHECK_EQUAL(err, SCRIPT_ERR_SIG_BADLENGTH); + BOOST_CHECK( + CheckTransactionSchnorrSignatureEncoding(Zero65_hb, flags, &err)); } }