Dear everyone, my name is cipher, a member of privacy layer team at Nervos. Glad to share some interesting ideas about privacy on blockchain with you guys. Nervos has made a donation to the Grin Community to help the privacy research and implementation (A Message from Nervos). I believe we together could push on-chain privacy one more step further. Here I present a draft design about a Mimblewimble layer on Nervos, hope to hear your questions, suggestions, ideas, and any kinds of feedback!
Mimblewimble is a protocol to solve the privacy problem on blockchain. CKB is the layer 1 of Nervos Network, a public blockchain. This article gives a scheme to use the mimblewimble protocol on CKB, which users can use to trade CKB or UDT privately. This article also proposes an optional auditable wallet to meet the regulatory needs of user assets, which is an optional feature, users can decide whether their assets are regulated or not.
Nervos CKB Introduction
Nervos CKB is a general purpose UTXO based blockchain system. Every TXO here consists of four fields, capacity, data, type script, and lock script. We call the TXO a cell. The capacity field is the same as the amount field of Bitcoin TXO, it’s the amount of the Nervos native token CKBytes. Which also refers to the maximal size of a cell. The data field is used to store any user defined data, which may follow the rules defined by the lock or type script. And the script fields record some program pointer to Turing complete code to serve as on-chain verifier when transaction happens.
A Nervos CKB transaction is kind of more complicated than Bitcoin. It includes several input cells to destroy and several output cells to generate, besides, it requires witnesses with arbitrary data to provide the proof or authentication to unlock the lock script of inputs. The difference between type script and lock script is execution timing. When a cell exists on the input side to be destroyed, its lock script will be automatically loaded and run. And for every cell both on the two sides, its type script will be automatically loaded and run. The scripts execution will require some extra data in witness, such as signature, merkle tree path, and so on.
Below illustrates a real transaction on CKB:
(image from: xuejie’s blog)
Type script and lock script share the same data structure. There are three fields in a script, including the code_hash, args, and hash_type. The code_hash could be regarded as the pointer to the RISC-V program binary, and the args could be regarded as the static arguments to run this program. We could safely ignore the hash_type field, which is trivial to the privacy layer design.
Let’s take the default lock script on CKB as an example, whose signature verifier is also called secp256k1_sighashall. Its code_hash points to an on-chan binary code, and its args are set to the cell owner’s pk_hash. When a cell is destroyed on the input side, the CKB-VM will load the secp256k1_sighashall verification code according to code_hash and execute it. By running the code, the VM will first hash the transaction raw data, and recover the signature public key with the hash result and the signature from the transaction witness. Finally, the VM will compare the hash of the recoveried public key with lock script args. The transaction will be appended to the new block if all the verifications pass.
Mimblewimble Cell structure
A MW cell is an imitator of the UTXO on Mimblewimble chain, like Grin and Beam. Its data field consists of the Pedersen Commitment of the token amount. Its Type Script constrains the cell’s behaviour. And its Lock Script is empty, so that no one could trace its owner by the Lock Script. Besides, an empty Lock Script leaves more space to the future private state channel network.
A deposit cell is a cell that stores the user’s asset when it goes from the transparent world to the mimblewimble world. The deposit cell has a special Lock Script, which verifies the proof from the withdrawer. Its verification logic changes slightly according to the different asset type,
Deposit transaction (transparent to private)
A deposit transaction makes the transparent assets locked in a Deposit cell, and generates one or more MW cells. At least two MW cells in outputs are suggested to hide the MW cells’ balance.
There are two kinds of deposit behaviours, one is deposit all assets into the deposit cell, the other one is only deposit a part of the assets into the deposit cell. And the number of the generated deposit cells and MW cells could be arbitrary.
A deposit transaction mainly involves inputs, witnesses and outputs fields. And we take the deposit transaction presented in the below figure as example. In the deposit transaction, the user deposits UDT of value v’ into two MW cells, and creates a new UDT of value v-v’.
Inputs field contains a UDT cell that is used to deposit assets of value v and provide mining fee by CKBytes. And in order to spend the input cells, the depositor must provide some witnesses to be verified by the Lock Script.
There are two kinds of witnesses:
One is to spend the input cell, and it contains one signature σ1. This is the signature of this transaction under the verification keys pk1.
And the other one is to be used in the Type Scripts of output cells. It contains an Excess E (that is the verification key of σ*), a signature σ* on an empty message, and two range proofs. Among them, Excess E is used to prove that the sum of output commitments is a commitment to the deposit value v, and providing a valid signature σ* means that the depositor knows the secret keys of the coins in MW cells. The range proofs π1 and π2 are to prove that the values committed in the output cells are in [0, v_max].
The deposit transaction shifts the assets from the transparent world to the Mimblewimble world, so the outputs field consists of some MW cells to exchange in the Mimblewimble world, some UDT deposit cells to lock the corresponding assets in the transparent world, and some UDT cells to contain the remaining amount (we only show two MW cells, a UDT deposit cell and a UDT cell in the figure below).
In a MW cell, the Data field contains the commitment, the Type Script uses the witnesses to verify the cell, and the Lock Script is empty.
The UDT deposit cell is similar to the UDT cell except that UDT deposit cell is destroyed by the Deposit Lock Script, which checks asset types and works with the Type Script to complete asset withdrawal.
The UDT cell is the same as the one in the inputs field except that the amount is v-v’.
The mining fee for this deposit transaction is provided by the UDT cells. There are CKBytes in the UDT cells, and the mining fee is the difference between the CKBytes in input UDT cell and the ones in output UDT cell, i.e., fee1-fee’1 in the following figure.
Figure. An example of a deposit transaction.
MW cells could be transferred in the same way as that on Grin and Beam. The sender has to interact with the receiver to generate a valid MW transaction. And multiple MW transactions can be merged into a single transaction to save storage and break the transaction linkability.
To initiate a private transaction, the sender and receiver have to prepare the signature and range proof for every output. All the necessary data should be included in the witness field. To make it easy to aggregate with each other, we have to design a flexible witness and verification data format.
To reduce the interactive round number, we use the scheme proposed in [FOS19]. In the scheme, the sender only needs to send a message to the receiver, who completes the final transaction. We show the process of generating a transfer transaction in the following figure.
Figure. The schematic of generating a transfer transaction.
When a sender wants to transfer some MW coins of value ρ, he needs to find some his own MW coins of value v ( v>ρ) as inputs, and generate some change coins and a special output coin of value ρ. Without loss of generality, we assume that there are two MW coins (C1,C2) as inputs, an MW coin (C_s) as a change coin, and two MW coins (C1’,C2’) as the outputs for the receiver.
The sender samples a randomness a1 to generate C_s that is a commitment to (v-ρ). The change coin belongs to the sender, so only the sender knows its secret key a1. To allow the receiver to generate his own output coins, the sender creates a special output coin C* that is a commitment to ρ in advance, then the receiver will get the secret key a2 of C* and spend it by creating two output coins C1’ and C2’. In other words, the final transaction tx is the aggregation of two transactions tx1 and tx2. In tx1, the sender spends C1 and C2, and outputs C_s and C*. And in tx2, the receiver spends C*, and creates two output coins C1’ and C2’. To complete tx1, the sender provides a signature under the verification key E=C_s+C*-C1-C2 to prove that the transaction is balanced and he knows the corresponding secret keys of involved coins. And for each output coin, the sender gives a range proof to prove that the value committed in it is in [0,v_max]. Analogously, the receiver does the same things to complete the transaction tx2, and aggregates tx1 and tx2 to generate the final transfer transaction tx.
As already noted by Poelstra in [Poe16], given the aggregate of two transactions for which no cut-through occurred, it is possible to distinguish the inputs and outputs of each original transaction by solving a simple subset sum problem based on the two kernel excesses contained in the aggregate transaction. In order to break the linkability of inputs and outputs in a aggregate transaction, like Grin, we add an offset for every transfer transaction so that Σoutputs-Σinputs is equal to ΣExcess+Σoffset*G. When two transfer transaction are aggregated into a transaction, the aggregate transaction contains the sum of the two offsets. We mark the content involving offset in green.
Figure. The schematic of generating a transfer transaction with an offset.
We present the structure of a transfer transaction through an example in the following figure.
Figure. An example of a transfer transaction without CKBytes as a fee.
A transfer transaction mainly involves inputs, witnesses and outputs fields.
Inputs field contains two MW cells, and the sum v of the values committed in the two cells are larger than the value ρ that should be transferred.
Unlike in a deposit transaction, the witnesses here are used only in Type Scripts. The witnesses consist of two Excesses E1 and E2 (that are the verification keys of σ and σ’), two signatures σ and σ’, and three range proofs. The valid signatures under the two Excesses means that the input coins are spent by the owners who knows the secret keys and the transaction is balanced. And to prevent others from tampering with the fee in witnesses field, we take it as the message signed. The range proofs are to prove that the values committed in the output cells are in [0, v_max].
In each MW cell, the Data field contains the commitment, the Type Script uses the witnesses to verify the cell, and the Lock Script is empty.
In our platform, miners can only accept CKBytes as transaction fees, thus we need to transfer the fee in witnesses to some CKBytes in capacity. To this end, we first allow users to broadcast the transactions shown in the above figure, and we set up some special nodes to supplement CKBytes for these transactions, then the final transactions that could be packaged by miners are shown in the following figure. The white boxes are the content of the original transaction, and the grey boxes are added by a special node. Specifically, the special node adds a CKB cell into the inputs field, and the CKB cell contains the CKBytes that would be used to pay for the fee. In order to spend the CKB cell, the special node must provide a valid signature σ1 on this transaction to unlock the cell. The special node creates a UDT cell in the outputs field to make profit from fee charged for this service. When the final transaction is confirmed, the earning of this special node is α*fee-(fee1-fee’1) CKBytes, where α is the exchange rate of UDT and CKBytes. There may be multiple special nodes to supplement different amounts of CKBytes for the same transaction, but the final transactions with more mining fee are easier to be packaged. In other words, as for a transfer transaction from a user, only a corresponding final transaction with more CKBytes as a fee would be packaged in the end.
Figure. An example of a transfer transaction with CKBytes as a fee.
Withdrawal transaction (private to transparent)
MW cells could exit from private status, by withdrawing assets from one or more deposit cells and unlocking the equivalent amount from the deposit cells. The user has to provide necessary information to prove that 1) he knows the secret keys of the withdrawing MW cells, 2) the withdrawing amount is the same as that subtracted from MW cells.
Without loss of generality, we give an example in the following figure, in which a portion of the amount in two MW cells are withdrawn as a UDT cell while the remaining amount exists in a MW cell, and only a UDT deposit cell is involved.
A transfer transaction mainly involves inputs, witnesses and outputs fields.
Inputs field contains two MW cells, a UDT deposit cell, and a CKB cell that is used to pay for the mining fee. We assume that the user wants to withdraw the UDT of value v, so the sum of values (v1+v2) committed in the input MW cells should be larger than v, and the amount x in the UDT deposit cell should also be larger than v.
Like in a deposit transaction, the witnesses here are used in Type Scripts and Lock Scripts. The signature σ is used to unlock the CKB cell, and the rest of witnesses are used in Type Scripts. The rest of witnesses consist of an Excess E (that is the verification key of σ*), a signature σ*, and a range proof. A valid signature σ* under the Excess E means that the input coins are spent by the owners who knows the secret keys and the transaction is balanced. The range proof is to prove that the values committed in the output MW cell are in [0, v_max].
There are four kinds of output cells:
- The first is the UDT cell that includes the amount v withdrawn from the MW cells;
- The second is the UDT Deposit cell. To withdraw the assets, the user needs to unlock the equivalent amount in the UDT deposit cells, which may contain more amount. Then the remaining amount will be locked in the new UDT deposit cell.
- The third is the MW cell that is the change for the MW cells of inputs.
- The fourth is related to the mining fee. The mining fee of this transaction is fee1-fee’1.
Figure. An example of a withdrawal transaction
Transaction fee issue
To cut off the trails, any private transaction or withdrawal transaction should not involve transparent inputs, which means that MW cell or deposit cell should pay the transaction fee, and they have to reserve some CKBytes for future operations.
Script verification logics
Deposit cell lock script
In the deposit period, a deposit cell is generated in the output side. According to the CKB verification rules, the output side Lock Script is not going to run. So in the deposit period, deposit Lock Script is actually inactive.
The consistency of CKBytes is enforced by native consensus, the consistency of sUDT is enforced by sUDT Type Script, and the consistency of MW cell is enforced by the MW Type Script.
When a deposit cell exists in the inputs, it executes the withdrawal verification process, which consists of the following steps:
- Check that if there are UDT cells in the output field, whose asset type is the same as that of the UDT deposit cell.
- If so, for the transparent cells belonging to the same asset type, check that the sum of the amount in outputs is equal to the sum of the amount in inputs.
Note that the users could use the withdrawal operation to release or pay transaction fees.
MW cell type script
The MW cell Type Script will automatically be executed to verify the MW consistency if a transaction includes a MW cell, no matter which side it exists, the input side or the output side.
In real life, some assets need to be regulated for the security of the financial environment. In our scheme, we allow users to decide whether their transactions are regulated or not. If a transaction should be regulated, the owner needs to provide information about the transaction, and the information can only be obtained by the auditor. To this end, the owner should encrypt his information using a public key pk, which is held by the auditor, and only the auditor holds the corresponding secret key sk. We denote the content provided by users for audit as ‘’Audit’’, and Audit=Enc(pk,info), where info is the information that should be known by the auditor.
The information provided by users to the auditor is mainly the amount of transactions and other information that needs to be provided according to the actual situation. Recall that the data in a MW cell is a commitment to a value v, which should be revealed to the auditor, but the blinding factor of the commitment cannot be known by the auditor, as it can be used to spend this MW cell. In order to guarantee that the user cannot reveal a fake value v’≠v, the user should provide a proof to convince the auditor that the revealed value is equal to the committed one. We use sigma protocol to generate the proof.
Sigma protocol is an efficient zero-knowledge proof, and we use it to prove that the prover knows the witness r such that r*G=D. We show the interactive version in the following figure. In practice, we need the user can generate his proof in a non-interactive way, so we apply Fiat-Shamir transform to realize a non-interactive sigma protocol. To be specific, the randomness e is not chosen by verifier, but is hash(a) that can be calculated directly by prover. Therefore, the proof π is <a||hash(a)||z>.
Figure. Sigma protocol for DLOG.
In our scheme, r is the blinding factor of a commitment, and D=C-v*H, where v is the revealed value. To summarize, info is <v_1, C_1, π_1>,<v_2, C_2, π_2>… When the auditor gets the Audit in a transaction, he needs to verify the following:
- C_i in the Audit is equal to the data in the corresponding MW cell;
- Computes D_i=C_i-v_i*H;
- Parse π_i=a_i || hash(a_i) || z_i, and check if z_i*G=a_i+hash(a_i)*G;
If all of the above items are verified, the auditor approves the value provided by the user.
This article presents a privacy scheme on CKB based on mimblewimble protocol. This scheme can be easily implemented with regulatory features. Users can do private transactions on CKB by using this scheme. However, there is room to improve the privacy of this scheme, for example, in the current scheme, the asset type is visible when users do private transactions. Next, we will improve this scheme by making it indistinguishable between assets to further improve the privacy of the scheme.