Autor: Tia, Techub News

A blockchain sacrifica eficiência devido ao seu design descentralizado, portanto, aumentar a velocidade de execução sempre foi um dos problemas críticos a serem resolvidos. A 'camada de execução' da blockchain é a parte fundamental que processa cada transação e a adiciona à cadeia. Para acelerar a capacidade de processamento, melhorar a camada de execução tornou-se uma das estratégias centrais, e a execução paralela é um avanço importante nesse aspecto.

Blockchains tradicionais geralmente processam transações uma a uma de forma sequencial, o que limita significativamente a velocidade das transações, especialmente em redes com alta densidade de transações, levando a congestionamentos. No entanto, através da execução paralela, várias transações podem ser processadas simultaneamente, aumentando drasticamente a eficiência de execução e aliviando a pressão na cadeia.

Para entender melhor o que é a paralelização, começaremos a introduzir a execução e tomaremos o modelo PBS do Ethereum após a fusão como exemplo para explicar o que é execução, ao mesmo tempo em que mostramos a posição da execução em todo o ciclo de vida das transações.

As etapas específicas da execução da transação

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

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

  3. Proposer valida e submete o bloco: uma vez que a construção do bloco é concluída, 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. Executar transações: após a submissão do bloco, os nós executam as transações dentro do bloco uma a uma. Esta é a fase crítica de atualização de estado, onde cada transação pode acionar chamadas de contrato inteligente, mudanças de saldo de conta ou alterações de estado.

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

  6. Sincronização de estado: cada nó sincroniza o resultado da execução do bloco (como saldos de conta, atualizações de estado de contrato, etc.) para seu estado local, e após executar cada transação, o nó calcula e armazena uma nova raiz de estado, que será utilizada como estado inicial no próximo bloco.

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

Do ponto de vista de todo o ciclo de vida da transação, a execução ocorre após o Proposer validar a estrutura do bloco e o conteúdo das transações enviados ao Builder. O processo de execução real requer o processamento das transações uma a uma e a atualização do estado correspondente da conta ou contrato. Após todas as transações serem executadas, o Proposer calcula uma nova raiz de estado (raiz Merkle), que resume todos os resultados da execução das transações atuais e o estado global final. Em termos simples, o processo completo de execução do bloco envolve uma série de cálculos necessários para mudar o Ethereum de um estado anterior para um próximo estado, desde a execução de cada transação até o cálculo da raiz Merkle.

Execução sequencial

O oposto da execução paralela é a execução sequencial, que é o método de execução mais comum atualmente nas blockchains. Normalmente, as transações são executadas em ordem sequencial. Quando uma transação é executada, o Ethereum atualiza o estado da conta e as informações relacionadas (como saldo, dados de armazenamento de contratos) na árvore de estado da conta, gerando um novo hash de estado da conta. Após todas as árvores de estado de conta serem atualizadas, um hash de nó raiz de estado, conhecido como raiz Merkle de estado, é formado. Após a finalização da raiz Merkle de estado, da raiz Merkle de transação e da raiz Merkle de recibo, o cabeçalho do bloco é então processado para gerar o hash desse bloco.

E dentro disso, a ordem de execução das transações é crucial. Como a árvore de Merkle é uma árvore binária de valores hash, o valor da raiz Merkle resultante varia com a ordem das transações.

Execução paralela

Em um ambiente de execução paralela, os nós tentam processar as transações dentro do bloco em paralelo. As transações não são executadas uma a uma em ordem sequencial, mas são alocadas em diferentes 'caminhos de execução' para que possam ser executadas simultaneamente. Através da execução paralela, o sistema pode processar as transações dentro do bloco de maneira mais eficiente, aumentando a taxa de transferência.

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

Conflito de estado

Como a execução paralela processa transações em diferentes caminhos simultaneamente, um dos grandes desafios da paralelização é o conflito de estado. Isso pode ocorrer quando várias transações tentam ler ou escrever na mesma parte dos dados (estado) da blockchain ao mesmo tempo. Se não for tratado adequadamente, isso pode levar a resultados de execução incertos. Devido à ordem diferente das atualizações de estado, os resultados de cálculo finais também podem variar. Por exemplo,

Suponha que existam duas transações, Transação A e 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 sequência, a ordem dos resultados da execução é determinística:

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

  • O saldo da conta aumenta primeiro para 10, totalizando 110.

  • Depois aumenta mais 20, resultando em 130.

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

  • O saldo da conta aumenta primeiro para 20, totalizando 120.

  • Depois aumenta mais 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.

No entanto, 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 suas próprias operações:

  1. A Transação A lê um saldo de 100 e, após o cálculo, atualiza o saldo para 110.

  2. A Transação B também lê um saldo de 100 e, após o cálculo, atualiza o saldo para 120.

