Conflito de contratos inteligente: Hyperledger Fabric vs MultiChain vs Ethereum vs Corda

Nó Fonte: 1585333

Há mais de uma maneira de colocar código em um blockchain

Na maioria das discussões sobre blockchains, não demora muito para que a noção de “contratos inteligentes” surja. Na imaginação popular, os contratos inteligentes automatizam a execução de interações entre partes, sem exigir um intermediário confiável. Ao expressar as relações jurídicas em código e não em palavras, prometem permitir que as transações ocorram diretamente e sem erros, sejam elas deliberadas ou não.

Do ponto de vista técnico, um contrato inteligente é algo mais específico: um código de computador que reside em uma blockchain e define as regras para as transações dessa cadeia. Esta descrição parece bastante simples, mas por trás dela existe uma grande variação na forma como estas regras são expressas, executadas e validadas. Ao escolher uma plataforma blockchain para uma nova aplicação, a pergunta “Esta plataforma suporta contratos inteligentes?” não é o certo para perguntar. Em vez disso, precisamos perguntar: “Que tipo de contratos inteligentes esta plataforma suporta?”

Neste artigo, meu objetivo é examinar algumas das principais diferenças entre as abordagens de contratos inteligentes e as compensações que elas representam. Farei isso examinando quatro plataformas blockchain empresariais populares que suportam alguma forma de código on-chain personalizado. Primeiro, a IBM Tela de hiperligação, que chama seus contratos de “chaincode”. Em segundo lugar, a nossa plataforma MultiChain, que apresenta filtros inteligentes na versão 2.0. Terceiro, Ethereum (e é permitido Quorum e Toca spin-offs), que popularizou o nome “contrato inteligente”. E finalmente, R3 Corda, que faz referência a “contratos” em suas transações. Apesar de toda a terminologia diferente, em última análise, todos se referem à mesma coisa – código específico da aplicação que define as regras de uma cadeia.

Antes de prosseguir, devo alertar o leitor que muito do conteúdo a seguir é de natureza técnica e pressupõe alguma familiaridade com programação geral e conceitos de banco de dados. Para o bem ou para o mal, isso não pode ser evitado – sem entrar em detalhes é impossível tomar uma decisão informada sobre usar um blockchain para um projeto específico e (se for o caso) o tipo certo de blockchain a ser usado.

Noções básicas de blockchain

Vamos começar com algum contexto. Imagine um aplicativo compartilhado por várias organizações, baseado em um banco de dados subjacente. Numa arquitetura centralizada tradicional, esta base de dados é alojada e administrada por uma única parte em quem todos os participantes confiam, mesmo que não confiem uns nos outros. As transações que modificam o banco de dados são iniciadas apenas por aplicações nos sistemas desta parte central, muitas vezes em resposta a mensagens recebidas dos participantes. O banco de dados simplesmente faz o que é mandado porque o aplicativo é implicitamente confiável para enviar apenas transações que façam sentido.

Blockchains fornecem uma forma alternativa de gerenciar um banco de dados compartilhado, sem um intermediário confiável. Numa blockchain, cada participante executa um “nó” que contém uma cópia da base de dados e processa de forma independente as transações que a modificam. Os participantes são identificados usando chaves públicas ou “endereços”, cada um dos quais possui uma chave privada correspondente conhecida apenas pelo proprietário da identidade. Embora as transações possam ser criadas por qualquer nó, elas são “assinadas digitalmente” pela chave privada do seu iniciador, a fim de provar a sua origem.

Os nós se conectam entre si de maneira peer-to-peer, propagando rapidamente as transações e os “blocos” nos quais elas são registradas e confirmadas pela rede. O próprio blockchain é literalmente uma cadeia desses blocos, que forma um registro ordenado de cada transação histórica. Um “algoritmo de consenso” é usado para garantir que todos os nós cheguem a um acordo sobre o conteúdo da blockchain, sem exigir controle centralizado. (Observe que parte desta descrição não se aplica ao Corda, no qual cada nó possui apenas uma cópia parcial do banco de dados e não há blockchain global. Falaremos mais sobre isso mais tarde.)

Em princípio, qualquer aplicativo de banco de dados compartilhado pode ser arquitetado usando um blockchain em seu núcleo. Mas fazer isso cria uma série de desafios técnicos que não existem num cenário centralizado:

  • Regras de transação. Se algum participante puder alterar diretamente o banco de dados, como podemos garantir que ele siga as regras da aplicação? O que impede um usuário de corromper o conteúdo do banco de dados de forma egoísta?
  • Determinismo. Depois que essas regras forem definidas, elas serão aplicadas diversas vezes por vários nós ao processar transações para sua própria cópia do banco de dados. Como podemos garantir que cada nó obtenha exatamente o mesmo resultado?
  • Prevenção de conflitos. Sem coordenação central, como lidamos com duas transações que seguem as regras do aplicativo, mas ainda assim entram em conflito entre si? Os conflitos podem resultar de uma tentativa deliberada de manipular o sistema ou ser o resultado inocente de má sorte e timing.

