Continuamos nosso ciclo sobre o funcionamento da blockchain Monero, e o artigo de hoje será dedicado ao protocolo RingCT (Transações Confidenciais em Anel), que introduz transações confidenciais e novas assinaturas em anel. Infelizmente, há pouca informação na internet sobre como ele funciona, e tentamos preencher essa lacuna.

Falaremos sobre como este protocolo oculta os valores das transferências na rede, por que foram abandonadas as clássicas assinaturas em anel do Cryptonote e como essa tecnologia irá evoluir no futuro.
Como esse protocolo é uma das tecnologias mais complexas do Monero, o leitor precisará de conhecimentos básicos sobre o funcionamento dessa blockchain e uma compreensão superficial de criptografia em curvas elípticas (para refrescar esses conhecimentos, é possível ler os primeiros capítulos do nosso artigo anterior sobre ).
O Protocolo RingCT
Uma das possíveis ameaças às criptomoedas Cryptonote é a análise da blockchain, baseada no conhecimento do valor e do tempo da transação enviada. Isso permite restringir significativamente a busca por saídas que interessam ao atacante. Para proteger-se dessa análise, o Monero implementou um protocolo de transações anônimas que oculta completamente os valores das transferências na rede.
Vale ressaltar que a ideia de ocultar valores não é nova. Um dos primeiros a descrevê-la foi o desenvolvedor do Bitcoin Core Greg Maxwell em seu . A implementação atual do RingCT é uma modificação dessa ideia, com a possibilidade de usar assinaturas em anel (como poderia ser diferente), e por isso recebeu o nome de Transações Confidenciais em Anel.
Além disso, o protocolo ajuda a resolver os problemas relacionados à fusão de saídas de troco — saídas de pequeno valor (geralmente resultantes de trocos em transações), que causavam mais problemas do que realmente valiam.
Em janeiro de 2017, ocorreu um hard fork da rede Monero, permitindo o uso opcional de transações confidenciais. Já em setembro do mesmo ano, com o hard fork da versão 6, essas transações se tornaram as únicas permitidas na rede.
O RingCT utiliza vários mecanismos: Assinaturas Anônimas em Grupo Espontâneas Linkáveis Multicamada (Multilayered Linkable Spontaneous Anonymous Group Signature, daqui em diante — MLSAG), o esquema de compromissos (Pedersen Commitments) e provas de intervalo (não há um termo estabelecido em português para este termo).
O protocolo RingCT introduz dois tipos de transações anônimas: simple e full. A primeira é gerada pela carteira quando a transação usa mais de uma entrada, enquanto a segunda ocorre na situação inversa. Elas diferem na validação dos valores das transações e nos dados assinados pela assinatura MLSAG (mais detalhes sobre isso a seguir). Vale ressaltar que transações do tipo full podem ser geradas com qualquer número de entradas, não havendo diferença fundamental. No livro , é mencionado que a decisão de limitar transações full a uma única entrada foi tomada rapidamente e pode mudar no futuro.
Assinatura MLSAG
Recordemos como são as entradas assinadas da transação. Cada transação gasta algum valor e gera. A geração de valor ocorre pela criação de saídas da transação (analogia direta — cédulas), e a saída que a transação gasta (já que, na vida real, gastamos cédulas de dinheiro) torna-se a entrada (cuidado, aqui é muito fácil se confundir).
A entrada refere-se a várias saídas, mas gasta apenas uma, criando assim uma "cortina de fumaça" para dificultar a análise do histórico de transferências. Se a transação tem mais de uma entrada, essa estrutura pode ser representada como uma matriz, onde as linhas são as entradas e as colunas são as saídas envolvidas. Para provar à rede que a transação gasta suas saídas (conhecendo suas chaves secretas), as entradas são assinadas por assinatura em anel. Essa assinatura garante que o signatário conhecia as chaves secretas de todos os elementos de alguma das colunas.
Transações confidenciais não usam mais as clássicas assinaturas em anel do , sendo substituídas pelas MLSAG — uma versão adaptada para múltiplas entradas das mesmas assinaturas em anel de camada única, .
Recebem o nome de multilayer porque assinam várias entradas ao mesmo tempo, cada uma misturada com várias outras, ou seja, assina-se uma matriz em vez de uma única linha. Como veremos adiante, isso ajuda a economizar no tamanho da assinatura.
Vamos analisar como é formada uma assinatura em anel, usando como exemplo uma transação que gasta 2 saídas reais e utiliza para a mistura m — 1 aleatórios do blockchain. Denotemos as chaves públicas das saídas que estamos gastando como
, e as imagens das chaves para elas, respectivamente:
Assim, obtemos uma matriz de tamanhos 2 x m. Primeiro, precisamos calcular os chamados challenges para cada par de saídas:

Começamos os cálculos com as saídas que gastamos, utilizando suas chaves públicas:
e números aleatórios
No final, obtemos os valores:
, que usamos para calcular o challenge
do próximo par de saídas (para facilitar a compreensão do que colocamos onde, destacamos esses valores com cores diferentes). Todos os próximos valores são calculados em círculo pelas fórmulas apresentadas na primeira ilustração. O challenge para o par de saídas reais é calculado por último.
Como podemos ver, em todas as colunas, exceto na que contém as saídas reais, são utilizados números gerados aleatoriamente
. Para π-da coluna, eles também serão necessários para nós. Transformamos
em s:
A assinatura em si é uma tupla de todos esses valores:

Em seguida, esses dados são gravados na transação.
Como podemos ver, o MLSAG contém apenas um challenge c0, o que economiza espaço na assinatura (que já exige muito espaço). Em seguida, qualquer verificador, usando os dados
, recupera os valores c1,…, cm e verifica que
. Assim, nosso anel se fechou e a assinatura passou na verificação.
Para transações RingCT do tipo full, uma linha adicional é adicionada à matriz com saídas misturadas, mas falaremos sobre isso mais adiante.
Compromissos de Pedersen
(geralmente usa-se o termo em inglês — commitments) são utilizados para que uma parte possa provar que conhece um determinado segredo (número) sem realmente revelá-lo. Por exemplo, você joga um certo número nos dados, calcula o compromisso e o transmite à parte verificada. Assim, no momento da revelação do número secreto, o verificador calcula o compromisso por conta própria, garantindo que você não o enganou.
No Monero, os compromissos são usados para ocultar os valores das transferências e aplicam a versão mais comum — compromissos de Pedersen. A propósito, um fato curioso — inicialmente, os desenvolvedores propuseram ocultar os valores simplesmente misturando, ou seja, adicionando saídas a valores arbitrários, para introduzir incerteza, mas depois mudaram para compromissos (não está claro se economizaram em tamanho de transação, como veremos a seguir).
Em geral, um compromisso tem a seguinte aparência:
Onde C — o valor do próprio compromisso, a — o valor oculto, H — um ponto fixo na curva elíptica (gerador adicional), x — uma máscara arbitrária, um fator oculto, gerado aleatoriamente. A máscara é necessária para que uma terceira parte não consiga facilmente encontrar o valor do commitment por tentativa e erro.
Ao gerar uma nova saída, a carteira calcula o commitment correspondente, e ao gastar, utiliza o valor calculado durante a geração ou recalcula-o — dependendo do tipo de transação.
RingCT simple
No caso de transações RingCT simples, para garantir que a transação criou saídas com um valor igual à soma das entradas (não gerou dinheiro do nada), a soma dos commitments das primeiras e segundas deve ser a mesma, ou seja:

O commitment da taxa é calculado de maneira um pouco diferente — sem máscara:
, onde a — o valor da taxa, que é publicamente acessível.
Essa abordagem permite provar à parte verificadora que estamos usando valores equivalentes, sem revelá-los.
Para tornar tudo mais claro, vamos considerar um exemplo. Suponha que a transação gaste duas saídas (ou seja, elas se tornam entradas) de 10 e 5 XMR e gere três saídas totalizando 12 XMR: 3, 4 e 5 XMR. Nesse caso, a taxa paga é de 3 XMR. Assim, a soma do dinheiro gasto mais a soma gerada e a taxa são iguais a 15 XMR. Vamos tentar calcular os commitments e observar a diferença entre suas somas (lembrando da matemática):