Nessa situação, a execução simultânea das transações resulta em um saldo final atualizado apenas para 120, e não para 130, pois as operações da Transação A e da Transação B 'cobrem' os resultados uma da outra, causando um conflito de estado.

Esses problemas de conflitos de estado são normalmente chamados de 'sobrescrita de dados', ou seja, quando as transações tentam modificar os mesmos dados simultaneamente, podendo sobrepor os resultados de cálculo uma da outra, levando a um estado final incorreto. Outro possível problema de conflito de estado é a incapacidade de garantir a ordem de execução. Como várias transações completam suas operações em diferentes momentos, diferentes ordens de execução podem causar resultados de cálculo diferentes, resultando em incerteza nos resultados.

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

Execução paralela otimista e determinística

Existem duas abordagens para lidar com possíveis conflitos de estado: execução paralela determinística e execução paralela otimista. Essas duas modalidades têm suas compensações em eficiência e complexidade de design.

A execução paralela determinística exige a declaração antecipada do acesso ao estado, onde o validador ou sequenciador verifica as declarações de acesso ao estado ao classificar as transações. Se várias transações tentarem escrever no mesmo estado, essas transações serão marcadas como conflitos e não serão executadas simultaneamente. A forma como cada cadeia implementa a declaração antecipada do acesso ao estado pode variar, mas geralmente inclui as seguintes abordagens:

  • Através de restrições contratuais: os desenvolvedores especificam diretamente o escopo de acesso ao estado no contrato inteligente. Por exemplo, a transferência de tokens ERC-20 requer acesso aos campos de saldo do remetente e do destinatário.

  • Através da declaração de dados estruturados na transação: campos especiais são adicionados à transação para marcar o acesso ao estado.

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

  • Declaração forçada através de estruturas: algumas estruturas exigem que os desenvolvedores especifiquem explicitamente o estado a ser acessado ao chamar funções

A execução paralela otimista processa as transações de forma otimista, aguardando a ocorrência de conflitos antes de reexecutar as transações afetadas em ordem. Para evitar conflitos, o núcleo do design da execução paralela otimista é prever rapidamente o estado através de dados históricos, análise estática, etc. Ou seja, o sistema assume, na ausência de validação completa, que algumas operações ou atualizações de estado são válidas, evitando o tempo de espera para todos os processos de validação, aumentando assim o desempenho e a taxa de transferência.

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

A execução paralela determinística evita possíveis conflitos que podem surgir da execução paralela otimista, realizando verificações de dependência de estado antes da transação, mas como exige a declaração precisa das dependências de estado antes da submissão da transação, isso coloca uma demanda maior sobre os desenvolvedores e aumenta a complexidade da implementação.

Dilema de paralelismo EVM

A abordagem para conflitos de estado não é apenas uma questão de determinismo e otimismo, mas também deve ser considerada do ponto de vista da arquitetura do banco de dados em cadeia durante o processo específico de implementação paralela. O problema dos conflitos de estado em paralelo é especialmente complicado na EVM sob a arquitetura da árvore de Merkle. A árvore de Merkle é uma estrutura de hash hierárquica, que precisa ter seu valor de hash raiz atualizado cada vez que uma transação modifica um dado de estado. Esse processo de atualização é recursivo, calculando-se camada por camada até chegar à raiz. Como o hash é irreversível, ou seja, só pode ser calculado a partir de dados de nível inferior após a conclusão da modificação, essa característica torna difícil a atualização paralela.

Se duas transações forem executadas em paralelo e acessarem o mesmo estado (como o saldo da conta), isso resultará em conflitos nos nós da árvore de Merkle. Resolver tais conflitos geralmente requer mecanismos adicionais de gerenciamento de transações para garantir que um valor de hash raiz consistente possa ser obtido em múltiplos ramos. Isso não é fácil de implementar para a EVM, pois requer um compromisso entre paralelização e consistência de estado.

Soluções de paralelismo não-EVM

Solana

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

O Solana é um exemplo de execução paralela determinística. Nele, cada transação precisa declarar claramente os recursos que irá acessar e os direitos de acesso necessários (somente leitura ou leitura/escrita) no momento da submissão. Esse design permite que os nós da blockchain analisem previamente os recursos que cada transação precisará acessar antes da execução. Como as dependências de todas as contas são claramente declaradas antes de uma transação começar a ser executada, os nós podem determinar quais transações acessarão as mesmas contas e quais transações podem ser executadas em paralelo de forma segura, permitindo um agendamento inteligente que evita conflitos e, assim, estabelece a base para a execução paralela.

Uma vez que cada transação declara os recursos e permissões necessários antes da execução, o Solana pode verificar se existem dependências de contas entre as transações (modelo Sealevel). Se não houver contas de leitura/escrita 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 é muito diferente do Ethereum, apresentando inovações-chave em sua arquitetura e mecânica, especialmente em relação ao modelo de contas e armazenamento de estados.

