summaryrefslogtreecommitdiff
path: root/bip-0119.mediawiki
diff options
context:
space:
mode:
Diffstat (limited to 'bip-0119.mediawiki')
-rw-r--r--bip-0119.mediawiki209
1 files changed, 101 insertions, 108 deletions
diff --git a/bip-0119.mediawiki b/bip-0119.mediawiki
index 29f6271..304f228 100644
--- a/bip-0119.mediawiki
+++ b/bip-0119.mediawiki
@@ -39,9 +39,12 @@ The recommended standardness rules additionally:
==Motivation==
-Covenants are restrictions on how a coin may be spent beyond key ownership. Covenants can be useful
-to construct smart contracts. As covenants are complex to implement and risk of introducing
-fungibility discriminants they have not been seriously considered for inclusion in Bitcoin.
+Covenants are restrictions on how a coin may be spent beyond key ownership. This is a general
+definition based on the legal definition which even simple scripts using CSV would satisfy.
+Covenants in Bitcoin transactions usually refer to restrictions on where coins can be transferred.
+Covenants can be useful to construct smart contracts. As covenants are complex to implement
+and risk of introducing fungibility discriminants they have not been seriously considered for
+inclusion in Bitcoin.
This BIP introduces a simple covenant called a *template* which enables a limited set of highly
valuable use cases without significant risk.
@@ -150,7 +153,7 @@ variant where the coins move along the control path.
CHECKTEMPLATEVERIFY makes it much easier to set up trustless CoinJoins than
previously because participants agree on a single output which pays all
-participants, which will be lower fee than before. Further Each participant
+participants, which will be lower fee than before. Further each participant
doesn't need to know the totality of the outputs committed to by that output,
they only have to verify their own sub-tree will pay them. These trees can
then, using a top-level Schnorr key, be interactively updated on a rolling basis
@@ -158,98 +161,88 @@ 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.
-
- case OP_CHECKTEMPLATEVERIFY:
- {
- // if flags not enabled; treat as a NOP4
- if (!(flags & SCRIPT_VERIFY_DEFAULT_CHECK_TEMPLATE_VERIFY_HASH)) {
- if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
- return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS);
- break;
- }
-
- if (stack.size() < 1)
- return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
-
- // If the argument was not 32 bytes, treat as OP_NOP4:
- switch (stack.back().size()) {
- case 32:
- if (!checker.CheckDefaultCheckTemplateVerifyHash(stack.back())) {
- return set_error(serror, SCRIPT_ERR_TEMPLATE_MISMATCH);
- }
- break;
- default:
- // future upgrade can add semantics for this opcode with different length args
- // so discourage use when applicable
- if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
- return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS);
- }
- }
- }
- break;
-
-Where
-
- bool CheckDefaultCheckTemplateVerifyHash(const std::vector<unsigned char>& 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);
- }
-
-The hash is computed as follows:
- // not DoS safe, for reference/testing!
- uint256 GetDefaultCheckTemplateVerifyHash(const CTransaction& tx, uint32_t input_index) {
- return GetDefaultCheckTemplateVerifyHash(tx, GetOutputsSHA256(tx), GetSequenceSHA256(tx), input_index);
- }
- // not DoS safe for reference/testing!
- uint256 GetDefaultCheckTemplateVerifyHash(const CTransaction& tx, const uint256& outputs_hash, const uint256& sequences_hash,
- const uint32_t input_index) {
- bool skip_scriptSigs = std::find_if(tx.vin.begin(), tx.vin.end(),
- [](const CTxIn& c) { return c.scriptSig != CScript(); }) == tx.vin.end();
- return skip_scriptSigs ? GetDefaultCheckTemplateVerifyHashEmptyScript(tx, outputs_hash, sequences_hash, input_index) :
- GetDefaultCheckTemplateVerifyHashWithScript(tx, outputs_hash, sequences_hash, GetScriptSigsSHA256(tx), input_index);
- }
- // DoS safe, fixed length hash!
- uint256 GetDefaultCheckTemplateVerifyHashWithScript(const CTransaction& tx, const uint256& outputs_hash, const uint256& sequences_hash,
- const uint256& scriptSig_hash, const uint32_t input_index) {
- auto h = CHashWriter(SER_GETHASH, 0)
- << tx.nVersion
- << tx.nLockTime
- << scriptSig_hash
- << uint32_t(tx.vin.size())
- << sequences_hash
- << uint32_t(tx.vout.size())
- << outputs_hash
- << input_index;
- return h.GetSHA256();
- }
- // DoS safe, fixed length hash!
- uint256 GetDefaultCheckTemplateVerifyHashEmptyScript(const CTransaction& tx, const uint256& outputs_hash, const uint256& sequences_hash,
- const uint32_t input_index) {
- auto h = CHashWriter(SER_GETHASH, 0)
- << tx.nVersion
- << tx.nLockTime
- << uint32_t(tx.vin.size())
- << sequences_hash
- << uint32_t(tx.vout.size())
- << outputs_hash
- << input_index;
- return h.GetSHA256();
- }
+The below code is the main logic for verifying CHECKTEMPLATEVERIFY, described
+in pythonic pseduocode. The canonical specification for the semantics of
+OP_CHECKTEMPLATEVERIFY as implemented in C++ in the context of Bitcoin Core can
+be seen in the reference implementation.
+
+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("<I", inp.nSequence) for inp in self.vin))
+ # The same value is also pre-computed for and defined in BIP-341 and can be shared
+ result["outputs"] = sha256(b"".join(out.serialize() for out in self.vout))
+ return result
+
+ # parameter precomputed must be passed in for DoS resistance
+ def get_default_check_template_hash(self, nIn, precomputed = None):
+ if precomputed == None:
+ precomputed = self.get_default_check_template_precomputed_data()
+ r = b""
+ # pack as 4 byte signed integer
+ r += struct.pack("<i", self.nVersion)
+ # pack as 4 byte unsigned integer
+ r += struct.pack("<I", self.nLockTime)
+ # we do not include the hash in the case where there is no
+ # scriptSigs
+ if "scriptSigs" in precomputed:
+ r += precomputed["scriptSigs"]
+ # pack as 4 byte unsigned integer
+ r += struct.pack("<I", len(self.vin))
+ r += precomputed["sequences"]
+ # pack as 4 byte unsigned integer
+ r += struct.pack("<I", len(self.vout))
+ r += precomputed["outputs"]
+ # pack as 4 byte unsigned integer
+ r += struct.pack("<I", nIn)
+ return sha256(r)
+
A PayToBareDefaultCheckTemplateVerifyHash output matches the following template:
- bool CScript::IsPayToBareDefaultCheckTemplateVerifyHash() const
- {
- // Extra-fast test for pay-to-basic-standard-template CScripts:
- return (this->size() == 34 &&
- (*this)[0] == 0x20 &&
- (*this)[33] == OP_CHECKTEMPLATEVERIFY);
- }
+ # Extra-fast test for pay-to-basic-standard-template CScripts:
+ def is_pay_to_bare_default_check_template_verify_hash(self):
+ return len(self) == 34 and self[0] == 0x20 and self[-1] == OP_CHECKTEMPLATEVERIFY
+
==Deployment==
@@ -271,14 +264,14 @@ For the avoidance of unclarity, the parameters to be determined are:
consensus.vDeployments[Consensus::DEPLOYMENT_CHECKTEMPLATEVERIFY].min_activation_height = 0;
Until BIP-119 reaches ACTIVE state and the
-SCRIPT_VERIFY_DEFAULT_CHECK_TEMPLATE_VERIFY_HASH flag is set, the network should
-execute a NOP4 as SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS for policy and a NOP for
-consensus.
+SCRIPT_VERIFY_DEFAULT_CHECK_TEMPLATE_VERIFY_HASH flag is enforced, node implementations should (are recommended to)
+execute a NOP4 as SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS (to deny entry to the mempool) for policy and must evaluate as
+a NOP for consensus (during block validation).
In order to facilitate using CHECKTEMPLATEVERIFY, the common case of a
PayToBareDefaultCheckTemplateVerifyHash
-with no scriptSig data shall be made standard to permit relaying. Future template types may be
-standardized later as policy changes.
+with no scriptSig data may (is recommended to) be made standard to permit relaying. Future template types may be
+standardized later as policy changes at the preference of the implementor.
==Reference Implementation==
@@ -538,9 +531,7 @@ is O(T) (the size of the transaction).
An example of a script that could experience an DoS issue without caching is:
-```
-<H> CTV CTV CTV... CTV
-```
+ <H> 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)
@@ -589,12 +580,13 @@ reuse-unsafe.
Because CHECKTEMPLATEVERIFY commits to the input index currently being spent, reused-keys are
guaranteed to execute in separate transactions which reduces the risk of "half-spend" type issues.
-====NOP-Default and Standardness Rules====
+====NOP-Default and Recommended Standardness Rules====
-If the argument length is not exactly 32, CHECKTEMPLATEVERIFY treats it as a NOP.
-Many OP_NOP upgrades prefer to fail in such circumstances. In particular, for
-CHECKTEMPLATEVERIFY, making an invalid argument a NOP permits future soft-forks to upgrade the
-semantics or loosed restrictions around the value being previously pushed only.
+If the argument length is not exactly 32, CHECKTEMPLATEVERIFY treats it as a NOP during
+consensus validation. Implementations are recommended to fail in such circumstances during non-consensus
+relaying and mempool validation. In particular, making an invalid-length argument a failure aids future
+soft-forks upgrades to be able to rely on the tighter standard restrictions to safely loosen
+the restrictions for standardness while tightening them for consensus with the upgrade's rules.
The standardness rules may lead an unscrupulous script developer to accidentally rely on the
stricter standardness rules to be enforced during consensus. Should that developer submit a
@@ -710,8 +702,9 @@ for an OP_NOP are a soft fork, so existing software will be fully functional wit
for mining and block validation. Similar soft forks for OP_CHECKSEQUENCEVERIFY and OP_CHECKLOCKTIMEVERIFY
(see BIP-0065 and BIP-0112) have similarly changed OP_NOP semantics without introducing compatibility issues.
-In contrast to previous forks, OP_CHECKTEMPLATEVERIFY will not make scripts
-valid for policy until the new rule is active.
+In contrast to previous forks, OP_CHECKTEMPLATEVERIFY's reference implementation does not allow transactions with spending
+scripts using it to be accepted to the mempool or relayed under standard policy until the new rule is active. Other implementations
+are recommended to follow this rule as well, but not required.
Older wallet software will be able to accept spends from OP_CHECKTEMPLATEVERIFY outputs, but will
require an upgrade in order to treat PayToBareDefaultCheckTemplateVerifyHash chains with a confirmed ancestor as