
Olá, queridos usuários Habr! Este artigo é sobre a Web 3.0 - a Internet descentralizada. A Web 3.0 introduz o conceito de descentralização como a base da Internet moderna. Muitos sistemas e redes de computadores exigem recursos de segurança e descentralização para atender às suas necessidades. Um registro distribuído usando a tecnologia blockchain fornece soluções eficientes para descentralização.
Blockchain é um registro distribuído. Você pode pensar nisso como um enorme banco de dados que vive para sempre, nunca mudando ao longo do tempo. O blockchain fornece a base para aplicativos e serviços da web descentralizados.
No entanto, o blockchain é mais do que apenas um banco de dados. Serve para aumentar a segurança e a confiança entre os membros da rede, aprimorando as transações comerciais online.
O consenso bizantino aumenta a confiabilidade da rede e resolve o problema de consistência.
A escalabilidade fornecida pela DLT altera as redes comerciais existentes.
O Blockchain oferece novos benefícios muito importantes:
- Evita erros dispendiosos.
- Garante transações transparentes.
- Digitaliza bens reais.
- Força contratos inteligentes.
- Aumenta a velocidade e a segurança dos pagamentos.
Desenvolvemos um PoE especial para pesquisar protocolos criptográficos e melhorar as soluções DLT e blockchain existentes.
A maioria dos sistemas de registro público não possui a propriedade de escalabilidade, tornando sua taxa de transferência bastante baixa. Por exemplo, o Ethereum processa apenas ~ 20 tx / s.
Muitas soluções foram desenvolvidas para aumentar a escalabilidade, mantendo a descentralização. No entanto, apenas duas das três vantagens - escalabilidade, segurança e descentralização - podem ser obtidas simultaneamente.
O uso de cadeias laterais fornece uma das soluções mais eficazes.
O conceito de plasma
O conceito Plasma se resume à idéia de que uma cadeia raiz processa um pequeno número de confirmações de cadeias filho, agindo assim como a camada mais segura e final para armazenar todos os estados intermediários. Cada cadeia filho funciona como seu próprio blockchain com seu próprio algoritmo de consenso, mas existem algumas ressalvas importantes.
- Contratos inteligentes são criados em uma cadeia raiz e agem como pontos de verificação para cadeias filho dentro da cadeia raiz.
- Uma cadeia filho é criada e funciona como sua própria blockchain com seu próprio consenso. Todos os estados da cadeia filho são protegidos por provas de fraude que garantem que todas as transições entre estados são válidas e aplicam um protocolo de retirada.
- Contratos inteligentes específicos para DApp ou lógica de aplicativo de cadeia filho podem ser implantados na cadeia filho.
- Os fundos podem ser transferidos da cadeia raiz para a cadeia filho.
Os validadores recebem incentivos econômicos para agir com honestidade e enviar compromissos à cadeia raiz - a camada final de liquidação da transação.
Como resultado, os usuários do DApp que trabalham na cadeia filho não precisam interagir com a cadeia raiz. Além disso, eles podem colocar seu dinheiro na cadeia raiz sempre que quiserem, mesmo que a cadeia filha seja invadida. Essas saídas da cadeia secundária permitem que os usuários armazenem seus fundos com segurança com as provas Merkle, confirmando a propriedade de uma certa quantia de fundos.
As principais vantagens do plasma estão relacionadas à sua capacidade de facilitar significativamente os cálculos que sobrecarregam a cadeia principal. Além disso, a blockchain Ethereum pode lidar com conjuntos de dados mais extensos e paralelos. O tempo removido da cadeia raiz também é transferido para os nós do Ethereum, que têm requisitos mais baixos de processamento e armazenamento.
O Plasma Cash atribui números de série exclusivos a tokens online. As vantagens desse esquema incluem a necessidade de confirmações, suporte mais simples para todos os tipos de tokens (incluindo tokens não fungíveis) e mitigação contra saídas em massa de uma cadeia infantil.
O conceito de "saídas em massa" de uma cadeia infantil é um problema enfrentado pelo plasma. Nesse cenário, as retiradas simultâneas coordenadas de uma cadeia filho podem levar a um poder computacional insuficiente para retirar todos os fundos. Como resultado, os usuários podem perder fundos.
Opções para implementar o plasma

