Skip to content

Commit

Permalink
An idea
Browse files Browse the repository at this point in the history
  • Loading branch information
Brandon Black committed Dec 14, 2023
1 parent 6943941 commit b4409ea
Showing 1 changed file with 212 additions and 111 deletions.
323 changes: 212 additions & 111 deletions bip-txhash.mediawiki
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,9 @@ OP_CHECKTXHASHVERIFY does the following:

* There is at least one element on the stack, fail otherwise.
* The element on the stack is at least 32 bytes long, fail otherwise.
* The first 32 bytes are interpreted as the TxHash and the remaining suffix
bytes specify the TxFieldSelector.
* The first 32 bytes are interpreted as the TxHash and the remaining suffix bytes specify the TxFieldSelector.
* If the TxFieldSelector is invalid, fail.
* The actual TxHash of the transaction at the current input index, calculated
using the given TxFieldSelector must be equal to the first 32 bytes of the
element on the stack, fail otherwise.
* The actual TxHash of the transaction at the current input index, calculated using the given TxFieldSelector must be equal to the first 32 bytes of the element on the stack, fail otherwise.

OP_TXHASH uses tapscript opcode OP_SUCCESS189 (0xbd) as a soft fork upgrade.
Expand All @@ -48,121 +45,225 @@ OP_TXHASH does the following:
* There is at least one element on the stack, fail otherwise.
* The element is interpreted as the TxFieldSelector and is popped off the stack.
* If the TxFieldSelector is invalid, fail.
* The 32-byte TxHash of the transaction at the current input index,
calculated using the given TxFieldSelector is pushed onto the stack.


The TxFieldSelector has the following semantics. We will give a brief conceptual
summary, followed by a reference implementation of the CalculateTxHash function.

* There are two special cases for the TxFieldSelector:
** the empty value, zero bytes long: it is set equal to TXFS_SPECIAL_TEMPLATE,
the de-facto default value which means everything except the prevouts and
the prevout scriptPubkeys.
TXFS_SPECIAL_TEMPLATE is 4 bytes long, as follows:
# TXFS_ALL
# TXFS_INPUTS_TEMPLATE | TXFS_OUTPUTS_ALL
# TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL
# TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL
** the 0x00 byte: it is set equal to TXFS_SPECIAL_ALL, which means "ALL"
and is primarily useful to emulate SIGHASH_ALL when OP_TXHASH is used in
combination with OP_CHECKSIGFROMSTACK.
TXFS_SPECIAL_TEMPLATE is 4 bytes long, as follows:
# TXFS_ALL
# TXFS_INPUTS_ALL | TXFS_OUTPUTS_ALL
# TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL
# TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL
* The first byte of the TxFieldSelector has its 8 bits assigned as follows,
from lowest to highest:
# version (TXFS_VERSION)
# locktime (TXFS_LOCKTIME)
# current input index (TXFS_CURRENT_INPUT_IDX)
# current input control block (or empty) (TXFS_CURRENT_INPUT_CONTROL_BLOCK)
# current script last OP_CODESEPARATOR position (or 0xffffffff) (TXFS_CURRENT_INPUT_LAST_CODESEPARATOR_POS)
# inputs (TXFS_INPUTS)
# outputs (TXFS_OUTPUTS)
* The last (highest) bit of the first byte (TXFS_CONTROL), we will call the
"control bit", and it can be used to control the behavior of the opcode. For
OP_TXHASH and OP_CHECKTXHASHVERIFY, the control bit is used to determine
whether the TxFieldSelector itself has to be included in the resulting hash.
(For potential other uses of the TxFieldSelector (like a hypothetical OP_TX),
this bit can be repurposed.)

* If either "inputs" or "outputs" is set to 1, expect another byte with its 8
bits assigning the following variables, from lowest to highest:
** Specifying which fields of the inputs will be selected:
# prevouts (TXFS_INPUTS_PREVOUTS)
# sequences (TXFS_INPUTS_SEQUENCES)
# scriptSigs (TXFS_INPUTS_SCRIPTSIGS)
# prevout scriptPubkeys (TXFS_INPUTS_PREV_SCRIPTPUBKEYS)
# prevout values (TXFS_INPUTS_PREV_VALUED)
# taproot annexes (TXFS_INPUTS_TAPROOT_ANNEXES)
** Specifying which fields of the outputs will be selected:
# scriptPubkeys (TXFS_OUTPUTS_SCRIPTPUBKEYS)
# values (TXFS_OUTPUTS_VALUES)
//TODO(stevenroose) check that the 7 and 8 render correctly

