Skip to content

Commit

Permalink
Add BIP 118 APO
Browse files Browse the repository at this point in the history
* Copy PR bitcoin#4 from bitcoin-inquisition (bitcoin-inquisition#4)
but change so that APO is always deployed and no tests
  • Loading branch information
CryptAxe committed Jan 13, 2024
1 parent d7259b2 commit e9e5a8b
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 33 deletions.
3 changes: 2 additions & 1 deletion src/policy/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS{MANDATORY_SCRIPT_VERI
SCRIPT_VERIFY_CONST_SCRIPTCODE |
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION |
SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS |
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE};
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE |
SCRIPT_VERIFY_ANYPREVOUT};

/** For convenience, standard but not mandatory verify flags. */
static constexpr unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS{STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS};
Expand Down
79 changes: 57 additions & 22 deletions src/script/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ static bool EvalChecksigPreTapscript(const valtype& vchSig, const valtype& vchPu
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);
assert(execdata.m_internal_key); // caller must provide the internal key

/*
* The following validation sequence is consensus critical. Please note how --
Expand All @@ -366,9 +367,23 @@ static bool EvalChecksigTapscript(const valtype& sig, const valtype& pubkey, Scr
if (pubkey.size() == 0) {
return set_error(serror, SCRIPT_ERR_PUBKEYTYPE);
} else if (pubkey.size() == 32) {
if (success && !checker.CheckSchnorrSignature(sig, pubkey, sigversion, execdata, serror)) {
if (success && !checker.CheckSchnorrSignature(sig, KeyVersion::TAPROOT, pubkey, sigversion, execdata, serror)) {
return false; // serror is set
}
} else if ((pubkey.size() == 1 || pubkey.size() == 33) && pubkey[0] == BIP118_PUBKEY_PREFIX) {
if ((flags & SCRIPT_VERIFY_DISCOURAGE_ANYPREVOUT) != 0) {
return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_PUBKEYTYPE);
} else if ((flags & SCRIPT_VERIFY_ANYPREVOUT) == 0) {
return true;
} else if (pubkey.size() == 1) {
if (success && !checker.CheckSchnorrSignature(sig, KeyVersion::ANYPREVOUT, *execdata.m_internal_key, sigversion, execdata, serror)) {
return false; // serror is set
}
} else { // pubkey.size() == 33
if (success && !checker.CheckSchnorrSignature(sig, KeyVersion::ANYPREVOUT, Span(pubkey).subspan(1), sigversion, execdata, serror)) {
return false; // serror is set
}
}
} else {
/*
* New public key version softforks should be defined before this `else` block.
Expand Down Expand Up @@ -1475,21 +1490,18 @@ static bool HandleMissingData(MissingDataBehavior mdb)
}

template<typename T>
bool SignatureHashSchnorr(uint256& hash_out, ScriptExecutionData& execdata, const T& tx_to, uint32_t in_pos, uint8_t hash_type, SigVersion sigversion, const PrecomputedTransactionData& cache, MissingDataBehavior mdb)
bool SignatureHashSchnorr(uint256& hash_out, ScriptExecutionData& execdata, const T& tx_to, uint32_t in_pos, uint8_t hash_type, SigVersion sigversion, KeyVersion keyversion, const PrecomputedTransactionData& cache, MissingDataBehavior mdb)
{
uint8_t ext_flag, key_version;
uint8_t ext_flag;
assert(keyversion == KeyVersion::TAPROOT || keyversion == KeyVersion::ANYPREVOUT);
assert(keyversion == KeyVersion::ANYPREVOUT ? sigversion == SigVersion::TAPSCRIPT : true);
switch (sigversion) {
case SigVersion::TAPROOT:
ext_flag = 0;
// key_version is not used and left uninitialized.
// key_version is not used
break;
case SigVersion::TAPSCRIPT:
ext_flag = 1;
// key_version must be 0 for now, representing the current version of
// 32-byte public keys in the tapscript signature opcode execution.
// An upgradable public key version (with a size not 32-byte) may
// request a different key_version with a new sigversion.
key_version = 0;
break;
default:
assert(false);
Expand All @@ -1508,13 +1520,26 @@ bool SignatureHashSchnorr(uint256& hash_out, ScriptExecutionData& execdata, cons
// Hash type
const uint8_t output_type = (hash_type == SIGHASH_DEFAULT) ? SIGHASH_ALL : (hash_type & SIGHASH_OUTPUT_MASK); // Default (no sighash byte) is equivalent to SIGHASH_ALL
const uint8_t input_type = hash_type & SIGHASH_INPUT_MASK;
if (!(hash_type <= 0x03 || (hash_type >= 0x81 && hash_type <= 0x83))) return false;
switch(hash_type) {
case 0: case 1: case 2: case 3:
case 0x81: case 0x82: case 0x83:
break;
case 0x41: case 0x42: case 0x43:
case 0xc1: case 0xc2: case 0xc3:
if (keyversion == KeyVersion::ANYPREVOUT) {
break;
} else {
return false;
}
default:
return false;
}
ss << hash_type;

// Transaction level data
ss << tx_to.nVersion;
ss << tx_to.nLockTime;
if (input_type != SIGHASH_ANYONECANPAY) {
if (input_type != SIGHASH_ANYONECANPAY && input_type != SIGHASH_ANYPREVOUT && input_type != SIGHASH_ANYPREVOUTANYSCRIPT) {
ss << cache.m_prevouts_single_hash;
ss << cache.m_spent_amounts_single_hash;
ss << cache.m_spent_scripts_single_hash;
Expand All @@ -1533,6 +1558,13 @@ bool SignatureHashSchnorr(uint256& hash_out, ScriptExecutionData& execdata, cons
ss << tx_to.vin[in_pos].prevout;
ss << cache.m_spent_outputs[in_pos];
ss << tx_to.vin[in_pos].nSequence;
} else if (input_type == SIGHASH_ANYPREVOUT) {
assert(keyversion == KeyVersion::ANYPREVOUT);
ss << cache.m_spent_outputs[in_pos];
ss << tx_to.vin[in_pos].nSequence;
} else if (input_type == SIGHASH_ANYPREVOUTANYSCRIPT) {
assert(keyversion == KeyVersion::ANYPREVOUT);
ss << tx_to.vin[in_pos].nSequence;
} else {
ss << in_pos;
}
Expand All @@ -1554,8 +1586,12 @@ bool SignatureHashSchnorr(uint256& hash_out, ScriptExecutionData& execdata, cons
// Additional data for BIP 342 signatures
if (sigversion == SigVersion::TAPSCRIPT) {
assert(execdata.m_tapleaf_hash_init);
ss << execdata.m_tapleaf_hash;
ss << key_version;
if (input_type != SIGHASH_ANYPREVOUTANYSCRIPT) {
ss << execdata.m_tapleaf_hash;
} else {
assert(keyversion == KeyVersion::ANYPREVOUT);
}
ss << uint8_t(keyversion);
assert(execdata.m_codeseparator_pos_init);
ss << execdata.m_codeseparator_pos;
}
Expand Down Expand Up @@ -1670,18 +1706,16 @@ bool GenericTransactionSignatureChecker<T>::CheckECDSASignature(const std::vecto
}

template <class T>
bool GenericTransactionSignatureChecker<T>::CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey_in, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror) const
bool GenericTransactionSignatureChecker<T>::CheckSchnorrSignature(Span<const unsigned char> sig, KeyVersion pubkeyver, Span<const unsigned char> pubkey_in, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror) const
{
assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
// Schnorr signatures have 32-byte public keys. The caller is responsible for enforcing this.
assert(pubkey_in.size() == 32);
// Note that in Tapscript evaluation, empty signatures are treated specially (invalid signature that does not
// abort script execution). This is implemented in EvalChecksigTapscript, which won't invoke
// CheckSchnorrSignature in that case. In other contexts, they are invalid like every other signature with
// size different from 64 or 65.
if (sig.size() != 64 && sig.size() != 65) return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_SIZE);

XOnlyPubKey pubkey{pubkey_in};
XOnlyPubKey pubkey_xonly{pubkey_in};

uint8_t hashtype = SIGHASH_DEFAULT;
if (sig.size() == 65) {
Expand All @@ -1690,10 +1724,10 @@ bool GenericTransactionSignatureChecker<T>::CheckSchnorrSignature(Span<const uns
}
uint256 sighash;
if (!this->txdata) return HandleMissingData(m_mdb);
if (!SignatureHashSchnorr(sighash, execdata, *txTo, nIn, hashtype, sigversion, *this->txdata, m_mdb)) {
if (!SignatureHashSchnorr(sighash, execdata, *txTo, nIn, hashtype, sigversion, pubkeyver, *this->txdata, m_mdb)) {
return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_HASHTYPE);
}
if (!VerifySchnorrSignature(sig, pubkey, sighash)) return set_error(serror, SCRIPT_ERR_SCHNORR_SIG);
if (!VerifySchnorrSignature(sig, pubkey_xonly, sighash)) return set_error(serror, SCRIPT_ERR_SCHNORR_SIG);
return true;
}

Expand Down Expand Up @@ -1856,12 +1890,13 @@ uint256 ComputeTaprootMerkleRoot(Span<const unsigned char> control, const uint25
return k;
}

static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, const std::vector<unsigned char>& program, const uint256& tapleaf_hash)
static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, const std::vector<unsigned char>& program, const uint256& tapleaf_hash, std::optional<XOnlyPubKey>& internal_key)
{
assert(control.size() >= TAPROOT_CONTROL_BASE_SIZE);
assert(program.size() >= uint256::size());
//! The internal pubkey (x-only, so no Y coordinate parity).
const XOnlyPubKey p{Span{control}.subspan(1, TAPROOT_CONTROL_BASE_SIZE - 1)};
internal_key = p;
//! The output pubkey (taken from the scriptPubKey).
const XOnlyPubKey q{program};
// Compute the Merkle root from the leaf and the provided path.
Expand Down Expand Up @@ -1915,7 +1950,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
execdata.m_annex_init = true;
if (stack.size() == 1) {
// Key path spending (stack size is 1 after removing optional annex)
if (!checker.CheckSchnorrSignature(stack.front(), program, SigVersion::TAPROOT, execdata, serror)) {
if (!checker.CheckSchnorrSignature(stack.front(), KeyVersion::TAPROOT, program, SigVersion::TAPROOT, execdata, serror)) {
return false; // serror is set
}
return set_success(serror);
Expand All @@ -1927,7 +1962,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
return set_error(serror, SCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZE);
}
execdata.m_tapleaf_hash = ComputeTapleafHash(control[0] & TAPROOT_LEAF_MASK, script);
if (!VerifyTaprootCommitment(control, program, execdata.m_tapleaf_hash)) {
if (!VerifyTaprootCommitment(control, program, execdata.m_tapleaf_hash, execdata.m_internal_key)) {
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH);
}
execdata.m_tapleaf_hash_init = true;
Expand Down
32 changes: 26 additions & 6 deletions src/script/interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <script/script_error.h>
#include <span.h>
#include <primitives/transaction.h>
#include <pubkey.h>

#include <optional>
#include <vector>
Expand All @@ -29,10 +30,12 @@ enum
SIGHASH_NONE = 2,
SIGHASH_SINGLE = 3,
SIGHASH_ANYONECANPAY = 0x80,
SIGHASH_ANYPREVOUT = 0x40,
SIGHASH_ANYPREVOUTANYSCRIPT = 0xc0,

SIGHASH_DEFAULT = 0, //!< Taproot only; implied when sighash byte is missing, and equivalent to SIGHASH_ALL
SIGHASH_OUTPUT_MASK = 3,
SIGHASH_INPUT_MASK = 0x80,
SIGHASH_INPUT_MASK = 0xc0,
};

/** Script verification flags.
Expand Down Expand Up @@ -141,6 +144,12 @@ enum : uint32_t {
// Making unknown public key versions (in BIP 342 scripts) non-standard
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = (1U << 20),

// Validating ANYPREVOUT public keys
SCRIPT_VERIFY_ANYPREVOUT = (1U << 22),

// Making ANYPREVOUT public key versions (in BIP 342 scripts) non-standard
SCRIPT_VERIFY_DISCOURAGE_ANYPREVOUT = (1U << 23),

// Constants to point to the highest flag in use. Add new flags above this line.
//
SCRIPT_VERIFY_END_MARKER
Expand Down Expand Up @@ -193,6 +202,12 @@ enum class SigVersion
TAPSCRIPT = 3, //!< Witness v1 with 32-byte program, not BIP16 P2SH-wrapped, script path spending, leaf version 0xc0; see BIP 342
};

enum class KeyVersion
{
TAPROOT = 0, //!< 32 byte public key
ANYPREVOUT = 1, //!< 1 or 33 byte public key, first byte is 0x01
};

struct ScriptExecutionData
{
//! Whether m_tapleaf_hash is initialized.
Expand All @@ -219,6 +234,9 @@ struct ScriptExecutionData

//! The hash of the corresponding output
std::optional<uint256> m_output_hash;

/** The taproot internal key. */
std::optional<XOnlyPubKey> m_internal_key = std::nullopt;
};