Então, onde entram os contratos inteligentes, os filtros inteligentes e o chaincode? Seu objetivo principal é trabalhar com a infraestrutura subjacente de um blockchain para resolver esses desafios. Os contratos inteligentes são o equivalente descentralizado do código do aplicativo – em vez de serem executados em um local central, eles são executados em vários nós do blockchain, criando ou validando as transações que modificam o conteúdo desse banco de dados.

Vamos começar com as regras de transação, o primeiro desses desafios, e ver como elas são expressas em Fabric, MultiChain, Ethereum e Corda respectivamente.

Regras de transação

As regras de transação desempenham uma função específica em bancos de dados baseados em blockchain – restringindo o transformações que pode ser executado no estado desse banco de dados. Isto é necessário porque as transações de uma blockchain podem ser iniciadas por qualquer um dos seus participantes, e esses participantes não confiam uns nos outros o suficiente para permitir que modifiquem o banco de dados à vontade.

Vejamos dois exemplos de por que as regras de transação são necessárias. Primeiro, imagine um blockchain projetado para agregar e registrar a data e hora de documentos PDF publicados por seus participantes. Neste caso, ninguém deveria ter o direito de remover ou alterar documentos, pois isso prejudicaria todo o propósito do sistema – a persistência de documentos. Em segundo lugar, considere uma blockchain que representa um livro-razão financeiro partilhado, que monitoriza os saldos dos seus utilizadores. Não podemos permitir que um participante aumente arbitrariamente o seu próprio saldo ou retire o dinheiro de terceiros.

Entradas e saídas

Nossas plataformas blockchain contam com duas abordagens amplas para expressar regras de transação. O primeiro, que chamo de “modelo de entrada-saída”, é usado no MultiChain e no Corda. Aqui, as transações listam explicitamente as linhas ou “estados” do banco de dados que elas excluem e criam, formando um conjunto de “entradas” e “saídas”, respectivamente. Modificar uma linha é expresso como a operação equivalente de excluir essa linha e criar uma nova em seu lugar.

Como as linhas do banco de dados são excluídas apenas nas entradas e criadas apenas nas saídas, cada entrada deve “gastar” a saída de uma transação anterior. O estado atual do banco de dados é definido como o conjunto de “saídas de transações não gastas” ou “UTXOs”, ou seja, saídas de transações anteriores que ainda não foram utilizadas. As transações também podem conter informações adicionais, chamadas “metadados”, “comandos” ou “anexos”, que não passam a fazer parte da base de dados, mas ajudam a definir o seu significado ou finalidade.

Dados estes três conjuntos de entradas, saídas e metadados, a validade de uma transação em MultiChain ou Corda é definida por algum código que pode realizar cálculos arbitrários nesses conjuntos. Este código pode validar a transação, ou então retornar um erro com uma explicação correspondente. Você pode pensar no modelo de entrada-saída como um “inspetor” automatizado que mantém uma lista de verificação que garante que as transações sigam todas as regras. Se a transação falhar em qualquer uma dessas verificações, ela será automaticamente rejeitada por todos os nós da rede.

Deve-se notar que, apesar de compartilharem o modelo de entrada-saída, MultiChain e Corda o implementam de forma muito diferente. No MultiChain, as saídas podem conter ativos e/ou dados em formato JSON, texto ou binário. As regras são definidas em “filtros de transação” ou “filtros de fluxo”, que podem ser configurados para verificar todas as transações, ou apenas aquelas que envolvem determinados ativos ou agrupamentos de dados. Por outro lado, um “estado” de saída Corda é representado por um objeto na linguagem de programação Java ou Kotlin, com campos de dados definidos. As regras de Corda são definidas em “contratos” anexados a estados específicos, e o contrato de um estado só é aplicado a transações que contenham esse estado em suas entradas ou saídas. Isso se relaciona com Corda modelo de visibilidade incomum, em que as transações só podem ser vistas pelas suas contrapartes ou por aquelas cujas transações subsequentes afetam.

Contratos e mensagens

