#币安HODLerTHE

A blockchain sacrifica eficiência devido ao seu design descentralizado, portanto, aumentar a velocidade de execução sempre foi uma questão que precisava ser resolvida. A "camada de execução" da blockchain é a parte crucial que processa cada transação e a adiciona à cadeia. Para acelerar a capacidade de processamento, a melhoria na camada de execução se tornou uma das estratégias centrais, e a execução paralela é um avanço significativo nesta área.

Blockchains tradicionais costumam adotar um método sequencial para processar transações, o que limita significativamente a velocidade das transações, especialmente em redes congestionadas. No entanto, através da execução paralela, múltiplas transações podem ser processadas simultaneamente, aumentando drasticamente a eficiência da execução e reduzindo a pressão na cadeia.

Para entender melhor o que é paralelismo, vamos começar a introduzir a execução e usar o Ethereum no modo PBS após o Merge como exemplo para explicar o que é a execução, ao mesmo tempo que mostramos a posição da execução em todo o ciclo de vida da transação.

Aspectos específicos da execução da transação

  1. As transações entram no pool de memória e são filtradas e ordenadas: esta é a fase de pré-processamento após a submissão da transação, que inclui a interação entre Mempool, Searcher e Builder, completando a filtragem e a ordenação das transações.

  2. O Builder constrói o bloco (mas não executa): o Builder organiza as transações lucrativas em um bloco para completar a embalagem e a ordenação das transações.

  3. O Proposer valida e submete o bloco: após a construção do bloco, o Builder envia a proposta do bloco ao Proposer. O Proposer valida a estrutura do bloco e o conteúdo das transações, e então submete formalmente o bloco à rede para iniciar a execução.

  4. Executando transações: após a submissão do bloco, os nós executam as transações no bloco uma por uma. Esta é a fase crítica de atualização de estado, onde cada transação pode acionar chamadas de contratos inteligentes, mudanças de saldo de conta ou alterações de estado.

  5. Testemunhas testemunham: os validadores testemunham os resultados da execução do bloco e a raiz do estado, confirmando-os como confirmação final. Isso garante a autenticidade e a validade do bloco na camada de execução e previne inconsistências.

  6. Sincronização de estado: cada nó sincroniza os resultados da execução do bloco (como saldos de contas, atualizações de estado de contratos, etc.) para seu estado local, computando e armazenando uma nova raiz de estado após a execução de cada transação, para usar como estado inicial no próximo bloco.

Claro, isso é apenas a sincronização do estado das transações por bloco. Para manter o estado mais recente na cadeia, geralmente, os nós sincronizam dados bloco a bloco e continuam a validar blocos e estados. No entanto, para alcançar a finalidade sob o mecanismo POS, os agregadores também precisam agregar as assinaturas dos validadores de cada Slot em uma assinatura completa e transmiti-la ao proponente do próximo Slot. Além disso, os validadores precisam confirmar o estado de todos os blocos dentro de um Epoch com base no número de votos após um Epoch, formando um ponto de verificação de estado de consenso temporário. Quando dois Epochs consecutivos obtêm o suporte da maioria dos validadores, os blocos e transações alcançam a finalidade.

Do ponto de vista do ciclo de vida da transação, a execução ocorre após a validação do Proposer da estrutura do bloco e do conteúdo das transações enviadas pelo Builder. O processo de execução real requer o processamento de cada transação individualmente e a atualização do respectivo estado da conta ou contrato. Após a execução de todas as transações, o Proposer calcula uma nova raiz de estado (raiz Merkle), que é um resumo dos resultados das transações atuais e do estado global final. Em termos simples, o processo completo de execução do bloco inclui uma série de cálculos necessários para transformar o Ethereum de um estado anterior para o próximo estado, desde a execução de cada transação até o cálculo da raiz Merkle.

Execução sequencial

O que se opõe ao paralelismo é a execução sequencial, que é o modo de execução mais comum atualmente nas blockchains. Normalmente, as transações são executadas em ordem. Após a execução de uma transação, o Ethereum atualiza o estado da conta e as informações relacionadas (como saldos, dados de armazenamento de contratos) na árvore de estado da conta, gerando um novo hash de estado da conta. Após a atualização de todas as árvores de estado das contas, forma-se o que é chamado de hash da raiz Merkle de estado. Após completar a raiz Merkle de estado, a raiz Merkle de transações e a raiz Merkle de recibos, o cabeçalho do bloco é submetido a uma computação de hash, gerando o hash do bloco.

E, dentro disso, a ordem de execução das transações é crucial. Como a árvore de Merkle é uma árvore binária de hashes, os valores da raiz Merkle formados em diferentes ordens serão diferentes.

