Bem vindo! Novo em Zcash?
The Zcash network is young, but evolving quickly! Sign up and we'll be in touch with monthly highlights on ecosystem growth, network development and how to get started with Zcash!

Idioma

Como Funcionam as Transações Entre Endereços Protegidos

Ariel Gabizon | Nov 29, 2016

No post Anatomia de Uma Transação Zcash' fornecemos uma visão geral sobre as transações da Zcash. O objetivo deste post é oferecer uma explicação mais simples sobre como as transações que preservam a privacidade funcionam na Zcash, bem como a respeito de onde exatamente as provas de conhecimento nulo entram em cena. Quanto ao vocabulário deste post, vamos nos concentrar exclusivamente nas transações entre endereços protegidos (normalmente chamados de z-addrs).

Para nos concentrarmos em compreender o aspecto da preservação da privacidade, vamos deixar de lado tudo o que tenha a ver com a obtenção de consenso no uso da Prova de Trabalho e do blockchain para focar em um node particular que tenha obtido a lista correta de saídas de transações não gastas.

Vamos primeiramente nos lembrar de como essa lista se parece no blockchain do Bitcoin. Cada saída de transação não gasta (UTXO) pode ser considerada como uma 'nota' não gasta descrita pelo endereço/chave pública do seu proprietário e pela quantidade de BTCs que contém. Para simplificar a apresentação, vamos supor que cada uma dessas notas contenha exatamente 1 BTC e que haja, no máximo, uma nota por endereço. Assim, em determinado momento, o banco de dados de um node consistirá de uma lista de notas não gastas, no qual cada nota pode ser descrita simplesmente pelo endereço do proprietário. Por exemplo, o banco de dados poderá se parecer com isto:

\(\mathsf{Note}_1=\) \((\mathsf{PK}_1)\), \(\mathsf{Note}_2=\) \((\mathsf{PK}_2)\), \(\mathsf{Note}_3=\) \((\mathsf{PK}_3)\)

Suponha que \(\mathsf{PK}_1\) seja o endereço de Alice e que ela queira enviar sua nota de 1 BTC ao endereço de Bob \(\mathsf{PK}_4.\) Ela envia uma mensagem que essencialmente diz "Transferir 1 BTC de \(\mathsf{PK}_1\) para \(\mathsf{PK}_4\)" a todos os nodes. Ela assina essa mensagem com a chave secreta \(\mathsf{sk}_1\) correspondente a \(\mathsf{PK}_1,\) e isso convence o node de que ela possui o direito de transferir dinheiro a partir de \(\mathsf{PK}_1.\) Após verificar a assinatura e analisar se realmente há uma nota de 1 BTC com endereço \(\mathsf{PK}_1,\) o node irá atualizar sua base de dados em conformidade:

\(\mathsf{Note}_4=\) \((\mathsf{PK}_4)\), \(\mathsf{Note}_2=\) \((\mathsf{PK}_2)\), \(\mathsf{Note}_3=\) \((\mathsf{PK}_3)\)

Agora, suponha que cada nota também contenha um 'número de série' aleatório (também conhecido como identificador exclusivo) \(r.\) Em breve, veremos que isso é útil para se obter privacidade. Dessa forma, o banco de dados poderá se parecer com isto:

\(\mathsf{Note}_1=\) \((\) \(\mathsf{PK}_1\) \(,\) \(r_1)\), \(\mathsf{Note}_2=\) \((\) \(\mathsf{PK}_2\) \(,\) \(r_2)\), \(\mathsf{Note}_3=\) \((\) \(\mathsf{PK}_3\) \(,\) \(r_3)\)

Um primeiro passo natural em direção à privacidade seria fazer com que o node armazenasse apenas "criptografias", ou simplesmente hashes, das notas, em vez das próprias notas.

\(\mathsf{H}_1=\) \(\mathbf{HASH}(\mathsf{Note}_1)\), \(\mathsf{H}_2=\) \(\mathbf{HASH}(\mathsf{Note}_2)\), \(\mathsf{H}_3=\) \(\mathbf{HASH}(\mathsf{Note}_3)\)

Como segundo passo para preservar a privacidade, o node continuará a armazenar o hash de uma nota, mesmo depois de ela ter sido gasta. Então, não mais se tratará de um banco de dados de notas não gastas, mas de um banco de dados de todas as notas que já existiram.

A questão principal, agora, é como distinguir, sem acabar com a privacidade, as notas que foram gastas das notas que não foram gastas. É aqui que o conjunto de nullifiers entra em cena. Esta é uma lista de hashes de todos os números de série de notas que foram gastas. Cada node armazena o conjunto de nullifiers, além do conjunto de notas com hash. Por exemplo, após \(\mathsf{Note}_2\) ser gasto, o banco de dados do node poderá ficar desta forma:

Hashed notes Nullifier set
\(\mathsf{H}_1=\) \(\mathbf{HASH}(\mathsf{Note}_1)\) \(\mathsf{nf}_1=\) \(\mathbf{HASH}(\mathsf{r}_2)\)
\(\mathsf{H}_2=\) \(\mathbf{HASH}(\mathsf{Note}_2)\)  
\(\mathsf{H}_3=\) \(\mathbf{HASH}(\mathsf{Note}_3)\)  

