diff --git a/src/test/data/script_tests.json b/src/test/data/script_tests.json
--- a/src/test/data/script_tests.json
+++ b/src/test/data/script_tests.json
@@ -3415,6 +3415,69 @@
     "CLEANSTACK",
     "v0 P2SH-P2WPKH Segwit Recovery spending a non-P2SH output"
 ],
+[
+    "0x09 0x300602010702010701 0x21 0x03a7bcb86f12d0635c850b6f0c945e4b4fcb400092a74b8d7e83275eb562d9fbb6",
+    "CHECKSIG",
+    "STRICTENC",
+    "OK",
+    "recovered-pubkey CHECKSIG 7,7 (wrapped r)"
+],
+[
+    "0x40 0x303d021d776879206d757374207765207375666665722077697468206563647361021c2121212121212121212121212121212121212121212121212121212101 0x21 0x02da78d331f65fd308ed7afbc80d48c908a83050276cb9bdc1fa414bfea4511570",
+    "CHECKSIG",
+    "STRICTENC",
+    "OK",
+    "recovered-pubkey CHECKSIG with 63-byte DER"
+],
+[
+    "0x40 0x303d021d776879206d757374207765207375666665722077697468206563647361021c2121212121212121212121212121212121212121212121212121212101 0x21 0x02da78d331f65fd308ed7afbc80d48c908a83050276cb9bdc1fa414bfea4511570",
+    "CHECKSIG",
+    "SCHNORR,STRICTENC",
+    "OK",
+    "recovered-pubkey CHECKSIG with 63-byte DER; schnorrflag"
+],
+[
+    "0x41 0x303e021d776879206d757374207765207375666665722077697468206563647361021d212121212121212121212121212121212121212121212121212121212101 0x21 0x03f5d556a48a11a677f1a8eb0771f6cd11b1bcf378478c586d54f18634521b833e",
+    "CHECKSIG",
+    "STRICTENC",
+    "OK",
+    "recovered-pubkey CHECKSIG with 64-byte DER"
+],
+[
+    "0x41 0x303e021d776879206d757374207765207375666665722077697468206563647361021d212121212121212121212121212121212121212121212121212121212101 0x21 0x03f5d556a48a11a677f1a8eb0771f6cd11b1bcf378478c586d54f18634521b833e",
+    "CHECKSIG",
+    "SCHNORR,STRICTENC",
+    "EVAL_FALSE",
+    "recovered-pubkey CHECKSIG with 64-byte DER; schnorrflag"
+],
+[
+    "0x42 0x303f021d776879206d757374207765207375666665722077697468206563647361021e21212121212121212121212121212121212121212121212121212121212101 0x21 0x02224d851056412fbe03d1e2e8ec9030f5ee99c7b403e9743f261625eb8dd22922",
+    "CHECKSIG",
+    "STRICTENC",
+    "OK",
+    "recovered-pubkey CHECKSIG with 65-byte DER"
+],
+[
+    "0x42 0x303f021d776879206d757374207765207375666665722077697468206563647361021e21212121212121212121212121212121212121212121212121212121212101 0x21 0x02224d851056412fbe03d1e2e8ec9030f5ee99c7b403e9743f261625eb8dd22922",
+    "CHECKSIG",
+    "SCHNORR,STRICTENC",
+    "OK",
+    "recovered-pubkey CHECKSIG with 65-byte DER; schnorrflag"
+],
+[
+    "0 0x41 0x303e021d776879206d757374207765207375666665722077697468206563647361021d212121212121212121212121212121212121212121212121212121212101 0x21 0x036cd1f91735dda2d984cfbc17ab0e9e7d754d7e4e1fceb691751cdd5c26b0aecc",
+    "1 SWAP 1 CHECKMULTISIG",
+    "STRICTENC",
+    "OK",
+    "recovered-pubkey CHECKMULTISIG with 64-byte DER"
+],
+[
+    "0 0x41 0x303e021d776879206d757374207765207375666665722077697468206563647361021d212121212121212121212121212121212121212121212121212121212101 0x21 0x036cd1f91735dda2d984cfbc17ab0e9e7d754d7e4e1fceb691751cdd5c26b0aecc",
+    "1 SWAP 1 CHECKMULTISIG",
+    "SCHNORR,STRICTENC",
+    "SIG_BADLENGTH",
+    "recovered-pubkey CHECKMULTISIG with 64-byte DER; schnorrflag"
+],
 
 ["CHECKSEQUENCEVERIFY tests"],
 ["", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "INVALID_STACK_OPERATION", "CSV automatically fails on an empty stack"],
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
@@ -390,6 +390,82 @@
         return *this;
     }
 
