How does Bitmatrix achieve parallel processing in a UTXO model?

Burak
Bitmatrix
Published in
10 min readJun 18, 2021

One benefit of the EVM model is that it allows for the parallelization of smart contract execution. Multiple independent, smart contract interactions used in different transactions can be processed at the same block in order.

In the UTXO model, the result of a transaction depends on the validity of its outpoint. Executing transactions in parallel in recurring covenant systems requires a “global state mempool” — which can not work today as each mempool could follow a different unconfirmed-transaction-chain fork.

Bitmatrix proposes a solution to this by leveraging Tapscript (BIP 342), Relative lock-time (BIP 68), Replace-by-fee (BIP 125), and some tricks.

A Bitmatrix swap involves three subsequent transactions:

  1. Commitment Transaction
  2. Pool Transaction
  3. Settlement Transaction

Commitment Transaction

The first transaction (Commitment Transaction) commits to reserving a slot in Pool Transaction. When users attempt to make a swap via bitmatrix.app, Commitment Transaction is signed and broadcasted by Marina wallet. This transaction includes a key-path eliminated P2TR output called “User commitment output” whose tapscript is constructed as follows:

////SUCCESSFUL SWAP PATH
OP_IF
//Require one confirmation to be included in Pool Transaction
<0x00000001> OP_CHECKSEQUENCEVERIFY OP_DROP
//Introspect state transition count (Index #6 [OP_RETURN])
<6> OP_INSPECTOUTPUTSCRIPTPUBKEY <1> <8> OP_SUBSTR_LAZY
//Check if timeout has not passed
<current_state_transition_count + timeout> OP_LESSTHAN OP_VERIFY
//Constrain to target swap output scriptPubkey OP_INSPECTCURRENTINPUTINDEX <2> OP_ADD OP_INSPECTOUTPUTSCRIPTPUBKEY <target_swap_output_scriptpubkey> OP_EQUALVERIFY//Constrain to target swap output asset
OP_INSPECTCURRENTINPUTINDEX <2> OP_ADD OP_INSPECTOUTPUTASSET <target_swap_output_asset_id> OP_EQUALVERIFY
//Constrain to target swap output value
OP_INSPECTCURRENTINPUTINDEX <2> OP_ADD OP_INSPECTOUTPUTVALUE <target_swap_output_amount> OP_EQUALVERIFY
////TIMEOUT REFUND PATH
OP_ELSE
//Timeout check
<0x00000001 + timeout> OP_CHECKSEQUENCEVERIFY OP_DROP
//Constrain to refund output scriptPubkey
<0> OP_INSPECTOUTPUTSCRIPTPUBKEY <refund_output_scriptpubkey> OP_EQUALVERIFY
//Constrain to refund output asset
<0> OP_INSPECTOUTPUTASSET OP_INSPECTCURRENTINPUTASSET OP_EQUALVERIFY
//Constrain to refund output value
<0> OP_INSPECTOUTPUTVALUE OP_INSPECTCURRENTINPUTVALUE OP_EQUALVERIFY
OP_ENDIF

Which compiles to bytecode:

0x630400000001B2755653CB5158C304[current_state_transition_count+timeout]9F6958CA529353CB23[target_swap_output_scriptpubkey]8858CA529300CB20[target_swap_output_asset_id]8858CA529351CB08[target_swap_output_amount]886704[0x00000001+timeout]B2750053CB23[refund_output_scriptpubkey]880000CB53CA880051CB53CA8868

Replacing [] square brackets with 0xFFFFFF... is our “tapscript_mask” to be used at a later stage:

0x630400000001B2755653CB5158C304FFFFFFFF9F6958CA529353CB23FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8858CA529300CB20FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8858CA529351CB08FFFFFFFFFFFFFFFF886704FFFFFFFFB2750053CB23FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF880000CB53CA880051CB53CA8868

Pool Transaction

Pool Transaction is the main transaction where each swap is taking place in a global state. A “User commitment output” can be spent in this transaction once confirmed.

