git / moonbite-core / doc/mweb/consensus.md · raw
# MWEB Consensus ## Prerequisites ### Notation * Let `G` and `H` be independent generators of the elliptic curve group * `H(α||β||γ)` represents BLAKE3 hash of concatenated serializations * Scalar multiplication denoted as `a⋅G` where `a` is scalar * Point addition/subtraction uses standard `+`/`-` operators### Cryptographic Assumptions* All elliptic curve operations use secp256k1 * BLAKE3 outputs are interpreted as scalars modulo the curve order when used in arithmetic### Glossary * **PMMR**: Pruned Merkle Mountain Range (data structure for storing UTXOs) * **TXO**: Transaction Output * **UTXO**: Unspent Transaction Output * **HogEx**: MWEB integration transaction ### Transaction Structure ##### Transaction * <code>x</code> = kernel offset (scalar) * <code>x'</code> = stealth offset (scalar) ##### Inputs * <code>F<sub>i</sub></code> = Input features (bitmask) * <code>0x01</code> = Additional input key * <code>0x02</code> = Extra Data (Indicating additional byte vector to be included in the signature message) * <code>O<sub>ID</sub></code> = ID of the output being spent * <code>C<sub>i</sub></code> = Input commitment (<code>C<sub>o</sub></code> of the output being spent) * <code>K<sub>i</sub></code> = Input stealth public key (if <code>0x01</code> is set in <code>F<sub>i</sub></code>)* <code>M<sub>i</sub></code> = Additional input message data (if <code>0x02</code> is set in <code>F<sub>i</sub></code>) * <code>σ</code> = Input signature ##### Kernels * <code>M</code> = Type, fee, etc. * <code>E</code> = Kernel excess * <code>E'</code> = (Optional) Stealth excess * <code>ψ</code> = Kernel signature ##### Outputs * <code>C<sub>o</sub></code> = Output commitment * <code>K<sub>s</sub></code> = Sender public key * <code>K<sub>o</sub></code> = Output public key * <code>K<sub>e</sub></code> = Key exchange public key * <code>t[0]</code> = View tag * <code>v'</code> = Encrypted value * <code>n'</code> = Encrypted nonce * <code>ρ</code> = Output signature ## RulesFor kernel-specific rules (feature bits, feature-gated fields, and kernel signature message layout), see [kernels.md](./kernels.md).#### Identifiers * The kernel ID (<code>K<sub>ID</sub></code>) is the BLAKE3 hash of the serialized kernel. * See [kernels.md](./kernels.md) for the kernel serialization format. * The output ID (<code>O<sub>ID</sub></code>) is the BLAKE3 hash of the serialized output. * <code>O<sub>ID</sub> = H(C<sub>o</sub>||H(O<sub>M</sub>)||H(π)||ρ)</code> --- #### Transaction Uniqueness* Output hashes (<code>O<sub>ID</sub></code>) in the UTXO set shall be unique at all times.* A block shall not contain 2 inputs that spend the same output ID (<code>O<sub>ID</sub></code>).* A block shall not be included when it contains an output ID (<code>O<sub>ID</sub></code>) which already exists in the UTXO set. * Block is still invalid even if it also contains an input with said output ID (<code>O<sub>ID</sub></code>).* A block shall not contain 2 outputs with the same ID (<code>O<sub>ID</sub></code>). * Block is still invalid even if it also contains an input with said output ID (<code>O<sub>ID</sub></code>).* A block *could* contain an input which spends an output created in the same block, provided the output ID (<code>O<sub>ID</sub></code>) was not already in the UTXO set. * To better support payment proofs, the outputs shall still be added to the TXO MMR, but shall be marked as spent. * A block shall not contain 2 kernels with the same ID (<code>K<sub>ID</sub></code>). --- #### Ordering* Inputs in a block shall be in ascending order by output ID (<code>O<sub>ID</sub></code>).* Outputs in a block shall be in ascending order by output ID (<code>O<sub>ID</sub></code>).* Kernels in a block shall be ordered by descending supply change (<code>v<sub>pegin</sub> - (v<sub>pegout</sub> + f)</code>), with ties broken by ascending kernel ID (<code>K<sub>ID</sub></code>).--- #### PMMRs * After building an MMR with all of the kernels from a block, the root shall match the header's kernel root. * The number of kernels in a block shall match the header's kernel size. * After adding all outputs from a block to the end of the output PMMR, the root shall match the header's output root. * After adding all outputs from a block to the end of the output PMMR, the size shall match the header's output size. --- #### UTXO LeafSet* A simple bitset shall be created and maintained to keep track of which TXOs in the output PMMR remain unspent.* The bit positions shall map 1-to-1 to to PMMR leaf indices. A `0` at that bit position means spent, whereas a `1` means unspent. * Ex: If byte 0 bit 2 is a `1`, that means TXO at leaf index `2` is unspent. If byte 2 bit 1 is a `0`, the TXO at leaf index `17` has been spent. etc.* The hash of the serialized UTXO set after applying the transactions in a block shall match the header's UTXO leafset hash.* Each input spent by a block must reference an existing unspent UTXO entry.* For each spent input, the input's commitment and output pubkey must exactly match the referenced UTXO.--- #### Signatures * Each kernel without a stealth excess (`E'`) shall have a valid signature (ψ) of <code>H(K<sub>M</sub>)</code> that verifies for the kernel's excess(`E`). * See [kernels.md](./kernels.md) for the signature message (<code>K<sub>M</sub></code>) serialization format * Each kernel with a stealth excess (`E'`) shall have a valid signature (ψ) of <code>H(K<sub>M</sub>)</code> for key <code>E<sub>agg</sub> = H(E||E')⋅E + E'</code>. * Each input shall have a valid signature (<code>σ</code>) of <code>I<sub>M</sub></code> for key <code>K<sub>sig</sub></code> * If an input stealth pubkey <code>K<sub>i</sub></code> is included (<code>0x01</code> set in <code>F<sub>i</sub></code>), <code>K<sub>sig</sub> = K<sub>i</sub> + H(K<sub>i</sub>||K<sub>o</sub>)⋅K<sub>o</sub></code>. Otherwise, <code>K<sub>sig</sub> = K<sub>o</sub></code>. * If extra data <code>M<sub>i</sub></code> is included (<code>0x02</code> set in <code>F<sub>i</sub></code>), <code>I<sub>M</sub> = H(F<sub>i</sub>||O<sub>ID</sub>||M<sub>i</sub>)</code>. Otherwise, <code>I<sub>M</sub> = H(F<sub>i</sub>||O<sub>ID</sub>)</code>. * Each output shall have a valid signature (<code>ρ</code>) of <code>(C<sub>o</sub>||O<sub>M</sub>||H(π))</code> for the output's sender pubkey (<code>K<sub>s</sub></code>). * `output_message` (<code>O<sub>M</sub></code>) is serialized as <code>O<sub>M</sub> = (K<sub>o</sub>||K<sub>e</sub>||t[0]||v'||n'||K<sub>s</sub>)</code> --- #### Bulletproofs * Each output shall be coupled with a bulletproof that proves the commitment is to a value in the range `[0, 2^64)`. * Each bulletproof (<code>π</code>) shall commit to the `output_message` using its `extra_commit` functionality. --- #### Kernel Sums * The sum of all output commitments (<code>C<sub>o</sub></code>) in the UTXO set at a given block height shall equal the sum of all kernel commitments (`E`) plus the `total_kernel_offset*G` (`x*G`) and the `expected_supply*H` (<code>v_total</code>) of the block. * <code>ΣC<sub>o</sub> = ΣE + (x⋅G) + (v_total⋅H)</code> where <code>v_total</code> is the total MWEB supply at the block height. --- #### Stealth Sums * The sum of all sender pubkeys (<code>K<sub>s</sub></code>) in a block and all input pubkeys (<code>K<sub>i</sub></code>) in the block must equal the sum of all stealth excesses (`E'`) plus `total_stealth_offset*G` (`x'*G`) plus the sum of all output pubkeys (<code>K<sub>o</sub></code>) being spent in the block. * <code>ΣK<sub>s</sub> + ΣK<sub>i</sub> = ΣE' + x'⋅G + ΣK<sub>o</sub></code> --- #### Pegging-In && Pegging-Out* Kernels may include an optional pegout, containing the `amount` & a `scriptPubKey` (serialized `CScript`) * The `scriptPubKey` for a pegout shall be non-empty. * Miners shall include a matching output in a block's `HogEx` transaction with the exact `amount` and `scriptPubKey` for each pegout in the block.* Kernels may include an optional pegin `amount` * Each pegin kernel must have a corresponding pegin output on the canonical LTC side where the value is <code>v<sub>pegin</sub></code>, witness version is 9, and witness program is the kernel ID (<code>K<sub>ID</sub></code>). * The extension block peg-in set must exactly match canonical pegins by `(kernel ID, amount)`. * Kernel IDs in the canonical block pegin set must be unique.* The total MWEB supply (<code>v_total</code>) shall increase by the sum of the block's pegins (<code>v<sub>pegin</sub></code>), and decrease by the sum of the block's pegouts (<code>v<sub>pegout</sub></code>) and the sum of all fees (`f`). * <code>v_total<sub>new</sub> = v_total<sub>prev</sub> + Σv<sub>pegin</sub> - (Σv<sub>pegout</sub> + Σf)</code>* Pegged-out coins require 6 blocks to mature before they can be spent.--- #### Block Weight* Outputs shall be counted as having a weight of 17 + 1 (if "standard fields" are included). * An additional 1 weight shall be added for every 42 bytes included in the `extra_data` field.* Kernels shall be counted as having a weight of either 2 (without stealth excess) or 3 (with stealth excess). * For each pegout script included, 1 weight shall be added per every 42 bytes of its `scriptPubKey`. * An additional 1 weight shall be added for every 42 bytes included in the `extra_data` field.* Extension blocks shall be capped at a maximum total weight of 200,000.* Extension blocks shall contain at most 50,000 inputs.* Inputs shall not contribute toward the block weight unless they contain `extra_data`, which will cost 1 weight per 42 bytes. #### HogEx* `HogEx` is a canonical transaction marked with the MWEB serialization flag and an empty MWEB transaction payload.* `HogEx` is only valid in blocks (never as a loose mempool transaction).* When MWEB is active: * A non-null extension block must be present. * The final transaction in the block must be marked as `HogEx`. * No earlier transaction in the block may be marked as `HogEx`.* When MWEB is not active, extension block data must be absent.* Transactions inside a block must not carry embedded MWEB transaction payloads (`HasMWEBTx() == false`), including `HogEx`.* `HogEx` must contain at least one output.* The first `HogEx` output (`vout[0]`) must be a HogAddr witness program: version `8`, 32-byte program. * That 32-byte program must equal the MWEB block hash committed by the extension block.* The extension block height must match the canonical block height.* Peg-in scripts (witness version `9`, 32-byte program) are not allowed in coinbase or `HogEx` outputs.* HogEx inputs must match block peg-ins exactly, in block/txout scan order: * For the first `HogEx` after activation, every `HogEx` input is a pegin input. * For later blocks, `HogEx.vin[0]` must spend the previous block's `HogEx` output `0`, and remaining `HogEx` inputs must be pegin inputs. * Missing or extra `HogEx` inputs are invalid.* `HogEx` outputs after the first (`vout[1..]`) are pegout outputs. * They must match the extension block pegouts by (`amount`, `scriptPubKey`) multiset equality.* Let <code>H<sub>prev</sub></code> be the previous `HogEx.vout[0]` amount (or `0` for first `HogEx`), and let <code>P</code> be this block's total pegins. * The `HogEx` fee must be <code>(H<sub>prev</sub> + P) - vout_total</code>, and this must equal the extension block total fee. * The new `HogEx.vout[0]` amount must equal <code>H<sub>prev</sub> + supply_change</code>, where <code>supply_change = P - (pegouts + fees)</code>.* Pegout maturity handling: * `HogEx.vout[0]` is not a pegout and is exempt from pegout maturity. * `HogEx.vout[1..]` are pegouts and require 6 confirmations before spending.* Current consensus does **not** globally forbid HogAddr-looking scripts outside `HogEx.vout[0]`; only the first output of `HogEx` is interpreted as the mandatory HogAddr commitment.