+    TestBuilder &PushECDSARecoveredPubKey(
+        const std::vector<uint8_t> &rdata, const std::vector<uint8_t> &sdata,
+        SigHashType sigHashType = SigHashType(), Amount amount = Amount::zero(),
+        uint32_t sigFlags = SCRIPT_ENABLE_SIGHASH_FORKID) {
+        // This calculates a pubkey to verify with a given ECDSA transaction
+        // signature.
+        uint256 hash = SignatureHash(script, CTransaction(spendTx), 0,
+                                     sigHashType, amount, nullptr, sigFlags);
+
+        assert(rdata.size() <= 32);
+        assert(sdata.size() <= 32);
+
+        // Our strategy: make a 'key recovery' signature, and just try all the
+        // recovery IDs. If none of them work then this means the 'r' value
+        // doesn't have any corresponding point, and the caller should pick a
+        // different r.
+        std::vector<uint8_t> vchSig(65, 0);
+        std::copy(rdata.begin(), rdata.end(),
+                  vchSig.begin() + (33 - rdata.size()));
+        std::copy(sdata.begin(), sdata.end(),
+                  vchSig.begin() + (65 - sdata.size()));
+
+        CPubKey key;
+        for (uint8_t recid : {0, 1, 2, 3}) {
+            vchSig[0] = 31 + recid;
+            if (key.RecoverCompact(hash, vchSig)) {
+                // found a match
+                break;
+            }
+        }
+        if (!key.IsValid()) {
+            throw std::runtime_error(
+                std::string("Could not generate pubkey for ") + HexStr(rdata));
+        }
+        std::vector<uint8_t> vchKey(key.begin(), key.end());
+
+        DoPush(vchKey);
+        return *this;
+    }
+
+    TestBuilder &
+    PushECDSASigFromParts(const std::vector<uint8_t> &rdata,
+                          const std::vector<uint8_t> &sdata,
+                          SigHashType sigHashType = SigHashType()) {
+        // Constructs a DER signature out of variable-length r and s arrays &
+        // adds hashtype byte.
+        assert(rdata.size() <= 32);
+        assert(sdata.size() <= 32);
+        assert(rdata.size() > 0);
+        assert(sdata.size() > 0);
+        assert(rdata[0] != 0);
+        assert(sdata[0] != 0);
+        std::vector<uint8_t> vchSig{0x30, 0x00, 0x02};
+        if (rdata[0] & 0x80) {
+            vchSig.push_back(rdata.size() + 1);
+            vchSig.push_back(0);
+            vchSig.insert(vchSig.end(), rdata.begin(), rdata.end());
+        } else {
+            vchSig.push_back(rdata.size());
+            vchSig.insert(vchSig.end(), rdata.begin(), rdata.end());
+        }
+        vchSig.push_back(0x02);
+        if (sdata[0] & 0x80) {
+            vchSig.push_back(sdata.size() + 1);
+            vchSig.push_back(0);
+            vchSig.insert(vchSig.end(), sdata.begin(), sdata.end());
+        } else {
+            vchSig.push_back(sdata.size());
+            vchSig.insert(vchSig.end(), sdata.begin(), sdata.end());
+        }
+        vchSig[1] = vchSig.size() - 2;
+        vchSig.push_back(static_cast<uint8_t>(sigHashType.getRawSigHashType()));
+        DoPush(vchSig);
+        return *this;
+    }
+
     TestBuilder &Push(const CPubKey &pubkey) {
         DoPush(std::vector<uint8_t>(pubkey.begin(), pubkey.end()));
         return *this;
@@ -2259,6 +2335,101 @@
             .Push(CScript() << OP_0 << ToByteVector(keys.pubkey0.GetID()))
             .ScriptError(SCRIPT_ERR_CLEANSTACK));
 
