Add crypto opcodes

This commit is contained in:
sanket1729 2021-08-01 19:21:33 +00:00
parent 3e7b8c6aed
commit 7193636d09
7 changed files with 170 additions and 33 deletions

View file

@ -173,12 +173,12 @@ XOnlyPubKey::XOnlyPubKey(Span<const unsigned char> bytes)
std::copy(bytes.begin(), bytes.end(), m_keydata.begin());
}
bool XOnlyPubKey::VerifySchnorr(const uint256& msg, Span<const unsigned char> sigbytes) const
bool XOnlyPubKey::VerifySchnorr(const Span<const unsigned char> msg, Span<const unsigned char> 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(), 32, &pubkey);
return secp256k1_schnorrsig_verify(secp256k1_context_verify, sigbytes.data(), msg.data(), msg.size(), &pubkey);
}
bool XOnlyPubKey::CheckPayToContract(const XOnlyPubKey& base, const uint256& hash, bool parity) const
@ -188,6 +188,18 @@ bool XOnlyPubKey::CheckPayToContract(const XOnlyPubKey& base, const uint256& has
return secp256k1_xonly_pubkey_tweak_add_check(secp256k1_context_verify, m_keydata.begin(), parity, &base_point, hash.begin());
}
bool CPubKey::TweakMulVerify(const CPubKey& untweaked, const uint256& tweak) const
{
assert(this->IsCompressed());
secp256k1_pubkey pk;
if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pk, untweaked.data(), untweaked.size())) return false;
if (!secp256k1_ec_pubkey_tweak_mul(secp256k1_context_verify, &pk, tweak.data())) return false;
unsigned char out_pk[CPubKey::COMPRESSED_SIZE];
size_t out_len = CPubKey::COMPRESSED_SIZE;
if (!secp256k1_ec_pubkey_serialize(secp256k1_context_verify, out_pk, &out_len, &pk, SECP256K1_EC_COMPRESSED)) return false;
return *this == CPubKey(out_pk, out_pk + out_len);
}
bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) const {
if (!IsValid())
return false;

View file

@ -206,6 +206,9 @@ public:
//! Derive BIP32 child pubkey.
bool Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc, std::vector<unsigned char>* tweak = nullptr /* ELEMENTS: vector of key tweak values that are filled out if non-null */) const;
//! Verify that when this public key is tweaked with tweak, the result is res
bool TweakMulVerify(const CPubKey& res, const uint256& tweak) const;
};
class XOnlyPubKey
@ -221,7 +224,7 @@ public:
*
* sigbytes must be exactly 64 bytes.
*/
bool VerifySchnorr(const uint256& msg, Span<const unsigned char> sigbytes) const;
bool VerifySchnorr(const Span<const unsigned char> msg, Span<const unsigned char> sigbytes) const;
bool CheckPayToContract(const XOnlyPubKey& base, const uint256& hash, bool parity) const;
const unsigned char& operator[](int pos) const { return *(m_keydata.begin() + pos); }

View file