Aqui vemos que, para que a equação se equilibre, as somas das máscaras das entradas e saídas precisam ser iguais. Para isso, a carteira gera aleatoriamente x1, y1, y2 e y3, e o restante x2 é calculado assim:
![]()
Usando essas máscaras, podemos provar a qualquer verificador que não geramos recursos acima do que gastamos, sem revelar os valores. Original, não é?
RingCT full
Nas transações RingCT completas, a verificação das somas das transferências ocorre de maneira um pouco mais elaborada. Nesses casos, a carteira não recalcula os commitments para as entradas, utilizando os valores calculados em sua geração. Assume-se, assim, que a diferença das somas agora não será igual a zero, mas sim:

Aqui z — a diferença das máscaras das entradas e saídas. Se considerarmos zG como uma chave pública (o que, de fato, é), então z — é a chave privada. Assim, conhecemos a chave pública e a correspondente chave privada. Com esses dados, podemos usá-los na assinatura em anel MLSAG juntamente com as chaves públicas das saídas misturadas:

Assim, uma assinatura em anel válida garantirá que sabemos todas as chaves privadas de uma das colunas, e a chave privada na última linha só podemos conhecer se a transação não gera fundos maior do que gasta. A propósito, aqui está a resposta para a pergunta "por que a diferença das somas dos commitments não resulta em zero" — se zG = 0, então revelaremos a coluna com saídas reais.
E como o destinatário dos fundos saberá quanto dinheiro lhe foi enviado? Aqui tudo é simples — o remetente da transação e o destinatário trocam chaves pelo protocolo de Diffie-Hellman, usando a chave da transação e a chave de visualização do destinatário para calcular um segredo compartilhado. O remetente registra em campos especiais da transação os dados sobre as somas das saídas, criptografados por essa chave compartilhada.
Provas de intervalo
E o que acontece se usarmos um número negativo como soma nas commitments? Isso pode levar à geração de moedas adicionais! Esse resultado é inaceitável, por isso é necessário garantir que as somas que usamos não sejam negativas (sem revelar essas somas, claro, caso contrário, todo esse esforço seria em vão). Em outras palavras, devemos provar que a soma está no intervalo [0, 2n — 1].
Para isso, a soma de cada saída é dividida em dígitos binários e se calcula um commitment para cada dígito separadamente. Como isso acontece, é melhor analisar com um exemplo.
Suponha que as somas sejam pequenas e caibam em 4 bits (na prática, isso é 64 bits), e criamos uma saída no valor de 5 XMR. Calculamos os commitments para cada dígito e o commitment total para toda a soma:
Em seguida, cada commitment é misturado com um substituto (Ci-2iH) e é assinado em pares por uma assinatura em anel Borromean (outro tipo de assinatura em anel), proposta por Greg Maxwell em 2015 (mais detalhes sobre isso podem ser lidos ):
Tudo isso é chamado de prova de intervalo e permite garantir que as somas usadas nas commitments estão dentro do intervalo [0, 2n — 1].
E agora?
Na implementação atual, as provas de intervalo ocupam muito espaço — 6176 bytes por saída. Isso leva a transações volumosas e, consequentemente, a taxas mais altas. Para reduzir o tamanho das transações, os desenvolvedores do Monero introduzem, em vez de assinaturas de Borromeo, bulletproofs — um mecanismo de prova de intervalo sem compromissos bit a bit. , elas podem reduzir o tamanho da prova de intervalo em até 94%. A propósito, em meados de julho, a tecnologia passou por da empresa Kudelski Security, que não encontrou falhas significativas nem na tecnologia em si, nem na sua implementação. A tecnologia já está sendo utilizada na rede de testes e, com o novo hard fork, provavelmente poderá migrar para a rede principal.
Envie suas perguntas, sugira temas para novos artigos sobre tecnologias no campo das criptomoedas e também siga nosso grupo em , para ficar atualizado sobre nossos eventos e publicações.
Fonte: habr.com
