Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Always Look On The Bright Side of the Annex #9

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ enum DeploymentPos : uint16_t {
DEPLOYMENT_TESTDUMMY,
DEPLOYMENT_CHECKTEMPLATEVERIFY, // Deployment of CTV (BIP 119)
DEPLOYMENT_ANYPREVOUT,
DEPLOYMENT_ANNEX,
// NOTE: Also add new deployments to VersionBitsDeploymentInfo in deploymentinfo.cpp
MAX_VERSION_BITS_DEPLOYMENTS
};
Expand Down
11 changes: 11 additions & 0 deletions src/policy/policy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,9 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs,

// limit annex format to allow for future expansion
if (annex.size() < 2 || annex.size() != static_cast<size_t>(annex[1]) + 2) return false;

// Verify the annex format is standard with no unknown records present
if (!IsAnnexStandard(annex)) return false;
}
if (stack.size() >= 2) {
// Script path spend (2 or more stack elements after removing optional annex)
Expand Down Expand Up @@ -335,6 +338,14 @@ size_t HasPayToAnchor(const CTransaction& tx)
return false;
}

bool IsAnnexStandard(const std::vector<unsigned char>& annex)
{
AnnexValidationResult result;
VerifyAnnex(annex, result);
if (result == AnnexValidationResult::FAILURE || result == AnnexValidationResult::UNKNOWN_RECORD) return false;
return true;
}

int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost, unsigned int bytes_per_sigop)
{
return (std::max(nWeight, nSigOpCost * bytes_per_sigop) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
Expand Down
2 changes: 2 additions & 0 deletions src/policy/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
*/
bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, bool allow_annex_data);

bool IsAnnexStandard(const std::vector<unsigned char>& annex_stack);

/**
* Check if given transaction has a IsPayToAnchor output.
**/
Expand Down
1 change: 1 addition & 0 deletions src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1322,6 +1322,7 @@ UniValue DeploymentInfo(const CBlockIndex* blockindex, const ChainstateManager&
SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_TAPROOT);
SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_CHECKTEMPLATEVERIFY);
SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_ANYPREVOUT);
SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_ANNEX);
return softforks;
}
} // anon namespace
Expand Down
60 changes: 60 additions & 0 deletions src/script/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <crypto/sha256.h>
#include <pubkey.h>
#include <script/script.h>
#include <streams.h>
#include <uint256.h>

typedef std::vector<unsigned char> valtype;
Expand Down Expand Up @@ -2016,6 +2017,57 @@ uint256 ComputeTaprootMerkleRoot(Span<const unsigned char> control, const uint25
return k;
}

bool VerifyAnnex(const std::vector<unsigned char>& annex_vec, AnnexValidationResult& result)
{
CDataStream annex(MakeByteSpan(annex_vec), ANNEX_SER_TYPE, ANNEX_SER_VERSION);

/* Pop annex tag */
uint8_t annex_tag;
annex.read(AsWritableBytes(Span{&annex_tag, 1}));
assert(annex_tag == ANNEX_TAG);

uint64_t nRecordType = 0;
while (!annex.empty()) {

/* We encode the difference between the previous type (initially 0),
* both minimising the encoding and ensuring a canonical ordering
* for record value. */
uint64_t nRecordDeltaLen = 0;
annex >> VARINT(nRecordDeltaLen);
nRecordType = nRecordType + (nRecordDeltaLen >> 7);
/* The record length is encoded as a delta */
uint64_t nRecordLength = nRecordDeltaLen & 0x7f;
if (nRecordLength == 0x7F) {
uint64_t nBuffRecordLength = 0;
annex >> VARINT(nBuffRecordLength);
nRecordLength += nBuffRecordLength;
}

/* Consensus rule: the annex is not short - end of data
* before reading is finished. */
if (annex.size() < nRecordLength)
result = AnnexValidationResult::FAILURE;
return false;

std::vector<unsigned char> vRecordValue;
vRecordValue.resize(nRecordLength);
annex.read(AsWritableBytes(Span{&vRecordValue, nRecordLength}));

switch (nRecordType) {
/* Consensus rule : record value must make sense, per
* the tag spec. */
case ANNEX_RECORD_POLICY_RESERVED:
/* No consensus validation on unstructured data */
continue;
default:
result = AnnexValidationResult::UNKNOWN_RECORD;
return true;
}
}
result = AnnexValidationResult::SUCCESS;
return true;
}

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);
Expand Down Expand Up @@ -2070,6 +2122,14 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
const valtype& annex = SpanPopBack(stack);
execdata.m_annex_hash = (HashWriter{} << annex).GetSHA256();
execdata.m_annex_present = true;
// BIPXXX: verify annex
if (flags & SCRIPT_VERIFY_ANNEX) {
AnnexValidationResult result;
VerifyAnnex(annex, result);
if (result == AnnexValidationResult::FAILURE) {
return set_error(serror, SCRIPT_ERR_ANNEX_WRONG_FORMAT);
}
}
} else {
execdata.m_annex_present = false;
}
Expand Down
21 changes: 21 additions & 0 deletions src/script/interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ enum : uint32_t {
// Making ANYPREVOUT public key versions (in BIP 342 scripts) non-standard
SCRIPT_VERIFY_DISCOURAGE_ANYPREVOUT = (1U << 25),

// Verify the annex reserved space
SCRIPT_VERIFY_ANNEX = (1U << 26),

// Constants to point to the highest flag in use. Add new flags above this line.
//
SCRIPT_VERIFY_END_MARKER
Expand Down Expand Up @@ -329,6 +332,23 @@ 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, KeyVersion keyversion, const PrecomputedTransactionData& cache, MissingDataBehavior mdb);

/// SerType used to serialize parameters in GCS filter encoding.
static constexpr int ANNEX_SER_TYPE = SER_NETWORK;

/// Protocol version used to serialize parameters in GCS filter encoding.
static constexpr int ANNEX_SER_VERSION = 0;

enum : uint64_t {
// Record reserved for unstructured data. No consensus constraints.
ANNEX_RECORD_POLICY_RESERVED = 0,
};

enum class AnnexValidationResult {
SUCCESS,
FAILURE,
UNKNOWN_RECORD,
};

template <class T>
class GenericTransactionSignatureChecker : public BaseSignatureChecker
{
Expand Down Expand Up @@ -393,6 +413,7 @@ uint256 ComputeTaprootMerkleRoot(Span<const unsigned char> control, const uint25
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* error = nullptr);
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = nullptr);
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = nullptr);
bool VerifyAnnex(const std::vector<unsigned char>& annex, AnnexValidationResult& result);

size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags);

Expand Down
3 changes: 3 additions & 0 deletions src/script/script_error.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ typedef enum ScriptError_t
SCRIPT_ERR_TAPSCRIPT_CHECKMULTISIG,
SCRIPT_ERR_TAPSCRIPT_MINIMALIF,

/* Annex */
SCRIPT_ERR_ANNEX_WRONG_FORMAT,

/* Constant scriptCode */
SCRIPT_ERR_OP_CODESEPARATOR,
SCRIPT_ERR_SIG_FINDANDDELETE,
Expand Down
5 changes: 5 additions & 0 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1932,6 +1932,11 @@ unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const Chainstat
flags |= SCRIPT_VERIFY_ANYPREVOUT;
}

// Enforce annex verification (BIPXXX)
if (DeploymentActiveAt(block_index, chainman, Consensus::DEPLOYMENT_ANNEX)) {
flags |= SCRIPT_VERIFY_ANNEX;
}

return flags;
}

Expand Down