mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-14 12:43:40 +02:00
Add crypto opcodes
This commit is contained in:
parent
3e7b8c6aed
commit
7193636d09
7 changed files with 170 additions and 33 deletions
|
|
@ -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) {}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue