@@ -161,8 +161,83 @@ forming a "Payment Pool".
161
161
162
162
==Detailed Specification ==
163
163
164
- The below code is the main logic for verifying CHECKTEMPLATEVERIFY, and is the canonical
165
- specification for the semantics of OP_CHECKTEMPLATEVERIFY.
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 can be seen in the reference implementations.
167
+
168
+ The execution of the opcode is as follows:
169
+ def execute_bip_119(self):
170
+ # Before soft-fork activation / failed activation
171
+ if not self.flags.script_verify_default_check_template_verify_hash:
172
+ # Potentially set for node-local policy to discourage premature use
173
+ if self.flags.script_verify_discourage_upgradable_nops:
174
+ return self.errors_with(errors.script_err_discourage_upgradable_nops)
175
+ return self.return_as_nop()
176
+ # CTV always requires at least one stack argument
177
+ if len(self.stack) < 1:
178
+ return self.errors_with(errors.script_err_invalid_stack_operation)
179
+ # CTV only verifies the hash against a 32 byte argument
180
+ if len(self.stack[-1]) == 32:
181
+ # Ensure the precomputed data required for anti-DoS is available,
182
+ # or cache it on first use
183
+ if self.context.precomputed_ctv_data == None:
184
+ self.context.precomputed_ctv_data = self.context.tx.get_default_check_template_precomputed_data()
185
+ if stack[-1] != self.context.tx.get_default_check_template_hash(self.context.nIn, self.context.precomputed_ctv_data)
186
+ return self.errors_with(errors.script_err_template_mismatch)
187
+ return self.return_as_nop()
188
+ # future upgrade can add semantics for this opcode with different length args
189
+ # so discourage use when applicable
190
+ if self.flags.script_verify_discourage_upgradable_nops:
191
+ return self.errors_with(errors.script_err_discourage_upgradable_nops)
192
+ else:
193
+ return self.return_as_nop()
194
+
195
+ The computation of this hash can be implemented as specified below (where self
196
+ is the transaction type). Care must be taken that in any validation context,
197
+ the precomputed data must be initialized to prevent Denial-of-Service attacks.
198
+ Any implementation *must* cache these parts of the hash computation to avoid
199
+ quadratic hashing DoS. All variable length computations must be precomputed
200
+ including hashes of the scriptsigs, sequences, and outputs. See the section
201
+ "Denial of Service and Validation Costs" below. This is not a performance
202
+ optimization.
203
+
204
+ def get_default_check_template_precomputed_data(self):
205
+ result = {}
206
+ # If there are no scriptSigs we do not need to precompute a hash
207
+ if any(inp.scriptSig for inp in self.vin):
208
+ result["scriptSigs"] = sha256(b"".join(ser_string(inp.scriptSig) for inp in self.vin))
209
+ # The same value is also pre-computed for and defined in BIP-341 and can be shared
210
+ result["sequences"] = sha256(b"".join(struct.pack("<I", inp.nSequence) for inp in self.vin))
211
+ # The same value is also pre-computed for and defined in BIP-341 and can be shared
212
+ result["outputs"] = sha256(b"".join(out.serialize() for out in self.vout))
213
+ return result
214
+
215
+ # parameter precomputed must be passed in for DoS resistance
216
+ def get_default_check_template_hash(self, nIn, precomputed = None):
217
+ if precomputed == None:
218
+ precomputed = self.get_default_check_template_precomputed_data()
219
+ r = b""
220
+ # pack as 4 byte signed integer
221
+ r += struct.pack("<i", self.nVersion)
222
+ # pack as 4 byte unsigned integer
223
+ r += struct.pack("<I", self.nLockTime)
224
+ # we do not include the hash in the case where there is no
225
+ # scriptSigs
226
+ if "scriptSigs" in precomputed:
227
+ r += precomputed["scriptSigs"]
228
+ # pack as 4 byte unsigned integer
229
+ r += struct.pack("<I", len(self.vin))
230
+ r += precomputed["sequences"]
231
+ # pack as 4 byte unsigned integer
232
+ r += struct.pack("<I", len(self.vout))
233
+ r += precomputed["outputs"]
234
+ # pack as 4 byte unsigned integer
235
+ r += struct.pack("<I", nIn)
236
+ return sha256(r)
237
+
238
+
239
+
240
+ The C++ is below:
166
241
167
242
case OP_CHECKTEMPLATEVERIFY:
168
243
{
@@ -196,10 +271,6 @@ specification for the semantics of OP_CHECKTEMPLATEVERIFY.
196
271
Where
197
272
198
273
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
274
return GetDefaultCheckTemplateVerifyHash(current_tx, current_input_index) == uint256(hash);
204
275
}
205
276
@@ -255,20 +326,37 @@ The hash is computed as follows, where the outputs_hash and sequences_hash are c
255
326
return h.GetSHA256();
256
327
}
257
328
258
- In python, this can be written as (but note this implementation is DoS-able).
259
329
260
- def get_default_check_template_hash(self, nIn):
261
- r = b""
262
- r += struct.pack("<i", self.nVersion)
263
- 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))
266
- r += struct.pack("<I", len(self.vin))
267
- r += sha256(b"".join(struct.pack("<I", inp.nSequence) for inp in self.vin))
268
- r += struct.pack("<I", len(self.vout))
269
- r += sha256(b"".join(out.serialize() for out in self.vout))
270
- r += struct.pack("<I", nIn)
271
- return sha256(r)
330
+
331
+ case OP_CHECKTEMPLATEVERIFY:
332
+ {
333
+ // if flags not enabled; treat as a NOP4
334
+ if (!(flags & SCRIPT_VERIFY_DEFAULT_CHECK_TEMPLATE_VERIFY_HASH)) {
335
+ if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
336
+ return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS);
337
+ break;
338
+ }
339
+
340
+ if (stack.size() < 1)
341
+ return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
342
+
343
+ // If the argument was not 32 bytes, treat as OP_NOP4:
344
+ switch (stack.back().size()) {
345
+ case 32:
346
+ if (!checker.CheckDefaultCheckTemplateVerifyHash(stack.back())) {
347
+ return set_error(serror, SCRIPT_ERR_TEMPLATE_MISMATCH);
348
+ }
349
+ break;
350
+ default:
351
+ // future upgrade can add semantics for this opcode with different length args
352
+ // so discourage use when applicable
353
+ if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
354
+ return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS);
355
+ }
356
+ }
357
+ }
358
+
359
+
272
360
273
361
A PayToBareDefaultCheckTemplateVerifyHash output matches the following template:
274
362
@@ -567,9 +655,7 @@ is O(T) (the size of the transaction).
567
655
568
656
An example of a script that could experience an DoS issue without caching is:
569
657
570
- ```
571
- <H> CTV CTV CTV... CTV
572
- ```
658
+ <H> CTV CTV CTV... CTV
573
659
574
660
Such a script would cause the intepreter to compute hashes (supposing N CTV's) over O(N*T) data.
575
661
If the scriptSigs non-nullity is not cached, then the O(T) transaction could be scanned over O(N)
0 commit comments