@ -433,6 +433,17 @@ public:
};
}
// Check the script has sufficient sigops budget for checksig(crypto) operation
inline bool update_validation_weight(ScriptExecutionData& execdata, ScriptError* serror)
{
assert(execdata.m_validation_weight_left_init);
execdata.m_validation_weight_left -= VALIDATION_WEIGHT_PER_SIGOP_PASSED;
if (execdata.m_validation_weight_left < 0) {
return set_error(serror, SCRIPT_ERR_TAPSCRIPT_VALIDATION_WEIGHT);
}
return true;
}
static bool EvalChecksigPreTapscript(const valtype& vchSig, const valtype& vchPubKey, CScript::const_iterator pbegincodehash, CScript::const_iterator pend, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& fSuccess)
{
assert(sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0);
@ -459,6 +470,47 @@ static bool EvalChecksigPreTapscript(const valtype& vchSig, const valtype& vchPu
return true;
}
static bool EvalTapScriptCheckSigFromStack(const valtype& sig, const valtype& vchPubKey, ScriptExecutionData& execdata, unsigned int flags, const valtype& msg, SigVersion sigversion, ScriptError* serror, bool& success)
{
// This code follows the behaviour of EvalCheckSigTapscript
assert(sigversion == SigVersion::TAPSCRIPT);
/*
* The following validation sequence is consensus critical. Please note how --
* upgradable public key versions precede other rules;
* the script execution fails when using empty signature with invalid public key;
* the script execution fails when using non-empty invalid signature.
*/
success = !sig.empty();
if (success) {
// Implement the sigops/witnesssize ratio test.
// Passing with an upgradable public key version is also counted.
if (!update_validation_weight(execdata, serror)) return false; // serror is set
}
if (vchPubKey.size() == 0) {
return set_error(serror, SCRIPT_ERR_PUBKEYTYPE);
} else if (vchPubKey.size() == 32) {
if (success) {
if (sig.size() != 64)
return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_SIZE);
const XOnlyPubKey pubkey{vchPubKey};
if (!pubkey.VerifySchnorr(msg, sig))
return set_error(serror, SCRIPT_ERR_SCHNORR_SIG);
}
} else {
/*
* New public key version softforks should be defined before this `else` block.
* Generally, the new code should not do anything but failing the script execution. To avoid
* consensus bugs, it should not modify any existing values (including `success`).
*/
if ((flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE) != 0) {
return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_PUBKEYTYPE);
}
}
return true;
}
static bool EvalChecksigTapscript(const valtype& sig, const valtype& pubkey, ScriptExecutionData& execdata, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& success)
{
assert(sigversion == SigVersion::TAPSCRIPT);
@ -473,11 +525,7 @@ static bool EvalChecksigTapscript(const valtype& sig, const valtype& pubkey, Scr
if (success) {
// Implement the sigops/witnesssize ratio test.
// Passing with an upgradable public key version is also counted.
assert(execdata.m_validation_weight_left_init);
execdata.m_validation_weight_left -= VALIDATION_WEIGHT_PER_SIGOP_PASSED;
if (execdata.m_validation_weight_left < 0) {
return set_error(serror, SCRIPT_ERR_TAPSCRIPT_VALIDATION_WEIGHT);
}
if (!update_validation_weight(execdata, serror)) return false; // serror is set
}
if (pubkey.size() == 0) {
return set_error(serror, SCRIPT_ERR_PUBKEYTYPE);
@ -519,6 +567,11 @@ static bool EvalChecksig(const valtype& sig, const valtype& pubkey, CScript::con
assert(false);
}
static const CHashWriter HASHER_TAPLEAF_ELEMENTS = TaggedHash("TapLeaf/elements");
static const CHashWriter HASHER_TAPBRANCH_ELEMENTS = TaggedHash("TapBranch/elements");
static const CHashWriter HASHER_TAPTWEAK_ELEMENTS = TaggedHash("TapTweak/elements");
static const CHashWriter HASHER_TAPSIGHASH_ELEMENTS = TaggedHash("TapSighash/elements");
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror)
{
static const CScriptNum bnZero(0);
@ -1664,29 +1717,40 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
valtype& vchSig = stacktop(-3);
valtype& vchData = stacktop(-2);
valtype& vchPubKey = stacktop(-1);
bool fSuccess;
// Different semantics for CHECKSIGFROMSTACK for taproot and pre-taproot
if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0)
{
// Sigs from stack have no hash byte ever
if (!CheckSignatureEncoding(vchSig, (flags | SCRIPT_NO_SIGHASH_BYTE), serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) {
//serror is set
return false;
}
// Sigs from stack have no hash byte ever
if (!CheckSignatureEncoding(vchSig, (flags | SCRIPT_NO_SIGHASH_BYTE), serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) {
//serror is set
return false;
valtype vchHash(CSHA256::OUTPUT_SIZE);
CSHA256().Write(vchData.data(), vchData.size()).Finalize(vchHash.data());
uint256 hash(vchHash);
CPubKey pubkey(vchPubKey);
fSuccess = pubkey.Verify(hash, vchSig);
// CHECKSIGFROMSTACK in pre-tapscript cannot be failed.
if (!fSuccess)
return set_error(serror, SCRIPT_ERR_CHECKSIGVERIFY);
} else {
// New BIP 340 semantics for CHECKSIGFROMSTACK
if (!EvalTapScriptCheckSigFromStack(vchSig, vchPubKey, execdata, flags, vchData, sigversion, serror, fSuccess)) return false;
}
valtype vchHash(CSHA256::OUTPUT_SIZE);
CSHA256().Write(vchData.data(), vchData.size()).Finalize(vchHash.data());
uint256 hash(vchHash);
CPubKey pubkey(vchPubKey);
bool fSuccess = pubkey.Verify(hash, vchSig);
popstack(stack);
popstack(stack);
popstack(stack);
stack.push_back(fSuccess ? vchTrue : vchFalse);
if (opcode == OP_CHECKSIGFROMSTACKVERIFY)
popstack(stack);
if (!fSuccess)
return set_error(serror, SCRIPT_ERR_CHECKSIGVERIFY);
{
if (fSuccess)
popstack(stack);
else
return set_error(serror, SCRIPT_ERR_CHECKSIGVERIFY);
}
}
break;
@ -2118,6 +2182,58 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
push8_le(stack, static_cast<int64_t>(num));
}
break;
case OP_ECMULSCALARVERIFY:
{
// OP_ECMULSCALARVERIFY is available post tapscript
if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
valtype& vchRes = stacktop(-3);
valtype& vchGenerator = stacktop(-2);
valtype& vchScalar = stacktop(-1);
CPubKey pk(vchGenerator);
CPubKey res(vchRes);
if (!pk.IsCompressed() || !res.IsCompressed())
return set_error(serror, SCRIPT_ERR_PUBKEYTYPE);
if (!update_validation_weight(execdata, serror)) return false; // serror is set
if (vchScalar.size() != 32 || !res.TweakMulVerify(pk, uint256(vchScalar)))
return set_error(serror, SCRIPT_ERR_ECMULTVERIFYFAIL);
popstack(stack);
popstack(stack);
popstack(stack);
}
break;
//crypto opcodes
case OP_TWEAKVERIFY:
{
// OP_TWEAKVERIFY is available post tapscript
if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
valtype& vchTweakedKey = stacktop(-3);
valtype& vchTweak = stacktop(-2);
valtype& vchInternalKey = stacktop(-1);
if (vchTweakedKey.size() != CPubKey::COMPRESSED_SIZE || (vchTweakedKey[0] != 0x02 && vchTweakedKey[0] != 0x03)
|| vchInternalKey.size() != 32 || vchTweak.size() != 32)
return set_error(serror, SCRIPT_ERR_PUBKEYTYPE);
if (!update_validation_weight(execdata, serror)) return false; // serror is set
const XOnlyPubKey tweakedXOnlyKey{Span<const unsigned char>{vchTweakedKey.data() + 1, vchTweakedKey.data() + CPubKey::COMPRESSED_SIZE}};
const uint256 tweak(vchTweak);
const XOnlyPubKey internalKey{vchInternalKey};
if (!tweakedXOnlyKey.CheckPayToContract(internalKey, tweak, vchTweakedKey[0] & 1))
return set_error(serror, SCRIPT_ERR_ECMULTVERIFYFAIL);
popstack(stack);
popstack(stack);
popstack(stack);
}
break;
default:
return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
@ -2490,12 +2606,6 @@ template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo,
template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo);
template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo);
static const CHashWriter HASHER_TAPLEAF_ELEMENTS = TaggedHash("TapLeaf/elements");
static const CHashWriter HASHER_TAPBRANCH_ELEMENTS = TaggedHash("TapBranch/elements");
static const CHashWriter HASHER_TAPTWEAK_ELEMENTS = TaggedHash("TapTweak/elements");
static const CHashWriter HASHER_TAPSIGHASH_ELEMENTS = TaggedHash("TapSighash/elements");
PrecomputedTransactionData::PrecomputedTransactionData(const uint256& hash_genesis_block)
: m_tapsighash_hasher(CHashWriter(HASHER_TAPSIGHASH_ELEMENTS) << hash_genesis_block << hash_genesis_block) {}

View file

@ -181,6 +181,11 @@ std::string GetOpName(opcodetype opcode)
case OP_SCRIPTNUMTOLE64 : return "OP_SCRIPTNUMTOLE64";
case OP_LE64TOSCRIPTNUM : return "OP_LE64TOSCRIPTNUM";
case OP_LE32TOLE64 : return "OP_LE32TOLE64";
// Crypto opcodes
case OP_ECMULSCALARVERIFY : return "OP_ECMULSCALARVERIFY";
case OP_TWEAKVERIFY : return "OP_TWEAKVERIFY";
case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE";
default:

View file

@ -259,11 +259,15 @@ enum opcodetype
OP_LE64TOSCRIPTNUM = 0xe1,
OP_LE32TOLE64 = 0xe2,
// Crypto opcodes
OP_ECMULSCALARVERIFY = 0xe3,
OP_TWEAKVERIFY = 0xe4,
OP_INVALIDOPCODE = 0xff,
};
// Maximum value that an opcode can be
static const unsigned int MAX_OPCODE = OP_LE32TOLE64; // 0xe6
static const unsigned int MAX_OPCODE = OP_TWEAKVERIFY; // 0xe4
std::string GetOpName(opcodetype opcode);

View file

@ -127,6 +127,8 @@ std::string ScriptErrorString(const ScriptError serror)
return "Arithmetic opcodes expect 8 bytes operands";
case SCRIPT_ERR_ARITHMETIC64:
return "Arithmetic opcode error";
case SCRIPT_ERR_ECMULTVERIFYFAIL:
return "EC scalar mult verify fail";
case SCRIPT_ERR_UNKNOWN_ERROR:
case SCRIPT_ERR_ERROR_COUNT:
default: break;

View file

@ -95,10 +95,11 @@ typedef enum ScriptError_t
SCRIPT_ERR_INTROSPECT_CONTEXT_UNAVAILABLE,
SCRIPT_ERR_INTROSPECT_INDEX_OUT_OF_BOUNDS,
SCRIPT_ERR_EXPECTED_8BYTES,
SCRIPT_ERR_ARITHMETIC64
SCRIPT_ERR_ARITHMETIC64,
SCRIPT_ERR_ECMULTVERIFYFAIL
} ScriptError;
#define SCRIPT_ERR_LAST SCRIPT_ERR_ARITHMETIC64
#define SCRIPT_ERR_LAST SCRIPT_ERR_ECMULTVERIFYFAIL
std::string ScriptErrorString(const ScriptError error);