Execução paralela

Em um ambiente de execução paralela, os nós tentam processar as transações no bloco em paralelo. Em vez de executar as transações uma por uma em ordem, as transações são alocadas a diferentes "caminhos de execução", permitindo que sejam executadas simultaneamente. Através da execução paralela, o sistema pode processar as transações no bloco de forma mais eficiente, aumentando a taxa de transferência.

Após a execução de todas as transações, os nós agregam os resultados da execução (ou seja, as atualizações de estado impactadas pelas transações) para formar um novo estado do bloco. Esse estado será adicionado à blockchain, representando o estado global mais recente na cadeia.

Conflito de estado

Devido ao fato de que o paralelismo processa transações em caminhos diferentes simultaneamente, um dos principais desafios do paralelismo é o conflito de estado. Ou seja, pode haver várias transações lendo ou escrevendo a mesma parte dos dados (estado) na blockchain no mesmo período de tempo. Se essa situação não for tratada adequadamente, pode resultar em resultados de execução incertos. Como a ordem de atualização do estado é diferente, os resultados finais do cálculo também podem ser diferentes. Por exemplo,

Suponha que existem duas transações, a Transação A e a Transação B, que tentam atualizar o saldo da mesma conta:

  • Transação A: aumentar o saldo da conta em 10.

  • Transação B: aumentar o saldo da conta em 20.

O saldo inicial da conta é 100.

Se executarmos em série, o resultado da ordem de execução será determinado:

1. Execute a Transação A primeiro, depois a Transação B:

  • O saldo da conta aumenta primeiro em 10, tornando-se 110.

  • Adicione mais 20, resultando em 130.

2. Execute a Transação B primeiro, depois a Transação A:

  • O saldo da conta aumenta primeiro em 20, tornando-se 120.

  • Em seguida, aumenta em 10, resultando em 130.

Em ambas as ordens, o saldo final é 130, pois o sistema garante a consistência da ordem de execução das transações.

Mas em um ambiente de execução paralela, a Transação A e a Transação B podem ler simultaneamente o saldo inicial de 100 e realizar seus respectivos cálculos:

  1. A Transação A lê o saldo como 100, calcula e atualiza o saldo para 110.

  2. A Transação B também lê o saldo como 100, calcula e atualiza o saldo para 120.

Neste caso, devido à execução simultânea das transações, o saldo final é atualizado apenas para 120, e não para 130, porque as operações da Transação A e da Transação B "sobrepuseram" os resultados uma da outra, causando um conflito de estado.

Esses tipos de problemas de conflito de estado são geralmente chamados de "sobrescrita de dados", ou seja, quando as transações tentam modificar os mesmos dados simultaneamente, podem sobrepor os resultados de cálculo uma da outra, resultando em um estado final incorreto. Outro tipo de conflito de estado que pode ocorrer é a incapacidade de garantir a ordem de execução. Como várias transações completam operações em diferentes períodos de tempo, isso pode resultar em diferentes ordens de execução. Ordens diferentes podem levar a resultados de cálculo diferentes, tornando o resultado incerto.

Para evitar essa incerteza, os sistemas de execução paralela de blockchain geralmente introduzem mecanismos de detecção de conflitos e retrocessos, ou realizam análises de dependência das transações com antecedência para garantir que possam ser executadas em paralelo sem afetar a consistência do estado final.

Paralelismo otimista vs paralelismo determinístico

Existem duas abordagens para lidar com possíveis problemas de conflitos de estado: paralelismo determinístico e paralelismo otimista. Essas duas abordagens têm trade-offs em eficiência e complexidade de design.

O paralelismo determinístico exige a declaração prévia do acesso ao estado. Validadores ou sequenciadores verificam os acessos declarados ao estado ao ordenar as transações. Se várias transações tentarem escrever para o mesmo estado, essas transações são marcadas como conflitos, evitando a execução simultânea. A forma como diferentes cadeias implementam essa declaração prévia do acesso ao estado pode variar, mas geralmente inclui as seguintes maneiras:

  • Através da restrição de normas de contratos: os desenvolvedores especificam diretamente no contrato inteligente o escopo de acesso ao estado. Por exemplo, uma transferência de token ERC-20 requer acesso aos campos de saldo do remetente e do destinatário.

  • Através da declaração de dados estruturados de transação: adicionar campos específicos na transação para marcar o acesso ao estado.

  • Através da análise do compilador: compiladores de linguagens de alto nível podem analisar estaticamente o código do contrato e gerar automaticamente um conjunto de acessos ao estado.

  • Através da declaração forçada pelo framework: alguns frameworks exigem que os desenvolvedores especifiquem explicitamente o estado que precisa ser acessado ao chamar funções.