A Pool Transaction includes 6 covenant spends on the input side and 6 new covenant state constructions on the output side, each from index #0 to index #5. An OP_RETURN field for reflecting state transition count and a transaction fee output are also included on the outputs side from index #6 to index #7. All covenants, OP_RETURN field, and the transaction fee output are enforced by “Flag covenant” and therefore cannot be disregarded. In addition to these enforced fields, up to 10 user-provided inputs and up to 10 user-provided outputs can be accommodated.

Constraining transaction capacity and transition period

The number of inputs exceeding 16 and/or the number of outputs exceeding 18 fails “Flag covenant,” making the whole Pool Transaction unspendable. This artificial limit is temporarily set for the initial version and can be extended in a further Bitmatrix version. This check is done by the “Flag covenant” as follows:

OP_INSPECTNUMINPUTS <16> OP_LESSTHANOREQUAL OP_VERIFY
OP_INSPECTNUMOUTPUTS <18> OP_LESSTHANOREQUAL OP_VERIFY

One of the steps to achieving a global state in mempool is making sure an unconfirmed-transaction-chain never occurs. This is so that a confirmed parent should have at most one unconfirmed child at all times. To achieve this, “Flag covenant” enforces one state transition per block:

<0x00000001> OP_CHECKSEQUENCEVERIFY

The sequence field 00000001 indicates a new pool state transition is not valid until 1 block has elapsed since the previous pool state transition confirms. With 1-minute blocks in Liquid, the number of swaps that can be processed in every minute is limited to 10. Bitmatrix users compete over a fee market to reserve a slot in this 10-seat Pool Transaction.

Enforcing replaceability

