diff --git a/build_msvc/libsecp256k1/libsecp256k1.vcxproj b/build_msvc/libsecp256k1/libsecp256k1.vcxproj
index 99fb63fb02..c42918d6e1 100644
--- a/build_msvc/libsecp256k1/libsecp256k1.vcxproj
+++ b/build_msvc/libsecp256k1/libsecp256k1.vcxproj
@@ -12,7 +12,7 @@
- ENABLE_MODULE_ECDH;ENABLE_MODULE_RECOVERY;%(PreprocessorDefinitions)
+ ENABLE_MODULE_ECDH;ENABLE_MODULE_RECOVERY;ENABLE_MODULE_EXTRAKEYS;ENABLE_MODULE_SCHNORRSIG;%(PreprocessorDefinitions)
..\..\src\secp256k1;%(AdditionalIncludeDirectories)
diff --git a/configure.ac b/configure.ac
index fbf56443f1..3df59e0b7b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1645,7 +1645,7 @@ if test x$need_bundled_univalue = xyes; then
AC_CONFIG_SUBDIRS([src/univalue])
fi
-ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --with-bignum=no --enable-module-recovery"
+ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --with-bignum=no --enable-module-recovery --enable-module-schnorrsig --enable-experimental"
AC_CONFIG_SUBDIRS([src/secp256k1])
AC_OUTPUT
diff --git a/src/pubkey.cpp b/src/pubkey.cpp
index fc14f41a0c..69e3d91392 100644
--- a/src/pubkey.cpp
+++ b/src/pubkey.cpp
@@ -7,6 +7,7 @@
#include
#include
+#include
namespace
{
@@ -166,6 +167,20 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_
return 1;
}
+XOnlyPubKey::XOnlyPubKey(Span bytes)
+{
+ assert(bytes.size() == 32);
+ std::copy(bytes.begin(), bytes.end(), m_keydata.begin());
+}
+
+bool XOnlyPubKey::VerifySchnorr(const uint256& msg, Span sigbytes) const
+{
+ assert(sigbytes.size() == 64);
+ secp256k1_xonly_pubkey pubkey;
+ if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &pubkey, m_keydata.data())) return false;
+ return secp256k1_schnorrsig_verify(secp256k1_context_verify, sigbytes.data(), msg.begin(), &pubkey);
+}
+
bool CPubKey::Verify(const uint256 &hash, const std::vector& vchSig) const {
if (!IsValid())
return false;
diff --git a/src/pubkey.h b/src/pubkey.h
index cd1049f66f..1a818037d1 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -9,6 +9,7 @@
#include
#include
+#include
#include
#include
@@ -206,6 +207,25 @@ public:
bool Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const;
};
+class XOnlyPubKey
+{
+private:
+ uint256 m_keydata;
+
+public:
+ /** Construct an x-only pubkey from exactly 32 bytes. */
+ XOnlyPubKey(Span bytes);
+
+ /** Verify a Schnorr signature against this public key.
+ *
+ * sigbytes must be exactly 64 bytes.
+ */
+ bool VerifySchnorr(const uint256& msg, Span sigbytes) const;
+
+ const unsigned char& operator[](int pos) const { return *(m_keydata.begin() + pos); }
+ size_t size() const { return m_keydata.size(); }
+};
+
struct CExtPubKey {
unsigned char nDepth;
unsigned char vchFingerprint[4];
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index af8fd4912e..aef6e28118 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -1521,6 +1521,12 @@ bool GenericTransactionSignatureChecker::VerifyECDSASignature(const std::vect
return pubkey.Verify(sighash, vchSig);
}
+template
+bool GenericTransactionSignatureChecker::VerifySchnorrSignature(Span sig, const XOnlyPubKey& pubkey, const uint256& sighash) const
+{
+ return pubkey.VerifySchnorr(sighash, sig);
+}
+
template
bool GenericTransactionSignatureChecker::CheckECDSASignature(const std::vector& vchSigIn, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
{
@@ -1543,6 +1549,30 @@ bool GenericTransactionSignatureChecker::CheckECDSASignature(const std::vecto
return true;
}
+template
+bool GenericTransactionSignatureChecker::CheckSchnorrSignature(Span sig, Span pubkey_in, SigVersion sigversion, ScriptError* serror) const
+{
+ assert(sigversion == SigVersion::TAPROOT);
+ // Schnorr signatures have 32-byte public keys. The caller is responsible for enforcing this.
+ assert(pubkey_in.size() == 32);
+ if (sig.size() != 64 && sig.size() != 65) return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_SIZE);
+
+ XOnlyPubKey pubkey{pubkey_in};
+
+ uint8_t hashtype = SIGHASH_DEFAULT;
+ if (sig.size() == 65) {
+ hashtype = SpanPopBack(sig);
+ if (hashtype == SIGHASH_DEFAULT) return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_HASHTYPE);
+ }
+ uint256 sighash;
+ assert(this->txdata);
+ if (!SignatureHashSchnorr(sighash, *txTo, nIn, hashtype, sigversion, *this->txdata)) {
+ return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_HASHTYPE);
+ }
+ if (!VerifySchnorrSignature(sig, pubkey, sighash)) return set_error(serror, SCRIPT_ERR_SCHNORR_SIG);
+ return true;
+}
+
template
bool GenericTransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const
{
diff --git a/src/script/interpreter.h b/src/script/interpreter.h
index b739528f0f..e54243c8f0 100644
--- a/src/script/interpreter.h
+++ b/src/script/interpreter.h
@@ -7,12 +7,14 @@
#define BITCOIN_SCRIPT_INTERPRETER_H
#include