From f57563f69c0a9e915a1090b01e2b382cdaf987a0 Mon Sep 17 00:00:00 2001 From: Sara Drakeley Date: Sat, 5 Feb 2022 10:45:09 -0800 Subject: [PATCH 01/21] Confidential Token Ids --- text/0000-confidential-token-ids.md | 270 ++++++++++++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 text/0000-confidential-token-ids.md diff --git a/text/0000-confidential-token-ids.md b/text/0000-confidential-token-ids.md new file mode 100644 index 00000000..f8393766 --- /dev/null +++ b/text/0000-confidential-token-ids.md @@ -0,0 +1,270 @@ +- Feature Name: `confidential_token_ids` +- Start Date: 2022-02-05 +- MCIP PR: [mobilecoinfoundation/mcips#0000](https://github.com/mobilecoinfoundation/mcips/pull/0000) +- MobileCoin Epic: TBD + +# Summary +[summary]: #summary + +Enable new token types to be added to the protocol and transacted on chain without revealing or leaving a transparent record of the token type. + +# Motivation +[motivation]: #motivation + +The MobileCoin ledger protocol currently supports one token type, MOB. We would like to allow the addition of assets to the ledger without revealing which assets were involved in any transaction. The transaction confidentiality and integrity guarantees should be the same for the MobileCoin blockchain no matter how many token types are supported. Namely, the sender, the recipient, and the amount should remain private to all except the participants in the transaction, and no double spending or unauthorized minting should be possible. In addition, we now specify that the asset type should remain private to all except the participants in the transaction. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +A Transaction Output (TXO) has a TokenType enum field (`token_id`). Transactions with inputs and outputs, including a fee output, must be composed of a single TokenType. Some tokens may have additional functionality, such as minting and burning, which require verification of the TokenType as part of the authorization of the action. + +Each TokenType has an explicit minimum fee specified by node operators, and included in the attestation handshake, ensuring that all nodes in the network are configured with the same fee minimums and TokenType sets. New TokenTypes can be added only with unanimous agreement from all node operators. + +A transaction specifies the TokenType for the entire transaction, and includes a "Proof of Opening" that all `token_ids` in the inputs and outputs are the same. This prevents spending one TokenType as a member of another TokenType. The TransactionBuilder now takes a `token_id` as an argument in its initializer. The TokenType is set in the clear on the TxPrefix for a transaction, which is only accessed in the enclave, after the payload is delivered via an encrypted, attested channel. This is similar to the TxInputs, and thus has a similar threat model for confidentiality. See [Confidentiality Analysis](#confidentiality-analysis) for an assessment of risk. + +Clients must choose how to expose multiple asset types to their users. There may be clients who wish to only expose a subset of asset types, or clients who wish to provide balances and transactionality in all asset types. + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +### Masked Token ID Field in the TXO + +A `TokenType` is added which specifies a `u32 token_id`. The `token_id` for each TXO represents the Token Type of that TXO, with the first `TokenType` being `MOB = token_id(0)`. New TokenTypes increase the `token_id` monotonically. For example, the `TokenType` struct may be something like, + +``` +struct TokenType { + + \\\ Token Name, such as MOB + string token_name, + + \\\ Token ID to be represented confidentially in each TXO + u32 token_id, + + \\\ Curve base point, unique for each TokenType, from which the TXO's commitment is + \\\ generated with the `masked_amount` + RistrettoPoint H_i +} +``` + +The `masked_token_id` field is added to the [`Amount`](https://github.com/mobilecoinfoundation/mobilecoin/blob/master/transaction/core/src/amount/mod.rs#L28) in each TXO, and functions similar to `masked_value`. The protocol representation of the TXO uses Protobuf, which enables backward compatability in this way: if the field is not populated (Protobuf bytes field of length 0), the default `token_id` is 0. Otherwise, the field is a Protobuf bytes field of length 4. Please see the [Upgrade Plan](#upgrade-plan) for more discussion on rolling out this change with consideration for backward compatibility. + +- To compute the 4 byte `masked_token_id` from the `token_id`, we hash the TXO shared secret, with a prefix, XORed with the `token_id`, and take the little endian representation of those bytes. + + ``` + masked_token_id = (token_id ^ Blake2B(token_id_tag | shared_secret)).to_le() + ``` + +- To recover the `token_id` from the `masked_token_id`, we reverse the process (if the bytes field is length = 4, otherwise `token_id = 0`). + + ``` + unmasked_token_id = masked_token_id ^ Blake2b(token_id_tag | shared_secret) + ``` + +### Proof of Opening: Proof of Knowledge of Uniform Token ID in the Transaction + +We would like to be able to prove that all transaction inputs and outputs are using the same `token_ids`. We do this using a non-interactive, zero knowledge Proof of Opening that the sender has the capability to unwrap the sum of output commitments in the transaction, in conjunction with the homomorphic encryption property of [Pedersen Commitments](https://www.cs.cornell.edu/courses/cs754/2001fa/129.PDF), namely that addition on the commitments preserves the additive relationship of the pre-committed values, as long as they are using the same group generator. + +- To prove each TXO's commitment to the `amount` value, we use a Pedersen commitment, consisting of a group generator (`H_i`) raised to the value of the `amount`, with a blinding factor to prevent brute-forcing the full range of values (`2^64`). We compute the blinding factor as the blinding group generator raised to a hash of the shared secret for the TXO. + + ``` + blinding_factor = Blake2B(blinding_tag | shared_secret) + commitment = H_i * Scalar::from(amount) + G * blinding_factor + ``` + + - Note, the group generators for Token Types are computed using hashing to a curve so that they are "orthogonal" for computable adversaries, meaning there is no discernable, nontrivial linear relationship between base points. See our [transaction core group generators reference](https://github.com/mobilecoinfoundation/mobilecoin/blob/master/transaction/core/src/ring_signature/mod.rs#L29). + - Note also, the blinding factor base point does not need to be unique per TokenType, so we use the same blinding factor base point for all TokenTypes. + - Note finally, the Proof of Opening requires all commitments in a transaction were generated using the same group generator, base point `H_i`. If the commitments in input and/or output TXOs in a transaction are constructed with different group generators, and the sender is able to unwrap the sum as specified in the protocol below, it implies that the sender knows a nontrivial linear relationship among the `H_i`, which contradicts the assumption that the `H_i` are orthogonal. + +The non-interactive zero knowledge Proof of Opening protocol is the following: (See [Zero Knowledge Proofs and Commitment Schemes, Page 27](https://www.cs.purdue.edu/homes/ninghui/courses/555_Spring12/handouts/555_Spring12_topic23.pdf)) + + +1. The prover establishes a value, `d`, calculated by hashing their secret values from the commitment (the `token_id` and the `blinding_factor`). Implementation note, we may use [Merlin](https://docs.rs/merlin/latest/merlin/) rather than Blake2B. + + ``` + y = Blake2B(proof_tag_t | token_id) + s = Blake2B(proof_tag_b | blinding_factor) + d = G * y + H * s + ``` + +2. The non-interactive verifier chooses challenge, `e`, by hashing the commitment and the value `d` from the previous step. + + ``` + e = Blake2B(proof_tag_e | commitment | d) + ``` + +3. The prover computes the following, and includes these values in the Proof of Opening: + + ``` + u = y + e * token_id + v = s + e * blinding_factor + ``` + +4. The verifier of the Proof of Opening must verify: + + ``` + G * u + H * v = d + commitment * e + ``` + + - This property follows a similar property of the [Schnorr Proof of Knowledge protocol](https://en.wikipedia.org/wiki/Proof_of_knowledge#Schnorr_protocol) + + +This protocol is non-interactive because the prover anticipates the verifier's `e` value, so requires no interaction from the verifier. + +Thus, the Proof of Knowledge could be represented, for example, with: + +``` +struct ProofOfKnowledge { + + /// Prover value established from hashes of commitment and blinding factor secrets + CompressedRistretto d, + + /// Prover value from the token_id response to the verifier's commitment-derived challenge + CurveScalar u, + + /// Prover value from the blinding_factor response to the verifier's commitment-derived challenge + CurveScalar v, +} +``` + +- Implementation note: The proof of knowledge should be constructed at [this line](https://github.com/mobilecoinfoundation/mobilecoin/blob/a9db0bc7bcac9bac1c1232b194003d02bccaf283/transaction/core/src/ring_signature/rct_bulletproofs.rs#L304) in the sign function. + +Then, the Transaction Validator need perform the following validation check, after calculating the sum of the outputs (in order to verify the sum of the inputs matches the sum of the outputs, and thus that no value was created or destroyed), at [this line](https://github.com/mobilecoinfoundation/mobilecoin/blob/a9db0bc7bcac9bac1c1232b194003d02bccaf283/transaction/core/src/ring_signature/rct_bulletproofs.rs#L170): + +``` +verify(G, H, commitment, proof_of_knowledge) { + e = Blake2B(proof_tag_e | commitment | proof_of_knowledge.d) + proof_of_knowledge.d + (commitment * e) == G * proof_of_knowledge.u + H * proof_of_knowledge.v +} +``` + +### Fee Outputs + +Currently, the enclave aggregates the fees for multiple transactions in a block, in order to mint a single fee output for the block and conserve space. To support multiple confidential TokenTypes, when minting fee outputs, the enclave must create multiple fee outputs if multiple TokenTypes were used in the block. A consideration here is that we would like for an observer not to discern whether a block contains transactions of multiple asset types by statistically analyzing the number of outputs to derive information about the number of fee outputs. + +A simple proposal is that the enclave discontinue fee aggregation, so that the number of fee outputs scale linearly with the number of transactions in a block (not output TXOs). For example, a block with 3 transactions includes 3 fee outputs. Because each transaction can mint up to 16 output TXOs, there may be a variable number of outputs in the block. + +Note that the untrusted portion of the node software knows how many transactions are in each block, and this is not considered statistically relevant information with respect to confidentiality. In other words, the current fee aggregation behavior is not meant as a confidentiality measure, purely as a space-saving measure. + +### Fee Ratio and Transaction Sorting + +Currently the [WellFormedTxContext](https://github.com/mobilecoinfoundation/mobilecoin/blob/a092039a4a5cc7f5e3e16eeadd0b2bc3a12667ae/consensus/enclave/api/src/lib.rs#L50) contains the fee, which must be used by untrusted to [sort transactions](https://github.com/mobilecoinfoundation/mobilecoin/blob/a092039a4a5cc7f5e3e16eeadd0b2bc3a12667ae/consensus/enclave/api/src/lib.rs#L131) when [combining transactions](https://github.com/mobilecoinfoundation/mobilecoin/blob/a092039a4a5cc7f5e3e16eeadd0b2bc3a12667ae/consensus/service/src/tx_manager/untrusted_interfaces.rs#L40) for consideration by the enclave, prioritizing higher fees for inclusion in the block during periods of network congestion. In order to keep the `token_id` confidential, we must provide a mechanism for untrusted to accurately sort transactions before asking the enclave to do work on the transactions, without revealing the `token_id`, and at the same time, allowing for different asset types to express differing minimum fee requirements. + +To derive a sorting order without revealing the transaction type, the `fee` in the WellFormedTxContext becomes a `fee_ratio`, which is calculated based on the ratio between the minimum fees configured by the node operator on startup. For example, the network operators may have configured the following fees: + +``` +{ + asset_000_fee = 1, + asset_111_fee = 2, + asset_888_fee = 3, +} +``` + +The ratio between the fees for the `token_ids` is the following, using the least common multiple of the fees: + +`token_id` | fee | `fee_ratio` multiplier +--- | --- | --- +000 | 1 | 6 +111 | 2 | 3 +888 | 3 | 2 + + +Then, imagine that a block contains the following set of transactions with the given fees: + +``` +[ + transaction_1 {token_id: 000, fee: 1}, + transaction_2 {token_id: 000, fee: 1.5}, + transaction_3 {token_id: 111, fee: 2}, + transaction_4 {token_id: 888, fee: 3}, + transaction_5 {token_id: 888, fee: 4} +] +``` + +The sort order would be: + +`transaction_id` | `token_id` | `fee_ratio` multiplier | original fee | `fee_ratio` result +--- | --- | --- | --- | --- +transaction_2 | 000 | 6 | 1.5 | 9 +transaction_5 | 888 | 2 | 4 | 8 +transaction_1 | 000 | 6 | 1 | 6 +transaction_3 | 111 | 3 | 2 | 6 +transaction_4 | 888 | 2 | 3 | 6 + +Note how the two transactions paying more than their minimum fee are sorted to the top, in proportion to how much they are paying above the minimum fee, while all minimum fee transactions have an equal chance of beeing sorted because their `fee_ratio` is equal. + +### Performance Impact: Size & Space Analysis + +- Each TXO has 6 new bytes allocated in the Amount object for the `masked_token_id` (4 bytes for the value, 2 bytes for Protobuf overhead) +- The RingCT bulletproof signature increases by at least 96 bytes to contain the Proof of Opening (plus Protobuf overhead). +- Each block now contains a number of fee outputs scaling linearly with the number of transactions in a block, as opposed to a single fee output aggregated from multiple transactions in a block. + - In practice, most clients currently output 2 TXOs, one for the recipient and one for change, unless performing a `split_txo` operation. Output growth on chain is currently dominated by the number of outputs in a transaction, and since blocks typically clear fast enough that there is only one transaction in a block for which to aggregate the fee, this should not have a deleterious effect on the space capacity of the chain, especially given that up to [16 outputs are permitted for every transaction](https://github.com/mobilecoinfoundation/mobilecoin/blob/e74d4c821cb213290975d6d5a9390778661cebfe/transaction/core/src/constants.rs#L17). + +### Confidentiality Analysis + +The [TxPrefix](https://github.com/mobilecoinfoundation/mobilecoin/blob/a9db0bc7bcac9bac1c1232b194003d02bccaf283/transaction/core/src/tx.rs#L149) contains the `token_id` and the `fee` in the clear. We, and any users of the protocol, need to clearly understand the footprint of this data, and the ways in which an attacker may try to discern the `token_id` of TXOs. + +The [TxPrefix](https://github.com/mobilecoinfoundation/mobilecoin/blob/edbc0162cbd647b6605fe817a8753508ca4515a2/transaction/core/src/tx.rs#L149) is included in the [Tx](https://github.com/mobilecoinfoundation/mobilecoin/blob/edbc0162cbd647b6605fe817a8753508ca4515a2/transaction/core/src/tx.rs#L103) that is delivered directly to the enclave through an attested channel at the [client\_tx_propose](https://github.com/mobilecoinfoundation/mobilecoin/blob/2d5190e60f6820cebcd585c19d16cecf9ba4d89b/consensus/enclave/impl/src/lib.rs#L299) endpoint. By attested, this means that the client would not deliver this payload unless the enclave's signed measurement matched the client's expectations. + +The [TxManager](https://github.com/mobilecoinfoundation/mobilecoin/blob/2d5190e60f6820cebcd585c19d16cecf9ba4d89b/consensus/service/src/tx_manager/mod.rs) verifies that the Tx is "well-formed" using a 2-step process. First, untrusted performs a [`well_formed_check`](https://github.com/mobilecoinfoundation/mobilecoin/blob/2d5190e60f6820cebcd585c19d16cecf9ba4d89b/consensus/service/src/validators.rs#L52:8) on the Proofs of Memberships, using a [TxContext](https://github.com/mobilecoinfoundation/mobilecoin/blob/2d5190e60f6820cebcd585c19d16cecf9ba4d89b/consensus/enclave/api/src/lib.rs#L183:12) whose TokenID is contained in the [LocallyEncryptedTx](https://github.com/mobilecoinfoundation/mobilecoin/blob/2d5190e60f6820cebcd585c19d16cecf9ba4d89b/consensus/enclave/api/src/lib.rs#L37:12) constructed by the enclave, and is therefore not in the clear to untrusted. Then the enclave [verifies that the Tx is "well-formed"](https://github.com/mobilecoinfoundation/mobilecoin/blob/cfa51d26ae943a9055698bb209c2fe06fa7a7cac/consensus/enclave/impl/src/lib.rs#L338), and outputs the [WellFormedEncryptedTx](https://github.com/mobilecoinfoundation/mobilecoin/blob/a092039a4a5cc7f5e3e16eeadd0b2bc3a12667ae/consensus/enclave/api/src/lib.rs#L46) and [WellFormedTxContext](https://github.com/mobilecoinfoundation/mobilecoin/blob/a092039a4a5cc7f5e3e16eeadd0b2bc3a12667ae/consensus/enclave/api/src/lib.rs#L50) for untrusted to manage as consensus rounds progress. Note that in this proposal, the WellFormedTxContext no longer contains the fee in the clear, but rather the `fee_ratio` as described in [Fee Ratio and Transaction Sorting](#fee-ratio-and-transaction-sorting). + +Untrusted proceeds to [sort the WellFormedTxContext](https://github.com/mobilecoinfoundation/mobilecoin/blob/a092039a4a5cc7f5e3e16eeadd0b2bc3a12667ae/consensus/enclave/api/src/lib.rs#L131) by `fee_ratio` as part of the [nominate phase](https://github.com/mobilecoinfoundation/mobilecoin/blob/cfa51d26ae943a9055698bb209c2fe06fa7a7cac/consensus/scp/src/slot.rs#L613). + +Now that the Tx is validated as "well-formed," sorted and combined for a ballot with other transactions, and is a candidate for inclusion in the consensus slot, the WellFormedEncryptedTx is [broadcast](https://github.com/mobilecoinfoundation/mobilecoin/blob/2f90154a445c769594dfad881463a2d4a003d7d6/peers/src/traits.rs#L28) to the node's attested peers as required by the consensus protocol, using the [`peer_tx_propose`](https://github.com/mobilecoinfoundation/mobilecoin/blob/2d5190e60f6820cebcd585c19d16cecf9ba4d89b/consensus/enclave/impl/src/lib.rs#L328) attested endpoint. + +The TxManager in untrusted for each node maintains a cache of encrypted txs and tx contexts that its enclave has found to be well-formed. Once the consensus protocol is complete, the untrusted TxManager calls [form_block]() which prompts the [enclave to `form_block`](https://github.com/mobilecoinfoundation/mobilecoin/blob/cfa51d26ae943a9055698bb209c2fe06fa7a7cac/consensus/enclave/impl/src/lib.rs#L421) using the full transaction contents. + +Because untrusted never sees the `fee` or `token_id` in the clear, the attacker would need to sustain an enclave exploit, such as a side channel attack, that would allow them to access data while the enclave is processing to discern the TokenType of a transaction. With this information, the attacker could, for the duration of the exploit, determine which asset type each transaction was composed of, and could also construct a transaction graph for statistical analysis of the inputs, which are also only visible in the TxPrefix, but not persisted to the chain. Once the integrity of SGX was restored, the attacker would no longer be able to obtain information about the transaction inputs or TokenType, and forward secrecy is preserved. Further, by being thoughtful about the memory access of the `token_id`, and using constant time operations, we can mitigate the data leakage even in the event of a side channel exploit. + +### Upgrade Plan + +As this is a substantial change to the ledger format and transaction protocol, we need to be thoughtful about what rollout looks like for this change to land with minimal impact on existing clients and maximum backward compatibility. + +The rollout proposal is the following: + +1. The enclaves are released containing the validation change to support Proof of Opening verification and the `masked_token_id` field in the ledger format for TXOs, starting a 90 day grace period timer. +2. Clients update to: + - at a minimum: + - construct transactions with the Proof of Opening and the `masked_token_id` in output TXOs, not allowing any new outputs to use the default empty bytes array for the `masked_token_id` field. + - default to `token_id = 0` when processing TXOs without a `masked_token_id` field + - obtain the `minimum_fee` for the default token (MOB) + - optionally expose multiple asset types by: + - obtain the `minimum fee` for all assets + - support constructing transactions with arbitrary `token_id` + - display balance for some set of `token_ids` +3. The consensus validator nodes are deployed and allow for both new style and old style transactions to be constructed, validated, and externalized to the ledger for some grace period. However, no new outputs can be spent in old-style transactions, and new outputs must have an explicit `token_id` even if it's an old-style transaction +4. After some event, such as a particularly specified minting transaction for a new asset type, the validators no longer allow new outputs to be spent in old-style transactions, and all new transactions must contain the Proof of Opening. +5. From this time forward, new asset types may be minted without an enclave upgrade. + +# Drawbacks +[drawbacks]: #drawbacks + +This is a substantial change to the protocol, and a breaking change for clients. Other drawbacks include: + +- Increasing complexity of TXOs and transaction construction & validation +- Increasing the transaction validation footprint in the enclave to include Proof of Opening +- Increasing the amount of data on the ledger +- `masked_token_id` bytes must be plumbed everywhere, in Fog and clients, but no more serious changes are needed in Fog. Note that if the TokenType was not confidential, Fog Ledger would need a much larger change in order to properly select rings + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +- We could use `u16` for the `token_ids` rather than `u32` to save space, but we would like to anticipate the possibility of more than 65,000 asset types. +- We could use Merlin rather than Blake2B +- We could use non-confidential token types, but this has two downsides: it bifurcates the ledger into two sets which could be statistically analyzed for `token_id`, and it requires much more logic around post processing the ledger for fog. With the proposal here, fog remains oblivious to `token_id` and need not complicate how it selects rings, for example. + +# Prior art +[prior-art]: #prior-art + +- This proposal contains a non-novel commitment scheme as specified in [Zero Knowledge Proofs and Commitment Schemes, Page 27](https://www.cs.purdue.edu/homes/ninghui/courses/555_Spring12/handouts/555_Spring12_topic23.pdf) and uses properties of [Pedersen Commitments](https://www.cs.cornell.edu/courses/cs754/2001fa/129.PDF) for the Proof of Opening +- [Andrew Poelstra describes Confidential Assets](https://blog.blockstream.com/en-blockstream-releases-elements-confidential-assets/) for Blockstream, along with [this whitepaper](https://blockstream.com/bitcoin17-final41.pdf) + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +No unresolved questions at this time. + +# Future possibilities +[future-possibilities]: #future-possibilities + +No future possibilities at this time. + From d40e45370f05a6b5556b2117b9bc392ee24d74a8 Mon Sep 17 00:00:00 2001 From: Sara Drakeley Date: Sat, 5 Feb 2022 14:20:18 -0800 Subject: [PATCH 02/21] Update PR number --- text/0000-confidential-token-ids.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-confidential-token-ids.md b/text/0000-confidential-token-ids.md index f8393766..9bbc05d3 100644 --- a/text/0000-confidential-token-ids.md +++ b/text/0000-confidential-token-ids.md @@ -1,6 +1,6 @@ - Feature Name: `confidential_token_ids` - Start Date: 2022-02-05 -- MCIP PR: [mobilecoinfoundation/mcips#0000](https://github.com/mobilecoinfoundation/mcips/pull/0000) +- MCIP PR: [mobilecoinfoundation/mcips#0025](https://github.com/mobilecoinfoundation/mcips/pull/0025) - MobileCoin Epic: TBD # Summary From 4f71d2ab1156868b4e483bc2c12ed7952728380d Mon Sep 17 00:00:00 2001 From: Sara Drakeley Date: Sat, 5 Feb 2022 14:23:38 -0800 Subject: [PATCH 03/21] Clarity --- text/0000-confidential-token-ids.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-confidential-token-ids.md b/text/0000-confidential-token-ids.md index 9bbc05d3..6d1986fc 100644 --- a/text/0000-confidential-token-ids.md +++ b/text/0000-confidential-token-ids.md @@ -16,7 +16,7 @@ The MobileCoin ledger protocol currently supports one token type, MOB. We would # Guide-level explanation [guide-level-explanation]: #guide-level-explanation -A Transaction Output (TXO) has a TokenType enum field (`token_id`). Transactions with inputs and outputs, including a fee output, must be composed of a single TokenType. Some tokens may have additional functionality, such as minting and burning, which require verification of the TokenType as part of the authorization of the action. +A Transaction Output (TXO) has a confidential TokenType enum field (`masked_token_id`). Transactions with inputs and outputs, including a fee output, must be composed of a single TokenType. Some tokens may have additional functionality, such as minting and burning, which require verification of the TokenType as part of the authorization of the action. Each TokenType has an explicit minimum fee specified by node operators, and included in the attestation handshake, ensuring that all nodes in the network are configured with the same fee minimums and TokenType sets. New TokenTypes can be added only with unanimous agreement from all node operators. From c8f9ff65f468dfbe3f04a9311300fff4b472e650 Mon Sep 17 00:00:00 2001 From: Sara Drakeley Date: Sat, 5 Feb 2022 14:26:20 -0800 Subject: [PATCH 04/21] Update commitment to use value of amount (not masked) --- text/0000-confidential-token-ids.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-confidential-token-ids.md b/text/0000-confidential-token-ids.md index 6d1986fc..5adb00ed 100644 --- a/text/0000-confidential-token-ids.md +++ b/text/0000-confidential-token-ids.md @@ -41,7 +41,7 @@ struct TokenType { u32 token_id, \\\ Curve base point, unique for each TokenType, from which the TXO's commitment is - \\\ generated with the `masked_amount` + \\\ generated with the value of the `amount` in the TXO RistrettoPoint H_i } ``` From c53b301fda6cf8310cdbf75c0648102d0fbf4031 Mon Sep 17 00:00:00 2001 From: Sara Drakeley Date: Sat, 5 Feb 2022 14:45:47 -0800 Subject: [PATCH 05/21] Breaks ledger into multiple sets correction --- text/0000-confidential-token-ids.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-confidential-token-ids.md b/text/0000-confidential-token-ids.md index 5adb00ed..4afaed80 100644 --- a/text/0000-confidential-token-ids.md +++ b/text/0000-confidential-token-ids.md @@ -250,7 +250,7 @@ This is a substantial change to the protocol, and a breaking change for clients. - We could use `u16` for the `token_ids` rather than `u32` to save space, but we would like to anticipate the possibility of more than 65,000 asset types. - We could use Merlin rather than Blake2B -- We could use non-confidential token types, but this has two downsides: it bifurcates the ledger into two sets which could be statistically analyzed for `token_id`, and it requires much more logic around post processing the ledger for fog. With the proposal here, fog remains oblivious to `token_id` and need not complicate how it selects rings, for example. +- We could use non-confidential token types, but this has two downsides: it breaks the ledger into multiple sets which could be statistically analyzed for `token_id`, and it requires much more logic around post processing the ledger for fog. With the proposal here, fog remains oblivious to `token_id` and need not complicate how it selects rings, for example. # Prior art [prior-art]: #prior-art From 2bbc86a1cad8acee1475473d9abdf6f7d1e6c30f Mon Sep 17 00:00:00 2001 From: Sara Drakeley Date: Mon, 14 Mar 2022 21:35:44 -0700 Subject: [PATCH 06/21] First round addressing notes and conforming to impl --- text/0000-confidential-token-ids.md | 48 +++++++++++++++++------------ 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/text/0000-confidential-token-ids.md b/text/0000-confidential-token-ids.md index 4afaed80..7a27444a 100644 --- a/text/0000-confidential-token-ids.md +++ b/text/0000-confidential-token-ids.md @@ -16,7 +16,7 @@ The MobileCoin ledger protocol currently supports one token type, MOB. We would # Guide-level explanation [guide-level-explanation]: #guide-level-explanation -A Transaction Output (TXO) has a confidential TokenType enum field (`masked_token_id`). Transactions with inputs and outputs, including a fee output, must be composed of a single TokenType. Some tokens may have additional functionality, such as minting and burning, which require verification of the TokenType as part of the authorization of the action. +A Transaction Output (TXO) has a confidential TokenType bytes field (`masked_token_id`). Transactions with inputs and outputs, including a fee output, must be composed of a single TokenType. Some tokens may have additional functionality, such as minting and burning, which require verification of the TokenType as part of the authorization of the action. Each TokenType has an explicit minimum fee specified by node operators, and included in the attestation handshake, ensuring that all nodes in the network are configured with the same fee minimums and TokenType sets. New TokenTypes can be added only with unanimous agreement from all node operators. @@ -29,7 +29,7 @@ Clients must choose how to expose multiple asset types to their users. There may ### Masked Token ID Field in the TXO -A `TokenType` is added which specifies a `u32 token_id`. The `token_id` for each TXO represents the Token Type of that TXO, with the first `TokenType` being `MOB = token_id(0)`. New TokenTypes increase the `token_id` monotonically. For example, the `TokenType` struct may be something like, +A `TokenType` is added which specifies a `u32 token_id`. We maintain a list of `KnownTokenId` in both the external proto definition, as well as in the transaction core crate. The `token_id` for each TXO represents the Token Type of that TXO, with the first `TokenType` being `MOB = token_id(0)`. New TokenTypes increase the `token_id` monotonically. For example, the `TokenType` struct may be something like, ``` struct TokenType { @@ -46,7 +46,7 @@ struct TokenType { } ``` -The `masked_token_id` field is added to the [`Amount`](https://github.com/mobilecoinfoundation/mobilecoin/blob/master/transaction/core/src/amount/mod.rs#L28) in each TXO, and functions similar to `masked_value`. The protocol representation of the TXO uses Protobuf, which enables backward compatability in this way: if the field is not populated (Protobuf bytes field of length 0), the default `token_id` is 0. Otherwise, the field is a Protobuf bytes field of length 4. Please see the [Upgrade Plan](#upgrade-plan) for more discussion on rolling out this change with consideration for backward compatibility. +The `masked_token_id` field is added to the [`Amount`](https://github.com/mobilecoinfoundation/mobilecoin/blob/cb10d41d404c552cab19de0163992923a878ed5e/transaction/core/src/amount/mod.rs#L46) in each TXO, and functions similar to `masked_value`. The protocol representation of the TXO uses Protobuf, which enables backward compatability in this way: if the field is not populated (Protobuf bytes field of length 0), the default `token_id` is 0. Otherwise, the field is a Protobuf bytes field of length 4. Please see the [Upgrade Plan](#upgrade-plan) for more discussion on rolling out this change with consideration for backward compatibility. - To compute the 4 byte `masked_token_id` from the `token_id`, we hash the TXO shared secret, with a prefix, XORed with the `token_id`, and take the little endian representation of those bytes. @@ -60,9 +60,31 @@ The `masked_token_id` field is added to the [`Amount`](https://github.com/mobile unmasked_token_id = masked_token_id ^ Blake2b(token_id_tag | shared_secret) ``` -### Proof of Opening: Proof of Knowledge of Uniform Token ID in the Transaction +### Amount -We would like to be able to prove that all transaction inputs and outputs are using the same `token_ids`. We do this using a non-interactive, zero knowledge Proof of Opening that the sender has the capability to unwrap the sum of output commitments in the transaction, in conjunction with the homomorphic encryption property of [Pedersen Commitments](https://www.cs.cornell.edu/courses/cs754/2001fa/129.PDF), namely that addition on the commitments preserves the additive relationship of the pre-committed values, as long as they are using the same group generator. +Previously, an `amount` of picomob in a TxOut is represented by a Pedersen commitment to that number, as well as a `masked_value`, which is created by XORing the number of picomob with a pseudorandom mask (derived by hashing a shared secret). The purpose of the `masked_value` is that when the recipient receives a TxOut, they can unmask this value, then reconstruct the Pedersen commitment from the number, to confirm successful recovery of the value. + +After this change: + +A `masked_token_id` is added, which, similarly to `masked_value`, provides a way for the recipient to learn the `token_id` (and then subsequently confirm it). The Pedersen commitment to the value now is made using a basepoint `H_i` which depends on the `token_id`. (The basepoint for the blinding factor is the same as before regardless of the `token_id`.) + +Thus, the recipient can attempt to scan a TxOut by first constructing the shared secret, then unmasking the `masked_amount` and `masked_token_id`, and then reconstructing the Pedersen commitment. If this reconstruction is successful, then the recipient knows the `amount` and the `token_id`, similarly as before the support for a confidential`token_id`. + +### Pedersen Generators + +The requirements for the Pedersen generators are: + +* The generator used for MOB is the same as before, so `H_0` is a known value. +* The set of all generators used for token ids should be cryptographically "orthogonal", meaning that, it should be intractable for anyone to find a linear relationship between them. +* The function mapping a `token_id`, `i`, to the generator `H_i` must be implemented in constant-time. + +The established value for `H_0` in mobilecoin is to take the `RISTRETTO_BASEPOINT` bytes, hash them using Blake2b, and then hash this to the ristretto curve using the elligator map exposed by curve25519-dalek. + +We propose to compute `H_i` by converting `i` to little endian bytes, and XOR'ing these bytes over the first four bytes of the `RISTRETTO_BASEPOINT` bytes before hashing them with Blake2b, because this is a simple way to meet these requirements. + +### Proof that a Uniform Token ID is in the Transaction + +We would like to be able to prove that all transaction inputs and outputs are using the same `token_ids`. This is implied by the homomorphic encryption property of [Pedersen Commitments](https://www.cs.cornell.edu/courses/cs754/2001fa/129.PDF), namely that addition on the commitments preserves the additive relationship of the pre-committed values, as long as they are using the same group generator. - To prove each TXO's commitment to the `amount` value, we use a Pedersen commitment, consisting of a group generator (`H_i`) raised to the value of the `amount`, with a blinding factor to prevent brute-forcing the full range of values (`2^64`). We compute the blinding factor as the blinding group generator raised to a hash of the shared secret for the TXO. @@ -219,21 +241,7 @@ Because untrusted never sees the `fee` or `token_id` in the clear, the attacker As this is a substantial change to the ledger format and transaction protocol, we need to be thoughtful about what rollout looks like for this change to land with minimal impact on existing clients and maximum backward compatibility. -The rollout proposal is the following: - -1. The enclaves are released containing the validation change to support Proof of Opening verification and the `masked_token_id` field in the ledger format for TXOs, starting a 90 day grace period timer. -2. Clients update to: - - at a minimum: - - construct transactions with the Proof of Opening and the `masked_token_id` in output TXOs, not allowing any new outputs to use the default empty bytes array for the `masked_token_id` field. - - default to `token_id = 0` when processing TXOs without a `masked_token_id` field - - obtain the `minimum_fee` for the default token (MOB) - - optionally expose multiple asset types by: - - obtain the `minimum fee` for all assets - - support constructing transactions with arbitrary `token_id` - - display balance for some set of `token_ids` -3. The consensus validator nodes are deployed and allow for both new style and old style transactions to be constructed, validated, and externalized to the ledger for some grace period. However, no new outputs can be spent in old-style transactions, and new outputs must have an explicit `token_id` even if it's an old-style transaction -4. After some event, such as a particularly specified minting transaction for a new asset type, the validators no longer allow new outputs to be spent in old-style transactions, and all new transactions must contain the Proof of Opening. -5. From this time forward, new asset types may be minted without an enclave upgrade. +The upgrade plan is described in full in [MCIP #26](https://github.com/mobilecoinfoundation/mcips/blob/main/text/0026-block-version-based-protocol-evolution.md). # Drawbacks [drawbacks]: #drawbacks From 69cc93780cfbff8edb08286723387377847b24a5 Mon Sep 17 00:00:00 2001 From: Sara Drakeley Date: Mon, 14 Mar 2022 22:03:15 -0700 Subject: [PATCH 07/21] Updating to match implementation --- text/0000-confidential-token-ids.md | 77 +++++++++++++++-------------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/text/0000-confidential-token-ids.md b/text/0000-confidential-token-ids.md index 7a27444a..e2a26906 100644 --- a/text/0000-confidential-token-ids.md +++ b/text/0000-confidential-token-ids.md @@ -16,7 +16,7 @@ The MobileCoin ledger protocol currently supports one token type, MOB. We would # Guide-level explanation [guide-level-explanation]: #guide-level-explanation -A Transaction Output (TXO) has a confidential TokenType bytes field (`masked_token_id`). Transactions with inputs and outputs, including a fee output, must be composed of a single TokenType. Some tokens may have additional functionality, such as minting and burning, which require verification of the TokenType as part of the authorization of the action. +A Transaction Output (TxOut) has a confidential TokenType bytes field (`masked_token_id`). Transactions with inputs and outputs, including a fee output, must be composed of a single TokenType. Some tokens may have additional functionality, such as minting and burning, which require verification of the TokenType as part of the authorization of the action. Each TokenType has an explicit minimum fee specified by node operators, and included in the attestation handshake, ensuring that all nodes in the network are configured with the same fee minimums and TokenType sets. New TokenTypes can be added only with unanimous agreement from all node operators. @@ -27,39 +27,19 @@ Clients must choose how to expose multiple asset types to their users. There may # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -### Masked Token ID Field in the TXO +### Masked Token ID Field in the TxOut -A `TokenType` is added which specifies a `u32 token_id`. We maintain a list of `KnownTokenId` in both the external proto definition, as well as in the transaction core crate. The `token_id` for each TXO represents the Token Type of that TXO, with the first `TokenType` being `MOB = token_id(0)`. New TokenTypes increase the `token_id` monotonically. For example, the `TokenType` struct may be something like, +For each token type that is added, we specify a `u32 token_id`. We maintain a list of `KnownTokenIds` in both the external proto definition, as well as in the transaction core crate. The first `TokenType` is `MOB = token_id(0)`, with the assumption that an unspecified `token_id` defaults to 0, for backward cmopatibility. New `token_ids` increase monotonically. For example, the `KnownTokenId` enum in the external proto may be something like, ``` -struct TokenType { +enum KnownTokenId { + + \\\ Token Name = token_id + MOB = 0; - \\\ Token Name, such as MOB - string token_name, - - \\\ Token ID to be represented confidentially in each TXO - u32 token_id, - - \\\ Curve base point, unique for each TokenType, from which the TXO's commitment is - \\\ generated with the value of the `amount` in the TXO - RistrettoPoint H_i } ``` -The `masked_token_id` field is added to the [`Amount`](https://github.com/mobilecoinfoundation/mobilecoin/blob/cb10d41d404c552cab19de0163992923a878ed5e/transaction/core/src/amount/mod.rs#L46) in each TXO, and functions similar to `masked_value`. The protocol representation of the TXO uses Protobuf, which enables backward compatability in this way: if the field is not populated (Protobuf bytes field of length 0), the default `token_id` is 0. Otherwise, the field is a Protobuf bytes field of length 4. Please see the [Upgrade Plan](#upgrade-plan) for more discussion on rolling out this change with consideration for backward compatibility. - -- To compute the 4 byte `masked_token_id` from the `token_id`, we hash the TXO shared secret, with a prefix, XORed with the `token_id`, and take the little endian representation of those bytes. - - ``` - masked_token_id = (token_id ^ Blake2B(token_id_tag | shared_secret)).to_le() - ``` - -- To recover the `token_id` from the `masked_token_id`, we reverse the process (if the bytes field is length = 4, otherwise `token_id = 0`). - - ``` - unmasked_token_id = masked_token_id ^ Blake2b(token_id_tag | shared_secret) - ``` - ### Amount Previously, an `amount` of picomob in a TxOut is represented by a Pedersen commitment to that number, as well as a `masked_value`, which is created by XORing the number of picomob with a pseudorandom mask (derived by hashing a shared secret). The purpose of the `masked_value` is that when the recipient receives a TxOut, they can unmask this value, then reconstruct the Pedersen commitment from the number, to confirm successful recovery of the value. @@ -70,23 +50,48 @@ A `masked_token_id` is added, which, similarly to `masked_value`, provides a way Thus, the recipient can attempt to scan a TxOut by first constructing the shared secret, then unmasking the `masked_amount` and `masked_token_id`, and then reconstructing the Pedersen commitment. If this reconstruction is successful, then the recipient knows the `amount` and the `token_id`, similarly as before the support for a confidential`token_id`. +The `masked_token_id` field is added to the [`Amount`](https://github.com/mobilecoinfoundation/mobilecoin/blob/cb10d41d404c552cab19de0163992923a878ed5e/transaction/core/src/amount/mod.rs#L46) in each TxOut, and functions similar to `masked_value`. The protocol representation of the TxOut uses Protobuf, which enables backward compatability in this way: if the field is not populated (Protobuf bytes field of length 0), the default `token_id` is 0. Otherwise, the field is a Protobuf bytes field of length 4. Please see the [Upgrade Plan](#upgrade-plan) for more discussion on rolling out this change with consideration for backward compatibility. + +* To compute the 4 byte `masked_token_id` from the `token_id`, we hash the TxOut shared secret, with a prefix, XORed with the `token_id`, and take the little endian representation of those bytes. + + ``` + masked_token_id = (token_id ^ Blake2B(AMOUNT_TOKEN_ID_DOMAIN_TAG | shared_secret)).to_le() + ``` + +* To recover the `token_id` from the `masked_token_id`, we reverse the process (if the bytes field is length = 4, otherwise `token_id = 0`). + + ``` + unmasked_token_id = masked_token_id ^ Blake2b(AMOUNT_TOKEN_ID_DOMAIN_TAG | shared_secret) + ``` + ### Pedersen Generators +Previously, we used the same curve base point for all Pedersen generators to Amounts. After this change, we will need a curve base point, unique for each `token_id`, from which the TxOut's commitment is generated with the value of the `token_id` in the TxOut. + The requirements for the Pedersen generators are: * The generator used for MOB is the same as before, so `H_0` is a known value. + * The set of all generators used for token ids should be cryptographically "orthogonal", meaning that, it should be intractable for anyone to find a linear relationship between them. + * The function mapping a `token_id`, `i`, to the generator `H_i` must be implemented in constant-time. The established value for `H_0` in mobilecoin is to take the `RISTRETTO_BASEPOINT` bytes, hash them using Blake2b, and then hash this to the ristretto curve using the elligator map exposed by curve25519-dalek. We propose to compute `H_i` by converting `i` to little endian bytes, and XOR'ing these bytes over the first four bytes of the `RISTRETTO_BASEPOINT` bytes before hashing them with Blake2b, because this is a simple way to meet these requirements. -### Proof that a Uniform Token ID is in the Transaction +For example, an implementation to obtain a generator for `token_id = i` could look like: + + ``` + B = RistrettoPoint::from_hash(Blake2b(HASH_TO_POINT_DOMAIN_TAG | RISTRETTO_BASEPOINT ^ token_id)) + B_blinding = RISTRETTO_BASEPOINT + ``` + +After this change, `range_proofs` and `ring_signature/mlsags` will be relative to a Pedersen generator, and it is not possible to construct a range proof relative to another generator if those generators are orthogonal. Thus, it is guaranteed that all transaction inputs and outputs are using the same `token_ids`. This is implied by the homomorphic encryption property of [Pedersen Commitments](https://www.cs.cornell.edu/courses/cs754/2001fa/129.PDF), namely that addition on the commitments preserves the additive relationship of the pre-committed values, as long as they are using the same group generator. [TODO: Why?] -We would like to be able to prove that all transaction inputs and outputs are using the same `token_ids`. This is implied by the homomorphic encryption property of [Pedersen Commitments](https://www.cs.cornell.edu/courses/cs754/2001fa/129.PDF), namely that addition on the commitments preserves the additive relationship of the pre-committed values, as long as they are using the same group generator. +### Validation -- To prove each TXO's commitment to the `amount` value, we use a Pedersen commitment, consisting of a group generator (`H_i`) raised to the value of the `amount`, with a blinding factor to prevent brute-forcing the full range of values (`2^64`). We compute the blinding factor as the blinding group generator raised to a hash of the shared secret for the TXO. +To prove each TxOut's commitment to the `amount` value, we use a Pedersen commitment, consisting of a group generator (`H_i`) raised to the value of the `amount`, with a blinding factor to prevent brute-forcing the full range of values (`2^64`). We compute the blinding factor as the blinding group generator raised to a hash of the shared secret for the TxOut. ``` blinding_factor = Blake2B(blinding_tag | shared_secret) @@ -95,7 +100,7 @@ We would like to be able to prove that all transaction inputs and outputs are us - Note, the group generators for Token Types are computed using hashing to a curve so that they are "orthogonal" for computable adversaries, meaning there is no discernable, nontrivial linear relationship between base points. See our [transaction core group generators reference](https://github.com/mobilecoinfoundation/mobilecoin/blob/master/transaction/core/src/ring_signature/mod.rs#L29). - Note also, the blinding factor base point does not need to be unique per TokenType, so we use the same blinding factor base point for all TokenTypes. - - Note finally, the Proof of Opening requires all commitments in a transaction were generated using the same group generator, base point `H_i`. If the commitments in input and/or output TXOs in a transaction are constructed with different group generators, and the sender is able to unwrap the sum as specified in the protocol below, it implies that the sender knows a nontrivial linear relationship among the `H_i`, which contradicts the assumption that the `H_i` are orthogonal. + - Note finally, the Proof of Opening requires all commitments in a transaction were generated using the same group generator, base point `H_i`. If the commitments in input and/or output TxOuts in a transaction are constructed with different group generators, and the sender is able to unwrap the sum as specified in the protocol below, it implies that the sender knows a nontrivial linear relationship among the `H_i`, which contradicts the assumption that the `H_i` are orthogonal. The non-interactive zero knowledge Proof of Opening protocol is the following: (See [Zero Knowledge Proofs and Commitment Schemes, Page 27](https://www.cs.purdue.edu/homes/ninghui/courses/555_Spring12/handouts/555_Spring12_topic23.pdf)) @@ -163,7 +168,7 @@ verify(G, H, commitment, proof_of_knowledge) { Currently, the enclave aggregates the fees for multiple transactions in a block, in order to mint a single fee output for the block and conserve space. To support multiple confidential TokenTypes, when minting fee outputs, the enclave must create multiple fee outputs if multiple TokenTypes were used in the block. A consideration here is that we would like for an observer not to discern whether a block contains transactions of multiple asset types by statistically analyzing the number of outputs to derive information about the number of fee outputs. -A simple proposal is that the enclave discontinue fee aggregation, so that the number of fee outputs scale linearly with the number of transactions in a block (not output TXOs). For example, a block with 3 transactions includes 3 fee outputs. Because each transaction can mint up to 16 output TXOs, there may be a variable number of outputs in the block. +A simple proposal is that the enclave discontinue fee aggregation, so that the number of fee outputs scale linearly with the number of transactions in a block (not output TxOuts). For example, a block with 3 transactions includes 3 fee outputs. Because each transaction can mint up to 16 output TxOuts, there may be a variable number of outputs in the block. Note that the untrusted portion of the node software knows how many transactions are in each block, and this is not considered statistically relevant information with respect to confidentiality. In other words, the current fee aggregation behavior is not meant as a confidentiality measure, purely as a space-saving measure. @@ -216,14 +221,14 @@ Note how the two transactions paying more than their minimum fee are sorted to t ### Performance Impact: Size & Space Analysis -- Each TXO has 6 new bytes allocated in the Amount object for the `masked_token_id` (4 bytes for the value, 2 bytes for Protobuf overhead) +- Each TxOut has 6 new bytes allocated in the Amount object for the `masked_token_id` (4 bytes for the value, 2 bytes for Protobuf overhead) - The RingCT bulletproof signature increases by at least 96 bytes to contain the Proof of Opening (plus Protobuf overhead). - Each block now contains a number of fee outputs scaling linearly with the number of transactions in a block, as opposed to a single fee output aggregated from multiple transactions in a block. - - In practice, most clients currently output 2 TXOs, one for the recipient and one for change, unless performing a `split_txo` operation. Output growth on chain is currently dominated by the number of outputs in a transaction, and since blocks typically clear fast enough that there is only one transaction in a block for which to aggregate the fee, this should not have a deleterious effect on the space capacity of the chain, especially given that up to [16 outputs are permitted for every transaction](https://github.com/mobilecoinfoundation/mobilecoin/blob/e74d4c821cb213290975d6d5a9390778661cebfe/transaction/core/src/constants.rs#L17). + - In practice, most clients currently output 2 TxOuts, one for the recipient and one for change, unless performing a `split_TxOut` operation. Output growth on chain is currently dominated by the number of outputs in a transaction, and since blocks typically clear fast enough that there is only one transaction in a block for which to aggregate the fee, this should not have a deleterious effect on the space capacity of the chain, especially given that up to [16 outputs are permitted for every transaction](https://github.com/mobilecoinfoundation/mobilecoin/blob/e74d4c821cb213290975d6d5a9390778661cebfe/transaction/core/src/constants.rs#L17). ### Confidentiality Analysis -The [TxPrefix](https://github.com/mobilecoinfoundation/mobilecoin/blob/a9db0bc7bcac9bac1c1232b194003d02bccaf283/transaction/core/src/tx.rs#L149) contains the `token_id` and the `fee` in the clear. We, and any users of the protocol, need to clearly understand the footprint of this data, and the ways in which an attacker may try to discern the `token_id` of TXOs. +The [TxPrefix](https://github.com/mobilecoinfoundation/mobilecoin/blob/a9db0bc7bcac9bac1c1232b194003d02bccaf283/transaction/core/src/tx.rs#L149) contains the `token_id` and the `fee` in the clear. We, and any users of the protocol, need to clearly understand the footprint of this data, and the ways in which an attacker may try to discern the `token_id` of TxOuts. The [TxPrefix](https://github.com/mobilecoinfoundation/mobilecoin/blob/edbc0162cbd647b6605fe817a8753508ca4515a2/transaction/core/src/tx.rs#L149) is included in the [Tx](https://github.com/mobilecoinfoundation/mobilecoin/blob/edbc0162cbd647b6605fe817a8753508ca4515a2/transaction/core/src/tx.rs#L103) that is delivered directly to the enclave through an attested channel at the [client\_tx_propose](https://github.com/mobilecoinfoundation/mobilecoin/blob/2d5190e60f6820cebcd585c19d16cecf9ba4d89b/consensus/enclave/impl/src/lib.rs#L299) endpoint. By attested, this means that the client would not deliver this payload unless the enclave's signed measurement matched the client's expectations. @@ -248,7 +253,7 @@ The upgrade plan is described in full in [MCIP #26](https://github.com/mobilecoi This is a substantial change to the protocol, and a breaking change for clients. Other drawbacks include: -- Increasing complexity of TXOs and transaction construction & validation +- Increasing complexity of TxOuts and transaction construction & validation - Increasing the transaction validation footprint in the enclave to include Proof of Opening - Increasing the amount of data on the ledger - `masked_token_id` bytes must be plumbed everywhere, in Fog and clients, but no more serious changes are needed in Fog. Note that if the TokenType was not confidential, Fog Ledger would need a much larger change in order to properly select rings From b0a6219ec761abffb3555ccf2e711625a0abc147 Mon Sep 17 00:00:00 2001 From: Sara Drakeley Date: Mon, 14 Mar 2022 22:42:14 -0700 Subject: [PATCH 08/21] Removing proof of opening and updating naming for fee priority --- text/0000-confidential-token-ids.md | 37 ++++++++--------------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/text/0000-confidential-token-ids.md b/text/0000-confidential-token-ids.md index e2a26906..d2efc854 100644 --- a/text/0000-confidential-token-ids.md +++ b/text/0000-confidential-token-ids.md @@ -87,7 +87,7 @@ For example, an implementation to obtain a generator for `token_id = i` could lo B_blinding = RISTRETTO_BASEPOINT ``` -After this change, `range_proofs` and `ring_signature/mlsags` will be relative to a Pedersen generator, and it is not possible to construct a range proof relative to another generator if those generators are orthogonal. Thus, it is guaranteed that all transaction inputs and outputs are using the same `token_ids`. This is implied by the homomorphic encryption property of [Pedersen Commitments](https://www.cs.cornell.edu/courses/cs754/2001fa/129.PDF), namely that addition on the commitments preserves the additive relationship of the pre-committed values, as long as they are using the same group generator. [TODO: Why?] +After this change, `range_proofs`, `rct_bulletproofs`, and `ring_signature/mlsags` will be relative to a Pedersen generator, and it is not possible to construct a range proof relative to another generator if those generators are orthogonal. Thus, it is guaranteed that all transaction inputs and outputs are using the same `token_ids`. This is implied by the homomorphic encryption property of [Pedersen Commitments](https://www.cs.cornell.edu/courses/cs754/2001fa/129.PDF), namely that addition on the commitments preserves the additive relationship of the pre-committed values, as long as they are using the same group generator. [TODO: Why?] ### Validation @@ -137,23 +137,6 @@ The non-interactive zero knowledge Proof of Opening protocol is the following: ( This protocol is non-interactive because the prover anticipates the verifier's `e` value, so requires no interaction from the verifier. -Thus, the Proof of Knowledge could be represented, for example, with: - -``` -struct ProofOfKnowledge { - - /// Prover value established from hashes of commitment and blinding factor secrets - CompressedRistretto d, - - /// Prover value from the token_id response to the verifier's commitment-derived challenge - CurveScalar u, - - /// Prover value from the blinding_factor response to the verifier's commitment-derived challenge - CurveScalar v, -} -``` - -- Implementation note: The proof of knowledge should be constructed at [this line](https://github.com/mobilecoinfoundation/mobilecoin/blob/a9db0bc7bcac9bac1c1232b194003d02bccaf283/transaction/core/src/ring_signature/rct_bulletproofs.rs#L304) in the sign function. Then, the Transaction Validator need perform the following validation check, after calculating the sum of the outputs (in order to verify the sum of the inputs matches the sum of the outputs, and thus that no value was created or destroyed), at [this line](https://github.com/mobilecoinfoundation/mobilecoin/blob/a9db0bc7bcac9bac1c1232b194003d02bccaf283/transaction/core/src/ring_signature/rct_bulletproofs.rs#L170): @@ -172,11 +155,11 @@ A simple proposal is that the enclave discontinue fee aggregation, so that the n Note that the untrusted portion of the node software knows how many transactions are in each block, and this is not considered statistically relevant information with respect to confidentiality. In other words, the current fee aggregation behavior is not meant as a confidentiality measure, purely as a space-saving measure. -### Fee Ratio and Transaction Sorting +### Fee Priority and Transaction Sorting Currently the [WellFormedTxContext](https://github.com/mobilecoinfoundation/mobilecoin/blob/a092039a4a5cc7f5e3e16eeadd0b2bc3a12667ae/consensus/enclave/api/src/lib.rs#L50) contains the fee, which must be used by untrusted to [sort transactions](https://github.com/mobilecoinfoundation/mobilecoin/blob/a092039a4a5cc7f5e3e16eeadd0b2bc3a12667ae/consensus/enclave/api/src/lib.rs#L131) when [combining transactions](https://github.com/mobilecoinfoundation/mobilecoin/blob/a092039a4a5cc7f5e3e16eeadd0b2bc3a12667ae/consensus/service/src/tx_manager/untrusted_interfaces.rs#L40) for consideration by the enclave, prioritizing higher fees for inclusion in the block during periods of network congestion. In order to keep the `token_id` confidential, we must provide a mechanism for untrusted to accurately sort transactions before asking the enclave to do work on the transactions, without revealing the `token_id`, and at the same time, allowing for different asset types to express differing minimum fee requirements. -To derive a sorting order without revealing the transaction type, the `fee` in the WellFormedTxContext becomes a `fee_ratio`, which is calculated based on the ratio between the minimum fees configured by the node operator on startup. For example, the network operators may have configured the following fees: +To derive a sorting order without revealing the transaction type, the `fee` in the `WellFormedTxContext` derives a `priority`, which is calculated based on some ratio between the minimum fees configured by the node operator on startup. For example, the network operators may have configured the following minimum fees: ``` { @@ -188,7 +171,7 @@ To derive a sorting order without revealing the transaction type, the `fee` in t The ratio between the fees for the `token_ids` is the following, using the least common multiple of the fees: -`token_id` | fee | `fee_ratio` multiplier +`token_id` | `fee` | `priority` multiplier --- | --- | --- 000 | 1 | 6 111 | 2 | 3 @@ -209,7 +192,7 @@ Then, imagine that a block contains the following set of transactions with the g The sort order would be: -`transaction_id` | `token_id` | `fee_ratio` multiplier | original fee | `fee_ratio` result +`transaction_id` | `token_id` | `priority` multiplier | original `fee` | `priority` result --- | --- | --- | --- | --- transaction_2 | 000 | 6 | 1.5 | 9 transaction_5 | 888 | 2 | 4 | 8 @@ -217,14 +200,14 @@ transaction_1 | 000 | 6 | 1 | 6 transaction_3 | 111 | 3 | 2 | 6 transaction_4 | 888 | 2 | 3 | 6 -Note how the two transactions paying more than their minimum fee are sorted to the top, in proportion to how much they are paying above the minimum fee, while all minimum fee transactions have an equal chance of beeing sorted because their `fee_ratio` is equal. +Note how the two transactions paying more than their minimum fee are sorted to the top, in proportion to how much they are paying above the minimum fee, while all minimum fee transactions have an equal chance of beeing sorted because their `priority` is equal. + +For users who are adjusting the fees of their submitted transactions to increase the likelihood of their transaction being accepted during times of congestion, they can observe the `priority` multiplier for their `token_id`, and the recently accepted `priority` values, and determine what fee value is appropriate to increase the chances of acceptance. ### Performance Impact: Size & Space Analysis - Each TxOut has 6 new bytes allocated in the Amount object for the `masked_token_id` (4 bytes for the value, 2 bytes for Protobuf overhead) -- The RingCT bulletproof signature increases by at least 96 bytes to contain the Proof of Opening (plus Protobuf overhead). -- Each block now contains a number of fee outputs scaling linearly with the number of transactions in a block, as opposed to a single fee output aggregated from multiple transactions in a block. - - In practice, most clients currently output 2 TxOuts, one for the recipient and one for change, unless performing a `split_TxOut` operation. Output growth on chain is currently dominated by the number of outputs in a transaction, and since blocks typically clear fast enough that there is only one transaction in a block for which to aggregate the fee, this should not have a deleterious effect on the space capacity of the chain, especially given that up to [16 outputs are permitted for every transaction](https://github.com/mobilecoinfoundation/mobilecoin/blob/e74d4c821cb213290975d6d5a9390778661cebfe/transaction/core/src/constants.rs#L17). +- Each block now contains a number of fee outputs scaling linearly with the total number of supported `token_ids`. This way we can continue to aggregate fees, but not reveal how many token types were included in the block. For blocks with fewer transactions than `token_ids`, the number of fee outputs is `min(num_token_ids, num_transactions_in_block)` ### Confidentiality Analysis @@ -268,7 +251,7 @@ This is a substantial change to the protocol, and a breaking change for clients. # Prior art [prior-art]: #prior-art -- This proposal contains a non-novel commitment scheme as specified in [Zero Knowledge Proofs and Commitment Schemes, Page 27](https://www.cs.purdue.edu/homes/ninghui/courses/555_Spring12/handouts/555_Spring12_topic23.pdf) and uses properties of [Pedersen Commitments](https://www.cs.cornell.edu/courses/cs754/2001fa/129.PDF) for the Proof of Opening +- [Spats](https://github.com/AaronFeickert/spats) is an extension to the [Spark](https://eprint.iacr.org/2021/1173) transaction protocol to support confidential assets - [Andrew Poelstra describes Confidential Assets](https://blog.blockstream.com/en-blockstream-releases-elements-confidential-assets/) for Blockstream, along with [this whitepaper](https://blockstream.com/bitcoin17-final41.pdf) # Unresolved questions From daf553276411bc7f0bd89530ee68949211f74ee4 Mon Sep 17 00:00:00 2001 From: Sara Drakeley Date: Mon, 14 Mar 2022 22:57:10 -0700 Subject: [PATCH 09/21] Further removal of opening and updating fee descriptions --- text/0000-confidential-token-ids.md | 83 +++++++---------------------- 1 file changed, 18 insertions(+), 65 deletions(-) diff --git a/text/0000-confidential-token-ids.md b/text/0000-confidential-token-ids.md index d2efc854..1bc0f021 100644 --- a/text/0000-confidential-token-ids.md +++ b/text/0000-confidential-token-ids.md @@ -11,16 +11,16 @@ Enable new token types to be added to the protocol and transacted on chain witho # Motivation [motivation]: #motivation -The MobileCoin ledger protocol currently supports one token type, MOB. We would like to allow the addition of assets to the ledger without revealing which assets were involved in any transaction. The transaction confidentiality and integrity guarantees should be the same for the MobileCoin blockchain no matter how many token types are supported. Namely, the sender, the recipient, and the amount should remain private to all except the participants in the transaction, and no double spending or unauthorized minting should be possible. In addition, we now specify that the asset type should remain private to all except the participants in the transaction. +The MobileCoin ledger protocol currently supports one token type, MOB. We would like to allow the addition of assets to the ledger without revealing which assets were involved in any transaction. The transaction confidentiality and integrity guarantees should be the same for the MobileCoin blockchain no matter how many token types are supported. Namely, the sender, the recipient, and the amount should remain private to all except the participants in the transaction, and no double spending or unauthorized minting should be possible. In addition, we now specify that the token type should remain private to all except the participants in the transaction. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation -A Transaction Output (TxOut) has a confidential TokenType bytes field (`masked_token_id`). Transactions with inputs and outputs, including a fee output, must be composed of a single TokenType. Some tokens may have additional functionality, such as minting and burning, which require verification of the TokenType as part of the authorization of the action. +A Transaction Output (TxOut) has a confidential `token_id` bytes field (`masked_token_id`). Transactions with inputs and outputs, including a fee output, must be composed of a single `token_id`. Some tokens may have additional functionality, such as minting and burning, which require verification of the `token_id` as part of the authorization of the action. -Each TokenType has an explicit minimum fee specified by node operators, and included in the attestation handshake, ensuring that all nodes in the network are configured with the same fee minimums and TokenType sets. New TokenTypes can be added only with unanimous agreement from all node operators. +Each `token_id` has an explicit minimum fee specified by node operators, and included in the attestation handshake, ensuring that all nodes in the network are configured with the same fee minimums and `token_id` sets. New `token_ids` can be added only with unanimous agreement from all node operators. -A transaction specifies the TokenType for the entire transaction, and includes a "Proof of Opening" that all `token_ids` in the inputs and outputs are the same. This prevents spending one TokenType as a member of another TokenType. The TransactionBuilder now takes a `token_id` as an argument in its initializer. The TokenType is set in the clear on the TxPrefix for a transaction, which is only accessed in the enclave, after the payload is delivered via an encrypted, attested channel. This is similar to the TxInputs, and thus has a similar threat model for confidentiality. See [Confidentiality Analysis](#confidentiality-analysis) for an assessment of risk. +A transaction specifies the `token_id` for the entire transaction, with a cryptographic guarantee that all inputs and outputs are of the same `token_id`. This prevents spending one `token_id` as a member of another `token_id`. The TransactionBuilder now takes a `token_id` as an argument in its initializer. The `token_id` is set in the clear on the `TxPrefix` for a transaction, which is only accessed in the enclave, after the payload is delivered via an encrypted, attested channel. This is similar to the `TxInputs`, and thus has a similar threat model for confidentiality. See [Confidentiality Analysis](#confidentiality-analysis) for an assessment of risk. Clients must choose how to expose multiple asset types to their users. There may be clients who wish to only expose a subset of asset types, or clients who wish to provide balances and transactionality in all asset types. @@ -29,7 +29,7 @@ Clients must choose how to expose multiple asset types to their users. There may ### Masked Token ID Field in the TxOut -For each token type that is added, we specify a `u32 token_id`. We maintain a list of `KnownTokenIds` in both the external proto definition, as well as in the transaction core crate. The first `TokenType` is `MOB = token_id(0)`, with the assumption that an unspecified `token_id` defaults to 0, for backward cmopatibility. New `token_ids` increase monotonically. For example, the `KnownTokenId` enum in the external proto may be something like, +For each token type that is added, we specify a `u32 token_id`. We maintain a list of `KnownTokenIds` in both the external proto definition, as well as in the transaction core crate. The first token type is `MOB = token_id(0)`, with the assumption that an unspecified `token_id` defaults to 0, for backward cmopatibility. New `token_ids` increase monotonically. For example, the `KnownTokenId` enum in the external proto may be something like, ``` enum KnownTokenId { @@ -48,9 +48,9 @@ After this change: A `masked_token_id` is added, which, similarly to `masked_value`, provides a way for the recipient to learn the `token_id` (and then subsequently confirm it). The Pedersen commitment to the value now is made using a basepoint `H_i` which depends on the `token_id`. (The basepoint for the blinding factor is the same as before regardless of the `token_id`.) -Thus, the recipient can attempt to scan a TxOut by first constructing the shared secret, then unmasking the `masked_amount` and `masked_token_id`, and then reconstructing the Pedersen commitment. If this reconstruction is successful, then the recipient knows the `amount` and the `token_id`, similarly as before the support for a confidential`token_id`. +Thus, the recipient can attempt to scan a TxOut by first constructing the shared secret, then unmasking the `masked_amount` and `masked_token_id`, and then reconstructing the Pedersen commitment. If this reconstruction is successful, then the recipient knows the `amount` and the `token_id`, similarly as before the support for a confidential `token_id`. -The `masked_token_id` field is added to the [`Amount`](https://github.com/mobilecoinfoundation/mobilecoin/blob/cb10d41d404c552cab19de0163992923a878ed5e/transaction/core/src/amount/mod.rs#L46) in each TxOut, and functions similar to `masked_value`. The protocol representation of the TxOut uses Protobuf, which enables backward compatability in this way: if the field is not populated (Protobuf bytes field of length 0), the default `token_id` is 0. Otherwise, the field is a Protobuf bytes field of length 4. Please see the [Upgrade Plan](#upgrade-plan) for more discussion on rolling out this change with consideration for backward compatibility. +The `masked_token_id` field is added to the [`Amount`](https://github.com/mobilecoinfoundation/mobilecoin/blob/cb10d41d404c552cab19de0163992923a878ed5e/transaction/core/src/amount/mod.rs#L46) in each TxOut. The protocol representation of the TxOut uses Protobuf, which enables backward compatability in this way: if the field is not populated (Protobuf bytes field of length 0), the default `token_id` is 0. Otherwise, the field is a Protobuf bytes field of length 4. Please see the [Upgrade Plan](#upgrade-plan) for more discussion on rolling out this change with consideration for backward compatibility. * To compute the 4 byte `masked_token_id` from the `token_id`, we hash the TxOut shared secret, with a prefix, XORed with the `token_id`, and take the little endian representation of those bytes. @@ -70,9 +70,9 @@ Previously, we used the same curve base point for all Pedersen generators to Amo The requirements for the Pedersen generators are: -* The generator used for MOB is the same as before, so `H_0` is a known value. +* The generator used for `MOB = token_id(0)` is the same as before, so `H_0` is a known value. -* The set of all generators used for token ids should be cryptographically "orthogonal", meaning that, it should be intractable for anyone to find a linear relationship between them. +* The set of all generators used for `token_ids` should be cryptographically "orthogonal", meaning that, it should be intractable for anyone to find a linear relationship between them. * The function mapping a `token_id`, `i`, to the generator `H_i` must be implemented in constant-time. @@ -87,9 +87,9 @@ For example, an implementation to obtain a generator for `token_id = i` could lo B_blinding = RISTRETTO_BASEPOINT ``` -After this change, `range_proofs`, `rct_bulletproofs`, and `ring_signature/mlsags` will be relative to a Pedersen generator, and it is not possible to construct a range proof relative to another generator if those generators are orthogonal. Thus, it is guaranteed that all transaction inputs and outputs are using the same `token_ids`. This is implied by the homomorphic encryption property of [Pedersen Commitments](https://www.cs.cornell.edu/courses/cs754/2001fa/129.PDF), namely that addition on the commitments preserves the additive relationship of the pre-committed values, as long as they are using the same group generator. [TODO: Why?] +After this change, `range_proofs`, `rct_bulletproofs`, and `ring_signature/mlsags` will be relative to a Pedersen generator, and it is not possible to construct a range proof relative to another generator if those generators are orthogonal. Thus, it is guaranteed that all transaction inputs and outputs are using the same `token_ids`. This is implied by the homomorphic encryption property of [Pedersen Commitments](https://www.cs.cornell.edu/courses/cs754/2001fa/129.PDF), namely that addition on the commitments preserves the additive relationship of the pre-committed values, as long as they are using the same group generator. [TODO: Explanation?] -### Validation +### Commitments To prove each TxOut's commitment to the `amount` value, we use a Pedersen commitment, consisting of a group generator (`H_i`) raised to the value of the `amount`, with a blinding factor to prevent brute-forcing the full range of values (`2^64`). We compute the blinding factor as the blinding group generator raised to a hash of the shared secret for the TxOut. @@ -98,60 +98,14 @@ To prove each TxOut's commitment to the `amount` value, we use a Pedersen commit commitment = H_i * Scalar::from(amount) + G * blinding_factor ``` - - Note, the group generators for Token Types are computed using hashing to a curve so that they are "orthogonal" for computable adversaries, meaning there is no discernable, nontrivial linear relationship between base points. See our [transaction core group generators reference](https://github.com/mobilecoinfoundation/mobilecoin/blob/master/transaction/core/src/ring_signature/mod.rs#L29). - - Note also, the blinding factor base point does not need to be unique per TokenType, so we use the same blinding factor base point for all TokenTypes. - - Note finally, the Proof of Opening requires all commitments in a transaction were generated using the same group generator, base point `H_i`. If the commitments in input and/or output TxOuts in a transaction are constructed with different group generators, and the sender is able to unwrap the sum as specified in the protocol below, it implies that the sender knows a nontrivial linear relationship among the `H_i`, which contradicts the assumption that the `H_i` are orthogonal. - -The non-interactive zero knowledge Proof of Opening protocol is the following: (See [Zero Knowledge Proofs and Commitment Schemes, Page 27](https://www.cs.purdue.edu/homes/ninghui/courses/555_Spring12/handouts/555_Spring12_topic23.pdf)) - - -1. The prover establishes a value, `d`, calculated by hashing their secret values from the commitment (the `token_id` and the `blinding_factor`). Implementation note, we may use [Merlin](https://docs.rs/merlin/latest/merlin/) rather than Blake2B. - - ``` - y = Blake2B(proof_tag_t | token_id) - s = Blake2B(proof_tag_b | blinding_factor) - d = G * y + H * s - ``` - -2. The non-interactive verifier chooses challenge, `e`, by hashing the commitment and the value `d` from the previous step. - - ``` - e = Blake2B(proof_tag_e | commitment | d) - ``` - -3. The prover computes the following, and includes these values in the Proof of Opening: - - ``` - u = y + e * token_id - v = s + e * blinding_factor - ``` - -4. The verifier of the Proof of Opening must verify: - - ``` - G * u + H * v = d + commitment * e - ``` - - - This property follows a similar property of the [Schnorr Proof of Knowledge protocol](https://en.wikipedia.org/wiki/Proof_of_knowledge#Schnorr_protocol) - - -This protocol is non-interactive because the prover anticipates the verifier's `e` value, so requires no interaction from the verifier. - - -Then, the Transaction Validator need perform the following validation check, after calculating the sum of the outputs (in order to verify the sum of the inputs matches the sum of the outputs, and thus that no value was created or destroyed), at [this line](https://github.com/mobilecoinfoundation/mobilecoin/blob/a9db0bc7bcac9bac1c1232b194003d02bccaf283/transaction/core/src/ring_signature/rct_bulletproofs.rs#L170): - -``` -verify(G, H, commitment, proof_of_knowledge) { - e = Blake2B(proof_tag_e | commitment | proof_of_knowledge.d) - proof_of_knowledge.d + (commitment * e) == G * proof_of_knowledge.u + H * proof_of_knowledge.v -} -``` +- Note, the group generators for `token_ids` are computed using hashing to a curve so that they are "orthogonal" for computable adversaries, meaning there is no discernable, nontrivial linear relationship between base points. See our [transaction core group generators reference](https://github.com/mobilecoinfoundation/mobilecoin/blob/master/transaction/core/src/ring_signature/mod.rs#L29). + - Note also, the blinding factor base point does not need to be unique per `token_id`, so we use the same blinding factor base point for all `token_ids`. ### Fee Outputs -Currently, the enclave aggregates the fees for multiple transactions in a block, in order to mint a single fee output for the block and conserve space. To support multiple confidential TokenTypes, when minting fee outputs, the enclave must create multiple fee outputs if multiple TokenTypes were used in the block. A consideration here is that we would like for an observer not to discern whether a block contains transactions of multiple asset types by statistically analyzing the number of outputs to derive information about the number of fee outputs. +Currently, the enclave aggregates the fees for multiple transactions in a block, in order to mint a single fee output for the block and conserve space. To support multiple confidential `token_ids`, when minting fee outputs, the enclave must create multiple fee outputs if multiple `token_ids` were used in the block. A consideration here is that we would like for an observer not to discern whether a block contains transactions of multiple asset types by statistically analyzing the number of outputs to derive information about the number of fee outputs. -A simple proposal is that the enclave discontinue fee aggregation, so that the number of fee outputs scale linearly with the number of transactions in a block (not output TxOuts). For example, a block with 3 transactions includes 3 fee outputs. Because each transaction can mint up to 16 output TxOuts, there may be a variable number of outputs in the block. +A simple proposal is that each block now contains a number of fee outputs scaling linearly with the total number of supported `token_ids`. This way we can continue to aggregate fees, but not reveal how many token types were included in the block. For blocks with fewer transactions than `token_ids`, the number of fee outputs is `min(num_token_ids, num_transactions_in_block)` Note that the untrusted portion of the node software knows how many transactions are in each block, and this is not considered statistically relevant information with respect to confidentiality. In other words, the current fee aggregation behavior is not meant as a confidentiality measure, purely as a space-saving measure. @@ -207,7 +161,7 @@ For users who are adjusting the fees of their submitted transactions to increase ### Performance Impact: Size & Space Analysis - Each TxOut has 6 new bytes allocated in the Amount object for the `masked_token_id` (4 bytes for the value, 2 bytes for Protobuf overhead) -- Each block now contains a number of fee outputs scaling linearly with the total number of supported `token_ids`. This way we can continue to aggregate fees, but not reveal how many token types were included in the block. For blocks with fewer transactions than `token_ids`, the number of fee outputs is `min(num_token_ids, num_transactions_in_block)` +- Each block now contains a number of fee outputs capped by `min(num_token_ids, num_transactions_in_block)` ### Confidentiality Analysis @@ -223,7 +177,7 @@ Now that the Tx is validated as "well-formed," sorted and combined for a ballot The TxManager in untrusted for each node maintains a cache of encrypted txs and tx contexts that its enclave has found to be well-formed. Once the consensus protocol is complete, the untrusted TxManager calls [form_block]() which prompts the [enclave to `form_block`](https://github.com/mobilecoinfoundation/mobilecoin/blob/cfa51d26ae943a9055698bb209c2fe06fa7a7cac/consensus/enclave/impl/src/lib.rs#L421) using the full transaction contents. -Because untrusted never sees the `fee` or `token_id` in the clear, the attacker would need to sustain an enclave exploit, such as a side channel attack, that would allow them to access data while the enclave is processing to discern the TokenType of a transaction. With this information, the attacker could, for the duration of the exploit, determine which asset type each transaction was composed of, and could also construct a transaction graph for statistical analysis of the inputs, which are also only visible in the TxPrefix, but not persisted to the chain. Once the integrity of SGX was restored, the attacker would no longer be able to obtain information about the transaction inputs or TokenType, and forward secrecy is preserved. Further, by being thoughtful about the memory access of the `token_id`, and using constant time operations, we can mitigate the data leakage even in the event of a side channel exploit. +Because untrusted never sees the `fee` or `token_id` in the clear, the attacker would need to sustain an enclave exploit, such as a side channel attack, that would allow them to access data while the enclave is processing to discern the `token_id` of a transaction. With this information, the attacker could, for the duration of the exploit, determine which asset type each transaction was composed of, and could also construct a transaction graph for statistical analysis of the inputs, which are also only visible in the TxPrefix, but not persisted to the chain. Once the integrity of SGX was restored, the attacker would no longer be able to obtain information about the transaction inputs or `token_id`, and forward secrecy is preserved. Further, by being thoughtful about the memory access of the `token_id`, and using constant time operations, we can mitigate the data leakage even in the event of a side channel exploit. ### Upgrade Plan @@ -237,9 +191,8 @@ The upgrade plan is described in full in [MCIP #26](https://github.com/mobilecoi This is a substantial change to the protocol, and a breaking change for clients. Other drawbacks include: - Increasing complexity of TxOuts and transaction construction & validation -- Increasing the transaction validation footprint in the enclave to include Proof of Opening - Increasing the amount of data on the ledger -- `masked_token_id` bytes must be plumbed everywhere, in Fog and clients, but no more serious changes are needed in Fog. Note that if the TokenType was not confidential, Fog Ledger would need a much larger change in order to properly select rings +- `masked_token_id` bytes must be plumbed everywhere, in Fog and clients, but no more serious changes are needed in Fog. Note that if the `token_id` was not confidential, Fog Ledger would need a much larger change in order to properly select rings # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives From 354fa6db5f4ff00831770f4719d897a82d61e307 Mon Sep 17 00:00:00 2001 From: Sara Drakeley Date: Mon, 14 Mar 2022 23:08:29 -0700 Subject: [PATCH 10/21] Update pedersen generators explanation --- text/0000-confidential-token-ids.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/text/0000-confidential-token-ids.md b/text/0000-confidential-token-ids.md index 1bc0f021..43f0827b 100644 --- a/text/0000-confidential-token-ids.md +++ b/text/0000-confidential-token-ids.md @@ -82,21 +82,21 @@ We propose to compute `H_i` by converting `i` to little endian bytes, and XOR'in For example, an implementation to obtain a generator for `token_id = i` could look like: - ``` - B = RistrettoPoint::from_hash(Blake2b(HASH_TO_POINT_DOMAIN_TAG | RISTRETTO_BASEPOINT ^ token_id)) - B_blinding = RISTRETTO_BASEPOINT - ``` +``` +B = RistrettoPoint::from_hash(Blake2b(HASH_TO_POINT_DOMAIN_TAG | RISTRETTO_BASEPOINT ^ token_id)) +B_blinding = RISTRETTO_BASEPOINT +``` -After this change, `range_proofs`, `rct_bulletproofs`, and `ring_signature/mlsags` will be relative to a Pedersen generator, and it is not possible to construct a range proof relative to another generator if those generators are orthogonal. Thus, it is guaranteed that all transaction inputs and outputs are using the same `token_ids`. This is implied by the homomorphic encryption property of [Pedersen Commitments](https://www.cs.cornell.edu/courses/cs754/2001fa/129.PDF), namely that addition on the commitments preserves the additive relationship of the pre-committed values, as long as they are using the same group generator. [TODO: Explanation?] +After this change, `range_proofs`, `rct_bulletproofs`, and `ring_signature/mlsags` will be relative to a Pedersen generator, and it is not possible to construct a range proof relative to another generator if those generators are orthogonal. Thus, it is guaranteed that all transaction inputs and outputs are using the same `token_ids`. This is implied by the homomorphic encryption property of [Pedersen Commitments](https://www.cs.cornell.edu/courses/cs754/2001fa/129.PDF), namely that addition on the commitments preserves the additive relationship of the pre-committed values, as long as they are using the same group generator. `H_i` is revealed to the verifier, who must show that the sum of inputs does not overflow, or `sum(outputs) = a * G + b * H_i` for some TxOut-specific values of `a` and `b`. ### Commitments To prove each TxOut's commitment to the `amount` value, we use a Pedersen commitment, consisting of a group generator (`H_i`) raised to the value of the `amount`, with a blinding factor to prevent brute-forcing the full range of values (`2^64`). We compute the blinding factor as the blinding group generator raised to a hash of the shared secret for the TxOut. - ``` - blinding_factor = Blake2B(blinding_tag | shared_secret) - commitment = H_i * Scalar::from(amount) + G * blinding_factor - ``` +``` +blinding_factor = Blake2B(blinding_tag | shared_secret) +commitment = H_i * Scalar::from(amount) + G * blinding_factor +``` - Note, the group generators for `token_ids` are computed using hashing to a curve so that they are "orthogonal" for computable adversaries, meaning there is no discernable, nontrivial linear relationship between base points. See our [transaction core group generators reference](https://github.com/mobilecoinfoundation/mobilecoin/blob/master/transaction/core/src/ring_signature/mod.rs#L29). - Note also, the blinding factor base point does not need to be unique per `token_id`, so we use the same blinding factor base point for all `token_ids`. From 00beea6c6676b8815cee516b74e2ed473e9346b7 Mon Sep 17 00:00:00 2001 From: Sara Drakeley Date: Mon, 14 Mar 2022 23:16:20 -0700 Subject: [PATCH 11/21] Add alternative around allowing only one asset for fees --- text/0000-confidential-token-ids.md | 1 + 1 file changed, 1 insertion(+) diff --git a/text/0000-confidential-token-ids.md b/text/0000-confidential-token-ids.md index 43f0827b..312b54f5 100644 --- a/text/0000-confidential-token-ids.md +++ b/text/0000-confidential-token-ids.md @@ -199,6 +199,7 @@ This is a substantial change to the protocol, and a breaking change for clients. - We could use `u16` for the `token_ids` rather than `u32` to save space, but we would like to anticipate the possibility of more than 65,000 asset types. - We could use Merlin rather than Blake2B +- We could only accept fees in one asset type, to prevent complication of client logic and enclave management of multiple fee aggregations - We could use non-confidential token types, but this has two downsides: it breaks the ledger into multiple sets which could be statistically analyzed for `token_id`, and it requires much more logic around post processing the ledger for fog. With the proposal here, fog remains oblivious to `token_id` and need not complicate how it selects rings, for example. # Prior art From f8e97a10f0b36a5b3271338d10f488973408b00e Mon Sep 17 00:00:00 2001 From: Sara Drakeley Date: Mon, 14 Mar 2022 23:20:53 -0700 Subject: [PATCH 12/21] Rename file --- ...0-confidential-token-ids.md => 0025-confidential-token-ids.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename text/{0000-confidential-token-ids.md => 0025-confidential-token-ids.md} (100%) diff --git a/text/0000-confidential-token-ids.md b/text/0025-confidential-token-ids.md similarity index 100% rename from text/0000-confidential-token-ids.md rename to text/0025-confidential-token-ids.md From 0bd1e8db748f8e6df8176109cd64910a09780731 Mon Sep 17 00:00:00 2001 From: Sara Drakeley Date: Thu, 21 Apr 2022 04:51:05 -0700 Subject: [PATCH 13/21] Improve notation --- text/0025-confidential-token-ids.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/text/0025-confidential-token-ids.md b/text/0025-confidential-token-ids.md index 312b54f5..430a4662 100644 --- a/text/0025-confidential-token-ids.md +++ b/text/0025-confidential-token-ids.md @@ -55,13 +55,13 @@ The `masked_token_id` field is added to the [`Amount`](https://github.com/mobile * To compute the 4 byte `masked_token_id` from the `token_id`, we hash the TxOut shared secret, with a prefix, XORed with the `token_id`, and take the little endian representation of those bytes. ``` - masked_token_id = (token_id ^ Blake2B(AMOUNT_TOKEN_ID_DOMAIN_TAG | shared_secret)).to_le() + masked_token_id = (token_id ^ Blake2B(AMOUNT_TOKEN_ID_DOMAIN_TAG || shared_secret)).to_le() ``` * To recover the `token_id` from the `masked_token_id`, we reverse the process (if the bytes field is length = 4, otherwise `token_id = 0`). ``` - unmasked_token_id = masked_token_id ^ Blake2b(AMOUNT_TOKEN_ID_DOMAIN_TAG | shared_secret) + unmasked_token_id = masked_token_id ^ Blake2b(AMOUNT_TOKEN_ID_DOMAIN_TAG || shared_secret) ``` ### Pedersen Generators @@ -83,7 +83,7 @@ We propose to compute `H_i` by converting `i` to little endian bytes, and XOR'in For example, an implementation to obtain a generator for `token_id = i` could look like: ``` -B = RistrettoPoint::from_hash(Blake2b(HASH_TO_POINT_DOMAIN_TAG | RISTRETTO_BASEPOINT ^ token_id)) +B = RistrettoPoint::from_hash(Blake2b(HASH_TO_POINT_DOMAIN_TAG || RISTRETTO_BASEPOINT ^ token_id)) B_blinding = RISTRETTO_BASEPOINT ``` @@ -218,3 +218,4 @@ No unresolved questions at this time. No future possibilities at this time. + | \ No newline at end of file From 594e47014f8223a7ed284f538e23649c11c2622a Mon Sep 17 00:00:00 2001 From: Chris Beck Date: Tue, 10 May 2022 15:47:22 -0600 Subject: [PATCH 14/21] Rewrite several sections and add detail --- text/0025-confidential-token-ids.md | 338 +++++++++++++++++++--------- 1 file changed, 228 insertions(+), 110 deletions(-) diff --git a/text/0025-confidential-token-ids.md b/text/0025-confidential-token-ids.md index 430a4662..ce5a4d8f 100644 --- a/text/0025-confidential-token-ids.md +++ b/text/0025-confidential-token-ids.md @@ -1,210 +1,330 @@ - Feature Name: `confidential_token_ids` - Start Date: 2022-02-05 - MCIP PR: [mobilecoinfoundation/mcips#0025](https://github.com/mobilecoinfoundation/mcips/pull/0025) -- MobileCoin Epic: TBD +- Implementation PR: [mobilecoinfoundation/mobilecoin#1438](https://github.com/mobilecoinfoundation/mobilecoin/pull/1438) # Summary [summary]: #summary -Enable new token types to be added to the protocol and transacted on chain without revealing or leaving a transparent record of the token type. +Enable new token types to be added to the protocol and transacted on chain without revealing the token type. # Motivation [motivation]: #motivation -The MobileCoin ledger protocol currently supports one token type, MOB. We would like to allow the addition of assets to the ledger without revealing which assets were involved in any transaction. The transaction confidentiality and integrity guarantees should be the same for the MobileCoin blockchain no matter how many token types are supported. Namely, the sender, the recipient, and the amount should remain private to all except the participants in the transaction, and no double spending or unauthorized minting should be possible. In addition, we now specify that the token type should remain private to all except the participants in the transaction. +The MobileCoin ledger protocol currently supports one token type, MOB. We would like to allow the addition of assets to the ledger without revealing which assets were involved in any transaction. The transaction confidentiality and integrity guarantees should be the same for the MobileCoin blockchain no matter how many token types are supported. + +Specifically, for both MOB and synthetic assets, +* the sender +* the recipient +* the amount + +of the transaction should remain private to the sender and recipient of the transaction. + +In addition we would also like that that the token id should remain private. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation -A Transaction Output (TxOut) has a confidential `token_id` bytes field (`masked_token_id`). Transactions with inputs and outputs, including a fee output, must be composed of a single `token_id`. Some tokens may have additional functionality, such as minting and burning, which require verification of the `token_id` as part of the authorization of the action. +A `token_id` is a 64-bit number. Every `TxOut` will now have a corresponding `token_id`, and this will be committed to the chain via a new `masked_token_id` field. +The `token_id` of a `TxOut` is recovered by a `TxOut` owner in a similar way to the `value`, during view key matching. + +The transaction object may now spend inputs and outputs of any token id, but in a single transaction all inputs and outputs must have the same `token_id`. -Each `token_id` has an explicit minimum fee specified by node operators, and included in the attestation handshake, ensuring that all nodes in the network are configured with the same fee minimums and `token_id` sets. New `token_ids` can be added only with unanimous agreement from all node operators. +The fee for such a transaction is also paid in that `token_id`. This is now specified in the `fee_token_id` field in the `TxPrefix`, and implies the token id for all inputs and outputs. -A transaction specifies the `token_id` for the entire transaction, with a cryptographic guarantee that all inputs and outputs are of the same `token_id`. This prevents spending one `token_id` as a member of another `token_id`. The TransactionBuilder now takes a `token_id` as an argument in its initializer. The `token_id` is set in the clear on the `TxPrefix` for a transaction, which is only accessed in the enclave, after the payload is delivered via an encrypted, attested channel. This is similar to the `TxInputs`, and thus has a similar threat model for confidentiality. See [Confidentiality Analysis](#confidentiality-analysis) for an assessment of risk. +The minimum fee varies from token id to token id. This is configured by network operators at startup, and hashed into the responder id during node-to-node attestation to ensure that the network +has one uniform value for minimum fee. -Clients must choose how to expose multiple asset types to their users. There may be clients who wish to only expose a subset of asset types, or clients who wish to provide balances and transactionality in all asset types. +Previously, the fee of a `Tx` is exposed directly to the nodes (outside of the enclave) so that they can sort transactions by fee. After this change, the fee value may reveal information about the +token id, because different tokens have different minimum fees. To mitigate this, the enclave divides the fee by the minimum fee, deriving a new value called `priority` which is revealed to the node +to sort the transactions. # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -### Masked Token ID Field in the TxOut +### Recap -For each token type that is added, we specify a `u32 token_id`. We maintain a list of `KnownTokenIds` in both the external proto definition, as well as in the transaction core crate. The first token type is `MOB = token_id(0)`, with the assumption that an unspecified `token_id` defaults to 0, for backward cmopatibility. New `token_ids` increase monotonically. For example, the `KnownTokenId` enum in the external proto may be something like, +Recall that in current revisions of MobileCoin, the value of a `TxOut` impacts the fields of the `TxOut` in two ways: -``` -enum KnownTokenId { +* It appears in the masked value, where it bytes have been XOR'ed with the result of hashing the `TxOut` shared-secret +* It appears in the Pedersen commitment (`Amount::Commitment`), which has the form `v * H + v_blinding * G` - \\\ Token Name = token_id - MOB = 0; +Here `H` is a curve point derived by hashing to curve, and `G` is the ristretto basepoint. `v_blinding`, the blinding factor, +is also derived by a hash of the `TxOut` shared secret. -} -``` +During view key matching, the recipient of a `TxOut` attempts to compute the shared secret, by key exchange with their view private key +against the `TxOut` public key. They cannot confirm that this shared secret computation was successful -- however, if it is successful, +then they can infer the value mask and the `v_blinding` value. They can use the value mask and the masked value to compute what `v` must be, +and they can then infer what the entire commitment should be. They can then check if this matches the commitment appearing in the `TxOut`. If +it does, then view key matching was successful. -### Amount +### Changes to TxOut and amount commitments -Previously, an `amount` of picomob in a TxOut is represented by a Pedersen commitment to that number, as well as a `masked_value`, which is created by XORing the number of picomob with a pseudorandom mask (derived by hashing a shared secret). The purpose of the `masked_value` is that when the recipient receives a TxOut, they can unmask this value, then reconstruct the Pedersen commitment from the number, to confirm successful recovery of the value. +We propose to similarly encode the token id in two places: -After this change: +* The `masked_token_id` is a new bytes field which contains the little endian bytes of the token id, XOR'ed with a value derived by Blake2b hashing of the shared secret. -A `masked_token_id` is added, which, similarly to `masked_value`, provides a way for the recipient to learn the `token_id` (and then subsequently confirm it). The Pedersen commitment to the value now is made using a basepoint `H_i` which depends on the `token_id`. (The basepoint for the blinding factor is the same as before regardless of the `token_id`.) + ``` + masked_token_id = (token_id.to_le_bytes() ^ Blake2B(AMOUNT_TOKEN_ID_DOMAIN_TAG || shared_secret)) + ``` -Thus, the recipient can attempt to scan a TxOut by first constructing the shared secret, then unmasking the `masked_amount` and `masked_token_id`, and then reconstructing the Pedersen commitment. If this reconstruction is successful, then the recipient knows the `amount` and the `token_id`, similarly as before the support for a confidential `token_id`. + For backwards compatibility, clients interpret `TxOut`'s with an empty `masked_token_id` as having token id zero. So, zero is the token id of MOB. -The `masked_token_id` field is added to the [`Amount`](https://github.com/mobilecoinfoundation/mobilecoin/blob/cb10d41d404c552cab19de0163992923a878ed5e/transaction/core/src/amount/mod.rs#L46) in each TxOut. The protocol representation of the TxOut uses Protobuf, which enables backward compatability in this way: if the field is not populated (Protobuf bytes field of length 0), the default `token_id` is 0. Otherwise, the field is a Protobuf bytes field of length 4. Please see the [Upgrade Plan](#upgrade-plan) for more discussion on rolling out this change with consideration for backward compatibility. +* The base point `H` in the Pedersen commitment now depends on the token id. -* To compute the 4 byte `masked_token_id` from the `token_id`, we hash the TxOut shared secret, with a prefix, XORed with the `token_id`, and take the little endian representation of those bytes. + For backwards compatibility, we must ensure that `H_0` is the same as the generator currently used for MOB. + + ``` + H = RistrettoPoint::from_hash(Blake2b(HASH_TO_POINT_DOMAIN_TAG || RISTRETTO_BASEPOINT_BYTES ^ token_id)) + ``` - ``` - masked_token_id = (token_id ^ Blake2B(AMOUNT_TOKEN_ID_DOMAIN_TAG || shared_secret)).to_le() - ``` + So we now have an amount commitment of the form -* To recover the `token_id` from the `masked_token_id`, we reverse the process (if the bytes field is length = 4, otherwise `token_id = 0`). + `v * H_i + v_blinding * G` - ``` - unmasked_token_id = masked_token_id ^ Blake2b(AMOUNT_TOKEN_ID_DOMAIN_TAG || shared_secret) - ``` + derived using the value, token id, and blinding factor. + + It is important for us that computing `H_i` from the token id can be done in constant-time. -### Pedersen Generators +View key matching is similarly extended: +* At the same time that we unmasked the masked value, we also attempt to unmask the token id. +* We confirm successful unmasking as before, using all of the value, token id, and blinding to derive the commitment, and reject if it is not a match. -Previously, we used the same curve base point for all Pedersen generators to Amounts. After this change, we will need a curve base point, unique for each `token_id`, from which the TxOut's commitment is generated with the value of the `token_id` in the TxOut. +In this way, every `TxOut` that a client recovers now has both a value and a token id. -The requirements for the Pedersen generators are: +The `masked_token_id` will be forbidden before block version, and required after block verison 2. -* The generator used for `MOB = token_id(0)` is the same as before, so `H_0` is a known value. +### Token id configuration -* The set of all generators used for `token_ids` should be cryptographically "orthogonal", meaning that, it should be intractable for anyone to find a linear relationship between them. +In order for the enclave to handle a token id, it must know the minimum fee value for that token id. +This is configured using a `tokens.toml` file which is provided at startup to the node. -* The function mapping a `token_id`, `i`, to the generator `H_i` must be implemented in constant-time. +The data in the `tokens.toml` file which goes to the enclave is hashed into the responder id. This ensures that +nodes that do not have the same configuration will fail to attest to eachother, and so the working consensus network +agrees about what tokens have been configured and what the minimum fees are. -The established value for `H_0` in mobilecoin is to take the `RISTRETTO_BASEPOINT` bytes, hash them using Blake2b, and then hash this to the ristretto curve using the elligator map exposed by curve25519-dalek. +Attempts to send transactions using tokens that haven't yet been configured are rejected, at the time that this fee lookup fails. -We propose to compute `H_i` by converting `i` to little endian bytes, and XOR'ing these bytes over the first four bytes of the `RISTRETTO_BASEPOINT` bytes before hashing them with Blake2b, because this is a simple way to meet these requirements. +All operations involving looking up minimum fees must be constant-time in the enclave to avoid revealing token ids over side-channels. -For example, an implementation to obtain a generator for `token_id = i` could look like: +### Fee Outputs -``` -B = RistrettoPoint::from_hash(Blake2b(HASH_TO_POINT_DOMAIN_TAG || RISTRETTO_BASEPOINT ^ token_id)) -B_blinding = RISTRETTO_BASEPOINT -``` +Currently, the enclave aggregates the fees for multiple transactions in a block, in order to mint a single fee output for the block and conserve space. To support multiple confidential `token_ids`, when minting fee outputs, the enclave must create multiple fee outputs if multiple `token_ids` were used in the block. -After this change, `range_proofs`, `rct_bulletproofs`, and `ring_signature/mlsags` will be relative to a Pedersen generator, and it is not possible to construct a range proof relative to another generator if those generators are orthogonal. Thus, it is guaranteed that all transaction inputs and outputs are using the same `token_ids`. This is implied by the homomorphic encryption property of [Pedersen Commitments](https://www.cs.cornell.edu/courses/cs754/2001fa/129.PDF), namely that addition on the commitments preserves the additive relationship of the pre-committed values, as long as they are using the same group generator. `H_i` is revealed to the verifier, who must show that the sum of inputs does not overflow, or `sum(outputs) = a * G + b * H_i` for some TxOut-specific values of `a` and `b`. +Naively, we could simply aggregate the fees as before, creating only exactly as many new fee outputs as are needed. However, this means that in a block with two transactions, the number of fee outputs revealed if these two users had the same token id or a different token id. If one of the users is the attacker, then this is a source of information leakage about the other user's token id. -### Commitments +Another way to aggregate fees would be to always mint the maximum possible number of fee outputs, one for each configured token. However, this could massively increase the ratio of fee outputs and massively increase the size of the blockchain for no good reason. -To prove each TxOut's commitment to the `amount` value, we use a Pedersen commitment, consisting of a group generator (`H_i`) raised to the value of the `amount`, with a blinding factor to prevent brute-forcing the full range of values (`2^64`). We compute the blinding factor as the blinding group generator raised to a hash of the shared secret for the TxOut. +We propose a more subtle strategy, which is that, the number of fee outputs is always `min(num_token_ids, num_transactions_in_block)`. This means that in the regime of blocks with only one transaction, +there is still only one fee output, and in a block with two transactions, there are always two fee outputs, one of which will be zero if the two transactions used the same fee token id. (This is there to +prevent the information leakage mentioned earlier.) In the regime of big blocks, the `min` will always select `num_token_ids` and will always be creating a fee output for every configured token id. +This is the worst-case anyways since in a big block it is possible that every configured token id is used. -``` -blinding_factor = Blake2B(blinding_tag | shared_secret) -commitment = H_i * Scalar::from(amount) + G * blinding_factor -``` +### Fee Priority and Transaction Sorting -- Note, the group generators for `token_ids` are computed using hashing to a curve so that they are "orthogonal" for computable adversaries, meaning there is no discernable, nontrivial linear relationship between base points. See our [transaction core group generators reference](https://github.com/mobilecoinfoundation/mobilecoin/blob/master/transaction/core/src/ring_signature/mod.rs#L29). - - Note also, the blinding factor base point does not need to be unique per `token_id`, so we use the same blinding factor base point for all `token_ids`. +Currently, the enclave returns a [WellFormedTxContext](https://github.com/mobilecoinfoundation/mobilecoin/blob/a092039a4a5cc7f5e3e16eeadd0b2bc3a12667ae/consensus/enclave/api/src/lib.rs#L50) object to the untrusted side, which contains the minimal data such as the TxHash and the fee which untrusted needs to handle it. The fee value is used both in the priority queue that holds submitted transactions +before they are proposed to the network, and in the combine step, always prioritizing higher fees. -### Fee Outputs +However, since different tokens may have different scaling factors and different minimum fees, the fee may reveal quite a bit about the token id being used. +To try to remove this information, while still permitting effective sorting, we make the following assumptions: -Currently, the enclave aggregates the fees for multiple transactions in a block, in order to mint a single fee output for the block and conserve space. To support multiple confidential `token_ids`, when minting fee outputs, the enclave must create multiple fee outputs if multiple `token_ids` were used in the block. A consideration here is that we would like for an observer not to discern whether a block contains transactions of multiple asset types by statistically analyzing the number of outputs to derive information about the number of fee outputs. +* Minimum fees will, over time, be calibrated across tokens so that the value of a minimum fee in one token is similar to the value of a minimum fee in another token. + This is necessary because the smallest minimum fee is the most likely DOS vector, so we don't want any of them to be much smaller than the others. +* Revealing the ratio of the Tx fee to the minimum fee still allows effective sorting. -A simple proposal is that each block now contains a number of fee outputs scaling linearly with the total number of supported `token_ids`. This way we can continue to aggregate fees, but not reveal how many token types were included in the block. For blocks with fewer transactions than `token_ids`, the number of fee outputs is `min(num_token_ids, num_transactions_in_block)` +If we term this ratio as the `priority` of a Tx, then `token_id` and the `priority` together determine the `fee`. In this sense we have factored the `fee` into two separate pieces of information. We keep the `token_id` as secret to the enclave, but we reveal the `priority` to allow for effective transaction prioritization. These `priority` numbers may ultimately be exposed to users as well so that they can predict what fee is needed to clear the network. -Note that the untrusted portion of the node software knows how many transactions are in each block, and this is not considered statistically relevant information with respect to confidentiality. In other words, the current fee aggregation behavior is not meant as a confidentiality measure, purely as a space-saving measure. +This means for instance that, any two `Tx` submitted at the same priority level are indistinguishable to untrusted even if they use different token ids. -### Fee Priority and Transaction Sorting +To actually compute the priority, we used the following approach: +* We implemented a constant-time u64 division algorithm for the enclave, so that fee can be divided by minimum fee in constant time. +* Before dividing fee by minimum fee, we divide minimum fee by 128. + +The reason to divide minimum fee by 128 is that, currently the network is not heavily loaded and all transactions pay the minimum fee. +If the network were to become loaded, then the smallest fee that would have a higher priority than minimum fee is `minimum_fee * 2`, since +we round down after dividing. + +Dividing by 128 first means that all priority values are at least 128, and increments of 1% of the `minimum_fee` lead to a difference in the computed priority value. +This is thought to increase flexibility in the fee market and help avoid large increases in the fee if the network is under load. -Currently the [WellFormedTxContext](https://github.com/mobilecoinfoundation/mobilecoin/blob/a092039a4a5cc7f5e3e16eeadd0b2bc3a12667ae/consensus/enclave/api/src/lib.rs#L50) contains the fee, which must be used by untrusted to [sort transactions](https://github.com/mobilecoinfoundation/mobilecoin/blob/a092039a4a5cc7f5e3e16eeadd0b2bc3a12667ae/consensus/enclave/api/src/lib.rs#L131) when [combining transactions](https://github.com/mobilecoinfoundation/mobilecoin/blob/a092039a4a5cc7f5e3e16eeadd0b2bc3a12667ae/consensus/service/src/tx_manager/untrusted_interfaces.rs#L40) for consideration by the enclave, prioritizing higher fees for inclusion in the block during periods of network congestion. In order to keep the `token_id` confidential, we must provide a mechanism for untrusted to accurately sort transactions before asking the enclave to do work on the transactions, without revealing the `token_id`, and at the same time, allowing for different asset types to express differing minimum fee requirements. +This also imposes a rule that the minimum fee for any token cannot be less than 128. However, this creates no significant problems. -To derive a sorting order without revealing the transaction type, the `fee` in the `WellFormedTxContext` derives a `priority`, which is calculated based on some ratio between the minimum fees configured by the node operator on startup. For example, the network operators may have configured the following minimum fees: +#### Examples + +For example, the network operators may have configured the following minimum fees: ``` -{ - asset_000_fee = 1, - asset_111_fee = 2, - asset_888_fee = 3, -} +[[tokens]] +token_id = 0 +minimum_fee = 400000000 + +[[tokens]] +token_id = 1 +minimum_fee = 1024 +[tokens.governors] +signers = """ +$GOVERNOR_PUBKEY_1 +""" +threshold = 1 + +[[tokens]] +token_id = 2 +minimum_fee = 2048 +[tokens.governors] +signers = """ +$GOVERNOR_PUBKEY_2 ``` -The ratio between the fees for the `token_ids` is the following, using the least common multiple of the fees: - -`token_id` | `fee` | `priority` multiplier ---- | --- | --- -000 | 1 | 6 -111 | 2 | 3 -888 | 3 | 2 +These minimum fees, after being divided by 128, have these values: +`token_id` | `minimum_fee / 128` +--- | --- +0 | 31250000 +1 | 8 +2 | 16 Then, imagine that a block contains the following set of transactions with the given fees: ``` [ - transaction_1 {token_id: 000, fee: 1}, - transaction_2 {token_id: 000, fee: 1.5}, - transaction_3 {token_id: 111, fee: 2}, - transaction_4 {token_id: 888, fee: 3}, - transaction_5 {token_id: 888, fee: 4} + transaction_1 {fee_token_id: 0, fee: 4000000000}, + transaction_2 {fee_token_id: 0, fee: 6000000000}, + transaction_3 {fee_token_id: 1, fee: 1024}, + transaction_4 {fee_token_id: 2, fee: 2048}, + transaction_5 {fee_token_id: 2, fee: 4096} ] ``` -The sort order would be: +The priority computation sort order would be: -`transaction_id` | `token_id` | `priority` multiplier | original `fee` | `priority` result +`transaction_id` | `token_id` | original `fee` | `minimum_fee / 128` | `priority` result --- | --- | --- | --- | --- -transaction_2 | 000 | 6 | 1.5 | 9 -transaction_5 | 888 | 2 | 4 | 8 -transaction_1 | 000 | 6 | 1 | 6 -transaction_3 | 111 | 3 | 2 | 6 -transaction_4 | 888 | 2 | 3 | 6 +transaction_5 | 2 | 4096 | 16 | 256 +transaction_2 | 0 | 6000000000 | 192 +transaction_3 | 1 | 1024 | 8 | 128 +transaction_4 | 2 | 2048 | 16 | 128 +transaction_1 | 0 | 4000000000 | 128 Note how the two transactions paying more than their minimum fee are sorted to the top, in proportion to how much they are paying above the minimum fee, while all minimum fee transactions have an equal chance of beeing sorted because their `priority` is equal. For users who are adjusting the fees of their submitted transactions to increase the likelihood of their transaction being accepted during times of congestion, they can observe the `priority` multiplier for their `token_id`, and the recently accepted `priority` values, and determine what fee value is appropriate to increase the chances of acceptance. -### Performance Impact: Size & Space Analysis +### Extended message digest + +Finally, we would like to make a cryptography improvement around the extended message which is signed by Ring MLSAGs. +Instead of signing the concatenation of the `TxPrefix` digest with numerous amount commitment and range proof bytes, +we would prefer to hash this all in a structured way using Merlin. This is being called the `extended_message_digest` +and starting in block version 2 this will always be used for signing when creating and validating Ring MLSAGs, replacing +the old `extended_message`. -- Each TxOut has 6 new bytes allocated in the Amount object for the `masked_token_id` (4 bytes for the value, 2 bytes for Protobuf overhead) +# Drawbacks +[drawbacks]: #drawbacks + +This is a substantial change to the protocol, and a breaking change for clients. Other drawbacks include: + +* Increasing complexity of `TxOut` and transaction construction & validation +* Increasing the amount of data on the ledger +* `masked_token_id` bytes must be plumbed everywhere, in Fog and clients, but no more serious changes are needed in Fog. Note that if the `token_id` was not confidential, Fog Ledger would need a much larger change in order to properly select rings + +Compared to a design like Spats, our choice has some drawbacks: + +* Our set of generators is not fixed at build time, but instead there is a limitless pool of Pedersen generators that could be used for tokens. + The enclave must now compute these at runtime, which may cause significant overhead. If we attempt to cache them, the caching strategy must be oblivious, + or somehow arranged so that the cache operation does not reveal which token id was used. +* The enclave necessarily knows the token id of every input and output. In an SGX compromise scenario this data is revealed. +* To support mixed transactions in the future, we would need to have a bulletproof per token id used, for present revisions of bulletproofs. + This is because our current bulletproofs implementation only supports one generator at a time. In the future this could perhaps be improved. + +Choosing to allow fees in tokens other than MOB creates some privacy problems, because this design means that the fee token id is necessarily the same as the +transaction token id, and the fee token id may be easier to infer. + +* The fee token id is known to the MobileCoin foundation. +* The priority number which is revealed seems effective for purposes of preventing a passive adversary from inferring the token id from the priority, but + not necessarily an active one. An adversary which is capable of changing minimum fees on nodes *and* causing user transactions to fail and then be resubmitted + may be able to infer the token id by inferring if priority numbers change when they do this. + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +## Confidentiality Analysis + +* This design reveals no information about the sender, recipient, amount, or token id to a passive adversary who cannot penetrate SGX. +* It is impossible for an adversary who actively submits transactions at the same time as another user, and has visibility into the blockchain + and into enclave memory access patterns, to infer the token id of the other user's transaction. +* There is a token id oracle for an adversary who has root access on a node to which users are submitting transactions. (However in our opinion this + attack is likely not very practical.) +* The token id is revealed to the MobileCoin foundation which receives the fees, so in blocks with a single transaction, this data is not confidential. + +## Performance Impact: Size & Space Analysis + +- Each TxOut has 10 new bytes allocated in the Amount object for the `masked_token_id` (8 bytes for the value, 2 bytes for Protobuf overhead) - Each block now contains a number of fee outputs capped by `min(num_token_ids, num_transactions_in_block)` -### Confidentiality Analysis +The size overhead has the most significant impact on Fog, which has to store `TxOut`'s in oblvious RAM. +The number of fee outputs is still one for any block with a single transaction. For blocks with a large number of transactions, fee aggregation +still ensures that there are at most `num_token_id`'s in the block, which is the minimum which could be theoretically required. In between, there +may be some blocks with more fee output's than woudl be required. -The [TxPrefix](https://github.com/mobilecoinfoundation/mobilecoin/blob/a9db0bc7bcac9bac1c1232b194003d02bccaf283/transaction/core/src/tx.rs#L149) contains the `token_id` and the `fee` in the clear. We, and any users of the protocol, need to clearly understand the footprint of this data, and the ways in which an attacker may try to discern the `token_id` of TxOuts. +## Changes contemplated -The [TxPrefix](https://github.com/mobilecoinfoundation/mobilecoin/blob/edbc0162cbd647b6605fe817a8753508ca4515a2/transaction/core/src/tx.rs#L149) is included in the [Tx](https://github.com/mobilecoinfoundation/mobilecoin/blob/edbc0162cbd647b6605fe817a8753508ca4515a2/transaction/core/src/tx.rs#L103) that is delivered directly to the enclave through an attested channel at the [client\_tx_propose](https://github.com/mobilecoinfoundation/mobilecoin/blob/2d5190e60f6820cebcd585c19d16cecf9ba4d89b/consensus/enclave/impl/src/lib.rs#L299) endpoint. By attested, this means that the client would not deliver this payload unless the enclave's signed measurement matched the client's expectations. +* We could use a smaller integer for token types, to save space, but we would like to anticipate the possibility of more than 4 billion asset types +* We could use Merlin rather than Blake2B at various places. We mostly tried to follow existing patterns in the design. +* We could use non-confidential token types to avoid implementation complexity, but this actually adds implementation complexity: it requires much more logic around post-processing the ledger for fog. With the proposal here, fog remains oblivious to `token_id` and need not complicate how it selects rings, for example. -The [TxManager](https://github.com/mobilecoinfoundation/mobilecoin/blob/2d5190e60f6820cebcd585c19d16cecf9ba4d89b/consensus/service/src/tx_manager/mod.rs) verifies that the Tx is "well-formed" using a 2-step process. First, untrusted performs a [`well_formed_check`](https://github.com/mobilecoinfoundation/mobilecoin/blob/2d5190e60f6820cebcd585c19d16cecf9ba4d89b/consensus/service/src/validators.rs#L52:8) on the Proofs of Memberships, using a [TxContext](https://github.com/mobilecoinfoundation/mobilecoin/blob/2d5190e60f6820cebcd585c19d16cecf9ba4d89b/consensus/enclave/api/src/lib.rs#L183:12) whose TokenID is contained in the [LocallyEncryptedTx](https://github.com/mobilecoinfoundation/mobilecoin/blob/2d5190e60f6820cebcd585c19d16cecf9ba4d89b/consensus/enclave/api/src/lib.rs#L37:12) constructed by the enclave, and is therefore not in the clear to untrusted. Then the enclave [verifies that the Tx is "well-formed"](https://github.com/mobilecoinfoundation/mobilecoin/blob/cfa51d26ae943a9055698bb209c2fe06fa7a7cac/consensus/enclave/impl/src/lib.rs#L338), and outputs the [WellFormedEncryptedTx](https://github.com/mobilecoinfoundation/mobilecoin/blob/a092039a4a5cc7f5e3e16eeadd0b2bc3a12667ae/consensus/enclave/api/src/lib.rs#L46) and [WellFormedTxContext](https://github.com/mobilecoinfoundation/mobilecoin/blob/a092039a4a5cc7f5e3e16eeadd0b2bc3a12667ae/consensus/enclave/api/src/lib.rs#L50) for untrusted to manage as consensus rounds progress. Note that in this proposal, the WellFormedTxContext no longer contains the fee in the clear, but rather the `fee_ratio` as described in [Fee Ratio and Transaction Sorting](#fee-ratio-and-transaction-sorting). +## Why not do amount commitments the Spats way -Untrusted proceeds to [sort the WellFormedTxContext](https://github.com/mobilecoinfoundation/mobilecoin/blob/a092039a4a5cc7f5e3e16eeadd0b2bc3a12667ae/consensus/enclave/api/src/lib.rs#L131) by `fee_ratio` as part of the [nominate phase](https://github.com/mobilecoinfoundation/mobilecoin/blob/cfa51d26ae943a9055698bb209c2fe06fa7a7cac/consensus/scp/src/slot.rs#L613). +The spats way of doing amount commitments differs from ours -- instead of doing "vector Pedersen commitments" with a "basis vector" for each token id (and the blinding factor), +Spats prefers to introduce a single basis vector for token ids, and then essentially a second blinding factor over these. Since it is no longer sound to simply add the amount +commitments, every commitment has to have its token id component subtracted before balance checking can be performed. -Now that the Tx is validated as "well-formed," sorted and combined for a ballot with other transactions, and is a candidate for inclusion in the consensus slot, the WellFormedEncryptedTx is [broadcast](https://github.com/mobilecoinfoundation/mobilecoin/blob/2f90154a445c769594dfad881463a2d4a003d7d6/peers/src/traits.rs#L28) to the node's attested peers as required by the consensus protocol, using the [`peer_tx_propose`](https://github.com/mobilecoinfoundation/mobilecoin/blob/2d5190e60f6820cebcd585c19d16cecf9ba4d89b/consensus/enclave/impl/src/lib.rs#L328) attested endpoint. +There are advantages and disadvantages here. -The TxManager in untrusted for each node maintains a cache of encrypted txs and tx contexts that its enclave has found to be well-formed. Once the consensus protocol is complete, the untrusted TxManager calls [form_block]() which prompts the [enclave to `form_block`](https://github.com/mobilecoinfoundation/mobilecoin/blob/cfa51d26ae943a9055698bb209c2fe06fa7a7cac/consensus/enclave/impl/src/lib.rs#L421) using the full transaction contents. +Advantages of this method: -Because untrusted never sees the `fee` or `token_id` in the clear, the attacker would need to sustain an enclave exploit, such as a side channel attack, that would allow them to access data while the enclave is processing to discern the `token_id` of a transaction. With this information, the attacker could, for the duration of the exploit, determine which asset type each transaction was composed of, and could also construct a transaction graph for statistical analysis of the inputs, which are also only visible in the TxPrefix, but not persisted to the chain. Once the integrity of SGX was restored, the attacker would no longer be able to obtain information about the transaction inputs or `token_id`, and forward secrecy is preserved. Further, by being thoughtful about the memory access of the `token_id`, and using constant time operations, we can mitigate the data leakage even in the event of a side channel exploit. +* Token Id values would be confidential even from the enclave +* Fewer elliptic curve operations to create generators (since caching them may cause cache timing attacks). +* Generalizes to mixed transactions easily without requiring multiple bulletproofs. -### Upgrade Plan +Drawbacks of this method: -As this is a substantial change to the ledger format and transaction protocol, we need to be thoughtful about what rollout looks like for this change to land with minimal impact on existing clients and maximum backward compatibility. +* Additional elliptic curve operations on client and server side to deal with the extra token id blinding factors and zeroing out of token id components. +* More data sent per Tx +* May prevent us from using cryptographic schemes which would need vector Pedersen commitments, or would be simpler if we had vector Pedersen commitments. -The upgrade plan is described in full in [MCIP #26](https://github.com/mobilecoinfoundation/mcips/blob/main/text/0026-block-version-based-protocol-evolution.md). +For example, if I want to prove to you that the sum of one set of outputs is equal to some fraction of the value of some other set of outputs, in a vector Pedersen +commitment world, I can multiply both sides and add, and then reveal a commitment to zero. When the token id is instead arranged as another value, these token id +components have to be zeroed before we can multiply, and they have to be grouped by token id before we can add. This may require revealing more information. +There may be other situations where using vector Pedersen commitments will be helpful. (On the other hand, there is a risk that this will be less useful than we think.) -# Drawbacks -[drawbacks]: #drawbacks +## Why not allow fees only in MOB -This is a substantial change to the protocol, and a breaking change for clients. Other drawbacks include: +Advantages of allowing fees only in MOB: -- Increasing complexity of TxOuts and transaction construction & validation -- Increasing the amount of data on the ledger -- `masked_token_id` bytes must be plumbed everywhere, in Fog and clients, but no more serious changes are needed in Fog. Note that if the `token_id` was not confidential, Fog Ledger would need a much larger change in order to properly select rings +* Mitigates privacy concerns around the token id oracle attack +* Simplifies the task of ensuring that fees are effectively rate limiting the network +* Simplifies the enclave code, since less constant-time handling is required around fees and fee token ids +* Ensures that MOB has a central role as the gas currency of the network -# Rationale and alternatives -[rationale-and-alternatives]: #rationale-and-alternatives +Disadvantages of allowing fees only in MOB: + +* Requires users to obtain MOB in order to use another currency sent to them by their friend. This adds complexity to the user experience. This is our overriding concern. +* Requires us to implement mixed transactions support in order to ship synthetic assets at all. This increases the scope of the implementation task. -- We could use `u16` for the `token_ids` rather than `u32` to save space, but we would like to anticipate the possibility of more than 65,000 asset types. -- We could use Merlin rather than Blake2B -- We could only accept fees in one asset type, to prevent complication of client logic and enclave management of multiple fee aggregations -- We could use non-confidential token types, but this has two downsides: it breaks the ledger into multiple sets which could be statistically analyzed for `token_id`, and it requires much more logic around post processing the ledger for fog. With the proposal here, fog remains oblivious to `token_id` and need not complicate how it selects rings, for example. +Additionally, if we are able to support mixed transactions later, then revealing the fee token id is less interesting, since the user need not be paying the fee +in the token they are actually sending. This makes the fee token id oracle less significant in the longer term. # Prior art [prior-art]: #prior-art +There are many blockchains that support synthetic assets, and it is worth referring to the Ethereum approach, as well as others +- [Ethereum ERC-20 token standard](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/) +- [Algorand standard assets](https://developer.algorand.org/docs/get-details/asa/) +- [Cardano Native tokens ](https://docs.cardano.org/native-tokens/learn) + +Our approach resembles Algorand and Cardano much more than Ethereum. It is interesting that even though they have smart contracts, they thought +that a native asset approach was superior to the ERC-20 approach. + +It is perhaps more interesting to compare with other confidential asset type proposals: - [Spats](https://github.com/AaronFeickert/spats) is an extension to the [Spark](https://eprint.iacr.org/2021/1173) transaction protocol to support confidential assets - [Andrew Poelstra describes Confidential Assets](https://blog.blockstream.com/en-blockstream-releases-elements-confidential-assets/) for Blockstream, along with [this whitepaper](https://blockstream.com/bitcoin17-final41.pdf) @@ -216,6 +336,4 @@ No unresolved questions at this time. # Future possibilities [future-possibilities]: #future-possibilities -No future possibilities at this time. - - | \ No newline at end of file +In the future, we would like to use MCIPs to introduce proposals that would actually assigning token ids to newly proposed synthetic assets. From 02268239863584128a87f79999c0ae60e88f8bf9 Mon Sep 17 00:00:00 2001 From: Chris Beck Date: Tue, 10 May 2022 21:29:23 -0600 Subject: [PATCH 15/21] Update text/0025-confidential-token-ids.md Co-authored-by: Eran Rundstein --- text/0025-confidential-token-ids.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0025-confidential-token-ids.md b/text/0025-confidential-token-ids.md index ce5a4d8f..edda7366 100644 --- a/text/0025-confidential-token-ids.md +++ b/text/0025-confidential-token-ids.md @@ -20,7 +20,7 @@ Specifically, for both MOB and synthetic assets, of the transaction should remain private to the sender and recipient of the transaction. -In addition we would also like that that the token id should remain private. +In addition we would also like that the token id should remain private. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation From 56e2c6b0a1731f82597ab8cb8371111cc0d95c98 Mon Sep 17 00:00:00 2001 From: sugargoat Date: Thu, 12 May 2022 11:39:55 -0700 Subject: [PATCH 16/21] Update text/0025-confidential-token-ids.md Co-authored-by: Chris Beck --- text/0025-confidential-token-ids.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0025-confidential-token-ids.md b/text/0025-confidential-token-ids.md index edda7366..22602405 100644 --- a/text/0025-confidential-token-ids.md +++ b/text/0025-confidential-token-ids.md @@ -11,7 +11,7 @@ Enable new token types to be added to the protocol and transacted on chain witho # Motivation [motivation]: #motivation -The MobileCoin ledger protocol currently supports one token type, MOB. We would like to allow the addition of assets to the ledger without revealing which assets were involved in any transaction. The transaction confidentiality and integrity guarantees should be the same for the MobileCoin blockchain no matter how many token types are supported. +The MobileCoin ledger protocol currently supports one token type, MOB. We would like to allow the addition of new assets to the ledger. The transaction confidentiality and integrity guarantees should be the same for the MobileCoin blockchain no matter how many token types are supported. Specifically, for both MOB and synthetic assets, * the sender From 61c3ebcf050a54e6cc6bc2783ce71fbfef80bb1e Mon Sep 17 00:00:00 2001 From: sugargoat Date: Thu, 12 May 2022 11:40:04 -0700 Subject: [PATCH 17/21] Update text/0025-confidential-token-ids.md Co-authored-by: Chris Beck --- text/0025-confidential-token-ids.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0025-confidential-token-ids.md b/text/0025-confidential-token-ids.md index 22602405..9087d263 100644 --- a/text/0025-confidential-token-ids.md +++ b/text/0025-confidential-token-ids.md @@ -33,7 +33,7 @@ The transaction object may now spend inputs and outputs of any token id, but in The fee for such a transaction is also paid in that `token_id`. This is now specified in the `fee_token_id` field in the `TxPrefix`, and implies the token id for all inputs and outputs. The minimum fee varies from token id to token id. This is configured by network operators at startup, and hashed into the responder id during node-to-node attestation to ensure that the network -has one uniform value for minimum fee. +has one uniform value for minimum fee for each token. Previously, the fee of a `Tx` is exposed directly to the nodes (outside of the enclave) so that they can sort transactions by fee. After this change, the fee value may reveal information about the token id, because different tokens have different minimum fees. To mitigate this, the enclave divides the fee by the minimum fee, deriving a new value called `priority` which is revealed to the node From 7448d2e561400bb533a7004dd02ed5320f8668fc Mon Sep 17 00:00:00 2001 From: sugargoat Date: Thu, 12 May 2022 11:40:14 -0700 Subject: [PATCH 18/21] Update text/0025-confidential-token-ids.md Co-authored-by: Chris Beck --- text/0025-confidential-token-ids.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/text/0025-confidential-token-ids.md b/text/0025-confidential-token-ids.md index 9087d263..67430c6c 100644 --- a/text/0025-confidential-token-ids.md +++ b/text/0025-confidential-token-ids.md @@ -39,6 +39,12 @@ Previously, the fee of a `Tx` is exposed directly to the nodes (outside of the e token id, because different tokens have different minimum fees. To mitigate this, the enclave divides the fee by the minimum fee, deriving a new value called `priority` which is revealed to the node to sort the transactions. +For technical reasons, the minimum fee of every token now has additional requirements: + +* It must be at least 128 (in the smallest representable units) +* It must be evenly divisible by 128 + +This is not expected to pose any significant problems in practice, since we anticipate that minimum fees are will be > 10^6 when measured in the smallest representable units. # Reference-level explanation [reference-level-explanation]: #reference-level-explanation From 362ed11e42c9bda6d817aa3e2fc3b87c58f4667b Mon Sep 17 00:00:00 2001 From: sugargoat Date: Thu, 12 May 2022 11:40:23 -0700 Subject: [PATCH 19/21] Update text/0025-confidential-token-ids.md Co-authored-by: Chris Beck --- text/0025-confidential-token-ids.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0025-confidential-token-ids.md b/text/0025-confidential-token-ids.md index 67430c6c..a3d64072 100644 --- a/text/0025-confidential-token-ids.md +++ b/text/0025-confidential-token-ids.md @@ -52,7 +52,7 @@ This is not expected to pose any significant problems in practice, since we anti Recall that in current revisions of MobileCoin, the value of a `TxOut` impacts the fields of the `TxOut` in two ways: -* It appears in the masked value, where it bytes have been XOR'ed with the result of hashing the `TxOut` shared-secret +* It appears in the `masked_value`. Here the bytes of the u64 value have been XOR'ed with the result of hashing the `TxOut` shared-secret * It appears in the Pedersen commitment (`Amount::Commitment`), which has the form `v * H + v_blinding * G` Here `H` is a curve point derived by hashing to curve, and `G` is the ristretto basepoint. `v_blinding`, the blinding factor, From 5eab6e1ee1bcaa4122826ff47cd928c3347bb091 Mon Sep 17 00:00:00 2001 From: sugargoat Date: Thu, 12 May 2022 11:40:33 -0700 Subject: [PATCH 20/21] Update text/0025-confidential-token-ids.md Co-authored-by: Chris Beck --- text/0025-confidential-token-ids.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0025-confidential-token-ids.md b/text/0025-confidential-token-ids.md index a3d64072..d78232c5 100644 --- a/text/0025-confidential-token-ids.md +++ b/text/0025-confidential-token-ids.md @@ -59,7 +59,7 @@ Here `H` is a curve point derived by hashing to curve, and `G` is the ristretto is also derived by a hash of the `TxOut` shared secret. During view key matching, the recipient of a `TxOut` attempts to compute the shared secret, by key exchange with their view private key -against the `TxOut` public key. They cannot confirm that this shared secret computation was successful -- however, if it is successful, +against the `TxOut` public key. They cannot confirm directly that this shared secret computation was successful -- however, if it is successful, then they can infer the value mask and the `v_blinding` value. They can use the value mask and the masked value to compute what `v` must be, and they can then infer what the entire commitment should be. They can then check if this matches the commitment appearing in the `TxOut`. If it does, then view key matching was successful. From d59d1eb40478eb25895bcf3015e407bac00fb26d Mon Sep 17 00:00:00 2001 From: sugargoat Date: Thu, 12 May 2022 11:40:45 -0700 Subject: [PATCH 21/21] Update text/0025-confidential-token-ids.md Co-authored-by: Chris Beck --- text/0025-confidential-token-ids.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/0025-confidential-token-ids.md b/text/0025-confidential-token-ids.md index d78232c5..931b4c1d 100644 --- a/text/0025-confidential-token-ids.md +++ b/text/0025-confidential-token-ids.md @@ -213,6 +213,8 @@ transaction_1 | 0 | 4000000000 | 128 Note how the two transactions paying more than their minimum fee are sorted to the top, in proportion to how much they are paying above the minimum fee, while all minimum fee transactions have an equal chance of beeing sorted because their `priority` is equal. +This kind of example also explains why we require the minimum fee to be evenly divisible by 128. For instance, if the minimum fee for some token were `1000`, we would have `1000 / 128` rounding down to `7`, and a payment paying the minimum fee of 1000 would have a priority of `1000/7 = 142`. It would be impossible to get a priority of 128 like with the other tokens, and this would be a source of information leakage, since an adversary could reasonably infer that priority 142 Tx's in a single block are probably using that token and paying the minimum fee. + For users who are adjusting the fees of their submitted transactions to increase the likelihood of their transaction being accepted during times of congestion, they can observe the `priority` multiplier for their `token_id`, and the recently accepted `priority` values, and determine what fee value is appropriate to increase the chances of acceptance. ### Extended message digest