A segunda abordagem, que chamo de “modelo de mensagem de contrato”, é usada no Hyperledger Fabric e no Ethereum. Aqui, vários “contratos inteligentes” ou “chaincodes” podem ser criados no blockchain, e cada um tem seu próprio banco de dados e código associado. O banco de dados de um contrato só pode ser modificado pelo seu código, e não diretamente pelas transações blockchain. Este padrão de design é semelhante ao “encapsulamento” de código e dados na programação orientada a objetos.

Com este modelo, uma transação blockchain começa como uma mensagem enviada a um contrato, com alguns parâmetros ou dados opcionais. O código do contrato é executado em reação à mensagem e aos parâmetros e é livre para ler e gravar seu próprio banco de dados como parte dessa reação. Os contratos também podem enviar mensagens para outros contratos, mas não podem acessar diretamente os bancos de dados uns dos outros. Na linguagem dos bancos de dados relacionais, os contratos atuam como Forçado “procedimentos armazenados”, onde todo acesso ao banco de dados ocorre através de algum código predefinido.

Tanto o Fabric quanto o Quorum, uma variação do Ethereum, complicam esse quadro ao permitir que uma rede defina múltiplos “canais” ou “estados privados”. O objetivo é mitigar o problema da confidencialidade da blockchain através da criação de ambientes separados, cada um dos quais visível apenas para um determinado subgrupo de participantes. Embora isto pareça promissor em teoria, na realidade os contratos e dados em cada canal ou estado privado estão isolados dos dos outros. Como resultado, em termos de contratos inteligentes, estes ambientes são equivalentes a blockchains separados.

Regras de exemplo

Vamos ver como implementar as regras de transação para um livro-razão financeiro de ativo único com esses dois modelos. Cada linha do banco de dados do nosso razão possui duas colunas, contendo o endereço do proprietário e a quantidade do ativo possuído. No modelo de entrada-saída, as transações devem satisfazer duas condições:

  1. A quantidade total de ativos nas saídas de uma transação deve corresponder ao total nas suas entradas. Isso evita que os usuários criem ou excluam dinheiro arbitrariamente.
  2. Toda transação deve ser assinada pelo proprietário de cada uma de suas entradas. Isso impede que os usuários gastem o dinheiro uns dos outros sem permissão.

Tomadas em conjunto, estas duas condições são tudo o que é necessário para criar um sistema financeiro simples mas viável.

No modelo contrato-mensagem, o contrato do ativo suporta uma mensagem de “envio de pagamento”, que leva três parâmetros: endereço do remetente, endereço do destinatário e quantidade a ser enviada. Em resposta, o contrato executa as quatro etapas a seguir:

  1. Verifique se a transação foi assinada pelo remetente.
  2. Verifique se o remetente possui fundos suficientes.
  3. Subtraia a quantidade solicitada da linha do remetente.
  4. Adicione essa quantidade à linha do destinatário.

Se alguma das verificações nas duas primeiras etapas falhar, o contrato será anulado e nenhum pagamento será efetuado.

Portanto, tanto os modelos de entrada-saída quanto de contrato-mensagem são formas eficazes de definir regras de transação e manter um banco de dados compartilhado seguro. Na verdade, a nível teórico, cada um destes modelos pode ser utilizado para simular o outro. Na prática, porém, o modelo mais apropriado dependerá da aplicação que está sendo construída. Cada transação afeta poucas ou muitas informações? Precisamos ser capazes de garantir a independência das transações? Cada dado tem um proprietário claro ou existe algum estado global a ser compartilhado?

Está além do nosso escopo explorar como as respostas devem influenciar a escolha entre esses dois modelos. Mas como orientação geral, ao desenvolver uma nova aplicação blockchain, vale a pena tentar expressar suas regras de transação em ambas as formas, e ver qual delas se ajusta mais naturalmente. A diferença se expressará em termos de: (a) facilidade de programação, (b) requisitos de armazenamento e rendimento e (c) velocidade de detecção de conflitos. Falaremos mais sobre esse último assunto mais adiante.

Regras integradas

Quando se trata de regras de transação, há uma maneira pela qual MultiChain difere especificamente de Fabric, Ethereum e Corda. Ao contrário dessas outras plataformas, o MultiChain possui várias abstrações integradas que fornecem alguns blocos de construção básicos para aplicativos baseados em blockchain, sem Exigindo desenvolvedores escrevam seu próprio código. Essas abstrações cobrem três áreas que são comumente necessárias: (a) permissões dinâmicas, (b) ativos transferíveis e (c) armazenamento de dados.

