Skip to content

Commit

Permalink
Port script
Browse files Browse the repository at this point in the history
  • Loading branch information
timemarkovqtum committed Jun 4, 2024
1 parent 8228e2c commit 5a1c7d5
Show file tree
Hide file tree
Showing 9 changed files with 491 additions and 14 deletions.
1 change: 1 addition & 0 deletions src/script/descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,7 @@ class PKDescriptor final : public DescriptorImpl
}
public:
PKDescriptor(std::unique_ptr<PubkeyProvider> prov, bool xonly = false) : DescriptorImpl(Vector(std::move(prov)), "pk"), m_xonly(xonly) {}
std::optional<OutputType> GetOutputType() const override { return OutputType::LEGACY; }
bool IsSingleType() const final { return true; }

std::optional<int64_t> ScriptSize() const override {
Expand Down
180 changes: 169 additions & 11 deletions src/script/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
#include <script/script.h>
#include <uint256.h>

typedef std::vector<unsigned char> valtype;

namespace {

inline bool set_success(ScriptError* ret)
Expand Down Expand Up @@ -60,7 +58,7 @@ static inline void popstack(std::vector<valtype>& stack)
stack.pop_back();
}

bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) {
bool IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) {
if (vchPubKey.size() < CPubKey::COMPRESSED_SIZE) {
// Non-canonical public key: too short
return false;
Expand Down Expand Up @@ -104,7 +102,7 @@ bool static IsCompressedPubKey(const valtype &vchPubKey) {
*
* This function is consensus-critical since BIP66.
*/
bool static IsValidSignatureEncoding(const std::vector<unsigned char> &sig) {
bool static IsValidSignatureEncoding(const std::vector<unsigned char> &sig, bool haveHashType = true) {
// Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash]
// * total-length: 1-byte length descriptor of everything that follows,
// excluding the sighash byte.
Expand All @@ -125,7 +123,7 @@ bool static IsValidSignatureEncoding(const std::vector<unsigned char> &sig) {
if (sig[0] != 0x30) return false;

// Make sure the length covers the entire signature.
if (sig[1] != sig.size() - 3) return false;
if (sig[1] != sig.size() - (haveHashType ? 3 : 2)) return false;

// Extract the length of the R element.
unsigned int lenR = sig[3];
Expand All @@ -138,7 +136,7 @@ bool static IsValidSignatureEncoding(const std::vector<unsigned char> &sig) {

// Verify that the length of the signature matches the sum of the length
// of the elements.
if ((size_t)(lenR + lenS + 7) != sig.size()) return false;
if ((size_t)(lenR + lenS + (haveHashType ? 7 : 6)) != sig.size()) return false;

// Check whether the R element is an integer.
if (sig[2] != 0x02) return false;
Expand Down Expand Up @@ -169,14 +167,14 @@ bool static IsValidSignatureEncoding(const std::vector<unsigned char> &sig) {
return true;
}

bool static IsLowDERSignature(const valtype &vchSig, ScriptError* serror) {
if (!IsValidSignatureEncoding(vchSig)) {
bool IsLowDERSignature(const valtype &vchSig, ScriptError* serror, bool haveHashType) {
if (!IsValidSignatureEncoding(vchSig, haveHashType)) {
return set_error(serror, SCRIPT_ERR_SIG_DER);
}
// https://bitcoin.stackexchange.com/a/12556:
// Also note that inside transaction signatures, an extra hashtype byte
// follows the actual signature data.
std::vector<unsigned char> vchSigCopy(vchSig.begin(), vchSig.begin() + vchSig.size() - 1);
std::vector<unsigned char> vchSigCopy(vchSig.begin(), vchSig.begin() + vchSig.size() - (haveHashType ? 1 : 0));
// If the S value is above the order of the curve divided by two, its
// complement modulo the order could have been used instead, which is
// one byte shorter when encoded correctly.
Expand All @@ -186,6 +184,13 @@ bool static IsLowDERSignature(const valtype &vchSig, ScriptError* serror) {
return true;
}

bool IsDERSignature(const valtype &vchSig, ScriptError* serror, bool haveHashType) {
if (!IsValidSignatureEncoding(vchSig, haveHashType)) {
return set_error(serror, SCRIPT_ERR_SIG_DER);
}
return true;
}

bool static IsDefinedHashtypeSignature(const valtype &vchSig) {
if (vchSig.size() == 0) {
return false;
Expand Down Expand Up @@ -1213,6 +1218,27 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
}
break;

//////////////////////////////////////////////////////// qtum
case OP_SENDER:
{
if(!(flags & SCRIPT_OUTPUT_SENDER))
return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
}
break;
case OP_SPEND:
{
return true; // temp
}
break;
case OP_CREATE:
case OP_CALL:
{
valtype scriptRest(pc - 1, pend);
stack.push_back(scriptRest);
return true; // temp
}
break;
////////////////////////////////////////////////////////
default:
return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
}
Expand Down Expand Up @@ -1360,6 +1386,22 @@ uint256 GetSequencesSHA256(const T& txTo)
return ss.GetSHA256();
}

template <class T>
uint256 GetFirstPrevoutSHA256(const T& txTo)
{
HashWriter ss{};
ss << txTo.vin[0].prevout;
return ss.GetSHA256();
}

template <class T>
uint256 GetFirstSequenceSHA256(const T& txTo)
{
HashWriter ss{};
ss << txTo.vin[0].nSequence;
return ss.GetSHA256();
}

/** Compute the (single) SHA256 of the concatenation of all txouts of a tx. */
template <class T>
uint256 GetOutputsSHA256(const T& txTo)
Expand Down Expand Up @@ -1391,6 +1433,27 @@ uint256 GetSpentScriptsSHA256(const std::vector<CTxOut>& outputs_spent)
return ss.GetSHA256();
}

CTxOut GetOutputWithoutSenderSig(const CTxOut& output)
{
return CTxOut(output.nValue, output.scriptPubKey.WithoutSenderSig());
}

template <class T>
uint256 GetOutputsOpSenderSHA256(const T& txTo)
{
HashWriter ss{};
for (const auto& txout : txTo.vout) {
if(txout.scriptPubKey.HasOpSender())
{
ss << GetOutputWithoutSenderSig(txout);
}
else
{
ss << txout;
}
}
return ss.GetSHA256();
}

} // namespace

Expand All @@ -1408,6 +1471,7 @@ void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent
// Determine which precomputation-impacting features this transaction uses.
bool uses_bip143_segwit = force;
bool uses_bip341_taproot = force;
bool uses_op_sender = txTo.HasOpSender();
for (size_t inpos = 0; inpos < txTo.vin.size() && !(uses_bip143_segwit && uses_bip341_taproot); ++inpos) {
if (!txTo.vin[inpos].scriptWitness.IsNull()) {
if (m_spent_outputs_ready && m_spent_outputs[inpos].scriptPubKey.size() == 2 + WITNESS_V1_TAPROOT_SIZE &&
Expand All @@ -1427,16 +1491,24 @@ void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent
if (uses_bip341_taproot && uses_bip143_segwit) break; // No need to scan further if we already need all.
}

if (uses_bip143_segwit || uses_bip341_taproot) {
if (uses_bip143_segwit || uses_bip341_taproot || uses_op_sender) {
// Computations shared between both sighash schemes.
m_prevouts_single_hash = GetPrevoutsSHA256(txTo);
m_sequences_single_hash = GetSequencesSHA256(txTo);
m_outputs_single_hash = GetOutputsSHA256(txTo);
if(uses_op_sender)
{
m_outputs_opsender_single_hash = GetOutputsOpSenderSHA256(txTo);
}
}
if (uses_bip143_segwit) {
if (uses_bip143_segwit || uses_op_sender) {
hashPrevouts = SHA256Uint256(m_prevouts_single_hash);
hashSequence = SHA256Uint256(m_sequences_single_hash);
hashOutputs = SHA256Uint256(m_outputs_single_hash);
if(uses_op_sender)
{
hashOutputsOpSender = SHA256Uint256(m_outputs_opsender_single_hash);
}
m_bip143_segwit_ready = true;
}
if (uses_bip341_taproot && m_spent_outputs_ready) {
Expand Down Expand Up @@ -1564,6 +1636,60 @@ bool SignatureHashSchnorr(uint256& hash_out, ScriptExecutionData& execdata, cons
return true;
}

template <class T>
uint256 SignatureHashOutput(const CScript& scriptCode, const T& txTo, unsigned int nOut, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
{
assert(nOut < txTo.vout.size());

uint256 hashPrevouts;
uint256 hashSequence;
uint256 hashOutputs;
const bool cacheready = cache && cache->m_bip143_segwit_ready;

if (nHashType & SIGHASH_ANYONECANPAY) {
assert(0 < txTo.vin.size());
hashPrevouts = SHA256Uint256(GetFirstPrevoutSHA256(txTo));
hashSequence = SHA256Uint256(GetFirstSequenceSHA256(txTo));
}

if (!(nHashType & SIGHASH_ANYONECANPAY)) {
hashPrevouts = cacheready ? cache->hashPrevouts : SHA256Uint256(GetPrevoutsSHA256(txTo));
}

if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
hashSequence = cacheready ? cache->hashSequence : SHA256Uint256(GetSequencesSHA256(txTo));
}


if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
hashOutputs = cacheready ? cache->hashOutputsOpSender : SHA256Uint256(GetOutputsOpSenderSHA256(txTo));
} else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nOut < txTo.vout.size()) {
HashWriter ss{};
ss << GetOutputWithoutSenderSig(txTo.vout[nOut]);
hashOutputs = ss.GetHash();
}

HashWriter ss{};

// Version
ss << txTo.nVersion;
// Input prevouts/nSequence (none/first/all, depending on flags)
ss << hashPrevouts;
ss << hashSequence;
// The output being signed
ss << GetOutputWithoutSenderSig(txTo.vout[nOut]);
ss << scriptCode;
ss << amount;
// Outputs (none/one/all, depending on flags)
ss << hashOutputs;
// Locktime
ss << txTo.nLockTime;
// Sighash type
ss << nHashType;

return ss.GetHash();
}

template <class T>
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
{
Expand Down Expand Up @@ -1785,6 +1911,38 @@ bool GenericTransactionSignatureChecker<T>::CheckSequence(const CScriptNum& nSeq
template class GenericTransactionSignatureChecker<CTransaction>;
template class GenericTransactionSignatureChecker<CMutableTransaction>;

template <class T>
bool GenericTransactionSignatureOutputChecker<T>::VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
{
return pubkey.Verify(sighash, vchSig);
}

template <class T>
bool GenericTransactionSignatureOutputChecker<T>::CheckECDSASignature(const std::vector<unsigned char>& vchSigIn, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
{
CPubKey pubkey(vchPubKey);
if (!pubkey.IsValid())
return false;

// Hash type is one byte tacked on to the end of the signature
std::vector<unsigned char> vchSig(vchSigIn);
if (vchSig.empty())
return false;
int nHashType = vchSig.back();
vchSig.pop_back();

uint256 sighash = SignatureHashOutput(scriptCode, *txTo, nOut, nHashType, amount, sigversion, this->txdata);

if (!VerifyECDSASignature(vchSig, pubkey, sighash))
return false;

return true;
}

// explicit instantiation
template class GenericTransactionSignatureOutputChecker<CTransaction>;
template class GenericTransactionSignatureOutputChecker<CMutableTransaction>;

static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CScript& exec_script, unsigned int flags, SigVersion sigversion, const BaseSignatureChecker& checker, ScriptExecutionData& execdata, ScriptError* serror)
{
std::vector<valtype> stack{stack_span.begin(), stack_span.end()};
Expand Down
41 changes: 40 additions & 1 deletion src/script/interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class CScriptNum;
class XOnlyPubKey;
struct CScriptWitness;

typedef std::vector<unsigned char> valtype;

/** Signature hash types/flags */
enum
{
Expand Down Expand Up @@ -143,6 +145,14 @@ enum : uint32_t {
// Making unknown public key versions (in BIP 342 scripts) non-standard
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = (1U << 20),

// Support sender address in contract output
//
SCRIPT_OUTPUT_SENDER = (1U << 29),

// Performs the compiled byte code
//
SCRIPT_EXEC_BYTE_CODE = (1U << 30),

// Constants to point to the highest flag in use. Add new flags above this line.
//
SCRIPT_VERIFY_END_MARKER
Expand All @@ -157,13 +167,14 @@ struct PrecomputedTransactionData
uint256 m_prevouts_single_hash;
uint256 m_sequences_single_hash;
uint256 m_outputs_single_hash;
uint256 m_outputs_opsender_single_hash;
uint256 m_spent_amounts_single_hash;
uint256 m_spent_scripts_single_hash;
//! Whether the 5 fields above are initialized.
bool m_bip341_taproot_ready = false;

// BIP143 precomputed data (double-SHA256).
uint256 hashPrevouts, hashSequence, hashOutputs;
uint256 hashPrevouts, hashSequence, hashOutputs, hashOutputsOpSender;
//! Whether the 3 fields above are initialized.
bool m_bip143_segwit_ready = false;

Expand Down Expand Up @@ -242,6 +253,9 @@ extern const HashWriter HASHER_TAPBRANCH; //!< Hasher with tag "TapBranch" pre-
template <class T>
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr);

template <class T>
uint256 SignatureHashOutput(const CScript& scriptCode, const T& txTo, unsigned int nOut, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr);

class BaseSignatureChecker
{
public:
Expand Down Expand Up @@ -334,6 +348,27 @@ class DeferringSignatureChecker : public BaseSignatureChecker
}
};

template <class T>
class GenericTransactionSignatureOutputChecker : public BaseSignatureChecker
{
private:
const T* txTo;
unsigned int nOut;
const CAmount amount;
const PrecomputedTransactionData* txdata;

protected:
virtual bool VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;

public:
GenericTransactionSignatureOutputChecker(const T* txToIn, unsigned int nOutIn, const CAmount& amountIn) : txTo(txToIn), nOut(nOutIn), amount(amountIn), txdata(nullptr) {}
GenericTransactionSignatureOutputChecker(const T* txToIn, unsigned int nOutIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nOut(nOutIn), amount(amountIn), txdata(&txdataIn) {}
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override;
};

using TransactionSignatureOutputChecker = GenericTransactionSignatureOutputChecker<CTransaction>;
using MutableTransactionSignatureOutputChecker = GenericTransactionSignatureOutputChecker<CMutableTransaction>;

/** Compute the BIP341 tapleaf hash from leaf version & script. */
uint256 ComputeTapleafHash(uint8_t leaf_version, Span<const unsigned char> script);
/** Compute the BIP341 tapbranch hash from two branches.
Expand All @@ -351,4 +386,8 @@ size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey,

int FindAndDelete(CScript& script, const CScript& b);

bool IsLowDERSignature(const valtype &vchSig, ScriptError* serror = NULL, bool haveHashType = true);
bool IsDERSignature(const valtype &vchSig, ScriptError* serror = NULL, bool haveHashType = true);
bool IsCompressedOrUncompressedPubKey(const valtype &vchPubKey);

#endif // BITCOIN_SCRIPT_INTERPRETER_H
Loading

0 comments on commit 5a1c7d5

Please sign in to comment.