O Ethereum precisa atualizar frequentemente a árvore de estado global (MPT) ao executar transações. Todos os estados das contas e contratos são armazenados em uma árvore de estado compartilhada, e qualquer transação precisa acessar e atualizar uma parte dessa árvore de estado. Em contraste, o Aptos divide as contas em unidades de estado independentes, onde cada objeto é um par chave-valor independente, existindo de maneira autônoma e sem influenciar uns aos outros, conectando-se apenas quando há uma relação de referência explícita. Não há caminhos de árvore compartilhados entre os objetos, não há competição por bloqueios, permitindo paralelismo completo.

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. Ao contrário do MPT do Ethereum, a Jellyfish Merkle Tree adota uma estrutura de árvore binária completa, o que simplifica 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 de forma independente, permitindo que as atualizações e consultas de várias contas sejam realizadas em paralelo.

O Aptos é uma execução paralela otimista que não exige que todas as dependências de contas sejam fornecidas previamente. Para isso, o Aptos utiliza o Block-STM, que estima as dependências com base na ordem de transações predefinida, reduzindo assim o número de interrupções.

EVM paralela

Comparado ao não EVM paralelo, 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 entender melhor isso, podemos observar como alguns projetos de EVM paralela (como Sui, Monad, Canto) abordam essas questões.

Sui

Assim como o Aptos, o Sui também utiliza um modelo de objetos para lidar com estados, adotando cada objeto (como contas, estados de contratos inteligentes) 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 geram conflitos diretos.

Embora o Sui utilize um modelo de objetos para gerenciar estados, ele ainda precisa se adaptar à EVM, utilizando uma camada de adaptação ou mecanismo abstrato adicional para conectar o modelo de objetos ao modelo de contas da EVM.

No Sui, o agendamento das transações utiliza uma estratégia de execução paralela otimista, presumindo que não existem conflitos entre as transações. Se ocorrer um conflito, o sistema utiliza um mecanismo de retrocesso para restaurar o estado.

O Sui utiliza um modelo de objetos e técnicas de isolamento de estado, conseguindo evitar efetivamente problemas de dependência de estado. Cada objeto é um recurso independente, permitindo que diferentes transações sejam executadas em paralelo, aumentando assim a taxa de transferência e a eficiência. Porém, esse método traz como trade-off a complexidade do modelo de objetos e o custo do mecanismo de retrocesso. Se ocorrerem conflitos entre transações, será necessário retroceder parte do estado, o que aumenta a carga do sistema e pode impactar a eficiência do processamento paralelo. Em comparação com sistemas não EVM paralelos (como o Solana), o Sui requer mais recursos computacionais e de armazenamento para manter uma paralelização eficiente.

Monad

Assim como o Sui, o Monad também adota a execução paralela otimista. No entanto, a execução paralela otimista do Monad ainda prevê algumas transações com dependências antes da execução real, principalmente por meio do analisador de código estático do Monad. Essa previsão exige acesso ao estado, e a forma 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 durante a execução paralela, o Monad também reestruturou o banco de dados.

A árvore de estado Monad é dividida em partições, cada partição mantém sua própria subárvore de estado. Durante a atualização, apenas as fatias relevantes precisam ser modificadas, sem a necessidade de reconstruir toda a árvore de estado. Uma tabela de índice de estado permite localizar rapidamente o estado dentro da partição, reduzindo a interação entre partições.

并行区块链全解:执行原理、代表项目及所处周期

Resumo

O núcleo da paralelização é melhorar a eficiência da camada de execução através de múltiplos caminhos, e para alcançar essa execução em múltiplos caminhos, a cadeia precisa implementar uma série de mecanismos de detecção de conflitos e retroceder para garantir que possam ser executados em paralelo sem afetar a consistência do estado final, além de realizar certas melhorias no banco de dados.

É claro que a melhoria da eficiência da camada de execução não se limita apenas à execução paralela; a otimização da fase de execução também pode ser feita reduzindo as operações de leitura e escrita que uma transação exige do banco de dados. A melhoria da velocidade geral da cadeia abrange uma gama ainda mais ampla, incluindo melhorias na eficiência da camada de consenso.

Cada tecnologia possui suas próprias condições limitantes específicas. A paralelização é apenas uma das maneiras de aumentar a eficiência, e a decisão final sobre o uso dessa tecnologia deve considerar se é amigável aos desenvolvedores e se pode ser realizada sem comprometer a descentralização. A combinação de tecnologias não é necessariamente melhor; pelo menos no caso do Ethereum, a paralelização não é tão atraente. Se considerarmos apenas o aumento da eficiência, a inclusão da paralelização pode não ser a melhor solução para o Ethereum, seja do ponto de vista da simplicidade ou da atual rota do Ethereum centrada no Rollup.