Por exemplo, MultiChain gerencia permissões para conexão à rede, envio e recebimento de transações, criação de ativos ou fluxos ou controle de permissões de outros usuários. Vários ativos fungíveis podem ser emitidos, transferidos, retirados ou trocados de forma segura e atômica. Qualquer número de “streams” pode ser criado em uma cadeia, para publicação, indexação e recuperação de dados dentro ou fora da cadeia em JSON, texto ou formatos binários. Todas as regras de transação para essas abstrações estão disponíveis imediatamente.

Ao desenvolver uma aplicação no MultiChain, é possível ignorar esta funcionalidade integrada e expressar regras de transação usando apenas filtros inteligentes. No entanto, os filtros inteligentes são projetados para funcionar em conjunto com suas abstrações integradas, permitindo que seu comportamento padrão seja restringido de maneiras personalizadas. Por exemplo, a permissão para determinadas atividades pode ser controlada por administradores específicos, em vez do comportamento padrão que qualquer administrador fará. A transferência de determinados ativos pode ser limitada no tempo ou exigir aprovação adicional acima de um determinado valor. Os dados em um fluxo específico podem ser validados para garantir que consistam apenas em estruturas JSON com campos e valores obrigatórios.

Em todos esses casos, os filtros inteligentes criam requisitos adicionais para que as transações sejam validadas, mas não remover as regras simples incorporadas. Isso pode ajudar a resolver um dos principais desafios nas aplicações blockchain: o fato de que um bug em algum código da cadeia pode levar a consequências desastrosas. Vimos inúmeros exemplos desse problema no blockchain público Ethereum, mais famoso no Fim do DAO e os votos de Bugs de multiassinatura de paridade. Pesquisas mais amplas encontraram um grande número de vulnerabilidades comuns em contratos inteligentes Ethereum que permitem que invasores roubem ou congelem fundos de outras pessoas.

É claro que os filtros inteligentes MultiChain também podem conter bugs, mas suas consequências são de escopo mais limitado. Por exemplo, as regras de ativos integradas evitam que um usuário gaste o dinheiro de outro ou faça desaparecer acidentalmente seu próprio dinheiro, independentemente da outra lógica que um filtro inteligente contenha. Se um bug for encontrado em um filtro inteligente, ele poderá ser desativado e substituído por uma versão corrigida, enquanto a integridade básica do razão é protegida. Filosoficamente, o MultiChain está mais próximo das arquiteturas de banco de dados tradicionais, onde a plataforma de banco de dados fornece uma série de abstrações integradas, como colunas, tabelas, índices e restrições. Recursos mais poderosos, como gatilhos e procedimentos armazenados, podem ser opcionalmente codificados pelos desenvolvedores de aplicativos, nos casos em que forem realmente necessários.

Regras de transação tecido MultiChain Ethereum Corda
Modelo Mensagem do contrato Entrada-saída Mensagem do contrato Entrada-saída
Integrados nenhum Permissões +
ativos + fluxos
nenhum nenhum

Determinismo

Vamos passar para a próxima parte do nosso confronto. Não importa qual abordagem escolhamos, as regras de transação personalizadas de um aplicativo blockchain são expressas como código de computador escrito por desenvolvedores de aplicativos. E diferentemente dos aplicativos centralizados, esse código será executado mais de uma vez e em mais de um local para cada transação. Isso ocorre porque vários nós de blockchain pertencentes a diferentes participantes precisam verificar e/ou executar essa transação por si próprios.

Essa execução repetida e redundante de código introduz um novo requisito raramente encontrado em aplicações centralizadas: o determinismo. No contexto da computação, determinismo significa que um trecho de código sempre dará a mesma resposta para os mesmos parâmetros, não importa onde e quando for executado. Isto é absolutamente crucial para o código que interage com uma blockchain porque, sem determinismo, o consenso entre os nós dessa cadeia pode quebrar catastroficamente.

Vamos ver como isso fica na prática, primeiro no modelo de entrada-saída. Se dois nós tiverem uma opinião diferente sobre se uma transação é válida, então um aceitará um bloco contendo essa transação e o outro não. Como cada bloco está explicitamente vinculado a um bloco anterior, isso criará uma “bifurcação” permanente na rede, com um ou mais nós não aceitando a opinião da maioria sobre todo o conteúdo do blockchain daquele ponto em diante. Os nós da minoria serão excluídos do estado de evolução do banco de dados e não poderão mais usar o aplicativo de maneira eficaz.

