Skip to content

Commit

Permalink
Implement Taproot signature validation
Browse files Browse the repository at this point in the history
* Integration into CheckSig by Pieter Wuille.
  • Loading branch information
jl2012 authored and sipa committed Apr 22, 2019
1 parent 081f7e0 commit a003d56
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 9 deletions.
112 changes: 103 additions & 9 deletions src/script/interpreter.cpp
Expand Up @@ -1265,6 +1265,83 @@ template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo,
template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo);
template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo);

CHashWriter TaggedHash(const std::string& tag)
{
CHashWriter writer(SER_GETHASH, 0);
uint256 taghash;
CSHA256().Write((unsigned char*)tag.data(), tag.size()).Finalize(taghash.begin());
writer << taghash << taghash;
return writer;
}

static const CHashWriter HasherTapSighash = TaggedHash("TapSighash");

template<typename T>
bool SignatureHashTap(uint256& hash_out, const T& tx_to, const unsigned int in_pos, const uint8_t hash_type, const SigVersion sigversion, const PrecomputedTransactionData& cache)
{
assert(in_pos < tx_to.vin.size());
assert(sigversion == SigVersion::TAPROOT);
assert(cache.ready && cache.m_amounts_spent_ready);

CHashWriter ss = HasherTapSighash;

// Epoch
uint8_t epoch = 0;
ss << epoch;

// Hash type
if ((hash_type > 3) && (hash_type < 0x81 || hash_type > 0x83)) return false;
ss << hash_type;
const uint8_t input_type = hash_type & SIGHASH_TAPINPUTMASK;
const uint8_t output_type = hash_type & SIGHASH_TAPOUTPUTMASK;

// Transaction level data
ss << tx_to.nVersion;
ss << tx_to.nLockTime;

if (input_type == SIGHASH_TAPDEFAULT) {
ss << cache.m_prevouts_hash;
ss << cache.m_amounts_spent_hash;
ss << cache.m_sequences_hash;
}
if (output_type == SIGHASH_TAPDEFAULT || output_type == SIGHASH_ALL) {
ss << cache.m_outputs_hash;
}

// Data about the input/prevout being spent
const CScript& scriptPubKey = cache.m_spent_outputs[in_pos].scriptPubKey;
uint8_t spend_type = scriptPubKey.IsPayToScriptHash() ? 1 : 0;
const auto* witstack = &tx_to.vin[in_pos].scriptWitness.stack;
if (witstack->size() > 1 && witstack->back().size() > 0 && witstack->back()[0] == 0xff) {
spend_type |= 2;
}

ss << spend_type;
ss << scriptPubKey;

if (input_type == SIGHASH_ANYONECANPAY) {
ss << tx_to.vin[in_pos].prevout;
ss << cache.m_spent_outputs[in_pos].nValue;
ss << tx_to.vin[in_pos].nSequence;
} else {
ss << uint16_t(in_pos);
}
if (spend_type & 2) {
ss << (CHashWriter(SER_GETHASH, 0) << witstack->back()).GetSHA256();
}

// Data about the output(s)
if (output_type == SIGHASH_SINGLE) {
if (in_pos >= tx_to.vout.size()) return false;
CHashWriter sha_single_output(SER_GETHASH, 0);
sha_single_output << tx_to.vout[in_pos];
ss << sha_single_output.GetSHA256();
}

hash_out = ss.GetSHA256();
return true;
}

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 @@ -1356,15 +1433,32 @@ bool GenericTransactionSignatureChecker<T>::CheckSig(const std::vector<unsigned
std::vector<unsigned char> vchSig(vchSigIn);
if (vchSig.empty())
return false;
int nHashType = vchSig.back();
vchSig.pop_back();

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

if (!VerifySignature(vchSig, pubkey, sighash, SignatureType::ECDSA))
return false;

return true;
switch (sigversion) {
case SigVersion::BASE:
case SigVersion::WITNESS_V0:
{
int nHashType = vchSig.back();
vchSig.pop_back();
uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, this->txdata);
return VerifySignature(vchSig, pubkey, sighash, SignatureType::ECDSA);
}
case SigVersion::TAPROOT:
{
uint8_t hashtype = SIGHASH_TAPDEFAULT;
if (vchSig.size() == 65) {
hashtype = vchSig.back();
if (hashtype == SIGHASH_TAPDEFAULT) return false;
vchSig.pop_back();
}
if (vchSig.size() != 64) return false;
uint256 sighash;
bool ret = SignatureHashTap(sighash, *txTo, nIn, hashtype, sigversion, *this->txdata);
if (!ret) return false;
return VerifySignature(vchSig, pubkey, sighash, SignatureType::SCHNORR);
}
default:
assert(false);
}
}

template <class T>
Expand Down
8 changes: 8 additions & 0 deletions src/script/interpreter.h
Expand Up @@ -26,6 +26,10 @@ enum
SIGHASH_NONE = 2,
SIGHASH_SINGLE = 3,
SIGHASH_ANYONECANPAY = 0x80,

SIGHASH_TAPDEFAULT = 0,
SIGHASH_TAPOUTPUTMASK = 3,
SIGHASH_TAPINPUTMASK = 0x80,
};

/** Script verification flags.
Expand Down Expand Up @@ -144,6 +148,7 @@ enum class SigVersion
{
BASE = 0,
WITNESS_V0 = 1,
TAPROOT = 2,
};

/** Signature hash sizes */
Expand All @@ -153,6 +158,9 @@ static constexpr size_t WITNESS_V0_KEYHASH_SIZE = 20;
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>
bool SignatureHashTap(uint256& hash_out, const T& tx_to, unsigned int in_pos, uint8_t hash_type, SigVersion sigversion, const PrecomputedTransactionData& cache);

class BaseSignatureChecker
{
public:
Expand Down

0 comments on commit a003d56

Please sign in to comment.