Skip to content

Commit 48a3294

Browse files
darosiorinstagibbs
andcommitted
script: implement OP_TEMPLATEHASH
This introduces a new Script operation exclusively available in Tapscript context: OP_TEMPLATEHASH. This operation pushes the hash of the spending transaction on the stack. See BIP xx for details. This operation is introduced as replacing OP_SUCCESS187 (0xbb). Co-Authored-By: Greg Sanders <[email protected]>
1 parent 6bed516 commit 48a3294

File tree

9 files changed

+76
-3
lines changed

9 files changed

+76
-3
lines changed

src/script/interpreter.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,6 +1213,20 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
12131213
}
12141214
break;
12151215

1216+
case OP_TEMPLATEHASH:
1217+
{
1218+
// OP_TEMPLATEHASH is only available in Tapscript. Note this is the exact same error
1219+
// returned in the default case, which would be the one hit by OP_SUCCESS187 before
1220+
// the introduction of OP_TEMPLATEHASH.
1221+
if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) {
1222+
return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
1223+
}
1224+
1225+
const uint256 template_hash{checker.GetTemplateHash(execdata)};
1226+
stack.push_back({template_hash.begin(), template_hash.end()});
1227+
}
1228+
break;
1229+
12161230
default:
12171231
return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
12181232
}
@@ -1461,6 +1475,7 @@ template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTr
14611475
const HashWriter HASHER_TAPSIGHASH{TaggedHash("TapSighash")};
14621476
const HashWriter HASHER_TAPLEAF{TaggedHash("TapLeaf")};
14631477
const HashWriter HASHER_TAPBRANCH{TaggedHash("TapBranch")};
1478+
const HashWriter HASHER_TEMPLATEHASH{TaggedHash("TemplateHash")};
14641479

14651480
static bool HandleMissingData(MissingDataBehavior mdb)
14661481
{
@@ -1781,6 +1796,27 @@ bool GenericTransactionSignatureChecker<T>::CheckSequence(const CScriptNum& nSeq
17811796
return true;
17821797
}
17831798

1799+
template<class T>
1800+
uint256 GenericTransactionSignatureChecker<T>::GetTemplateHash(ScriptExecutionData& execdata) const
1801+
{
1802+
assert(txdata && txdata->m_bip341_taproot_ready);
1803+
assert(execdata.m_annex_init);
1804+
1805+
HashWriter ss{HASHER_TEMPLATEHASH};
1806+
ss << txTo->version;
1807+
ss << txTo->nLockTime;
1808+
ss << txdata->m_sequences_single_hash;
1809+
ss << txdata->m_outputs_single_hash;
1810+
const uint8_t annex_present = (execdata.m_annex_present ? 1 : 0);
1811+
ss << annex_present;
1812+
ss << nIn;
1813+
if (execdata.m_annex_present) {
1814+
ss << execdata.m_annex_hash;
1815+
}
1816+
1817+
return ss.GetSHA256();
1818+
}
1819+
17841820
// explicit instantiation
17851821
template class GenericTransactionSignatureChecker<CTransaction>;
17861822
template class GenericTransactionSignatureChecker<CMutableTransaction>;
@@ -1800,6 +1836,16 @@ static bool ExecuteWitnessScript(const std::span<const valtype>& stack_span, con
18001836
}
18011837
// New opcodes will be listed here. May use a different sigversion to modify existing opcodes.
18021838
if (IsOpSuccess(opcode)) {
1839+
// Do not return early success on OP_TEMPLATEHASH (OP_SUCCESS187) once it is active. It is
1840+
// non-standard until then.
1841+
if (opcode == OP_TEMPLATEHASH) {
1842+
if (flags & SCRIPT_VERIFY_DISCOURAGE_TEMPLATEHASH) {
1843+
return set_error(serror, SCRIPT_ERR_DISCOURAGE_TEMPLATEHASH);
1844+
}
1845+
if (flags & SCRIPT_VERIFY_TEMPLATEHASH) {
1846+
continue;
1847+
}
1848+
}
18031849
if (flags & SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS) {
18041850
return set_error(serror, SCRIPT_ERR_DISCOURAGE_OP_SUCCESS);
18051851
}

src/script/interpreter.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,11 @@ class BaseSignatureChecker
271271
return false;
272272
}
273273

274+
virtual uint256 GetTemplateHash(ScriptExecutionData& execdata) const
275+
{
276+
return {};
277+
}
278+
274279
virtual ~BaseSignatureChecker() = default;
275280
};
276281

@@ -307,6 +312,7 @@ class GenericTransactionSignatureChecker : public BaseSignatureChecker
307312
bool CheckSchnorrSignature(std::span<const unsigned char> sig, std::span<const unsigned char> pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override;
308313
bool CheckLockTime(const CScriptNum& nLockTime) const override;
309314
bool CheckSequence(const CScriptNum& nSequence) const override;
315+
uint256 GetTemplateHash(ScriptExecutionData& execdata) const override;
310316
};
311317

312318
using TransactionSignatureChecker = GenericTransactionSignatureChecker<CTransaction>;
@@ -338,6 +344,11 @@ class DeferringSignatureChecker : public BaseSignatureChecker
338344
{
339345
return m_checker.CheckSequence(nSequence);
340346
}
347+
348+
uint256 GetTemplateHash(ScriptExecutionData& execdata) const override
349+
{
350+
return m_checker.GetTemplateHash(execdata);
351+
}
341352
};
342353