O paralelismo otimista processa as transações de forma otimista primeiro e, quando um conflito ocorre, re-executa as transações afetadas na ordem. Para evitar o máximo possível a ocorrência de conflitos, o núcleo do design do paralelismo otimista é prever e supor rapidamente o estado através de dados históricos, análise estática, etc. Ou seja, o sistema, sem verificar completamente, supõe que certas operações ou atualizações de estado são válidas, evitando o tempo de espera de todos os processos de verificação, aumentando assim o desempenho e a taxa de transferência.

Embora o paralelismo otimista possa evitar conflitos tanto quanto possível por meio de algumas previsões rápidas e suposições sobre o estado, ainda existem alguns desafios inevitáveis, especialmente em relação à execução de contratos ou transações entre cadeias. Se os conflitos ocorrerem com frequência, a reexecução pode significativamente desacelerar o desempenho do sistema e aumentar o consumo de recursos computacionais.

O paralelismo determinístico evita conflitos potenciais ao realizar verificações de dependência de estado antes da transação, mas isso exige que os desenvolvedores declarem com precisão as dependências de estado antes da submissão da transação, aumentando a complexidade de implementação.

Dilema do EVM Paralelo

Lidar com conflitos de estado não se limita apenas à distinção entre determinístico e otimista; no processo de implementação do paralelismo, também é necessário considerar a arquitetura do banco de dados da cadeia. O problema de conflitos de estado em EVM sob a arquitetura da árvore de Merkle é particularmente difícil. A árvore de Merkle é uma estrutura de hash hierárquica, e após cada transação que modifica um determinado dado de estado, a raiz de hash da árvore de Merkle também precisa ser atualizada. Esse processo de atualização é recursivo, calculando de baixo para cima até o nó raiz. Como o hash é irreversível, a atualização do nível superior só pode ser calculada após a conclusão das alterações nos dados do nível inferior, o que dificulta a atualização paralela.

Se duas transações forem executadas em paralelo e acessarem o mesmo estado (como o saldo da conta), isso causará conflitos nos nós da árvore Merkle. Resolver esse tipo de conflito geralmente requer um mecanismo adicional de gerenciamento de transações para garantir que um valor hash de raiz consistente seja obtido em múltiplos ramos. Isso não é fácil de implementar para o EVM, pois requer fazer trade-offs entre paralelização e consistência de estado.

Soluções paralelas não EVM

Solana

Diferente da árvore de estado global do Ethereum, Solana utiliza um modelo de contas. Cada conta é um espaço de armazenamento independente, armazenado no livro-razão, evitando assim problemas de conflito de caminhos.

Solana é paralela determinística. No Solana, cada transação precisa declarar claramente as contas que irá acessar e as permissões necessárias (somente leitura ou leitura/escrita) no momento da submissão. Esse design permite que os nós da blockchain analisem antecipadamente os recursos que cada transação precisa acessar antes da execução. Como as transações já declararam todas as relações de dependência de contas antes do início da execução, os nós podem identificar quais transações acessarão as mesmas contas e quais podem ser executadas em paralelo de forma segura, permitindo o agendamento inteligente e evitando conflitos, estabelecendo assim a base para o agendamento paralelo.

Como cada transação já declarou os contas e permissões que precisa acessar antes da execução, Solana pode verificar se há relações de dependência entre as transações (modelo Sealevel). Se não houver contas compartilhadas entre as transações, o sistema pode alocá-las em diferentes processadores para execução paralela.

Aptos

O design de execução paralela do Aptos difere significativamente do Ethereum, pois faz algumas inovações-chave em termos de arquitetura e mecanismos, principalmente na modelagem de contas e no armazenamento de estados.

O Ethereum precisa frequentemente atualizar a árvore de estado global (MPT) ao executar transações. O estado de todas as contas e contratos é armazenado em uma árvore de estado compartilhada, e qualquer transação precisa acessar e atualizar uma parte dessa árvore de estado. O Aptos, por outro lado, divide as contas em unidades de estado independentes, onde cada objeto é um par chave-valor independente, permitindo que objetos existam independentemente e se relacionem apenas quando houver uma relação de referência clara. Não há caminhos de árvore públicos entre os objetos, evitando competição por bloqueios e permitindo uma execução totalmente paralela.