* We define as follows:
** TXFS_ALL = TXFS_VERSION | TXFS_LOCKTIME | TXFS_CURRENT_INPUT_IDX | TXFS_CURRENT_INPUT_CONTROL_BLOCK
| TXFS_CURRENT_INPUT_LAST_CODESEPARATOR_POS | TXFS_INPUTS | TXFS_OUTPUTS | TXFS_CONTROL
** TXFS_INPUTS_ALL = TXFS_INPUTS_PREVOUTS | TXFS_INPUTS_SEQUENCES | TXFS_INPUTS_SCRIPTSIGS
| TXFS_INPUTS_PREV_SCRIPTPUBKEYS | TXFS_INPUTS_PREV_VALUES | TXFS_INPUTS_TAPROOT_ANNEXES
** TXFS_INPUTS_TEMPLATE = TXFS_INPUTS_SEQUENCES | TXFS_INPUTS_SCRIPTSIGS | TXFS_INPUTS_PREV_VALUES
| TXFS_INPUTS_TAPROOT_ANNEXES
** TXFS_OUTPUTS_ALL = TXFS_OUTPUTS_SCRIPTPUBKEYS | TXFS_OUTPUTS_VALUES
For both inputs and then outputs, do the following:

* If the "in/outputs" field is set to 1, another additional byte is expected:
** The highest bit (TXFS_INOUT_NUMBER) indicates whether the "number of
in-/outputs" should be committed to.
** For the remaining bits, there are three exceptional values:
*** 0x00 (TXFS_INOUT_SELECTION_NONE) means "no in/outputs"
(hence only the number of them as 0x80 (TXFS_INOUT_NUMBER)).
*** 0x40 (TXFS_INOUT_SELECTION_CURRENT) means "select only the in/output of
the current input index" (it is invalid when current index exceeds number
of outputs).
*** 0x3f (TXFS_INOUT_SELECTION_ALL) means "select all in/outputs".
** The second highest bit (TXFS_INOUT_SELECTION_MODE) is the "specification mode":
*** Set to 0 it means "leading mode".
*** Set to 1 it means "individual mode".
** The third highest bit (TXFS_INOUT_SELECTION_SIZE) is used to indicate the
"index size", i.e. the number of bytes will be used to represent in/output
indices.
** In "leading mode",
*** With "index size" set to 0, the remaining lowest 5 bits of the first byte
will be interpreted as the number of leading in/outputs to select.
*** With "index size" set to 1, the remaining lowest 5 bits of the first byte
together with the 8 bits of the next byte will be interpreted as the
number of leading in/outputs to select.
** In "individual mode", the remaining lowest 5 bits of the first byte will be
interpreted as `n`, the number of individual in/outputs to select.
*** With "index size" set to 0, interpret the following `n` individual bytes
as the indices of an individual in/outputs to select.
*** With "index size" set to 1, interpret the next `n` pairs of two bytes as
the indices of individual in/outputs to select.

Effectively, this allows a user to select
* The 32-byte TxHash of the transaction at the current input index, calculated using the given TxFieldSelector is pushed onto the stack.

The TxFieldSelector is comprised of between 0 and 64 bytes and allows for granular selection of nearly any aspect of the transaction being verified for hashing.