343354
/** Compute the BIP341 tapleaf hash from leaf version & script. */

src/script/script.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ std::string GetOpName(opcodetype opcode)
149149
// Opcode added by BIP 342 (Tapscript)
150150
case OP_CHECKSIGADD : return "OP_CHECKSIGADD";
151151

152+
// Opcode added by BIP xx (Tapscript-only, formerly OP_SUCCESS187)
153+
case OP_TEMPLATEHASH : return "OP_TEMPLATEHASH";
154+
152155
case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE";
153156

154157
default:

src/script/script.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,9 @@ enum opcodetype
209209
// Opcode added by BIP 342 (Tapscript)
210210
OP_CHECKSIGADD = 0xba,
211211

212+
// Opcode added by BIP xx (Tapscript-only, formerly OP_SUCCESS187)
213+
OP_TEMPLATEHASH = 0xbb,
214+
212215
OP_INVALIDOPCODE = 0xff,
213216
};
214217

src/script/script_error.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ std::string ScriptErrorString(const ScriptError serror)
7979
return "OP_SUCCESSx reserved for soft-fork upgrades";
8080
case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_PUBKEYTYPE:
8181
return "Public key version reserved for soft-fork upgrades";
82+
case SCRIPT_ERR_DISCOURAGE_TEMPLATEHASH:
83+
return "Templatehash is not active";
8284
case SCRIPT_ERR_PUBKEYTYPE:
8385
return "Public key is neither compressed or uncompressed";
8486
case SCRIPT_ERR_CLEANSTACK:

src/script/script_error.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ typedef enum ScriptError_t
5959
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION,
6060
SCRIPT_ERR_DISCOURAGE_OP_SUCCESS,
6161
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_PUBKEYTYPE,
62+
SCRIPT_ERR_DISCOURAGE_TEMPLATEHASH,
6263

6364
/* segregated witness */
6465
SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH,

src/test/script_tests.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ static ScriptErrorDesc script_errors[]={
9393
{SCRIPT_ERR_WITNESS_PUBKEYTYPE, "WITNESS_PUBKEYTYPE"},
9494
{SCRIPT_ERR_OP_CODESEPARATOR, "OP_CODESEPARATOR"},
9595
{SCRIPT_ERR_SIG_FINDANDDELETE, "SIG_FINDANDDELETE"},
96+
{SCRIPT_ERR_DISCOURAGE_TEMPLATEHASH, "DISCOURAGE_TEMPLATEHASH"},
9697
};
9798

9899
static std::string FormatScriptError(ScriptError_t err)

test/functional/feature_taproot.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,7 +1141,7 @@ def predict_sigops_ratio(n, dummy_size):
11411141
hashtype = lambda _: random.choice(VALID_SIGHASHES_TAPROOT)
11421142
for opval in range(76, 0x100):
11431143
opcode = CScriptOp(opval)
1144-
if not is_op_success(opcode):
1144+
if not is_op_success(opcode, is_temphash_active=True):
11451145
continue
11461146
scripts = [
11471147
("bare_success", CScript([opcode])),
@@ -1172,7 +1172,7 @@ def predict_sigops_ratio(n, dummy_size):
11721172
# Non-OP_SUCCESSx (verify that those aren't accidentally treated as OP_SUCCESSx)
11731173
for opval in range(0, 0x100):
11741174
opcode = CScriptOp(opval)
1175-
if is_op_success(opcode):
1175+
if is_op_success(opcode, is_temphash_active=True):
11761176
continue
11771177
scripts = [
11781178
("normal", CScript([OP_RETURN, opcode] + [OP_NOP] * 75)),

test/functional/test_framework/script.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,9 @@ def __new__(cls, n):
250250
# BIP 342 opcodes (Tapscript)
251251
OP_CHECKSIGADD = CScriptOp(0xba)
252252

253+
# BIP xx opcode (Tapscript-only, formerly OP_SUCCESS187)
254+
OP_TEMPLATEHASH = CScriptOp(0xbb)
255+
253256
OP_INVALIDOPCODE = CScriptOp(0xff)
254257

255258
OPCODE_NAMES.update({
@@ -365,6 +368,7 @@ def __new__(cls, n):
365368
OP_NOP9: 'OP_NOP9',
366369
OP_NOP10: 'OP_NOP10',
367370
OP_CHECKSIGADD: 'OP_CHECKSIGADD',
371+
OP_TEMPLATEHASH: 'OP_TEMPLATEHASH',
368372
OP_INVALIDOPCODE: 'OP_INVALIDOPCODE',
369373
})
370374

@@ -936,5 +940,7 @@ def taproot_construct(pubkey, scripts=None, treat_internal_as_infinity=False):
936940
leaves = dict((name, TaprootLeafInfo(script, version, merklebranch, leaf)) for name, version, script, merklebranch, leaf in ret)
937941
return TaprootInfo(CScript([OP_1, tweaked]), pubkey, negated + 0, tweak, leaves, h, tweaked)
938942

939-
def is_op_success(o):
943+
def is_op_success(o, is_temphash_active):
944+
if o == OP_TEMPLATEHASH:
945+
return not is_temphash_active
940946
return o == 0x50 or o == 0x62 or o == 0x89 or o == 0x8a or o == 0x8d or o == 0x8e or (o >= 0x7e and o <= 0x81) or (o >= 0x83 and o <= 0x86) or (o >= 0x95 and o <= 0x99) or (o >= 0xbb and o <= 0xfe)

0 commit comments

Comments
 (0)