@@ -161,8 +161,83 @@ 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.
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:
166241
167242 case OP_CHECKTEMPLATEVERIFY:
168243 {
@@ -196,10 +271,6 @@ specification for the semantics of OP_CHECKTEMPLATEVERIFY.
196271Where
197272
198273 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.
203274 return GetDefaultCheckTemplateVerifyHash(current_tx, current_input_index) == uint256(hash);
204275 }
205276
@@ -255,20 +326,37 @@ The hash is computed as follows, where the outputs_hash and sequences_hash are c
255326 return h.GetSHA256();
256327 }
257328
258- In python, this can be written as (but note this implementation is DoS-able).
259329
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+
272360
273361A PayToBareDefaultCheckTemplateVerifyHash output matches the following template:
274362
@@ -567,9 +655,7 @@ is O(T) (the size of the transaction).
567655
568656An example of a script that could experience an DoS issue without caching is:
569657
570- ```
571- <H> CTV CTV CTV... CTV
572- ```
658+ <H> CTV CTV CTV... CTV
573659
574660Such a script would cause the intepreter to compute hashes (supposing N CTV's) over O(N*T) data.
575661If the scriptSigs non-nullity is not cached, then the O(T) transaction could be scanned over O(N)
0 commit comments