Agora vamos ver o que acontece se o consenso falhar no modelo contrato-mensagem. Se dois nós tiverem uma opinião diferente sobre como um contrato deve responder a uma mensagem específica, isso pode levar a uma diferença no conteúdo de seus bancos de dados. Isto, por sua vez, pode afetar a resposta do contrato a mensagens futuras, incluindo mensagens que envia a outros contratos. O resultado final é uma divergência crescente entre a visão dos diferentes nós sobre o estado do banco de dados. (O campo “raiz de estado” nos blocos Ethereum garante que qualquer diferença nas respostas dos contratos leve imediatamente a uma bifurcação da blockchain totalmente catastrófica, em vez de correr o risco de permanecer oculto por um período de tempo.)

Fontes de não determinismo

Portanto, o não-determinismo no código blockchain é claramente um problema. Mas se os blocos básicos da computação, como a aritmética, são determinísticos, com que devemos nos preocupar? Bem, acontece que algumas coisas:

  • Obviamente, geradores de números aleatórios, já que, por definição, são projetados para produzir resultados diferentes a cada vez.
  • Verificando a hora atual, pois os nós não processarão transações exatamente ao mesmo tempo e, em qualquer caso, seus relógios poderão estar dessincronizados. (Ainda é possível implementar regras dependentes do tempo fazendo referência a carimbos de data/hora dentro do próprio blockchain.)
  • Consultar recursos externos, como a Internet, arquivos de disco ou outros programas em execução em um computador. Não é possível garantir que estes recursos dêem sempre a mesma resposta e podem ficar indisponíveis.
  • Executar vários trechos de código em “threads” paralelos, pois isso leva a uma “condição de corrida” onde a ordem em que esses processos terminam não pode ser prevista.
  • Executar quaisquer cálculos de ponto flutuante que possam fornecer respostas minimamente diferentes em diferentes arquiteturas de processador de computador.

Nossas quatro plataformas blockchain empregam diversas abordagens diferentes para evitar essas armadilhas.

Execução determinística

Comecemos pelo Ethereum, já que sua abordagem é a mais “pura”. Os contratos Ethereum são expressos em um formato de propósito especial denominado “bytecode Ethereum”, que é executado pela Máquina Virtual Ethereum (“EVM”). Os programadores não escrevem bytecode diretamente, mas sim o geram ou “compilam” a partir de uma linguagem de programação semelhante a JavaScript chamada Solidity. (Outras linguagens costumavam estar disponíveis, mas desde então foram descontinuadas.) O determinismo é garantido pelo fato de que o bytecode Solidity e Ethereum não podem codificar nenhuma operação não determinística – é simples assim.

Os filtros MultiChain e os contratos Corda escolhem uma abordagem diferente, adaptando linguagens de programação e ambientes de execução existentes. MultiChain usa JavaScript rodando no Google V8 motor, que também forma o núcleo do navegador Chrome e da plataforma Node.js, mas com fontes de não determinismo desabilitadas. Da mesma forma, Corda usa Java ou Kotlin, ambos compilados em “bytecode Java” que é executado em uma máquina virtual Java (“JVM”). Por enquanto, Corda usa a JVM não determinística padrão da Oracle, mas está em andamento um trabalho para integrar um versão determinística. Enquanto isso, os desenvolvedores de contratos Corda devem tomar cuidado para não permitir o não-determinismo em seu código.

Como o purismo do Ethereum se compara à abordagem evolutiva adotada pelo MultiChain e Corda? A principal vantagem do Ethereum é a minimização do risco – uma máquina virtual construída para um propósito específico tem menos probabilidade de conter uma fonte inadvertida de não determinismo. Embora tal descuido pudesse ser corrigido por uma atualização de software, seria prejudicial para qualquer cadeia que tivesse o azar de encontrá-lo. O problema do Ethereum, entretanto, é que o Solidity e o EVM constituem um ecossistema pequeno e nascente no contexto mais amplo das linguagens de programação e ambientes de tempo de execução. Em comparação, JavaScript e Java são os dois principais idiomas no Github, são executados em bilhões de dispositivos digitais e têm tempos de execução otimizados ao longo de décadas. Presumivelmente, é por isso que o blockchain público Ethereum está considerando uma transição para eWASM, uma bifurcação determinística do padrão emergente WebAssembly.

Determinismo por endosso

Quando se trata de determinismo, o Hyperledger Fabric adota uma abordagem completamente diferente. No Fabric, quando um nó “cliente” deseja enviar uma mensagem para algum chaincode, ele primeiro envia essa mensagem para alguns nós “endossantes”. Cada um desses nós executa o chaincode de forma independente, formando uma opinião sobre a mensagem. efeito no banco de dados desse chaincode. Estas opiniões são devolvidas ao cliente juntamente com uma assinatura digital que constitui um “endosso” formal. Se o cliente receber endossos suficientes do resultado pretendido, ele cria uma transação contendo esses endossos e a transmite para inclusão na cadeia.

