Skip to content

Commit de5ccf1

Browse files
committed
simplicity: allow exact annex padding in policy
1 parent c990492 commit de5ccf1

6 files changed

Lines changed: 48 additions & 9 deletions

File tree

src/policy/policy.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
274274
// Check policy limits for Taproot spends:
275275
// - MAX_STANDARD_TAPSCRIPT_STACK_ITEM_SIZE limit for stack item size
276276
// - No annexes
277+
// ELEMENTS: allow annexes for simplicity transactions
277278
if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE && !p2sh) {
278279
// Missing witness; invalid by consensus rules
279280
if (i >= tx.witness.vtxinwit.size()) {
@@ -282,8 +283,16 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
282283
// Taproot spend (non-P2SH-wrapped, version 1, witness program size 32; see BIP 341)
283284
Span stack{tx.witness.vtxinwit[i].scriptWitness.stack};
284285
if (stack.size() >= 2 && !stack.back().empty() && stack.back()[0] == ANNEX_TAG) {
285-
// Annexes are nonstandard as long as no semantics are defined for them.
286-
return false;
286+
SpanPopBack(stack); // drop the annex
287+
const auto& control_block = SpanPopBack(stack);
288+
if (!control_block.empty() && (control_block[0] & TAPROOT_LEAF_MASK) == TAPROOT_LEAF_TAPSIMPLICITY) {
289+
// Annexes are allowed for Simplicity spends
290+
// checks for zero padding and exact size are done in CheckSimplicity
291+
return true;
292+
} else {
293+
// Annexes are nonstandard as long as no semantics are defined for them.
294+
return false;
295+
}
287296
}
288297
if (stack.size() >= 2) {
289298
// Script path spend (2 or more stack elements after removing optional annex)

src/policy/policy.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VE
8181
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION |
8282
SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS |
8383
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE |
84-
SCRIPT_VERIFY_SIMPLICITY;
84+
SCRIPT_VERIFY_SIMPLICITY |
85+
SCRIPT_VERIFY_ANNEX_PADDING;
8586

8687

8788
/** For convenience, standard but not mandatory verify flags. */

src/script/interpreter.cpp

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3119,14 +3119,14 @@ uint32_t GenericTransactionSignatureChecker<T>::GetnIn() const
31193119
}
31203120

31213121
template <class T>
3122-
bool GenericTransactionSignatureChecker<T>::CheckSimplicity(const valtype& program, const valtype& witness, const rawElementsTapEnv& simplicityRawTap, int64_t budget, ScriptError* serror) const
3122+
bool GenericTransactionSignatureChecker<T>::CheckSimplicity(const valtype& program, const valtype& witness, const rawElementsTapEnv& simplicityRawTap, int64_t minCost, int64_t budget, ScriptError* serror) const
31233123
{
31243124
simplicity_err error;
31253125
elementsTapEnv* simplicityTapEnv = simplicity_elements_mallocTapEnv(&simplicityRawTap);
31263126

31273127
assert(txdata->m_simplicity_tx_data);
31283128
assert(simplicityTapEnv);
3129-
if (!simplicity_elements_execSimplicity(&error, 0, txdata->m_simplicity_tx_data.get(), nIn, simplicityTapEnv, txdata->m_hash_genesis_block.data(), 0, budget, 0, program.data(), program.size(), witness.data(), witness.size())) {
3129+
if (!simplicity_elements_execSimplicity(&error, 0, txdata->m_simplicity_tx_data.get(), nIn, simplicityTapEnv, txdata->m_hash_genesis_block.data(), minCost, budget, 0, program.data(), program.size(), witness.data(), witness.size())) {
31303130
assert(!"simplicity_elements_execSimplicity internal error");
31313131
}
31323132
simplicity_elements_freeTapEnv(simplicityTapEnv);
@@ -3278,9 +3278,11 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
32783278
// BIP341 Taproot: 32-byte non-P2SH witness v1 program (which encodes a P2C-tweaked pubkey)
32793279
if (!(flags & SCRIPT_VERIFY_TAPROOT)) return set_success(serror);
32803280
if (stack.size() == 0) return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY);
3281+
valtype annex;
32813282
if (stack.size() >= 2 && !stack.back().empty() && stack.back()[0] == ANNEX_TAG) {
32823283
// Drop annex (this is non-standard; see IsWitnessStandard)
3283-
const valtype& annex = SpanPopBack(stack);
3284+
// ELEMENTS: store the annex for CheckSimplicity
3285+
annex = SpanPopBack(stack);
32843286
execdata.m_annex_hash = (CHashWriter(SER_GETHASH, 0) << annex).GetSHA256();
32853287
execdata.m_annex_present = true;
32863288
} else {
@@ -3322,7 +3324,27 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
33223324
simplicityRawTap.controlBlock = control.data();
33233325
simplicityRawTap.pathLen = (control.size() - TAPROOT_CONTROL_BASE_SIZE) / TAPROOT_CONTROL_NODE_SIZE;
33243326
simplicityRawTap.scriptCMR = script_bytes.data();
3325-
return checker.CheckSimplicity(simplicity_program, simplicity_witness, simplicityRawTap, budget, serror);
3327+
// If there is no annex, or we are in consensus checking mode, minCost is set to 0 which effectively disables any overweight cost checks.
3328+
int64_t minCost = 0;
3329+
if ((flags & SCRIPT_VERIFY_ANNEX_PADDING) && annex.size() > 0) {
3330+
valtype zero_padding(annex.size(), 0);
3331+
zero_padding[0] = ANNEX_TAG;
3332+
if (annex != zero_padding) {
3333+
return set_error(serror, SCRIPT_ERR_SIMPLICITY_PADDING_NONZERO);
3334+
}
3335+
// Compute what the budget would have been without the padding.
3336+
// budget includes the padding cost, so subtracting this stack item won't underflow.
3337+
minCost = budget - ::GetSerializeSize(annex);
3338+
// If the annex exists and is empty (i.e. its size is 1), then the only way the annex could be smaller is by eliminating it entirely.
3339+
// So we use the above computed value for minCost. Note: in that case the minCost is 2 WU less than the budget.
3340+
3341+
// If the annex exists and is non-empty, then we add to minCost the value of an annex that contains one fewer byte.
3342+
if (zero_padding.size() > 1) {
3343+
zero_padding.pop_back();
3344+
minCost += ::GetSerializeSize(zero_padding);
3345+
}
3346+
}
3347+
return checker.CheckSimplicity(simplicity_program, simplicity_witness, simplicityRawTap, minCost, budget, serror);
33263348
}
33273349
if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION) {
33283350
return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION);

src/script/interpreter.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ enum : uint32_t {
159159
//
160160
SCRIPT_VERIFY_SIMPLICITY = (1U << 23),
161161

162+
// Check exact annex padding policy for simplicity spends
163+
//
164+
SCRIPT_VERIFY_ANNEX_PADDING = (1U << 24),
165+
162166
// Constants to point to the highest flag in use. Add new flags above this line.
163167
//
164168
SCRIPT_VERIFY_END_MARKER
@@ -343,7 +347,7 @@ class BaseSignatureChecker
343347
return std::numeric_limits<uint32_t>::max();
344348
}
345349

346-
virtual bool CheckSimplicity(const std::vector<unsigned char>& witness, const std::vector<unsigned char>& program, const rawElementsTapEnv& simplicityRawTap, int64_t budget, ScriptError* serror) const
350+
virtual bool CheckSimplicity(const std::vector<unsigned char>& witness, const std::vector<unsigned char>& program, const rawElementsTapEnv& simplicityRawTap, int64_t minCost, int64_t budget, ScriptError* serror) const
347351
{
348352
return false;
349353
}
@@ -391,7 +395,7 @@ class GenericTransactionSignatureChecker : public BaseSignatureChecker
391395

392396
const PrecomputedTransactionData* GetPrecomputedTransactionData() const override;
393397
uint32_t GetnIn() const override;
394-
bool CheckSimplicity(const std::vector<unsigned char>& program, const std::vector<unsigned char>& witness, const rawElementsTapEnv& simplicityRawTap, int64_t budget, ScriptError* serror) const override;
398+
bool CheckSimplicity(const std::vector<unsigned char>& program, const std::vector<unsigned char>& witness, const rawElementsTapEnv& simplicityRawTap, int64_t minCost, int64_t budget, ScriptError* serror) const override;
395399
};
396400

397401
using TransactionSignatureChecker = GenericTransactionSignatureChecker<CTransaction>;

src/script/script_error.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ std::string ScriptErrorString(const ScriptError serror)
134134
return "EC scalar mult verify fail";
135135
case SCRIPT_ERR_SIMPLICITY_WRONG_LENGTH:
136136
return "Simplicity witness has incorrect length";
137+
case SCRIPT_ERR_SIMPLICITY_PADDING_NONZERO:
138+
return "Simplicity annex padding must be all zeros";
137139
case SCRIPT_ERR_SIMPLICITY_DATA_OUT_OF_RANGE:
138140
return SIMPLICITY_ERR_MSG(SIMPLICITY_ERR_DATA_OUT_OF_RANGE);
139141
case SCRIPT_ERR_SIMPLICITY_DATA_OUT_OF_ORDER:

src/script/script_error.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ typedef enum ScriptError_t
9898

9999
/* Elements: Simplicity related errors */
100100
SCRIPT_ERR_SIMPLICITY_WRONG_LENGTH,
101+
SCRIPT_ERR_SIMPLICITY_PADDING_NONZERO,
101102
SCRIPT_ERR_SIMPLICITY_NOT_YET_IMPLEMENTED,
102103
SCRIPT_ERR_SIMPLICITY_DATA_OUT_OF_RANGE,
103104
SCRIPT_ERR_SIMPLICITY_DATA_OUT_OF_ORDER,

0 commit comments

Comments
 (0)