We continue our series on the Monero blockchain, and today's article will focus on the RingCT protocol (Ring Confidential Transactions), which introduces confidential transactions and new ring signatures. Unfortunately, there is little information online about how it works, and we have tried to fill this gap.

We will discuss how this protocol allows the network to conceal the amounts of transfers, why it abandoned the classic ring signatures typical of Cryptonote, and how this technology will develop further.
Since this protocol is one of the most complex technologies in Monero, readers will need a basic understanding of how this blockchain operates and a superficial grasp of elliptic curve cryptography (to refresh this knowledge, one can read the first chapters of our previous article on ).
The RingCT Protocol
One possible attack on Cryptonote currencies is blockchain analysis based on knowledge of the amount and time of a sent transaction. This allows to significantly narrow down the search area for outputs of interest to an attacker. To protect against such analysis, Monero has implemented an anonymous transaction protocol that completely conceals the amounts of transfers in the network.
It's worth noting that the idea of concealing amounts is not new. One of the first to describe it was Bitcoin Core developer Greg Maxwell in his . The current implementation of RingCT serves as a modification to it with the ability to use ring signatures (as expected), and thus it got its name — Ring Confidential Transactions.
Moreover, the protocol helps eliminate issues with mixing dust outputs — outputs of small amounts (usually resulting from change in transactions) that caused more problems than they were worth.
In January 2017, a hard fork of the Monero network took place, allowing the optional use of confidential transactions. By September of the same year, from the hard fork version 6, such transactions became the only allowed ones in the network.
RingCT employs several mechanisms: Multilayered Linkable Spontaneous Anonymous Group Signatures (MLSAG), Pedersen Commitments, and range proofs (there is no established translation for this term into Russian).
The RingCT protocol introduces two types of anonymous transactions: simple and full. The former is generated by the wallet when a transaction uses more than one input, while the latter occurs in the opposite situation. They differ in the validation of transaction amounts and the data signed with the MLSAG signature (we will discuss this in more detail below). Notably, full transactions can be generated with any number of inputs, as there is no fundamental difference. In the book it is mentioned that the decision to limit full transactions to a single input was made hastily and may change in the future.
MLSAG signature
Let’s recall what the signed inputs of a transaction represent. Each transaction spends some funds and generates. The generation of funds occurs by creating outputs of the transaction (a direct analogy would be banknotes), and the output that the transaction spends (since in real life we spend actual banknotes) becomes the input (be careful, it can be easy to get confused here).
An input references multiple outputs but only spends one, thereby creating a 'smoke veil' to complicate the analysis of transaction history. If a transaction has more than one input, this structure can be represented as a matrix, where the rows are inputs and the columns are the mixed outputs. To prove to the network that the transaction spends its own outputs (knows their secret keys), inputs are signed with a ring signature. This signature guarantees that the signer knew the secret keys of all elements of any of the columns.
Confidential transactions no longer use the classical ring signatures; they have been replaced by MLSAG — an adapted version for multiple inputs of similar single-layer ring signatures, .
They are called multi-layered because they sign multiple inputs at once, each mixed with several others, i.e., a matrix is signed rather than a single row. As we will see later, this helps save on signature size.
Let’s explore how a ring signature is formed using the example of a transaction that spends 2 real outputs and uses m - 1 random ones from the blockchain for mixing. We will denote the public keys of the outputs being spent as
, and the key images for them respectively:
Thus, we have a matrix of size 2 x m. First, we need to calculate the so-called challenges for each pair of outputs:

We start computing with the outputs we are spending, using their public keys:
and random numbers
As a result, we get the values:
, which we use to calculate the challenge
for the next pair of outputs (to make it easier to understand what fits where, we highlighted these values in different colors). All subsequent values are calculated in a loop based on the formulas shown in the first illustration. The last to be calculated is the challenge for the pair of real outputs.
As we can see, in all columns except the one containing the real outputs, randomly generated numbers are used
. For π-th column, we will need those as well. Let's transform
to s:
The signature itself consists of a tuple of all these values:

