From fa09f7f85711226b114b835d176bcb85895040a4 Mon Sep 17 00:00:00 2001 From: Jeremy Rubin Date: Thu, 28 Apr 2022 09:43:30 -0700 Subject: [BIP-119] Reimplement CTV in higher level pythonic pseduocode and clarify DoS Caching requirements. --- bip-0119.mediawiki | 130 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 108 insertions(+), 22 deletions(-) diff --git a/bip-0119.mediawiki b/bip-0119.mediawiki index 07ce53a..f4e65a8 100644 --- a/bip-0119.mediawiki +++ b/bip-0119.mediawiki @@ -161,8 +161,83 @@ forming a "Payment Pool". ==Detailed Specification== -The below code is the main logic for verifying CHECKTEMPLATEVERIFY, and is the canonical -specification for the semantics of OP_CHECKTEMPLATEVERIFY. +The below code is the main logic for verifying CHECKTEMPLATEVERIFY, described +in pythonic pseduocode. The canonical specification for the semantics of +OP_CHECKTEMPLATEVERIFY can be seen in the reference implementations. + +The execution of the opcode is as follows: + def execute_bip_119(self): + # Before soft-fork activation / failed activation + if not self.flags.script_verify_default_check_template_verify_hash: + # Potentially set for node-local policy to discourage premature use + if self.flags.script_verify_discourage_upgradable_nops: + return self.errors_with(errors.script_err_discourage_upgradable_nops) + return self.return_as_nop() + # CTV always requires at least one stack argument + if len(self.stack) < 1: + return self.errors_with(errors.script_err_invalid_stack_operation) + # CTV only verifies the hash against a 32 byte argument + if len(self.stack[-1]) == 32: + # Ensure the precomputed data required for anti-DoS is available, + # or cache it on first use + if self.context.precomputed_ctv_data == None: + self.context.precomputed_ctv_data = self.context.tx.get_default_check_template_precomputed_data() + if stack[-1] != self.context.tx.get_default_check_template_hash(self.context.nIn, self.context.precomputed_ctv_data) + return self.errors_with(errors.script_err_template_mismatch) + return self.return_as_nop() + # future upgrade can add semantics for this opcode with different length args + # so discourage use when applicable + if self.flags.script_verify_discourage_upgradable_nops: + return self.errors_with(errors.script_err_discourage_upgradable_nops) + else: + return self.return_as_nop() + +The computation of this hash can be implemented as specified below (where self +is the transaction type). Care must be taken that in any validation context, +the precomputed data must be initialized to prevent Denial-of-Service attacks. +Any implementation *must* cache these parts of the hash computation to avoid +quadratic hashing DoS. All variable length computations must be precomputed +including hashes of the scriptsigs, sequences, and outputs. See the section +"Denial of Service and Validation Costs" below. This is not a performance +optimization. + + def get_default_check_template_precomputed_data(self): + result = {} + # If there are no scriptSigs we do not need to precompute a hash + if any(inp.scriptSig for inp in self.vin): + result["scriptSigs"] = sha256(b"".join(ser_string(inp.scriptSig) for inp in self.vin)) + # The same value is also pre-computed for and defined in BIP-341 and can be shared + result["sequences"] = sha256(b"".join(struct.pack("& hash) { - // note: for anti-DoS, a real implementation *must* cache parts of this computation - // to avoid quadratic hashing DoS all variable length computations must be precomputed - // including hashes of the scriptsigs, sequences, and outputs. See the section - // "Denial of Service and Validation Costs" below. return GetDefaultCheckTemplateVerifyHash(current_tx, current_input_index) == uint256(hash); } @@ -255,20 +326,37 @@ The hash is computed as follows, where the outputs_hash and sequences_hash are c return h.GetSHA256(); } -In python, this can be written as (but note this implementation is DoS-able). - def get_default_check_template_hash(self, nIn): - r = b"" - r += struct.pack(" CTV CTV CTV... CTV -``` + CTV CTV CTV... CTV Such a script would cause the intepreter to compute hashes (supposing N CTV's) over O(N*T) data. If the scriptSigs non-nullity is not cached, then the O(T) transaction could be scanned over O(N) -- cgit v1.2.3