Flag covenant’s sequence number (0x00000001) is already opting into Replace-by-fee while enforcing one state transition per block. However, it’s also required to make sure every single input is signaling replaceability. “Flag covenant” (index #0) makes sure the other 5 covenants from index #1 to #5 have also opted into Replace-by-fee:

//Make sure L-BTC covenant signals replaceability
<1> OP_INSPECTINPUTSEQUENCE <0xFFFFFFFD> OP_EQUALVERIFY
//Make sure Pair token covenant signals replaceability
<2> OP_INSPECTINPUTSEQUENCE <0xFFFFFFFD> OP_EQUALVERIFY
//Make sure LP token covenant signals replaceability
<3> OP_INSPECTINPUTSEQUENCE <0xFFFFFFFD> OP_EQUALVERIFY
//Make sure LP reward covenant signals replaceability
<4> OP_INSPECTINPUTSEQUENCE <0xFFFFFFFD> OP_EQUALVERIFY
//Make sure State transition counter covenant signals replaceability
<5> OP_INSPECTINPUTSEQUENCE <0xFFFFFFFD> OP_EQUALVERIFY

Inspecting and validating user-provided inputs

To inspect user-provided P2TR inputs in Pool Transaction, tapscript bytedata for each input is provided in witness. Inside the script, each tapscript bytedata is tweaked with a pre-determined internal key with an unknown discrete logarithm to compare to tweaked keys in previous scriptPubkeys. A bitwise AND between the encoded tapscript_mask and each tapscript bytedata are computed, and the result is compared to each tapscript bytedata.

--Witness://Tapscript bytedata for each input provided in witness
<user_input_10_tapscript>
<user_input_9_tapscript>
<user_input_8_tapscript>
<user_input_7_tapscript>
<user_input_6_tapscript>
<user_input_5_tapscript>
<user_input_4_tapscript>
<user_input_3_tapscript>
<user_input_2_tapscript>
<user_input_1_tapscript>
--Script://tapscript_mask encoded in script
<0x630400000001B2755653CB5158C304FFFFFFFF9F6958CA529353CB23FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8858CA529300CB20FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8858CA529351CB08FFFFFFFFFFFFFFFF886704FFFFFFFFB2750053CB23FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF880000CB53CA880051CB53CA8868>
//A point with unknown discrete logarithm (script-path-only)
<0x0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace80..>
//Number of user-provided inputs
OP_INSPECTNUMINPUTS <6> OP_SUB
//Inspect user-provided input 1 tapscript and check script validity
<6> OP_INSPECTINPUTSCRIPTPUBKEY OP_DUP <0> <1> OP_OPSUBSTR <0x51> OP_EQUALVERIFY <3> <32> OP_SUBSTR <2> OP_PICK <4> OP_PICK OP_TAPTWEAK <1> <32> OP_SUBSTR OP_EQUALVERIFY <3> OP_ROLL OP_DUP <3> OP_PICK OP_AND OP_EQUALVERIFY
//Inspect user-provided input 2 tapscript and check script validity
<1> OP_SUB OP_DUP <0> OP_GREATERTHAN OP_IF <7> OP_INSPECTINPUTSCRIPTPUBKEY OP_DUP <0> <1> OP_OPSUBSTR <0x51> OP_EQUALVERIFY <3> <32> OP_SUBSTR <2> OP_PICK <4> OP_PICK OP_TAPTWEAK <1> <32> OP_SUBSTR OP_EQUALVERIFY <3> OP_ROLL OP_DUP <3> OP_PICK OP_AND OP_EQUALVERIFY OP_ENDIF
//Inspect user-provided input 3 tapscript and check script validity
<1> OP_SUB OP_DUP <0> OP_GREATERTHAN OP_IF <8> OP_INSPECTINPUTSCRIPTPUBKEY OP_DUP <0> <1> OP_OPSUBSTR <0x51> OP_EQUALVERIFY <3> <32> OP_SUBSTR <2> OP_PICK <4> OP_PICK OP_TAPTWEAK <1> <32> OP_SUBSTR OP_EQUALVERIFY <3> OP_ROLL OP_DUP <3> OP_PICK OP_AND OP_EQUALVERIFY OP_ENDIF
//Inspect user-provided input 4 tapscript and check script validity
<1> OP_SUB OP_DUP <0> OP_GREATERTHAN OP_IF <9> OP_INSPECTINPUTSCRIPTPUBKEY OP_DUP <0> <1> OP_OPSUBSTR <0x51> OP_EQUALVERIFY <3> <32> OP_SUBSTR <2> OP_PICK <4> OP_PICK OP_TAPTWEAK <1> <32> OP_SUBSTR OP_EQUALVERIFY <3> OP_ROLL OP_DUP <3> OP_PICK OP_AND OP_EQUALVERIFY OP_ENDIF
//Inspect user-provided input 5 tapscript and check script validity
<1> OP_SUB OP_DUP <0> OP_GREATERTHAN OP_IF <10> OP_INSPECTINPUTSCRIPTPUBKEY OP_DUP <0> <1> OP_OPSUBSTR <0x51> OP_EQUALVERIFY <3> <32> OP_SUBSTR <2> OP_PICK <4> OP_PICK OP_TAPTWEAK <1> <32> OP_SUBSTR OP_EQUALVERIFY <3> OP_ROLL OP_DUP <3> OP_PICK OP_AND OP_EQUALVERIFY OP_ENDIF
//Inspect user-provided input 6 tapscript and check script validity
<1> OP_SUB OP_DUP <0> OP_GREATERTHAN OP_IF <11> OP_INSPECTINPUTSCRIPTPUBKEY OP_DUP <0> <1> OP_OPSUBSTR <0x51> OP_EQUALVERIFY <3> <32> OP_SUBSTR <2> OP_PICK <4> OP_PICK OP_TAPTWEAK <1> <32> OP_SUBSTR OP_EQUALVERIFY <3> OP_ROLL OP_DUP <3> OP_PICK OP_AND OP_EQUALVERIFY OP_ENDIF
//Inspect user-provided input 7 tapscript and check script validity
<1> OP_SUB OP_DUP <0> OP_GREATERTHAN OP_IF <12> OP_INSPECTINPUTSCRIPTPUBKEY OP_DUP <0> <1> OP_OPSUBSTR <0x51> OP_EQUALVERIFY <3> <32> OP_SUBSTR <2> OP_PICK <4> OP_PICK OP_TAPTWEAK <1> <32> OP_SUBSTR OP_EQUALVERIFY <3> OP_ROLL OP_DUP <3> OP_PICK OP_AND OP_EQUALVERIFY OP_ENDIF
//Inspect user-provided input 8 tapscript and check script validity
<1> OP_SUB OP_DUP <0> OP_GREATERTHAN OP_IF <13> OP_INSPECTINPUTSCRIPTPUBKEY OP_DUP <0> <1> OP_OPSUBSTR <0x51> OP_EQUALVERIFY <3> <32> OP_SUBSTR <2> OP_PICK <4> OP_PICK OP_TAPTWEAK <1> <32> OP_SUBSTR OP_EQUALVERIFY <3> OP_ROLL OP_DUP <3> OP_PICK OP_AND OP_EQUALVERIFY OP_ENDIF
//Inspect user-provided input 9 tapscript and check script validity
<1> OP_SUB OP_DUP <0> OP_GREATERTHAN OP_IF <14> OP_INSPECTINPUTSCRIPTPUBKEY OP_DUP <0> <1> OP_OPSUBSTR <0x51> OP_EQUALVERIFY <3> <32> OP_SUBSTR <2> OP_PICK <4> OP_PICK OP_TAPTWEAK <1> <32> OP_SUBSTR OP_EQUALVERIFY <3> OP_ROLL OP_DUP <3> OP_PICK OP_AND OP_EQUALVERIFY OP_ENDIF
//Inspect user-provided input 10 tapscript and check script validity
<1> OP_SUB OP_DUP <0> OP_GREATERTHAN OP_IF <15> OP_INSPECTINPUTSCRIPTPUBKEY OP_DUP <0> <1> OP_OPSUBSTR <0x51> OP_EQUALVERIFY <3> <32> OP_SUBSTR <2> OP_PICK <4> OP_PICK OP_TAPTWEAK <1> <32> OP_SUBSTR OP_EQUALVERIFY <3> OP_ROLL OP_DUP <3> OP_PICK OP_AND OP_EQUALVERIFY OP_ENDIF

Enforce lexicographical ordering of outpoints

To ensure a “global state” to prevent mempool conflicts, user-provided inputs must be ordered lexicographically. The first 8-byte of each outpoint hash are compared to one another:

//Number of user-provided inputs
OP_INSPECTNUMINPUTS <6> OP_SUB
//Compare first two user-provided inputs
<1> OP_GREATERTHAN OP_IF <6> OP_INSPECTINPUTOUTPOINT OP_SHA256 <0> <8> OP_SUBSTR <7> OP_INSPECTINPUTOUTPOINT OP_SHA256 <0> <8> OP_SUBSTR OP_GREATERTHAN64 OP_EQUALVERIFY OP_SUB OP_ENDIF
//Compare 2nd-3rd user-provided inputs
<1> OP_GREATERTHAN OP_IF <7> OP_INSPECTINPUTOUTPOINT OP_SHA256 <0> <8> OP_SUBSTR <8> OP_INSPECTINPUTOUTPOINT OP_SHA256 <0> <8> OP_SUBSTR OP_GREATERTHAN64 OP_EQUALVERIFY OP_SUB OP_ENDIF
//Compare 3rd-4th user-provided inputs
<1> OP_GREATERTHAN OP_IF <8> OP_INSPECTINPUTOUTPOINT OP_SHA256 <0> <8> OP_SUBSTR <9> OP_INSPECTINPUTOUTPOINT OP_SHA256 <0> <8> OP_SUBSTR OP_GREATERTHAN64 OP_EQUALVERIFY OP_SUB OP_ENDIF
//Compare 4th-5th user-provided inputs
<1> OP_GREATERTHAN OP_IF <9> OP_INSPECTINPUTOUTPOINT OP_SHA256 <0> <8> OP_SUBSTR <10> OP_INSPECTINPUTOUTPOINT OP_SHA256 <0> <8> OP_SUBSTR OP_GREATERTHAN64 OP_EQUALVERIFY OP_SUB OP_ENDIF
//Compare 5th-6th user-provided inputs
<1> OP_GREATERTHAN OP_IF <10> OP_INSPECTINPUTOUTPOINT OP_SHA256 <0> <8> OP_SUBSTR <11> OP_INSPECTINPUTOUTPOINT OP_SHA256 <0> <8> OP_SUBSTR OP_GREATERTHAN64 OP_EQUALVERIFY OP_SUB OP_ENDIF
//Compare 6th-7th user-provided inputs
<1> OP_GREATERTHAN OP_IF <11> OP_INSPECTINPUTOUTPOINT OP_SHA256 <0> <8> OP_SUBSTR <12> OP_INSPECTINPUTOUTPOINT OP_SHA256 <0> <8> OP_SUBSTR OP_GREATERTHAN64 OP_EQUALVERIFY OP_SUB OP_ENDIF
//Compare 7th-8th user-provided inputs
<1> OP_GREATERTHAN OP_IF <12> OP_INSPECTINPUTOUTPOINT OP_SHA256 <0> <8> OP_SUBSTR <13> OP_INSPECTINPUTOUTPOINT OP_SHA256 <0> <8> OP_SUBSTR OP_GREATERTHAN64 OP_EQUALVERIFY OP_SUB OP_ENDIF
//Compare 8th-9th user-provided inputs
<1> OP_GREATERTHAN OP_IF <13> OP_INSPECTINPUTOUTPOINT OP_SHA256 <0> <8> OP_SUBSTR <14> OP_INSPECTINPUTOUTPOINT OP_SHA256 <0> <8> OP_SUBSTR OP_GREATERTHAN64 OP_EQUALVERIFY OP_SUB OP_ENDIF
//Compare 9th-10th user-provided inputs
<1> OP_GREATERTHAN OP_IF <14> OP_INSPECTINPUTOUTPOINT OP_SHA256 <0> <8> OP_SUBSTR <15> OP_INSPECTINPUTOUTPOINT OP_SHA256 <0> <8> OP_SUBSTR OP_GREATERTHAN64 OP_EQUALVERIFY OP_SUB OP_ENDIF

Inspecting user-provided outputs to avoid transaction pinning

Similar to inspecting user-provided inputs, every user-provided output is inspected to ensure they are signaling a one-block state transition to avoid transaction-pinning attacks potentially from the child transactions. Script for enforcing one-block state transition is:

<0x00000001> OP_CHECKSEQUENCEVERIFY OP_DROP

Which compiles to bytedata:

0x0400000001b275

The first 7-bytes of each output script must start with (0x0400000001b275) to enforce sequence number 00000001:

--Witness://Tapscript bytedata for each output provided in witness
<user_output_10_tapscript>
<user_output_9_tapscript>
<user_output_8_tapscript>
<user_output_7_tapscript>
<user_output_6_tapscript>
<user_output_5_tapscript>
<user_output_4_tapscript>
<user_output_3_tapscript>
<user_output_2_tapscript>
<user_output_1_tapscript>
--Script://A point with unknown discrete logarithm (script-path-only)
<0x0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace80..>
//Number of user-provided outputs
OP_INSPECTNUMINPUTS <8> OP_SUB
//Inspect user-provided output 1 script and check script
<8> OP_INSPECTOUTPUTSCRIPTPUBKEY <3> <32> OP_SUBSTR <3> OP_PICK <3> OP_PICK OP_TAPTWEAK <1> <32> OP_SUBSTR OP_EQUALVERIFY <2> OP_ROLL <0> <7> OP_SUBSTR <0x0400000001b275> OP_EQUALVERIFY
//Inspect user-provided output 2 script and check script validity
<1> OP_SUB OP_DUP <0> OP_GREATERTHAN OP_IF <9> OP_INSPECTOUTPUTSCRIPTPUBKEY <3> <32> OP_SUBSTR <3> OP_PICK <3> OP_PICK OP_TAPTWEAK <1> <32> OP_SUBSTR OP_EQUALVERIFY <2> OP_ROLL <0> <7> OP_SUBSTR <0x0400000001b275> OP_EQUALVERIFY OP_ENDIF
//Inspect user-provided output 3 script and check script validity
<1> OP_SUB OP_DUP <0> OP_GREATERTHAN OP_IF <10> OP_INSPECTOUTPUTSCRIPTPUBKEY <3> <32> OP_SUBSTR <3> OP_PICK <3> OP_PICK OP_TAPTWEAK <1> <32> OP_SUBSTR OP_EQUALVERIFY <2> OP_ROLL <0> <7> OP_SUBSTR <0x0400000001b275> OP_EQUALVERIFY OP_ENDIF
//Inspect user-provided output 4 script and check script validity
<1> OP_SUB OP_DUP <0> OP_GREATERTHAN OP_IF <11> OP_INSPECTOUTPUTSCRIPTPUBKEY <3> <32> OP_SUBSTR <3> OP_PICK <3> OP_PICK OP_TAPTWEAK <1> <32> OP_SUBSTR OP_EQUALVERIFY <2> OP_ROLL <0> <7> OP_SUBSTR <0x0400000001b275> OP_EQUALVERIFY OP_ENDIF
//Inspect user-provided output 5 script and check script validity
<1> OP_SUB OP_DUP <0> OP_GREATERTHAN OP_IF <12> OP_INSPECTOUTPUTSCRIPTPUBKEY <3> <32> OP_SUBSTR <3> OP_PICK <3> OP_PICK OP_TAPTWEAK <1> <32> OP_SUBSTR OP_EQUALVERIFY <2> OP_ROLL <0> <7> OP_SUBSTR <0x0400000001b275> OP_EQUALVERIFY OP_ENDIF
//Inspect user-provided output 6 script and check script validity
<1> OP_SUB OP_DUP <0> OP_GREATERTHAN OP_IF <13> OP_INSPECTOUTPUTSCRIPTPUBKEY <3> <32> OP_SUBSTR <3> OP_PICK <3> OP_PICK OP_TAPTWEAK <1> <32> OP_SUBSTR OP_EQUALVERIFY <2> OP_ROLL <0> <7> OP_SUBSTR <0x0400000001b275> OP_EQUALVERIFY OP_ENDIF
//Inspect user-provided output 7 script and check script validity
<1> OP_SUB OP_DUP <0> OP_GREATERTHAN OP_IF <14> OP_INSPECTOUTPUTSCRIPTPUBKEY <3> <32> OP_SUBSTR <3> OP_PICK <3> OP_PICK OP_TAPTWEAK <1> <32> OP_SUBSTR OP_EQUALVERIFY <2> OP_ROLL <0> <7> OP_SUBSTR <0x0400000001b275> OP_EQUALVERIFY OP_ENDIF
//Inspect user-provided output 8 script and check script validity
<1> OP_SUB OP_DUP <0> OP_GREATERTHAN OP_IF <15> OP_INSPECTOUTPUTSCRIPTPUBKEY <3> <32> OP_SUBSTR <3> OP_PICK <3> OP_PICK OP_TAPTWEAK <1> <32> OP_SUBSTR OP_EQUALVERIFY <2> OP_ROLL <0> <7> OP_SUBSTR <0x0400000001b275> OP_EQUALVERIFY OP_ENDIF
//Inspect user-provided output 9 script and check script validity
<1> OP_SUB OP_DUP <0> OP_GREATERTHAN OP_IF <16> OP_INSPECTOUTPUTSCRIPTPUBKEY <3> <32> OP_SUBSTR <3> OP_PICK <3> OP_PICK OP_TAPTWEAK <1> <32> OP_SUBSTR OP_EQUALVERIFY <2> OP_ROLL <0> <7> OP_SUBSTR <0x0400000001b275> OP_EQUALVERIFY OP_ENDIF
//Inspect user-provided output 10 script and check script validity
<1> OP_SUB OP_DUP <0> OP_GREATERTHAN OP_IF <17> OP_INSPECTOUTPUTSCRIPTPUBKEY <3> <32> OP_SUBSTR <3> OP_PICK <3> OP_PICK OP_TAPTWEAK <1> <32> OP_SUBSTR OP_EQUALVERIFY <2> OP_ROLL <0> <7> OP_SUBSTR <0x0400000001b275> OP_EQUALVERIFY OP_ENDIF

Enforcing feeconomics to comply with BIP-125

Inside the Pool Transaction, all 6 covenants on the input side, and all user-provided inputs signal replaceability. This is so that within one minute, anyone can submit newer versions of “Pool Transaction” as long as paying a greater feerate and a greater absolute fee. Since “Commitment transaction” is of-type anyone-can-spend, aggregator nodes on the network can monitor the mempool, listen to “Commitment Transactions”, lexicographically order them, and include them in “Pool Transaction”. Commitment Transaction includes an OP_RETURN field for aggregator nodes to interpret how to spend “User commitment output” in “Pool transaction”.

The fourth rule of BIP-125 requires that the replacement transaction must also pay for its own bandwidth at or above the rate set by the node’s minimum relay fee setting. If “Pool Transaction” has remaining slots (when user-provided inputs are less than 10), the sum of total fees offered is divided by 10 and multiplied by the number of user-provided inputs — resulting in total transaction fees that must be paid where the remainder is paid to Liquidity providers. If “Pool Transaction” is fully reserved (when user-provided inputs hit 10), half of the total fees offered are paid as transaction fees, and the remaining half is paid to Liquidity providers. With the minimum relay fee of 0.1 satoshi/vByte in Liquid and an approximate 3,000 vByte size for a full “Pool Transaction”, each replacement version must offer ≈300 satoshis higher than the sum of the previous “Pool Transaction” version:

--Witness://Total fees offered is provided in witness
<total_fees_offered>
--Script:OP_INSPECTNUMINPUTS <16> OP_LESSTHAN //If Pool Transaction is not full
OP_IF
//Calculate total fees should be paid as fee
<2> OP_DIV64 OP_DUP OP_DUP <10> OP_DIV64 OP_INSPECTNUMINPUTS <6> OP_SUB OP_MUL64 OP_DUP OP_TOALTSTACK
//Calculate total fees should be paid as LP rewards
OP_SUB OP_ADD <4> OP_INSPECTOUTPUTVALUE <4> OP_INSPECTINPUTVALUE OP_SUB OP_EQUALVERIFY
//If Pool Transaction is full
OP_ELSE
//Make sure half total_fees_offered is paid as fee
<2> OP_DIV OP_DUP <7> OP_INSPECTOUTPUTVALUE OP_EQUALVERIFY
//Make sure remaining half total_fees_offered is paid as LP reward
<4> OP_INSPECTOUTPUTVALUE <4> OP_INSPECTINPUTVALUE OP_SUB OP_EQUALVERIFY

OP_ENDIF

Settlement Transaction

Each “Pool Transaction” output script (i.e user_output_5_tapscript) is an anyone-can-spend type tapscript that allows relayer nodes to spend it on “Settlement Transaction” as “User settlement input”. “Settlement Transaction” includes a “Fee provider input ” from “Commitment Transaction” to pay fees.

The first transaction (Commitment Transaction) can take from 0 to 1 minute to confirm, depending on when it’s broadcasted in a minute. The second transaction (Pool Transaction) takes 1 minute to confirm after its parent (Commitment Transaction) is confirmed. The third transaction (Settlement Transaction) takes 1 minute to confirm after its parent (Pool Transaction) is confirmed. However, it is not required to wait for “Settlement Transaction” as it can be spent zero-conf.

Adding all together, a typical Bitmatrix swap takes from 1 to 2 minutes to complete. Since the transactions are of type anyone-can-spend, it does not require interactivity during the process. A user can broadcast the first transaction and close the browser. Everything else is taken care of by aggregator nodes.

--

--