Next, this data is recorded in the transaction.
As we can see, MLSAG contains only one challenge c0, which helps save on the size of the signature (which already requires a lot of space). Then any verifier, using the data
, reconstructs the values c1,…, cm and verifies that
. Thus, our ring is closed and the signature has passed the verification.
For RingCT transactions of the full type, an additional line is added to the matrix with mixed outputs, but we will discuss this below.
Pedersen Commitments
(the English term 'commitments' is more commonly used) are used so that one party can prove it knows a certain secret (number) without actually revealing it. For example, you roll a number on dice, compute the commitment, and pass it to the verifying party. Thus, at the moment of revealing the secret number, the verifier independently calculates the commitment, thereby ensuring you did not deceive them.
In Monero, commitments are used to hide the amounts of transfers and employs the most common variant — Pedersen commitments. By the way, an interesting fact — initially, developers suggested hiding amounts using regular mixing, meaning adding outputs to arbitrary sums to introduce uncertainty, but later switched to commitments (though it is not a fact that it saved on transaction size, as we will see below).
In general, a commitment looks as follows:
Where C — the value of the commitment itself, a — the hidden amount, H — a fixed point on an elliptical curve (additional generator), and x — an arbitrary mask, a hiding factor, randomly generated. The mask is necessary so that a third party cannot simply guess the value of commitment through brute force.
When generating a new output, the wallet calculates the commitment for it, and when spending, it either takes the value calculated during generation or recalculates it—depending on the type of transaction.
RingCT simple
In the case of simple RingCT transactions, to ensure that the transaction creates outputs equal to the sum of inputs (i.e., does not create money out of thin air), the sum of the first and second commitments must be the same, that is:

The commitment of the fee is calculated slightly differently—without the mask:
, where a — the amount of the fee, which is publicly accessible.
This approach allows us to prove to the verifying party that we are using the same amounts without revealing them.
To clarify, let's consider an example. Suppose the transaction spends two outputs (i.e., they become inputs) of 10 and 5 XMR and generates three outputs totaling 12 XMR: 3, 4, and 5 XMR. It pays a fee of 3 XMR. Thus, the total amount of spent money plus the generated amount and the fee equals 15 XMR. Let's try to calculate the commitments and see the difference in their sums (let's remember math):

Here we see that for the equation to hold—the sums of the masks of inputs and outputs need to be the same. To achieve this, the wallet randomly generates x1, y1, y2, and y3, while the remaining x2 is calculated as follows:
![]()
By using these masks, we can prove to any verifier that we are not generating more funds than we are spending, without revealing the amounts. Original, isn’t it?
RingCT full
In full RingCT transactions, the verification of the transfer sums is a bit more intricate. In these transactions, the wallet does not recalculate commitments for inputs, but uses those calculated during their generation. It should be assumed that the difference in sums will no longer equal zero, but instead:

Here z — the difference in masks of inputs and outputs. If we consider zG as a public key (which it essentially is), then z — this is a private key. Thus, we know the public key and its corresponding private keys. With this information, we can use them in the MLSAG ring signature along with the public keys of the mixed outputs:

Thus, a valid ring signature will guarantee that we know all private keys of one of the columns, and the private key in the last row can only be known if the transaction does not generate more funds than it spends. By the way, here’s the answer to the question "why does the difference of commitments sums not equal zero" — if zG = 0, then we will disclose the column with real outputs.
But how does the recipient know how much money they received? Here it’s simple — the transaction sender and recipient exchange keys using the Diffie-Hellman protocol, utilizing the transaction key and the recipient’s view key to compute a shared secret. The sender records the amounts of the outputs in special transaction fields, encrypted with this shared key.
Range proofs
What if a negative number is used as the sum in commitments? This could lead to generating additional coins! Such an outcome is unacceptable, so we need to ensure that the amounts we use are non-negative (without revealing these amounts, of course; otherwise, all that effort would be in vain). In other words, we must prove that the sum lies in the range [0, 2n — 1].
To do this, the sum of each output is divided into binary digits and a commitment is computed for each digit separately. It’s better to illustrate this with an example.
Suppose our sums are small and fit into 4 bits (in practice, this is 64 bits), and we create an output for the amount of 5 XMR. We calculate commitments for each digit and the overall commitment for the entire amount:
Next, each commitment is mixed with a surrogate (Ci-2iH) and pairwise signed with a Borromean ring signature (another type of ring signature), proposed by Greg Maxwell in 2015 (more details can be found in ):
All of this is called a range proof and allows us to guarantee that the amounts used in commitments lie within the specified range. [0, 2n — 1].
What's next?
In the current implementation, range proofs take up a lot of space — 6176 bytes per output. This leads to larger transactions and, consequently, higher fees. To reduce the size of transactions, Monero developers are introducing bulletproofs instead of Bulletproof signatures — a mechanism for range proofs without bitwise commitments. , they can reduce the size of range proofs by up to 94%. Interestingly, in mid-July, the technology underwent by Kudelski Security, which found no significant flaws in either the technology itself or its implementation. The technology is already being used in the test network, and with the upcoming hard fork, it may also transition to the main network.
Feel free to ask your questions, suggest topics for new articles about technologies in the field of cryptocurrencies, and subscribe to our group on , to stay updated on our events and publications.
Source: habr.com