Para garantir o determinismo, cada pedaço do chaincode possui uma “política de endosso” que define exatamente qual nível de aprovação é necessário para tornar válidas suas transações. Por exemplo, a política de um chaincode pode declarar que são necessários endossos de pelo menos metade dos nós do blockchain. Outro pode exigir o endosso de qualquer uma das três partes confiáveis. De qualquer forma, cada nó pode verificar de forma independente se os endossos necessários foram recebidos.

Para esclarecer a diferença, o determinismo na maioria das plataformas blockchain é baseado na pergunta: “Qual é o resultado da execução deste código nestes dados?” – e precisamos ter certeza absoluta de que cada nó responderá a esta pergunta de forma idêntica. Por outro lado, o determinismo no Fabric é baseado em uma questão diferente: “Será que um número suficiente de endossantes concorda com o resultado da execução deste código com base nesses dados?” Responder a isso é uma questão bastante simples de contar, e não há espaço para o não-determinismo se infiltrar.

O determinismo por endosso do Fabric tem uma série de consequências interessantes. Primeiro, o chaincode pode ser escrito em muitas linguagens de programação diferentes, uma vez que estas não precisam ser adaptadas para o determinismo (atualmente há suporte para Go, Java e JavaScript). Em segundo lugar, o chaincode pode ser ocultado de alguns participantes de uma blockchain, uma vez que só precisa ser executado por clientes e endossantes (o próprio banco de dados é globalmente visível). Finalmente, e mais notavelmente, o chaincode do Fabric pode fazer coisas que são proibidas em outros ambientes blockchain, como verificar o tempo usando uma API da web online. Na pior das hipóteses, onde cada endossante obtém uma resposta diferente desta API, o cliente não conseguirá obter endossos suficientes para qualquer resultado específico e nenhuma transação ocorrerá. (Deve-se notar que os membros da equipe do Fabric ainda recomendar usando lógica determinística dentro do chaincode, para evitar surpresas.)

Qual o preço que a Fabric paga por essa flexibilidade? Se o objetivo de um blockchain é remover intermediários de um aplicativo baseado em banco de dados compartilhado, então a dependência da Fabric de endossantes dá um grande passo para longe desse objetivo. Para os participantes da cadeia, não é mais suficiente seguir as regras do chaincode – eles também precisam que outros nós concordem que o fizeram. Pior ainda, um subconjunto malicioso de endossantes poderia aprovar alterações no banco de dados que não seguem o chaincode. Isto dá aos endossantes muito mais poder do que os validadores em blockchains regulares, que podem censurar transações, mas não podem violar as regras do blockchain. Os desenvolvedores de aplicativos Blockchain devem decidir se essa compensação faz sentido em seu caso específico.

Determinismo tecido MultiChain Ethereum Corda
Modelo Endossos Tempo de execução adaptado VM desenvolvida especificamente Tempo de execução adaptado
Idiomas Ir + Java + JavaScript JavaScript Solidity Java + Kotlin
Visibilidade do código Contrapartes +
endossantes
Blockchain Blockchain Contrapartes +
dependentes
Forçados Não Sim Sim Não por enquanto)

Prevenção de conflitos

Até agora, discutimos como diferentes plataformas de blockchain expressam regras de transação em código e como garantem deterministicamente que cada nó aplique essas regras de forma idêntica. Agora é hora de falar sobre um terceiro aspecto do nosso confronto: como cada plataforma lida com a possibilidade de que duas transações, que são válidas por si mesmas, entrem em conflito uma com a outra? No exemplo mais simples, imagine que Alice tem US$ 10 em um livro-razão financeiro e transmite duas transações – uma enviando US$ 8 para Bob e a outra enviando US$ 7 para Charlie. Claramente, apenas uma destas transacções pode ter sucesso.

Dois modelos

Podemos começar agrupando a abordagem da MultiChain e da Corda para esse problema. Conforme descrito anteriormente, ambos usam um modelo de entrada-saída para representar transações e suas regras, no qual cada entrada de transação gasta uma saída de transação anterior. Isto leva a um princípio simples para prevenir conflitos: cada produto só pode ser gasto uma vez. Os filtros MultiChain e os contratos Corda podem contar com suas respectivas plataformas para aplicar essa restrição de forma absoluta. Como os US$ 10 de Alice são representados por uma saída de transação anterior, essa regra de gasto único automaticamente impede que ela os envie para Bob e Charlie.

