Skip to content

Commit 77a7213

Browse files
authored
Merge pull request bitcoin#1309 from JeremyRubin/anti-dos-119
[BIP-119] Clean Up Spec of Opcode
2 parents 9b120a1 + 78fc9f2 commit 77a7213

File tree

1 file changed

+73
-111
lines changed

1 file changed

+73
-111
lines changed

bip-0119.mediawiki

Lines changed: 73 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -161,124 +161,88 @@ forming a "Payment Pool".
161161

162162
==Detailed Specification==
163163

164-
The below code is the main logic for verifying CHECKTEMPLATEVERIFY, and is the canonical
165-
specification for the semantics of OP_CHECKTEMPLATEVERIFY.
166-
167-
case OP_CHECKTEMPLATEVERIFY:
168-
{
169-
// if flags not enabled; treat as a NOP4
170-
if (!(flags & SCRIPT_VERIFY_DEFAULT_CHECK_TEMPLATE_VERIFY_HASH)) {
171-
if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
172-
return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS);
173-
break;
174-
}
175-
176-
if (stack.size() < 1)
177-
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
178-
179-
// If the argument was not 32 bytes, treat as OP_NOP4:
180-
switch (stack.back().size()) {
181-
case 32:
182-
if (!checker.CheckDefaultCheckTemplateVerifyHash(stack.back())) {
183-
return set_error(serror, SCRIPT_ERR_TEMPLATE_MISMATCH);
184-
}
185-
break;
186-
default:
187-
// future upgrade can add semantics for this opcode with different length args
188-
// so discourage use when applicable
189-
if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
190-
return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS);
191-
}
192-
}
193-
}
194-
break;
195-
196-
Where
197-
198-
bool CheckDefaultCheckTemplateVerifyHash(const std::vector<unsigned char>& hash) {
199-
// note: for anti-DoS, a real implementation *must* cache parts of this computation
200-
// to avoid quadratic hashing DoS all variable length computations must be precomputed
201-
// including hashes of the scriptsigs, sequences, and outputs. See the section
202-
// "Denial of Service and Validation Costs" below.
203-
return GetDefaultCheckTemplateVerifyHash(current_tx, current_input_index) == uint256(hash);
204-
}
205-
206-
The hash is computed as follows, where the outputs_hash and sequences_hash are computed as defined in BIP-341.
207-
208-
/** Compute the (single) SHA256 of the concatenation of all scriptSigs in a tx. */
209-
template <class T>
210-
uint256 GetScriptSigsSHA256(const T& txTo)
211-
{
212-
CHashWriter ss(SER_GETHASH, 0);
213-
for (const auto& in : txTo.vin) {
214-
ss << in.scriptSig;
215-
}
216-
return ss.GetSHA256();
217-
}
218-
// not DoS safe, for reference/testing!
219-
uint256 GetDefaultCheckTemplateVerifyHash(const CTransaction& tx, uint32_t input_index) {
220-
return GetDefaultCheckTemplateVerifyHash(tx, GetOutputsSHA256(tx), GetSequenceSHA256(tx), input_index);
221-
}
222-
// not DoS safe for reference/testing!
223-
uint256 GetDefaultCheckTemplateVerifyHash(const CTransaction& tx, const uint256& outputs_hash, const uint256& sequences_hash,
224-
const uint32_t input_index) {
225-
bool skip_scriptSigs = std::find_if(tx.vin.begin(), tx.vin.end(),
226-
[](const CTxIn& c) { return c.scriptSig != CScript(); }) == tx.vin.end();
227-
return skip_scriptSigs ? GetDefaultCheckTemplateVerifyHashEmptyScript(tx, outputs_hash, sequences_hash, input_index) :
228-
GetDefaultCheckTemplateVerifyHashWithScript(tx, outputs_hash, sequences_hash, GetScriptSigsSHA256(tx), input_index);
229-
}
230-
// DoS safe, fixed length hash!
231-
uint256 GetDefaultCheckTemplateVerifyHashWithScript(const CTransaction& tx, const uint256& outputs_hash, const uint256& sequences_hash,
232-
const uint256& scriptSig_hash, const uint32_t input_index) {
233-
auto h = CHashWriter(SER_GETHASH, 0)
234-
<< tx.nVersion
235-
<< tx.nLockTime
236-
<< scriptSig_hash
237-
<< uint32_t(tx.vin.size())
238-
<< sequences_hash
239-
<< uint32_t(tx.vout.size())
240-
<< outputs_hash
241-
<< input_index;
242-
return h.GetSHA256();
243-
}
244-
// DoS safe, fixed length hash!
245-
uint256 GetDefaultCheckTemplateVerifyHashEmptyScript(const CTransaction& tx, const uint256& outputs_hash, const uint256& sequences_hash,
246-
const uint32_t input_index) {
247-
auto h = CHashWriter(SER_GETHASH, 0)
248-
<< tx.nVersion
249-
<< tx.nLockTime
250-
<< uint32_t(tx.vin.size())
251-
<< sequences_hash
252-
<< uint32_t(tx.vout.size())
253-
<< outputs_hash
254-
<< input_index;
255-
return h.GetSHA256();
256-
}
257-
258-
In python, this can be written as (but note this implementation is DoS-able).
259-
260-
def get_default_check_template_hash(self, nIn):
164+
The below code is the main logic for verifying CHECKTEMPLATEVERIFY, described
165+
in pythonic pseduocode. The canonical specification for the semantics of
166+
OP_CHECKTEMPLATEVERIFY as implemented in C++ in the context of Bitcoin Core can
167+
be seen in the reference implementation.
168+
169+
The execution of the opcode is as follows:
170+
def execute_bip_119(self):
171+
# Before soft-fork activation / failed activation
172+
if not self.flags.script_verify_default_check_template_verify_hash:
173+
# Potentially set for node-local policy to discourage premature use
174+
if self.flags.script_verify_discourage_upgradable_nops:
175+
return self.errors_with(errors.script_err_discourage_upgradable_nops)
176+
return self.return_as_nop()
177+
# CTV always requires at least one stack argument
178+
if len(self.stack) < 1:
179+
return self.errors_with(errors.script_err_invalid_stack_operation)
180+
# CTV only verifies the hash against a 32 byte argument
181+
if len(self.stack[-1]) == 32:
182+
# Ensure the precomputed data required for anti-DoS is available,
183+
# or cache it on first use
184+
if self.context.precomputed_ctv_data == None:
185+
self.context.precomputed_ctv_data = self.context.tx.get_default_check_template_precomputed_data()
186+
if stack[-1] != self.context.tx.get_default_check_template_hash(self.context.nIn, self.context.precomputed_ctv_data)
187+
return self.errors_with(errors.script_err_template_mismatch)
188+
return self.return_as_nop()
189+
# future upgrade can add semantics for this opcode with different length args
190+
# so discourage use when applicable
191+
if self.flags.script_verify_discourage_upgradable_nops:
192+
return self.errors_with(errors.script_err_discourage_upgradable_nops)
193+
else:
194+
return self.return_as_nop()
195+
196+
The computation of this hash can be implemented as specified below (where self
197+
is the transaction type). Care must be taken that in any validation context,
198+
the precomputed data must be initialized to prevent Denial-of-Service attacks.
199+
Any implementation *must* cache these parts of the hash computation to avoid
200+
quadratic hashing DoS. All variable length computations must be precomputed
201+
including hashes of the scriptsigs, sequences, and outputs. See the section
202+
"Denial of Service and Validation Costs" below. This is not a performance
203+
optimization.
204+
205+
def get_default_check_template_precomputed_data(self):
206+
result = {}
207+
# If there are no scriptSigs we do not need to precompute a hash
208+
if any(inp.scriptSig for inp in self.vin):
209+
result["scriptSigs"] = sha256(b"".join(ser_string(inp.scriptSig) for inp in self.vin))
210+
# The same value is also pre-computed for and defined in BIP-341 and can be shared
211+
result["sequences"] = sha256(b"".join(struct.pack("<I", inp.nSequence) for inp in self.vin))
212+
# The same value is also pre-computed for and defined in BIP-341 and can be shared
213+
result["outputs"] = sha256(b"".join(out.serialize() for out in self.vout))
214+
return result
215+
216+
# parameter precomputed must be passed in for DoS resistance
217+
def get_default_check_template_hash(self, nIn, precomputed = None):
218+
if precomputed == None:
219+
precomputed = self.get_default_check_template_precomputed_data()
261220
r = b""
221+
# pack as 4 byte signed integer
262222
r += struct.pack("<i", self.nVersion)
223+
# pack as 4 byte unsigned integer
263224
r += struct.pack("<I", self.nLockTime)
264-
if any(inp.scriptSig for inp in self.vin):
265-
r += sha256(b"".join(ser_string(inp.scriptSig) for inp in self.vin))
225+
# we do not include the hash in the case where there is no
226+
# scriptSigs
227+
if "scriptSigs" in precomputed:
228+
r += precomputed["scriptSigs"]
229+
# pack as 4 byte unsigned integer
266230
r += struct.pack("<I", len(self.vin))
267-
r += sha256(b"".join(struct.pack("<I", inp.nSequence) for inp in self.vin))
231+
r += precomputed["sequences"]
232+
# pack as 4 byte unsigned integer
268233
r += struct.pack("<I", len(self.vout))
269-
r += sha256(b"".join(out.serialize() for out in self.vout))
234+
r += precomputed["outputs"]
235+
# pack as 4 byte unsigned integer
270236
r += struct.pack("<I", nIn)
271237
return sha256(r)
272238
239+
273240
A PayToBareDefaultCheckTemplateVerifyHash output matches the following template:
274241

275-
bool CScript::IsPayToBareDefaultCheckTemplateVerifyHash() const
276-
{
277-
// Extra-fast test for pay-to-basic-standard-template CScripts:
278-
return (this->size() == 34 &&
279-
(*this)[0] == 0x20 &&
280-
(*this)[33] == OP_CHECKTEMPLATEVERIFY);
281-
}
242+
# Extra-fast test for pay-to-basic-standard-template CScripts:
243+
def is_pay_to_bare_default_check_template_verify_hash(self):
244+
return len(self) == 34 and self[0] == 0x20 and self[-1] == OP_CHECKTEMPLATEVERIFY
245+
282246

283247
==Deployment==
284248

@@ -567,9 +531,7 @@ is O(T) (the size of the transaction).
567531

568532
An example of a script that could experience an DoS issue without caching is:
569533

570-
```
571-
<H> CTV CTV CTV... CTV
572-
```
534+
<H> CTV CTV CTV... CTV
573535
574536
Such a script would cause the intepreter to compute hashes (supposing N CTV's) over O(N*T) data.
575537
If the scriptSigs non-nullity is not cached, then the O(T) transaction could be scanned over O(N)

0 commit comments

Comments
 (0)