-
Notifications
You must be signed in to change notification settings - Fork 5.8k
Comments:BIP 0174
-
Partial signature entries use the map key
{0x02}|{public key}. However, a single public key may be involved in signing multiple inputs, and the signature will differ in each one. Since the map keys are to be unique, this is a problem. I suggest the following map key instead:{0x02}|{input index}|{pubkey index} -
HD derivation entries waste space by repeating the public key. I recommend using the following map key instead:
{0x03}|{input index}|{pubkey index}, which also matches previous point.
However, a single public key may be involved in signing multiple inputs, and the signature will differ in each one. Since the map keys are to be unique, this is a problem.
Each input has its own partial signatures map; it isn't global.
HD derivation entries waste space by repeating the public key. I recommend using the following map key instead:
{0x03}|{input index}|{pubkey index}, which also matches previous point.
The hd keypaths (unlike the partial signatures map) is global. Public keys are not duplicated in the map.
I understand the response to the first question.
As to the second question - the public key is already present in the redeem script, so it does not seem necessary to duplicate it in the map key. Instead, could refer to the public key index within the redeem script.
Another issue I see is related to change outputs. In order to securely compute the net amount transferred out of a wallet, change addresses must be verified. In order to do this, the redeem script of the output, as well as the BIP-32 derivation path of each pubkey therein must be provided. The way the redeem script element is described, it seems to apply only to inputs. That can be fixed with a doc change. I would further comment about the potential use for change outputs.
In fact, this can also apply to non-change outputs, if we want to prove to Signers that an output goes to a separate, white-listed, HD wallet.
As to the second question - the public key is already present in the redeem script, so it does not seem necessary to duplicate it in the map key. Instead, could refer to the public key index within the redeem script.
This would change the scope of the keypaths from being global to being per-input. This means that if the same output script is being spent from in multiple inputs (which can often happen), then the keypaths are going to be duplicated for every input. Furthermore, that makes the hd keypaths useless for change output stuff (the current form is actually necessary for Ledger Nano S support right now).
The way the redeem script element is described, it seems to apply only to inputs. That can be fixed with a doc change. I would further comment about the potential use for change outputs.
In fact, this can also apply to non-change outputs, if we want to prove to Signers that an output goes to a separate, white-listed, HD wallet.
Any field in the globals section (except the transaction itself and the number of inputs field) can apply to both inputs and outputs of the transaction. I can make that more clear in the BIP itself. Also see #3 of Rationale
`
BIP: 119 Layer: Consensus (soft fork) Title: CHECKTEMPLATEVERIFY Author: Jeremy Rubin [email protected] Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0119 Status: Draft Type: Standards Track Created: 2020-01-06 License: BSD-3-Clause
==Abstract==
This BIP proposes a new opcode, OP_CHECKTEMPLATEVERIFY, to be activated as a change to the semantics of OP_NOP4.
==Summary==
OP_CHECKTEMPLATEVERIFY uses opcode OP_NOP4 (0xb3) as a soft fork upgrade.
OP_CHECKTEMPLATEVERIFY does the following:
- There is at least one element on the stack, fail otherwise
- The element on the stack is 32 bytes long, NOP otherwise
- The DefaultCheckTemplateVerifyHash of the transaction at the current input index is equal to the element on the stack, fail otherwise
The DefaultCheckTemplateVerifyHash commits to the serialized version, locktime, scriptSigs hash (if any non-null scriptSigs), number of inputs, sequences hash, number of outputs, outputs hash, and currently executing input index.
The recommended standardness rules additionally:
- Reject non-32 byte as SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS.
==Motivation==
This BIP introduces a transaction template, a simple spending restriction that pattern matches a transaction against a hashed transaction specification. OP_CHECKTEMPLATEVERIFY reduces many of the trust, interactivity, and storage requirements inherent with the use of pre-signing in applications. For more details on applications, please see the references.
==Detailed Specification==
The below code is the main logic for verifying CHECKTEMPLATEVERIFY, described in pythonic pseudocode. 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:
# 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 the hashes do not match, return error
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 ser_compact_size(l): r = b"" if l < 253: # Serialize as unsigned char r = struct.pack("B", l) elif l < 0x10000: # Serialize as unsigned char 253 followed by unsigned 2 byte integer (little endian) r = struct.pack("<BH", 253, l) elif l < 0x100000000: # Serialize as unsigned char 254 followed by unsigned 4 byte integer (little endian) r = struct.pack("<BI", 254, l) else: # Serialize as unsigned char 255 followed by unsigned 8 byte integer (little endian) r = struct.pack("<BQ", 255, l) return r
def ser_string(s): return ser_compact_size(len(s)) + s
class CTxOut: def serialize(self): r = b"" # serialize as signed 8 byte integer (little endian) r += struct.pack("<q", self.nValue) r += ser_string(self.scriptPubKey) return r
def get_default_check_template_precomputed_data(self): result = {} # If there are no s`**