Apesar dessa semelhança, é importante apontar uma diferença fundamental na forma como o MultiChain e o Corda evitam conflitos. No MultiChain, cada nó vê todas as transações e, portanto, pode verificar de forma independente se cada saída é gasta apenas uma vez. Qualquer transação que realize um gasto duplo em relação a uma transação previamente confirmada será rejeitada instantânea e automaticamente. Por outro lado, em Corda não existe uma blockchain global, pelo que são necessários “notários” para evitar estes gastos duplos. Cada estado de saída do Corda é atribuído a um notário, que deve assinar qualquer transação que gaste essa saída, confirmando que não foi gasta antes. Os participantes de uma blockchain devem confiar que os notários seguirão esta regra honestamente, e notários mal-intencionados podem causar estragos à vontade. Tal como acontece com os endossos no Fabric, este “gasto único como serviço”O design tem vantagens em termos de confidencialidade, mas reintroduz intermediários, indo contra a corrente do blockchain. (É importante esclarecer que os notários Corda podem ser administrados por grupos de participantes usando um algoritmo de consenso, para que a integridade do livro-razão ainda possa ser protegida contra malfeitores individuais).

Vamos passar para Ethereum. Para lembrar, Ethereum usa contratos e mensagens em vez de entradas e saídas. Como resultado, conflitos de transação, como os dois pagamentos de Alice, não são imediatamente visíveis para o mecanismo blockchain. Em vez disso, eles são detectados e bloqueados pelo contract que processa as transações, após a confirmação de seu pedido na cadeia. Ao processar cada um dos pagamentos de Alice, o contrato verifica se o saldo dela é suficiente. Se a transação que paga $8 para Bob ocorrer primeiro, ela será processada normalmente, deixando Alice com $2 em sua conta. Como resultado, quando o contrato processa a segunda transação pagando US$ 7 a Charlie, ele percebe que Alice não possui os fundos necessários e a transação é abortada.

Resultados vs contratos

Até agora, vimos duas técnicas diferentes para evitar transações conflitantes – resultados de gasto único em MultiChain e Corda e verificação baseada em contrato em Ethereum. Então, o que é melhor?

Para ajudar a responder a esta pergunta, vamos considerar um exemplo de conta “1 de 2 com múltiplas assinaturas” que detém US$ 100 em nome de Gavin e Helen e permite que qualquer um deles gaste esse dinheiro de forma independente. Gavin instrui seu aplicativo a pagar US$ 80 para Donna e, alguns segundos depois, Helen quer enviar US$ 40 para Edward. Dado que não existem fundos suficientes para ambos os pagamentos, estas transacções entrariam inevitavelmente em conflito. No caso de ambas as transações serem transmitidas, o resultado será determinado por quem entrar primeiro na cadeia. Observe que, diferentemente do exemplo de Alice, esse conflito é acidentalmente, já que ninguém está tentando quebrar as regras do aplicativo – eles simplesmente tiveram azar.

Ao considerar a probabilidade de ocorrência desse conflito, a questão principal é esta: depois que Gavin enviar sua transação, quanto tempo o nó de Helen levará para saber que seu pagamento poderá falhar? Quanto mais curto for esse período, maior será a probabilidade de Helen ser impedida de tentar fazer esse pagamento, evitando que ela e seu pedido tenham uma surpresa posterior.

Com o modelo de entrada-saída, qualquer conflito entre transações é diretamente visível para a plataforma blockchain, uma vez que as duas transações tentarão explicitamente gastar a mesma saída anterior. No MultiChain, isso acontece assim que a transação de Gavin é propagada para o nó de Helen, geralmente em um segundo ou menos. Em Corda, o notário da saída recusará o pedido de assinatura da transação de Helen, uma vez que já assinou a de Gavin, então Helen saberá instantaneamente que seu pagamento falhará. (Embora se o notário Corda for distribuído, ele poderá ter que esperar alguns segundos por uma resposta.) De qualquer forma, não há necessidade de esperar que uma transação seja confirmada e ordenada no blockchain.

E o modelo do Ethereum? Nesse caso, não há maneira imediata de a plataforma blockchain saber que ocorrerá um conflito. Embora o nó de Helen possa ver a transação de Gavin na rede, ele não pode saber como isso afetará a transação de Helen, uma vez que, da sua perspectiva, estas são simplesmente duas mensagens sendo enviadas para o mesmo contrato. Talvez dez segundos depois, uma vez confirmada a ordem final das transações conflitantes no blockchain, o nó de Helen recalculará o resultado real em vez do resultado esperado, e seu aplicativo atualizará sua exibição de acordo. Enquanto isso, Gavin e Helen ficarão no escuro.