/** Signature hash sizes */
Expand All @@ -233,6 +251,8 @@ static constexpr size_t TAPROOT_CONTROL_NODE_SIZE = 32;
static constexpr size_t TAPROOT_CONTROL_MAX_NODE_COUNT = 128;
static constexpr size_t TAPROOT_CONTROL_MAX_SIZE = TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * TAPROOT_CONTROL_MAX_NODE_COUNT;

static constexpr uint8_t BIP118_PUBKEY_PREFIX = 0x01;

extern const HashWriter HASHER_TAPSIGHASH; //!< Hasher with tag "TapSighash" pre-fed to it.
extern const HashWriter HASHER_TAPLEAF; //!< Hasher with tag "TapLeaf" pre-fed to it.
extern const HashWriter HASHER_TAPBRANCH; //!< Hasher with tag "TapBranch" pre-fed to it.
Expand All @@ -248,7 +268,7 @@ class BaseSignatureChecker
return false;
}

virtual bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror = nullptr) const
virtual bool CheckSchnorrSignature(Span<const unsigned char> sig, KeyVersion pubkeyver, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror = nullptr) const
{
return false;
}
Expand Down Expand Up @@ -276,7 +296,7 @@ enum class MissingDataBehavior
};

template<typename T>
bool SignatureHashSchnorr(uint256& hash_out, ScriptExecutionData& execdata, const T& tx_to, uint32_t in_pos, uint8_t hash_type, SigVersion sigversion, const PrecomputedTransactionData& cache, MissingDataBehavior mdb);
bool SignatureHashSchnorr(uint256& hash_out, ScriptExecutionData& execdata, const T& tx_to, uint32_t in_pos, uint8_t hash_type, SigVersion sigversion, KeyVersion keyversion, const PrecomputedTransactionData& cache, MissingDataBehavior mdb);