TxFieldSelectors of 0 and 1 byte represent pre-defined modes that closely resemeble other existing transaction components for hashing (specifically BIP119's DefaultCheckTemplateVerifyHash, BIP341/BIP342's signature hashes, and BIP118's signature hashes). Until activation, additional pre-defined modes can be added.

In TxFieldSelectors of 2 or more bytes, the first byte represents general transaction fields, fields specific to the input being validated, and specifies whether input/output fields will be specifically selected or set to a default. The default fields from inputs and outputs are the same as those hashed in BIP341/BIP342 signature hashing.

If specific input and output fields are selected, all fields commonly included in BIP341/342 signature hashes for inputs other than the one being validated are selectable, along with the taproot annex.

The next byte of the TxFieldSelector (required as byte 2 if no input/output selections are made, or optional as byte 3 if input/output field selections are made) specifies whether the number of inputs/outputs is selected, whether a subtotal of values from the selected inputs/outputs is selected, and defines the method of selecting inputs/outputs. Four input/output selection modes are defined: corresponding (similar to SINGLE), leading, exclude trailing, and individual. If the input/output selection byte is omitted, it selects all inputs if any input bit is set in the input/output field selection and none otherwise and selects all outputs if any output bit is set in the input/output field selection and none otherwise.

If leading or exclude trailing mode are selected an additional compact varint is read from the TxFieldSelector, first for inputs and then for outputs. If the end of the field selector is encountered when attempting to read the first byte of one of these compact varints, it is treated as zero.

If individual selection is specified for both inputs and outputs, the number of individual inputs is read as a single byte from the TxFieldSelector.

The remaining bytes of the TxFieldSelector specify the individual selected input and output indices (inputs first) as compact varints, in ascending order.

This allows a user to select
* all in/outputs
* the current input index
* the leading in/outputs up to 8192
* up to 32 individually selected in/outputs
* no in/outputs
* the current input (and/or its corresponding output)
* any number of leading in/outputs (including by excluding any number of trailing in/outputs)
* up to 61 individual in/outputs (exact number depending on whether their indices can be represented by a 1-byte compact varint)
The TxFieldSelector is invalid when
* a byte is expected but missing
* additional unexpected bytes are present
* index size is set to 1 while not being necessary
* a leading number of individual index is selected out of bounds of the in/outputs
* individual indices are duplicated or not in increasing order
* fewer individual input indices are available than requested
* a leading or trailing index, or individual index is selected out of bounds of the in/outputs
* individual in/output indices are duplicated or not in increasing order
* individual in/output mode is selected but no indices are specified
These limitations are to avoid potential TxFieldSelector malleability. It is
however allowed to use leading mode where it could be "all". This
is important to allow for optional addition of extra inputs or outputs.
//TODO(stevenroose) should we disallow individual that could be leading?

=== Specification ===

We name 3 bytes of the field selector and their bits:

TxFlags - always the first byte
* 7 control<ref>The control bit is used in TXHASH(/VERIFY) to specify whether to include the TxFieldSelector itself in the hash. Other applications of the TxFieldSelector may repurpose this bit.</ref> (TXFS_CONTROL)
* 6 version (TXFS_VERSION)
* 5 locktime (TXFS_LOCKTIME)
* 4 current input index (TXFS_CURRENT_INPUT_INDEX)
* 3 current input control block or script code<ref>The first non-empty item of: control block, witness script, redeem script, scriptPubkey.</ref> (TXFS_CURRENT_INPUT_SCRIPT)
* 2 current script last OP_CODESEPARATOR position (or 0xffffffff) (TXFS_CURRENT_INPUT_CODESEP)
* 1 current input annex (TXFS_CURRENT_INPUT_ANNEX)
* 0 io flags (TXFS_IO_FLAGS)
We define the byte 0xFF as TXFS_FLAGS_ALL.

IOFlags - second byte if the lowest byte of TxFlags is set, otherwise absent
* 7 prevouts (TXFS_INPUTS_PREVOUTS)
* 6 sequences (TXFS_INPUTS_SEQUENCES)
* 5 scriptSigs (TXFS_INPUTS_SCRIPTSIGS)
* 4 prevout scriptPubkeys (TXFS_INPUTS_SCRIPTPUBKEYS)
* 3 prevout values (TXFS_INPUTS_VALUES)
* 2 taproot annexes (TXFS_INPUTS_ANNEXES)
* 1 output scriptPubkeys (TXFS_OUTPUTS_SCRIPTPUBKEYS)
* 0 output values (TXFS_OUTPUTS_VALUES)
We define the byte 0xF8 as TXFS_INPUTS_COMMON and 0x03 as TXFS_OUTPUTS_COMMON

IOSel - optional third byte if the lowest byte of TxFlags is set, otherwise required second
* 7 numInputs (TXFS_NUM_INPUTS)
* 6 inputTotal (TXFS_INPUTS_TOTAL)
* 5 input selection 1
* 4 input selection 2
* 3 numOutputs (TXFS_NUM_OUTPUTS)
* 2 outputTotal (TXFS_OUTPUTS_TOTAL)
* 1 output selection 1
* 0 output selection 2
For input and output selection bits, we define the following four values:
* 00 corresponding (TXFS_CORRESPONDING_INPUT, TXFS_CORRESPONDING_OUTPUT)
* 01 leading (TXFS_LEADING_INPUTS, TXFS_LEADING_OUTPUTS)
* 10 exclude trailing (TXFS_EXCLUDE_TRAILING_INPUTS, TXFS_EXCLUDE_TRAILING_OUTPUTS)
* 11 individual (TXFS_INDIVIDUAL_INPUTS, TXFS_INDIVIDUAL_OUTPUTS)
The TxFieldSelector is parsed exactly as follows:

* Fail if ''len(TxFieldSelector) > 64''
* Let ''txfs' = 0x00'' if ''len(TxFieldSelector) = 0'', otherwise let ''txfs' = TxFieldSelector''
* If ''len(txfs') >= 2'':
** Let ''txfs = txfs' ''
* Else:
** Let ''txfs'' take its value from the following table according to the value of ''txfs' ''
*** ''0x00 => 0x716BAA'' DefaultCheckTemplateVerifyHash<ref>'''DefaultCheckTemplateVerifyHash'''
TXFS_VERSION|TXFS_LOCKTIME|TXFS_CURRENT_INPUT_INDEX|TXFS_IO_FLAGS
TXFS_INPUTS_SEQUENCES|TXFS_INPUTS_SCRIPTSIGS|TXFS_INPUTS_VALUES|TXFS_OUTPUTS_SCRIPTPUBKEYS|TXFS_OUTPUTS_VALUES
TXFS_NUM_INPUTS|TXFS_EXCLUDE_TRAILING_INPUTS|TXFS_NUM_OUTPUTS|TXFS_EXCLUDE_TRAILING_OUTPUTS</ref>
*** ''0x01 => 0xFFFB'' SIGHASH_ALL<ref>'''SIGHASH_ALL'''
TXFS_FLAGS_ALL
TXFS_INPUTS_SIGHASH|TXFS_OUTPUTS_SIGHASH</ref>
*** ''0x02 => 0xFFF8'' SIGHASH_ALL|SIGHASH_NONE<ref>'''SIGHASH_ALL|SIGHASH_NONE'''
TXFS_FLAGS_ALL
TXFS_INPUTS_SIGHASH</ref>
*** ''0x03 => 0xFFFB20'' SIGHASH_ALL|SIGHASH_SINGLE<ref>'''SIGHASH_ALL|SIGHASH_SINGLE'''
TXFS_FLAGS_ALL
TXFS_INPUTS_SIGHASH|TXFS_OUTPUTS_SIGHASH
TXFS_EXCLUDE_TRAILING_INPUTS|TXFS_CORRESPONDING_OUTPUT</ref>
*** ''0x41 => 0xFF7B02'' SIGHASH_ANYPREVOUT|SIGHASH_ALL<ref>'''SIGHASH_ANYPREVOUT|SIGHASH_ALL'''
TXFS_FLAGS_ALL
TXFS_INPUTS_SEQUENCES|TXFS_INPUTS_SCRIPTSIGS|TXFS_INPUTS_SCRIPTPUBKEYS|TXFS_INPUTS_VALUES|TXFS_OUTPUTS_COMMON
TXFS_CORRESPONDING_INPUT|TXFS_EXCLUDE_TRAILING_OUTPUTS</ref>
*** ''0x42 => 0xFF7801'' SIGHASH_ANYPREVOUT|SIGHASH_NONE<ref>'''SIGHASH_ANYPREVOUT|SIGHASH_NONE'''
TXFS_FLAGS_ALL
TXFS_INPUTS_SEQUENCES|TXFS_INPUTS_SCRIPTSIGS|TXFS_INPUTS_SCRIPTPUBKEYS|TXFS_INPUTS_VALUES
TXFS_CORRESPONDING_INPUT|TXFS_LEADING_OUTPUTS</ref>
*** ''0x43 => 0xFF7B00'' SIGHASH_ANYPREVOUT|SIGHASH_SINGLE<ref>'''SIGHASH_ANYPREVOUT|SIGHASH_SINGLE'''
TXFS_FLAGS_ALL
TXFS_INPUTS_SEQUENCES|TXFS_INPUTS_SCRIPTSIGS|TXFS_INPUTS_SCRIPTPUBKEYS|TXFS_INPUTS_VALUES|TXFS_OUTPUTS_COMMON
TXFS_CORRESPONDING_INPUT|TXFS_CORRESPONDING_OUTPUT</ref>
*** ''0x81 => 0xFFFB02'' SIGHASH_ANYONECANPAY|SIGHASH_ALL<ref>'''SIGHASH_ANYONECANPAY|SIGHASH_ALL'''
TXFS_FLAGS_ALL
TXFS_INPUTS_SIGHASH|TXFS_OUTPUTS_SIGHASH
TXFS_CORRESPONDING_INPUT|TXFS_EXCLUDE_TRAILING_OUTPUTS</ref>
*** ''0x82 => 0xFFF801'' SIGHASH_ANYONECANPAY|SIGHASH_NONE<ref>'''SIGHASH_ANYONECANPAY|SIGHASH_NONE'''
TXFS_FLAGS_ALL
TXFS_INPUTS_SIGHASH
TXFS_CORRESPONDING_INPUT|TXFS_LEADING_OUTPUTS</ref>
*** ''0x83 => 0xFFFB00'' SIGHASH_ANYONECANPAY|SIGHASH_SINGLE<ref>'''SIGHASH_ANYONECANPAY|SIGHASH_SINGLE'''
TXFS_FLAGS_ALL
TXFS_INPUTS_SIGHASH|TXFS_OUTPUTS_SIGHASH
TXFS_CORRESPONDING_INPUT|TXFS_CORRESPONDING_OUTPUT</ref>
*** ''0xc1 => 0xFB4302'' SIGHASH_ANYPREVOUTANYSCRIPT|SIGHASH_ALL<ref>'''SIGHASH_ANYPREVOUTANYSCRIPT|SIGHASH_ALL'''
TXFS_FLAGS_ALL & ~TXFS_CURRENT_INPUT_SCRIPT
TXFS_INPUTS_SEQUENCES|TXFS_OUTPUTS_COMMON
TXFS_CORRESPONDING_INPUT|TXFS_EXCLUDE_TRAILING_OUTPUTS</ref>
*** ''0xc2 => 0xFB4001'' SIGHASH_ANYPREVOUTANYSCRIPT|SIGHASH_NONE<ref>'''SIGHASH_ANYPREVOUTANYSCRIPT|SIGHASH_NONE'''
TXFS_FLAGS_ALL & ~TXFS_CURRENT_INPUT_SCRIPT
TXFS_INPUTS_SEQUENCES
TXFS_CORRESPONDING_INPUT|TXFS_LEADING_OUTPUTS</ref>
*** ''0xc3 => 0xFB4300'' SIGHASH_ANYPREVOUTANYSCRIPT|SIGHASH_SINGLE<ref>'''SIGHASH_ANYPREVOUTANYSCRIPT|SIGHASH_SINGLE'''
TXFS_FLAGS_ALL & ~TXFS_CURRENT_INPUT_SCRIPT
TXFS_INPUTS_SEQUENCES|TXFS_OUTPUTS_COMMON
TXFS_CORRESPONDING_INPUT|TXFS_CORRESPONDING_OUTPUT</ref>
** Otherwise fail
* Let ''txFlags = txfs[0]''
* If ''txFlags & TXFS_IO_FLAGS = TXFS_IO_FLAGS'':
** Let ''ioFlags = txfs[1]''
** Let ''ioSelPos = 2''
* Else:
** Let ''ioFlags = TXFS_INPUTS_COMMON | TXFS_OUTPUTS_COMMON''
** Let ''ioSelPos = 1''
* If ''len(txfs) > ioSelPos'':
** Let ''ioSel = txfs[ioSelPos]''
* Else:
** If ''ioFlags & 0xFC = 0'':
*** Let ''iSel = TXFS_LEADING_INPUTS''
** Else:
*** Let ''iSel = TXFS_EXCLUDE_TRAILING_INPUTS''
** If ''ioFlags & 0x03 = 0'':
*** Let ''oSel = TXFS_LEADING_OUTPUTS''
** Else:
*** Let ''oSel = TXFS_EXCLUDE_TRAILING_OUTPUTS''
** Let ''ioSel = iSel | oSel''
* If ''ioSel & 0x30 = TXFS_INDIVIDUAL_INPUTS'' and ''ioSel & 0x03 = TXFS_INDIVIDUAL_OUTPUTS'':
** Fail if ''len(txfs) <= ioSelPos + 1''
** Let ''nInputs = txfs[ioSelPos + 1]''
** Let ''nOutputs = -1''
** Let ''selPos = ioSelPos + 2''
* Else:
** If ''ioSel & 0x30 = TXFS_LEADING_INPUTS'' or ''ioSel & 0x30 = TXFS_EXCLUDE_TRAILING_INPUTS'':
*** If ''len(txfs) > ioSelPos + 1'':
**** Let ''nOutputsPos, nInputs = readCompactVarInt(txfs, ioSelPos + 1)''
*** Else:
**** Let ''nOutputsPos = ioSelPos + 1''
**** Let ''nInputs = 0''
** Else:
*** Let ''nInputs = -1''
*** Let ''nOutputsPos = ioSelPos + 1''
** If ''ioSel & 0x03 = TXFS_LEADING_OUTPUTS'' or ''ioSel & 0x03 = TXFS_EXCLUDE_TRAILING_OUTPUTS'':
*** If ''len(txfs) > nOutputsPos'':
**** Let ''selPos, nOutputs = readCompactVarInt(txfs, nOutputsPos)''
*** Else:
**** Let ''nOutputs = 0''
**** Let ''selPos = nOutputsPos''
** Else:
*** Let ''nOutputs = -1''
*** Let ''selPos = nOutputsPos''
* Fail if ''nInputs > vin'' or ''nOutputs > vout''
* Let ''selectedIndices = readCompactVarints(TxFieldSelector, selPos)''
* If ''ioSel & 0x30 = TXFS_INDIVIDUAL_INPUTS'' and ''ioSel & 0x03 = TXFS_INDIVIDUAL_OUTPUTS'':
** Fail if ''len(selectedIndices) < nInputs + 1''
** Let ''selectedInputs = selectedIndices[0..nInputs]''
** Let ''selectedOutputs = selectedIndices[nInputs...]''
* Else if ''ioSel & 0x30 = TXFS_INDIVIDUAL_INPUTS'':
** Fail if ''len(selectedIndices) = 0)
** Let ''selectedInputs = selectedIndices''
* Else if ''ioSel & 0x03 = TXFS_INDIVIDUAL_OUTPUTS'':
** Fail if ''len(selectedIndices) = 0)
** Let ''selectedOutputs = selectedIndices''
* If ''ioSel & 0x30 = TXFS_CORRESPONDING_INPUT'':
** Let ''selectedInputs = [nIn]''
* If ''ioSel & 0x03 = TXFS_CORRESPONDING_OUTPUT'':
** Fail if ''nIn > vout''
** Let ''selectedOutputs = [nIn]''
* If ''ioSel & 0x30 = TXFS_LEADING_INPUTS''
** Let ''selectedInputs = [0..nInputs]''
* If ''ioSel & 0x30 = TXFS_EXCLUDE_TRAILING_INPUTS''
** Let ''selectedInputs = [0..(vin-nInputs)]''
* If ''ioSel & 0x03 = TXFS_LEADING_OUTPUTS''
** Let ''selectedOutputs = [0..nOutputs]''
* If ''ioSel & 0x03 = TXFS_EXCLUDE_TRAILING_OUTPUTS''
** Let ''selectedOutputs = [0..(vout-nOutputs)]''
* Fail if ''selectedInputs'' or ''selectedOutputs'' are not set
* Fail if ''selectedInputs'' or ''selectedOutputs'' are not unique
* Fail if ''selectedInputs'' or ''selectedOutputs'' are not in ascending order
The values ''txFlags'', ''ioFlags'', and ''ioSel''; and the arrays
''selectedInputs'', and ''selectedOutputs'' are then passed to the tx hashing
function.

=== Notes ===

<references />

===Resource limits===

Expand Down

0 comments on commit b4409ea

Please sign in to comment.