O plasma básico tem muitas opções de implementação.
As principais diferenças se referem a:
- arquivando informações sobre armazenamento estadual e métodos de apresentação;
- tipos de token (divisível, indivisível);
- segurança de transação;
- tipo de algoritmo de consenso.
As principais variações do plasma incluem:
- Plasma baseado em UTXO - cada transação consiste em entradas e saídas. Uma transação pode ser realizada e gasta. A lista de transações não gastas é o estado da própria cadeia filha.
- Plasma baseado em conta - essa estrutura contém o reflexo e o saldo de cada conta. É usado no Ethereum, pois cada conta pode ser de dois tipos: uma conta de usuário e uma conta de contrato inteligente. A simplicidade é uma vantagem importante do plasma baseado em contas. Ao mesmo tempo, a falta de escalabilidade é uma desvantagem. Uma propriedade especial, "nonce", é usada para impedir a execução de uma transação duas vezes.
Para entender as estruturas de dados usadas na blockchain Plasma Cash e como os compromissos funcionam, é necessário esclarecer o conceito de Árvore Merkle.
Árvores Merkle e seu uso no plasma
A Merkle Tree é uma estrutura de dados extremamente importante no mundo da blockchain. Ele nos permite capturar um determinado conjunto de dados e ocultar os dados, mas provar que algumas informações estavam no conjunto. Por exemplo, se tivermos dez números, poderíamos criar uma prova para esses números e, em seguida, provar que algum número específico estava nesse conjunto. Essa prova teria um tamanho pequeno e constante, o que torna barato publicar no Ethereum.
Você pode usar esse princípio para um conjunto de transações e provar que uma transação específica está nesse conjunto. É exatamente isso que um operador faz. Cada bloco consiste em um conjunto de transações que se transforma em uma árvore Merkle. A raiz dessa árvore é uma prova publicada no Ethereum, juntamente com cada bloco de plasma.
Os usuários devem poder sacar seus fundos da cadeia de plasma. Para isso, eles enviam uma transação de "saída" para a Ethereum.
O Plasma Cash usa uma Merkle Tree especial que elimina a necessidade de validar um bloco inteiro. Basta validar apenas os ramos que correspondem ao token do usuário.
Para transferir um token, é necessário analisar seu histórico e verificar apenas os tokens necessários para um determinado usuário. Ao transferir um token, o usuário simplesmente envia o histórico inteiro para outro usuário, que pode autenticar todo o histórico e, o mais importante, fazê-lo rapidamente.

Estruturas de dados do Plasma Cash para armazenamento de estado e histórico
É recomendável usar apenas as árvores Merkle selecionadas, porque é necessário obter provas de inclusão e não inclusão para uma transação em um bloco. Por exemplo:
- Árvore de merkle esparsa
- Árvore Patricia
Desenvolvemos nossas próprias implementações Sparse Merkle Tree e Patricia Tree para o nosso cliente.
Uma árvore Merkle esparsa é semelhante a uma árvore Merkle padrão, exceto que seus dados são indexados e cada ponto de dados é colocado em uma folha que corresponde ao índice desse ponto de dados.
Suponha que tenhamos uma árvore Merkle de quatro folhas. Vamos preencher esta árvore com as letras A e D, para demonstração. A letra A é a primeira letra do alfabeto, então a colocaremos na primeira folha. Da mesma forma, colocaremos D na quarta folha.
Então, o que acontece na segunda e terceira folhas? Eles devem ser deixados em branco. Mais precisamente, um valor especial (por exemplo, zero) é usado em vez de uma letra.
A árvore eventualmente fica assim:

A prova de inclusão funciona da mesma maneira que em uma árvore Merkle comum. O que acontece se quisermos provar que C não faz parte dessa árvore Merkle? Elementar! Sabemos que se C faz parte de uma árvore, estaria na terceira folha. Se C não fizer parte da árvore, a terceira folha deve ser zero.
Tudo o que é necessário é uma prova de inclusão Merkle padrão, mostrando que a terceira folha é zero.
A melhor característica de uma Sparse Merkle Tree é que ela fornece repositórios para valores-chave dentro da Merkle Tree!
Uma parte do código do protocolo PoE constrói uma Sparse Merkle Tree:
class SparseTree { //... buildTree() { if (Object.keys(this.leaves).length > 0) { this.levels = [] this.levels.unshift(this.leaves) for (let level = 0; level < this.depth; level++) { let currentLevel = this.levels[0] let nextLevel = {} Object.keys(currentLevel).forEach((leafKey) => { let leafHash = currentLevel[leafKey] let isEvenLeaf = this.isEvenLeaf(leafKey) let parentLeafKey = leafKey.slice(0, -1) let neighborLeafKey = parentLeafKey + (isEvenLeaf ? '1' : '0') let neighborLeafHash = currentLevel[neighborLeafKey] if (!neighborLeafHash) { neighborLeafHash = this.defaultHashes[level] } if (!nextLevel[parentLeafKey]) { let parentLeafHash = isEvenLeaf ? ethUtil.sha3(Buffer.concat([leafHash, neighborLeafHash])) : ethUtil.sha3(Buffer.concat([neighborLeafHash, leafHash])) if (level == this.depth - 1) { nextLevel['merkleRoot'] = parentLeafHash } else { nextLevel[parentLeafKey] = parentLeafHash } } }) this.levels.unshift(nextLevel) } } } }
Este código é bastante trivial. Temos um repositório de valores-chave com uma prova de inclusão / não inclusão.
Em cada iteração, um nível específico de uma árvore final é preenchido, começando com o último. Dependendo se a chave da folha atual é par ou ímpar, pegamos duas folhas adjacentes e contamos o hash do nível atual. Se chegarmos ao fim, escreveremos um único merkleRoot - um hash comum.
Você precisa entender que essa árvore é preenchida com valores inicialmente vazios. Se armazenássemos uma quantidade enorme de IDs de tokens, teríamos um tamanho de árvore enorme e seria longo!
Existem muitos remédios para essa não otimização, mas decidimos mudar essa árvore para uma árvore Patricia.
Uma árvore Patricia é uma combinação de árvore Radix e árvore Merkle.
Uma chave de dados Radix Tree armazena o caminho para os próprios dados, o que nos permite criar uma estrutura de dados otimizada para a memória.

Aqui está uma implementação desenvolvida para o nosso cliente:
buildNode(childNodes, key = '', level = 0) { let node = {key} this.iterations++ if (childNodes.length == 1) { let nodeKey = level == 0 ? childNodes[0].key : childNodes[0].key.slice(level - 1) node.key = nodeKey let nodeHashes = Buffer.concat([Buffer.from(ethUtil.sha3(nodeKey)), childNodes[0].hash]) node.hash = ethUtil.sha3(nodeHashes) return node } let leftChilds = [] let rightChilds = [] childNodes.forEach((node) => { if (node.key[level] == '1') { rightChilds.push(node) } else { leftChilds.push(node) } }) if (leftChilds.length && rightChilds.length) { node.leftChild = this.buildNode(leftChilds, '0', level + 1) node.rightChild = this.buildNode(rightChilds, '1', level + 1) let nodeHashes = Buffer.concat([Buffer.from(ethUtil.sha3(node.key)), node.leftChild.hash, node.rightChild.hash]) node.hash = ethUtil.sha3(nodeHashes) } else if (leftChilds.length && !rightChilds.length) { node = this.buildNode(leftChilds, key + '0', level + 1) } else if (!leftChilds.length && rightChilds.length) { node = this.buildNode(rightChilds, key + '1', level + 1) } else if (!leftChilds.length && !rightChilds.length) { throw new Error('invalid tree') } return node }
Nós nos movemos recursivamente e construímos as subárvores esquerda e direita separadas. Uma chave foi construída como um caminho nesta árvore.
Esta solução é ainda mais trivial. É bem otimizado e funciona mais rápido. De fato, uma árvore Patricia pode ser otimizada ainda mais com a introdução de novos tipos de nós - nó de extensão, nó de ramificação e assim por diante, conforme feito no protocolo Ethereum. Mas a implementação atual atende a todos os nossos requisitos - temos uma estrutura de dados rápida e com otimização de memória.
Ao implementar essas estruturas de dados no projeto de nosso cliente, tornamos possível o dimensionamento do Plasma Cash. Isso nos permite verificar o histórico de um token e a inclusão / não inclusão do token em uma árvore, acelerando bastante a validação de blocos e a própria Cadeia Criança Plasma.
Ligações:
- Plasma de papel branco
- Hub Git
- Casos de uso e descrição da arquitetura
- Papel de rede relâmpago