Mas não devemos concluir daí que o modelo de entrada-saída funciona sempre melhor. Considere uma variação do nosso cenário de exemplo, onde Gavin e Helen solicitam pagamentos menores de US$ 40 do saldo original de US$ 100, exatamente ao mesmo tempo. No modelo de entrada-saída, essas transações entrariam em conflito, uma vez que ambas gastam a mesma linha do banco de dados que contém esses US$ 100, e apenas um dos pagamentos seria bem-sucedido. Mas no Ethereum, ambas as transações seriam processadas com sucesso, independentemente da sua ordem final, uma vez que a conta contém fundos suficientes para ambas. Neste caso, Ethereum cumpre mais fielmente as intenções de Gavin e Helen.

Conjuntos de leitura e gravação

Por fim, vamos falar sobre o Fabric, cuja abordagem baseada em endosso é um híbrido dessas duas técnicas. Conforme explicado anteriormente, quando um nó “cliente” do Fabric deseja enviar uma mensagem para um contrato, ele primeiro pede a alguns nós endossantes que executem essa mensagem em seu nome. Os nós endossantes fazem isso de maneira semelhante ao Ethereum – executando o contrato em seu banco de dados local – mas esse processo é observado em vez de aplicado imediatamente. Cada endossante registra o conjunto de linhas que seriam lidas e escritas, anotando também a versão exata dessas linhas naquele momento. Este “conjunto de leitura e gravação” de linhas versionadas é explicitamente referenciado no endosso e incluído na transação que o cliente transmite.

Os conflitos entre as transações do Fabric são resolvidos assim que seu pedido é finalizado na cadeia. Cada nó processa cada transação de forma independente, verificando as políticas de endosso e aplicando as alterações especificadas no banco de dados. Entretanto, se uma transação ler ou gravar uma versão de linha do banco de dados que já tenha sido modificada por uma transação anterior, essa segunda transação será ignorada. Voltando aos pagamentos conflitantes de Alice para Bob e Charlie, ambas as transações lerão e modificarão a mesma versão de linha, contendo os $10 com os quais Alice começou. Portanto, a segunda transação será abortada de forma segura e automática.

A abordagem do Fabric para resolução de conflitos funciona muito bem, mas em termos de desempenho e flexibilidade combina o pior dos dois modelos anteriores. Como os endossos convertem transações em conjuntos específicos de leitura e gravação, os pagamentos simultâneos, mas compatíveis, de US$ 40 de Gavin e Helen levariam a um conflito que o Ethereum evita. No entanto, o Fabric não ganha a vantagem de velocidade do modelo de entrada-saída, uma vez que os endossantes executam contratos contra a versão mais recente do banco de dados confirmada pela blockchain, ignorando as transações não confirmadas. Portanto, se Helen iniciar seu pagamento alguns segundos depois de Gavin, mas antes que o pagamento de Gavin seja confirmado no blockchain, o Fabric criará transações conflitantes que um modelo puro de entrada-saída evita.

Prevenção de conflitos tecido MultiChain Ethereum Corda
Modelo Conjuntos de leitura e gravação Gasto único Verificações de contrato Gasto único
Verificação Independente Independente Independente Notários confiáveis
Velocidade ~10s (confirmação) ~1s (propagação) ~10s (confirmação) 0~5s (notário)

Uma escolha complexa

Neste artigo, revisamos muitas das diferentes maneiras pelas quais Corda, Ethereum, Fabric e MultiChain abordam os principais desafios dos “contratos inteligentes”, ou código de aplicativo incorporado em um blockchain. E cada plataforma tem respostas diferentes às nossas três questões principais: Como são representadas as regras de transação? Como o código é executado de forma determinística? E como evitamos conflitos?

Então, quem é o vencedor do nosso confronto de contratos inteligentes? Já deveria estar óbvio que não existe uma resposta simples. Cada plataforma representa um compromisso complexo e multidirecional entre flexibilidade, simplicidade, desempenho, desintermediação, segurança e confidencialidade. Portanto, a escolha da plataforma para uma aplicação específica deve começar com uma compreensão detalhada do modelo de confiança dessa aplicação, dos tipos de transações que ela envolve e dos seus prováveis ​​padrões de conflito. Se você encontrar alguém promovendo uma solução específica de contrato inteligente antes de saber as respostas a essas perguntas, sugiro insistir educadamente, mas com firmeza, que adote uma abordagem “mais inteligente”.

Por favor, poste comentários no LinkedIn.

Carimbo de hora:

Mais de Multichain