+    {
+        // There is a point with x = 7 + order but not x = 7.
+        // Since r = x mod order, this can have valid signatures, as
+        // demonstrated here.
+        std::vector<uint8_t> rdata{7};
+        std::vector<uint8_t> sdata{7};
+        tests.push_back(TestBuilder(CScript() << OP_CHECKSIG,
+                                    "recovered-pubkey CHECKSIG 7,7 (wrapped r)",
+                                    SCRIPT_VERIFY_STRICTENC)
+                            .PushECDSASigFromParts(rdata, sdata)
+                            .PushECDSARecoveredPubKey(rdata, sdata));
+    }
+    {
+        // Arbitrary r value that is 29 bytes long, to give room for varying
+        // the length of s:
+        std::vector<uint8_t> rdata = ParseHex(
+            "776879206d757374207765207375666665722077697468206563647361");
+        std::vector<uint8_t> sdata(58 - rdata.size() - 1, 33);
+        tests.push_back(
+            TestBuilder(CScript() << OP_CHECKSIG,
+                        "recovered-pubkey CHECKSIG with 63-byte DER",
+                        SCRIPT_VERIFY_STRICTENC)
+                .PushECDSASigFromParts(rdata, sdata)
+                .PushECDSARecoveredPubKey(rdata, sdata));
+        tests.push_back(
+            TestBuilder(
+                CScript() << OP_CHECKSIG,
+                "recovered-pubkey CHECKSIG with 63-byte DER; schnorrflag",
+                SCRIPT_VERIFY_STRICTENC | SCRIPT_ENABLE_SCHNORR)
+                .PushECDSASigFromParts(rdata, sdata)
+                .PushECDSARecoveredPubKey(rdata, sdata));
+    }
+    {
+        // 64-byte ECDSA sig works before schnorr flag activation, but
+        // not after.
+        std::vector<uint8_t> rdata = ParseHex(
+            "776879206d757374207765207375666665722077697468206563647361");
+        std::vector<uint8_t> sdata(58 - rdata.size(), 33);
+        tests.push_back(
+            TestBuilder(CScript() << OP_CHECKSIG,
+                        "recovered-pubkey CHECKSIG with 64-byte DER",
+                        SCRIPT_VERIFY_STRICTENC)
+                .PushECDSASigFromParts(rdata, sdata)
+                .PushECDSARecoveredPubKey(rdata, sdata));
+        tests.push_back(
+            TestBuilder(
+                CScript() << OP_CHECKSIG,
+                "recovered-pubkey CHECKSIG with 64-byte DER; schnorrflag",
+                SCRIPT_VERIFY_STRICTENC | SCRIPT_ENABLE_SCHNORR)
+                .PushECDSASigFromParts(rdata, sdata)
+                .PushECDSARecoveredPubKey(rdata, sdata)
+                .ScriptError(SCRIPT_ERR_EVAL_FALSE));
+    }
+    {
+        std::vector<uint8_t> rdata = ParseHex(
+            "776879206d757374207765207375666665722077697468206563647361");
+        std::vector<uint8_t> sdata(58 - rdata.size() + 1, 33);
+        tests.push_back(
+            TestBuilder(CScript() << OP_CHECKSIG,
+                        "recovered-pubkey CHECKSIG with 65-byte DER",
+                        SCRIPT_VERIFY_STRICTENC)
+                .PushECDSASigFromParts(rdata, sdata)
+                .PushECDSARecoveredPubKey(rdata, sdata));
+        tests.push_back(
+            TestBuilder(
+                CScript() << OP_CHECKSIG,
+                "recovered-pubkey CHECKSIG with 65-byte DER; schnorrflag",
+                SCRIPT_VERIFY_STRICTENC | SCRIPT_ENABLE_SCHNORR)
+                .PushECDSASigFromParts(rdata, sdata)
+                .PushECDSARecoveredPubKey(rdata, sdata));
+    }
+    {
+        // Try 64-byte ECDSA sig again, in multisig.
+        std::vector<uint8_t> rdata = ParseHex(
+            "776879206d757374207765207375666665722077697468206563647361");
+        std::vector<uint8_t> sdata(58 - rdata.size(), 33);
+        tests.push_back(
+            TestBuilder(CScript()
+                            << OP_1 << OP_SWAP << OP_1 << OP_CHECKMULTISIG,
+                        "recovered-pubkey CHECKMULTISIG with 64-byte DER",
+                        SCRIPT_VERIFY_STRICTENC)
+                .Num(0)
+                .PushECDSASigFromParts(rdata, sdata)
+                .PushECDSARecoveredPubKey(rdata, sdata));
+        tests.push_back(
+            TestBuilder(
+                CScript() << OP_1 << OP_SWAP << OP_1 << OP_CHECKMULTISIG,
+                "recovered-pubkey CHECKMULTISIG with 64-byte DER; schnorrflag",
+                SCRIPT_VERIFY_STRICTENC | SCRIPT_ENABLE_SCHNORR)
+                .Num(0)
+                .PushECDSASigFromParts(rdata, sdata)
+                .PushECDSARecoveredPubKey(rdata, sdata)
+                .ScriptError(SCRIPT_ERR_SIG_BADLENGTH));
+    }
+
     std::set<std::string> tests_set;
 
     {