Como é feita uma transação

Agora, suponha que Alice possua \(\mathsf{Note}_1\) e queira enviá-lo para Bob, cuja chave pública é \(\mathsf{PK}_4.\) Para simplificar, vamos presumir que Alice e Bob têm um canal privado entre eles, embora isso não seja realmente necessário na Zcash. Basicamente, Alice invalidará sua nota ao publicar seu nullifier, e, ao mesmo tempo, criará uma nova nota controlada por Bob.

Mais precisamente, ela faz o seguinte:

  1. Ela escolhe aleatoriamente um novo número de série \(r_4\) e define a nova nota \(\mathsf{Note}_4=\) \((\) \(\mathsf{PK}_4\) \(,\) \(r_4).\)
  2. Ela envia \(\mathsf{Note}_4\) para Bob de forma particular.
  3. Ela envia o nullifier de \(\mathsf{Note}_1,\) \(\mathsf{nf}_2=\) \(\mathbf{HASH}(\mathsf{r}_1)\) para todos os nodes.
  4. Ela envia o hash da nova nota \(\mathsf{H}_4=\) \(\mathbf{HASH}(\mathsf{Note}_4)\) para todos os nodes.

Agora, quando um node receber \(\mathsf{nf}_2\) e \(\mathsf{H}_4,\) ele verificará se a nota correspondente a \(\mathsf{nf}_2\) já foi gasta, simplesmente analisando se \(\mathsf{nf}_2\) já existe no conjunto de nullifiers. Em caso negativo, o node adicionará \(\mathsf{nf}_2\) ao conjunto de nullifiers e incluirá \(\mathsf{H}_4\) no conjunto de notas com hash — validando, assim, a transação entre Alice e Bob.

Hashed notes Nullifier set
\(\mathsf{H}_1=\) \(\mathbf{HASH}(\mathsf{Note}_1)\) \(\mathsf{nf}_1=\) \(\mathbf{HASH}(\mathsf{r}_2)\)
\(\mathsf{H}_2=\) \(\mathbf{HASH}(\mathsf{Note}_2)\) \(\mathsf{nf}_2=\) \(\mathbf{HASH}(\mathsf{r}_1)\)
\(\mathsf{H}_3=\) \(\mathbf{HASH}(\mathsf{Note}_3)\)  
\(\mathsf{H}_4=\) \(\mathbf{HASH}(\mathsf{Note}_4)\)  

... Porém, espere um segundo: verificamos que \(\mathsf{Note}_1\) não foi gasto anteriormente... Mas não verificamos se ele pertence a Alice. Na verdade, sequer verificamos se ele era uma nota "real", isto é, real no sentido de que seu hash fazia parte da tabela do node de notas com hash. A maneira simples de corrigir isso seria Alice simplesmente publicar \(\mathsf{Note}_1,\) em vez de seu hash; mas, obviamente, isso prejudicaria a privacidade que estamos tentando obter.

É aqui que as provas de conhecimento nulo chegam para ajudar:

Além das etapas acima, Alice publicará uma string de prova \(\pi\) para convencer os nodes de que quem quer que seja que publicou esta transação conhece os valores \(\mathsf{PK}_1,\) \(\mathsf{sk}_1,\) e \(r_1\) de forma que

  1. O hash da nota \(\mathsf{Note}_1=\) (\(\mathsf{PK}_1,\) \(r_1)\) existe no conjunto de notas com hash.
  2. \(\mathsf{sk}_1\) é a chave privada correspondente a \(\mathsf{PK}_1\) (assim, portanto, quem quer que a conheça é o proprietário legítimo de \(\mathsf{Note}_1)\).
  3. O hash de \(r_1\) é \(\mathsf{nf}_2\) (e, portanto, se \(\mathsf{nf}_2\) - que, agora, sabemos ser o nullifer de \(\mathsf{Note}_1\) - não estiver atualmente no conjunto de nullifiers, \(\mathsf{Note}_1\) ainda não foi gasto).

As propriedades das provas de conhecimento nulo garantirão que nenhuma informação sobre \(\mathsf{PK}_1,\) \(\mathsf{sk}_1\) ou \(r_1\) seja revelada por \(\pi\).

Os principais locais acima onde trapaceamos ou omitimos detalhes

Ressaltamos que esta descrição foi simplificada e recomendamos a leitura da especificação do protocolo para detalhes completos.

Aqui estão alguns dos principais aspectos desconsiderados:

  1. As notas com hash devem ser armazenadas não apenas na forma de lista, mas em uma árvore de Merkle. Essa medida possui papel importante para tornar eficientes as provas de conhecimento nulo. Além disso, é preciso armazenar um compromisso de ocultação e vinculação computacional da nota, e não apenas seu hash.
  2. O nullifier deve ser definido de maneira um pouco mais complexa, para garantir a privacidade futura do destinatário em relação ao remetente.
  3. Não entramos em detalhes sobre como eliminar a necessidade de um canal privado entre remetente e destinatário.