template <class T>
class GenericTransactionSignatureChecker : public BaseSignatureChecker
Expand All @@ -296,7 +316,7 @@ class GenericTransactionSignatureChecker : public BaseSignatureChecker
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn, MissingDataBehavior mdb) : txTo(txToIn), m_mdb(mdb), nIn(nInIn), amount(amountIn), txdata(nullptr) {}
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn, MissingDataBehavior mdb) : txTo(txToIn), m_mdb(mdb), nIn(nInIn), 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;
bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override;
bool CheckSchnorrSignature(Span<const unsigned char> sig, KeyVersion keyversion, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override;
bool CheckLockTime(const CScriptNum& nLockTime) const override;
bool CheckSequence(const CScriptNum& nSequence) const override;
};
Expand All @@ -317,9 +337,9 @@ class DeferringSignatureChecker : public BaseSignatureChecker
return m_checker.CheckECDSASignature(scriptSig, vchPubKey, scriptCode, sigversion);
}

bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override
bool CheckSchnorrSignature(Span<const unsigned char> sig, KeyVersion pubkeyver, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override
{
return m_checker.CheckSchnorrSignature(sig, pubkey, sigversion, execdata, serror);
return m_checker.CheckSchnorrSignature(sig, pubkeyver, pubkey, sigversion, execdata, serror);
}

bool CheckLockTime(const CScriptNum& nLockTime) const override
Expand Down
4 changes: 2 additions & 2 deletions src/script/sign.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ bool MutableTransactionSignatureCreator::CreateSchnorrSig(const SigningProvider&
execdata.m_tapleaf_hash = *leaf_hash;
}
uint256 hash;
if (!SignatureHashSchnorr(hash, execdata, m_txto, nIn, nHashType, sigversion, *m_txdata, MissingDataBehavior::FAIL)) return false;
if (!SignatureHashSchnorr(hash, execdata, m_txto, nIn, nHashType, sigversion, KeyVersion::TAPROOT, *m_txdata, MissingDataBehavior::FAIL)) return false;
sig.resize(64);
// Use uint256{} as aux_rnd for now.
if (!key.SignSchnorr(hash, sig, merkle_root, {})) return false;
Expand Down Expand Up @@ -719,7 +719,7 @@ class DummySignatureChecker final : public BaseSignatureChecker
public:
DummySignatureChecker() = default;
bool CheckECDSASignature(const std::vector<unsigned char>& sig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override { return sig.size() != 0; }
bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror) const override { return sig.size() != 0; }
bool CheckSchnorrSignature(Span<const unsigned char> sig, KeyVersion pubkeyver, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror) const override { return sig.size() != 0; }
bool CheckLockTime(const CScriptNum& nLockTime) const override { return true; }
bool CheckSequence(const CScriptNum& nSequence) const override { return true; }
};
Expand Down
4 changes: 2 additions & 2 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1119,7 +1119,7 @@ bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws)
const CTransaction& tx = *ws.m_ptx;
TxValidationState& state = ws.m_state;

constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_NONE;

// Check input scripts and signatures.
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
Expand Down Expand Up @@ -2271,7 +2271,7 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const Ch
// mainnet.
// For simplicity, always leave P2SH+WITNESS+TAPROOT on except for the two
// violating blocks.
uint32_t flags{SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_TAPROOT};
uint32_t flags{SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_TAPROOT | SCRIPT_VERIFY_ANYPREVOUT};
const auto it{consensusparams.script_flag_exceptions.find(*Assert(block_index.phashBlock))};
if (it != consensusparams.script_flag_exceptions.end()) {
flags = it->second;
Expand Down

0 comments on commit e9e5a8b

Please sign in to comment.