A estrutura de dados subjacente do Aptos é a Jellyfish Merkle Tree. O estado de cada objeto é finalmente armazenado no JMT, como pares chave-valor independentes. Diferente da MPT do Ethereum, a Jellyfish Merkle Tree tem uma estrutura de árvore binária completa, simplificando assim os caminhos de armazenamento e consulta dos nós, reduzindo drasticamente o tempo de verificação. Além disso, a posição de cada conta na árvore é fixa, e os nós na árvore são armazenados independentemente, permitindo que atualizações e consultas de várias contas sejam feitas em paralelo.

Aptos é paralelismo otimista e não requer a declaração prévia de todas as dependências das contas. Para isso, o Aptos utiliza Block-STM, que estima dependências com base na ordem de transação predefinida, reduzindo assim o número de abortos.

EVM Paralelo

Em comparação com paralelismos não EVM, o EVM paralelo enfrenta maiores dificuldades técnicas ao lidar com dependências de estado, detecção de conflitos, gerenciamento de Gas e mecanismos de retrocesso. Para compreender melhor isso, podemos observar como alguns projetos EVM paralelos (como Sui, Monad, Canto) lidam com esses problemas.

Sui

Sui, assim como Aptos, também usa um modelo de objetos para lidar com o estado, adotando cada objeto (por exemplo, estado de conta, estado de contrato inteligente) como um recurso independente, e esses objetos são diferenciados por identificadores únicos. Quando as transações envolvem objetos diferentes, essas transações podem ser processadas em paralelo, pois operam em diferentes estados e não causam conflitos diretos.

Embora Sui use um modelo de objetos para gerenciar o estado, para ser compatível com o EVM, a arquitetura do Sui utiliza uma camada de adaptação adicional ou mecanismos de abstração para conectar o modelo de objetos ao modelo de contas do EVM.

No Sui, a programação das transações utiliza uma estratégia de paralelismo otimista, assumindo que não há conflitos entre as transações. Se um conflito ocorrer, o sistema utilizará um mecanismo de retrocesso para restaurar o estado.

Sui utiliza um modelo de objetos e tecnologia de isolamento de estado, evitando eficazmente problemas de dependência de estado. Cada objeto é um recurso independente, e transações diferentes podem ser executadas em paralelo, melhorando assim a taxa de transferência e a eficiência. No entanto, o trade-off desse método é a complexidade do modelo de objetos e o custo do mecanismo de retrocesso. Se ocorrer um conflito entre transações, pode ser necessário reverter parte do estado, aumentando a carga no sistema e potencialmente afetando a eficiência do processamento paralelo. Comparado a sistemas paralelos não EVM (como Solana), Sui requer mais recursos computacionais e de armazenamento para manter a alta eficiência de paralelismo.

Monad

Assim como Sui, Monad também adota paralelismo otimista. No entanto, o paralelismo otimista de Monad prevê algumas transações com dependências antes da execução real da transação, principalmente através da análise estática de código do Monad. Essa previsão requer acesso ao estado, e a maneira como o estado é armazenado no banco de dados do Ethereum torna o acesso ao estado muito difícil. Para tornar a leitura do estado mais eficiente no processo de paralelismo, Monad também reformulou o banco de dados.

A árvore de estados Monad é dividida em partições, cada partição mantém sua própria subárvore de estado. Ao atualizar, apenas as fatias relevantes precisam ser modificadas, sem necessidade de reconstruir toda a árvore de estados. Através de uma tabela de índice de estado, é possível localizar rapidamente o estado dentro da partição, reduzindo a interação entre partições.

Resumo

O núcleo da paralelização é aumentar a eficiência da camada de execução por meio de execução de múltiplos caminhos, e para alcançar a execução de múltiplos caminhos, a cadeia precisa realizar uma série de detecções de conflitos e mecanismos de retrocesso para garantir que possam ser executados em paralelo sem afetar a consistência do estado final, e realizar algumas melhorias no banco de dados.

Claro, a melhoria da eficiência da camada de execução não se limita apenas ao paralelismo; a otimização da fase de execução também pode ser realizada reduzindo as operações de leitura e escrita necessárias para cada transação no banco de dados. A melhoria da velocidade geral da cadeia abrange uma gama mais ampla, incluindo melhorias na eficiência da camada de consenso.

Cada tecnologia tem suas condições específicas de limitação. O paralelismo é apenas uma das maneiras de aumentar a eficiência, e a decisão final de usar essa tecnologia deve considerar se é amigável para os desenvolvedores, se pode ser implementada sem sacrificar a descentralização, entre outros fatores. O acúmulo de tecnologias não é necessariamente melhor; pelo menos, para o Ethereum, o paralelismo não é tão atraente, pois, se considerado apenas do ponto de vista da eficiência, a introdução do paralelismo não é a solução ótima para o Ethereum, seja em termos de simplicidade ou considerando o atual roteiro do Ethereum centrado em Rollup.