A Máquina Virtual Solana (SVM) está a tornar-se amplamente adotada como a camada de execução para várias soluções de Camada-2 (L2). No entanto, uma limitação chave no design original do SVM é a obscuridade da sua raiz de estado global. Isto coloca desafios significativos para os sistemas de rollup, onde as raízes de estado globais são críticas para garantir a integridade, permitir provas de fraude e suportar operações entre camadas.
Num rollup, o proponente envia a raiz do estado L2 (raiz de Merkle) para a camada-1 (L1) periodicamente, estabelecendo checkpoints para a cadeia L2. Estes checkpoints permitem provas de inclusão para qualquer estado de conta, possibilitando a execução sem estado de um checkpoint para outro. As provas de fraude dependem deste mecanismo, uma vez que os participantes podem fornecer provas de inclusão para verificar entradas válidas durante disputas. Além disso, as árvores de Merkle reforçam a segurança das pontes canônicas ao permitir que os utilizadores gerem provas de inclusão para transações de levantamento, garantindo interações sem confiança entre L2 e L1.
Para enfrentar esses desafios, Rede SOONintroduz árvores de Merkle na camada de execução do SVM, permitindo que os clientes forneçam provas de inclusão. EM BREVE integra-se com a Prova de História usando entradas únicas para incorporar raízes de estado diretamente em blockchains baseados em SVM. Com esta inovação, a pilha SOON pode suportar novos rollups baseados em SVM com segurança, escalabilidade e utilidade aprimoradas.
Solana sempre foi projetada com alta capacidade de processamento como objetivo principal, exigindo compensações de design deliberadas — especialmente durante seu desenvolvimento inicial — para alcançar seu desempenho inovador. Entre essas compensações, uma das decisões mais impactantes centrou-se em como e quando Solana merklizaria seu estado.
No final, esta decisão criou desafios significativos para os clientes na comprovação do estado global, bem como na inclusão de transações e na verificação simples de pagamentos (SPV). A falta de uma raiz de estado hashada de forma consistente representando o estado SVM merklizado coloca dificuldades consideráveis para projetos como clientes leves e rollups.
Vamos dar uma olhada em como a merklização é feita em outras cadeias e depois identificar os desafios apresentados pela arquitetura do protocolo da Solana.
No Bitcoin, as transações são armazenadas num bloco usando um Árvore de Merkle, cuja raiz está armazenada no cabeçalho do bloco. O protocolo Bitcoin irá hash os inputs e outputs de uma transação (bem como alguns metadados adicionais) num ID da transação (TxID). Para provar o estado no Bitcoin, um usuário pode simplesmente calcular uma prova de Merkle para verificar o TxID em relação à raiz de Merkle do bloco.
Este processo de verificação também valida o estado, uma vez que o ID da Tx é único para algum conjunto de entradas e saídas, ambos os quais refletem mudanças no estado do endereço. Note que as transações de Bitcoin também podem conter Scripts Taproot, que produzem saídas de transação que podem ser verificadas durante a verificação, frequentemente, rerodando o script usando as entradas da transação e os dados de testemunho do script, e validando as suas saídas.
Semelhante ao Bitcoin, o Ethereum armazena transações usando uma estrutura de dados personalizada (derivada de uma árvore de Merkle) chamada de Árvore Trie de Merkle Patricia(MPT). Esta estrutura de dados é projetada para atualizações rápidas e otimização em conjuntos de dados grandes. Naturalmente, isso ocorre porque o Ethereum tem significativamente mais entradas e saídas para gerenciar do que o Bitcoin.
O Máquina Virtual Ethereum(EVM) atua como uma máquina de estado global. O EVM é essencialmente um ambiente de computação distribuído gigantesco que suporta executávelcontratos inteligentes, cada um dos quais reserva seu próprio espaço de endereço na memória global. Como resultado, os clientes que desejam verificar o estado no Ethereum devem levar em consideração não apenas o resultado de uma transação (registos, código de retorno, etc.), mas também as alterações no estado global como resultado da transação.
Felizmente, o EVM faz uso inteligente de três estruturas de trie importantes, que armazenam suas raízes em cada cabeçalho de bloco.
Dado uma transação, um cliente pode provar a sua inclusão num bloco avaliando a raiz da árvore de transações (como no Bitcoin), o seu resultado avaliando a árvore de recibos e as alterações ao estado global avaliando a árvore de estado.
Uma das razões para a alta capacidade de processamento da Solana é o fato de não possuir a estrutura de multi-árvore que o Ethereum possui. Os líderes da Solana calculam árvores de Merkle ao criar blocos, mas elas têm uma estrutura diferente das do EVM. Infelizmente, aí reside o problema para os rollups baseados em SVM.
Solana merklizes transações em entradas, e pode haver várias entradas por slot; portanto, várias raízes de transação por bloco. Além disso, Solana calcula uma raiz de Merkle do estado da conta apenas uma vez por época (cerca de 2,5 dias), e esta raiz não está disponível no livro-razão.
Na verdade, os cabeçalhos de bloco do Solana não contêm nenhuma raiz de Merkle. Em vez disso, contêm o anterior e o atual blockhash, que é calculada através da Prova de HistóriaO algoritmo de Prova de História (PoH). A PoH requer que os validadores se registem continuamente "ticks" ao fazer hash de forma recursiva das entradas de bloco, que podem estar vazias ou conter lotes de transações. O tick final (hash) do algoritmo PoH é o blockhash desse bloco.
O problema com a Prova de História é que torna muito difícil provar o estado a partir de um blockhash. O Solana é projetado para transmitir hashes de PoH para manter seu conceito de tempo decorrido. A raiz da transação só está disponível para um pulso de PoH que continha uma entrada com transações, não o bloco como um todo, e não há raiz de estado armazenada em nenhuma entrada.
No entanto, existe outro hash que os clientes podem usar: o hash do banco. Por vezes referido como um hash de slot, os hashes do banco estão disponíveis no SlotHashes sysvarconta, que pode ser consultada por um cliente. Um hash de banco é criado para cada bloco (slot) a partir de um punhado de entradas:
Como se pode ver, o hash do banco está sobrecarregado com vários inputs de hash, o que adiciona complexidade para os clientes que tentam provar informações sobre transações ou estado. Além disso, apenas um hash de banco para um banco que realizou um "hash de contas de época" - o hash de todas as contas uma vez por época - terá essa raiz específica incluída nele. Além disso, a conta SlotHashes sysvar é truncada para os últimos 512 hashes de banco.
rede SOONé uma SVM L2 em cima do Ethereum. Ao integrar a merkleização em BREVE, priorizamos o uso de soluções comprovadas e bem estabelecidas em prol da estabilidade, em vez de reinventar a roda. Ao decidir qual estrutura de dados ou algoritmos de hashing usar, consideramos sua compatibilidade com os contratos L1 do Pilha de otimismo, sua capacidade de se integrar perfeitamente na arquitetura Solana e se podem alcançar alto desempenho sob o modelo de conta da Solana.
Descobrimos que à medida que o número de contas aumenta, o modelo de armazenamento Merkle Patricia Trie (MPT) baseado em LSM-Treeproduziria mais amplificação de leitura/escrita de disco, resultando em perdas de desempenho. No final, decidimos integrar o Erigon MPTao aproveitar o excelente trabalho em Rust feito pelo rETHequipa e adicionar suporte para o modelo de conta Solana.
Como mencionado anteriormente, o trie de estado do SOON é uma MPT construída para suportar contas Solana. Como tal, definimos um tipo de conta compatível com SVM para servir como os dados de cada nó folha.
struct TrieSolanaAccount {
lamports: u64,
dados: Vec
executável: bool,
rent_epoch: u64,
proprietário: Pubkey,
}
Para habilitar o módulo MPT a subscrever o estado mais recente das contas SVM em tempo real, introduzimos um notificador de conta. Durante a Fase Bancária, o notificador de conta informa o módulo MPT das alterações de estado da conta, e o MPT atualiza incrementalmente essas alterações na estrutura de trie.
É importante notar que o módulo MPT apenas atualiza suas subárvores a cada 50 slots e não calcula a raiz do estado no final de cada slot. Esta abordagem é adotada por duas razões. Primeiro, calcular a raiz do estado a cada slot afetaria significativamente o desempenho. Em segundo lugar, a raiz do estado só é necessária quando o proponente submete um outputRootpara o L1. Portanto, só precisa de ser calculado periodicamente, com base na frequência de submissão do proponente.
outputRoot = keccak256(versão, raiz_do_estado, raiz_de_retirada, hash_do_bloco_l2)
EM BREVE O módulo MPT mantém simultaneamente dois tipos de estruturas de trie: a Trie do Estado para o estado global e a Trie de Retirada para transações de retirada. O proponente gera periodicamente uma raiz de saída pelo estado raiz e raiz de retirada, e submete-a à L1.
Atualmente, o SOON calcula a raiz do estado e a raiz de retirada uma vez a cada 450 slots e anexa-a à blockchain L2. Como resultado, a consistência dos dados MPT em outros nós da rede pode ser garantida. No entanto, a estrutura de bloco do Solana não inclui um cabeçalho de bloco, o que significa que não há lugar para armazenar a raiz do estado. Vamos dar uma olhada mais de perto na estrutura básica da blockchain Solana e depois explorar como o SOON introduz o UniqueEntry para armazenar a raiz do estado.
A blockchain Solana é composta por slots, que são gerados pelo módulo PoH. Um slot contém múltiplas entradas, sendo que cada entrada inclui ticks e transações. Nas camadas de rede e armazenamento, um slot é armazenado e transmitido usando fragmentoscomo a menor unidade. Fragmentos podem ser convertidos para e a partir de entradas.
pub struct Entry {
O número de hashes desde o ID de entrada anterior.
pub num_hashes: u64,
/// O hash SHA-256num_hashes
após o ID de entrada anterior.
pub hash: Hash,
/// Uma lista não ordenada de transações que foram observadas antes do ID de entrada ser
/// gerado. Eles podem ter sido observados antes de um ID de Entrada anterior, mas foram
/// empurrado de volta para esta lista para garantir a interpretação determinística do livro-razão.
transações públicas: Vec
}
Seguimos a estrutura blockchain gerada pela PoH e mantivemos a estrutura de fragmentação, permitindo-nos reutilizar a camada de armazenamento existente, a camada de rede e o framework RPC da Solana. Para armazenar dados adicionais na blockchain L2, introduzimos o UniqueEntry. Esta característica permite-nos personalizar a carga da entrada e definir as regras de validação independentes para os dados.
pub const UNIQUE_ENTRY_NUM_HASHES_FLAG: u64 = 0x8000_0000_0000_0000;
/// A entrada exclusiva é um tipo de entrada especial. O que é útil quando precisamos de armazenar alguns dados em
/// blockstore but do not want to verify it.
///
/// A disposição de num_hashes
é:
/// |…1 bit…|…63 bit…|
/// \ _ _/
/// \ \
/// bandeira campo personalizado
pub trait UniqueEntry: Sized {
fn encode_to_entries(&self) -> Vec
FN decode_from_entries(
entradas: impl IntoIterator<Item = Entry>,
) -> Result
}
pub fn unique_entry(custom_field: u64, hash: Hash) -> Entry {
Entrada {
num_hashes: num_hashes(custom_field), hash, transactions: vec![],
}
}
pub fn num_hashes(custom_field: u64) -> u64 {
assert!(custom_field < (1 << 63));
UNIQUE_ENTRY_NUM_HASHES_FLAG | custom_field
}
pub fn is_unique(entry: &Entry) -> bool {
entry.num_hashes & UNIQUE_ENTRY_NUM_HASHES_FLAG != 0
}
Num UniqueEntry, num_hashes é usado como um layout de bits, onde o primeiro bit de sinal é usado para distinguir entre uma Entry e um Unique Entry, e os seguintes 63 bits são usados para definir o tipo de payload. O campo de hash serve como o payload, contendo os dados personalizados necessários.
Vamos ver um exemplo de utilização de três entradas únicas para armazenar o hash do slot, raiz do estado e raiz de retirada.
Entrada única de raiz MPT.
pub struct MptRoot {
pub slot: Slot,
pub state_root: B256,
pub withdrawal_root: B256,
}
impl UniqueEntry for MptRoot {
fn encode_to_entries(&self) -> Vec
deixe a entrada do slot = unique_entry(0, slot_to_hash(self.slot)); deixe a entrada da raiz do estado = unique_entry(1, self.state_root.0.into()); deixe a entrada da raiz de retirada = unique_entry(2, self.withdrawal_root.0.into()); vec![slot_entry, state_root_entry, withdrawal_root_entry]
}
fn decode_from_entries(
entradas: impl IntoIterator
) -> Result
deixe mut entradas = entradas.into_iter(); deixe entrada = entradas.next().ok_or(UniqueEntryError::NoMoreEntries)?; deixe slot = hash_to_slot(entrada.hash); deixe entrada = entradas.next().ok_or(UniqueEntryError::NoMoreEntries)?; deixe state_root = B256::from(entrada.hash.to_bytes()); deixe entrada = entradas.next().ok_or(UniqueEntryError::NoMoreEntries)?; deixe withdrawal_root = B256::from(entrada.hash.to_bytes()); Ok(MptRoot { slot, state_root, withdrawal_root, })
}
}
Uma vez que o UniqueEntry redefine a semântica de num_hashes, não pode ser processado durante a fase de verificação do PoH. Portanto, no início do processo de verificação, filtramos primeiro as entradas únicas e encaminhamo-las para um fluxo de verificação personalizado com base no seu tipo de carga útil. As entradas regulares restantes continuam através do processo de verificação original do PoH.
A ponte nativa é uma peça crucial da infraestrutura para soluções de Camada 2, responsável pela transmissão de mensagens entre a L1 e a L2. As mensagens da L1 para a L2 são chamadas de transações de depósito, enquanto as mensagens da L2 para a L1 são chamadas de transações de retirada.
As transações de depósito são tipicamente tratadas pelo pipeline de derivaçãoe apenas exigem que o utilizador envie uma única transação na L1. No entanto, as transações de levantamento são mais complexas e exigem que o utilizador envie três transações para completar o processo:
A prova de inclusão de uma transação de levantamento garante que o levantamento ocorreu no L2, aumentando significativamente a segurança da ponte canônica.
Na pilha OP, o hash da transação de retirada do usuário é armazenado na variável de estado correspondente aoOptimismPortalcontrato. A interface RPC eth_getProof é então usada para fornecer aos usuários uma prova de inclusão para uma transação de saque específica.
Ao contrário do EVM, os dados do contrato SVM são armazenados no campo de dados das contas, e todos os tipos de contas existem no mesmo nível hierárquico.
SOON introduziu um programa de ponte nativa: Bridge1111111111111111111111111111111111111. Sempre que um usuário inicia uma transação de saque, o programa de ponte gera um índice globalmente único para cada transação de saque e usa este índice como semente para criar um novo Conta Derivada do Programa(PDA) para armazenar a transação de levantamento correspondente.
pub struct RetiradaTransacao {
contador de levantamentos
pub nonce: U256,
/// usuário que deseja sacar
remetente público: Pubkey,
/// endereço do usuário em L1
alvo público: Endereço,
/// retirar montante em lamports
valor público: U256,
/// limite de gás em L1
pub gas_limit: U256,
/// dados de retirada em L1
dados de publicação: L1WithdrawalCalldata,
}
Definimos a estrutura WithdrawalTransaction para armazenar transações de saque no campo de dados do PDA.
Este design funciona de forma semelhante ao OP Stack. Assim que o outputRoot contendo a transação de levantamento for submetido ao L1, o utilizador pode submeter uma prova de inclusão para a transação de levantamento ao L1 para iniciar o período de desafio.
Depois que o proponente enviar o outputRoot para L1, significa que o estado de L2 foi resolvido. Se um desafiante detetar que o proponente enviou um estado incorreto, pode iniciar um desafio para proteger os fundos na ponte.
Um dos aspectos mais críticos na construção de um sistema à prova de falhas é permitir que a blockchain faça a transição do estado S1 para o estado S2 de forma stateless. Isso garante que o contrato do árbitro em L1 possa reproduzir as instruções do programa de forma stateless para realizar a arbitragem.
No entanto, no início deste processo, o desafiante deve provar que todas as entradas iniciais no estado S1 são válidas. Estas entradas iniciais incluem os estados das contas participantes (por exemplo, lamports, dados, proprietário, etc.). Ao contrário do EVM, o SVM separa naturalmente o estado da conta da computação. No entanto, API SVM da Anzapermite que as contas sejam passadas para o SVM através de um traço TransactionProcessingCallback, como mostrado abaixo.
pub trait TransactionProcessingCallback {
fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option
fn obter_dados_compartilhados_conta(&self, pubkey: &Pubkey) -> Opção
fn add_builtin_account(&self, _name: &str, _program_id: &Pubkey) {}
}
Assim, só precisamos de usar o estado S1 juntamente com as provas de inclusão das contas de entrada para verificar a validade dos inputs do programa de desafio.
EM BREVE marca um marco importante para o desenvolvimento do ecossistema do SVM. Ao integrar a merklização, EM BREVE aborda a falta de uma raiz de estado global da Solana, permitindo recursos essenciais como provas de inclusão para provas de falhas, saques seguros e execução sem estado.
Além disso, o design de merklização da SOON dentro da VM pode permitir clientes leves em cadeias baseadas em VM, mesmo que a própria Solana atualmente não suporte clientes leves. Até é possível que parte do design possa ajudar a trazer clientes leves para a cadeia principal também.
Usando Árvores de Patricia Merkle (MPT) para gestão de estado, a SOON alinha-se em breve com a infraestrutura da Ethereum, melhorando a interoperabilidade e promovendo soluções L2 baseadas em SVM. Esta inovação fortalece o ecossistema SVM, melhorando a segurança, a escalabilidade e a compatibilidade, ao mesmo tempo que fomenta o crescimento de aplicações descentralizadas.
Compartir
A Máquina Virtual Solana (SVM) está a tornar-se amplamente adotada como a camada de execução para várias soluções de Camada-2 (L2). No entanto, uma limitação chave no design original do SVM é a obscuridade da sua raiz de estado global. Isto coloca desafios significativos para os sistemas de rollup, onde as raízes de estado globais são críticas para garantir a integridade, permitir provas de fraude e suportar operações entre camadas.
Num rollup, o proponente envia a raiz do estado L2 (raiz de Merkle) para a camada-1 (L1) periodicamente, estabelecendo checkpoints para a cadeia L2. Estes checkpoints permitem provas de inclusão para qualquer estado de conta, possibilitando a execução sem estado de um checkpoint para outro. As provas de fraude dependem deste mecanismo, uma vez que os participantes podem fornecer provas de inclusão para verificar entradas válidas durante disputas. Além disso, as árvores de Merkle reforçam a segurança das pontes canônicas ao permitir que os utilizadores gerem provas de inclusão para transações de levantamento, garantindo interações sem confiança entre L2 e L1.
Para enfrentar esses desafios, Rede SOONintroduz árvores de Merkle na camada de execução do SVM, permitindo que os clientes forneçam provas de inclusão. EM BREVE integra-se com a Prova de História usando entradas únicas para incorporar raízes de estado diretamente em blockchains baseados em SVM. Com esta inovação, a pilha SOON pode suportar novos rollups baseados em SVM com segurança, escalabilidade e utilidade aprimoradas.
Solana sempre foi projetada com alta capacidade de processamento como objetivo principal, exigindo compensações de design deliberadas — especialmente durante seu desenvolvimento inicial — para alcançar seu desempenho inovador. Entre essas compensações, uma das decisões mais impactantes centrou-se em como e quando Solana merklizaria seu estado.
No final, esta decisão criou desafios significativos para os clientes na comprovação do estado global, bem como na inclusão de transações e na verificação simples de pagamentos (SPV). A falta de uma raiz de estado hashada de forma consistente representando o estado SVM merklizado coloca dificuldades consideráveis para projetos como clientes leves e rollups.
Vamos dar uma olhada em como a merklização é feita em outras cadeias e depois identificar os desafios apresentados pela arquitetura do protocolo da Solana.
No Bitcoin, as transações são armazenadas num bloco usando um Árvore de Merkle, cuja raiz está armazenada no cabeçalho do bloco. O protocolo Bitcoin irá hash os inputs e outputs de uma transação (bem como alguns metadados adicionais) num ID da transação (TxID). Para provar o estado no Bitcoin, um usuário pode simplesmente calcular uma prova de Merkle para verificar o TxID em relação à raiz de Merkle do bloco.
Este processo de verificação também valida o estado, uma vez que o ID da Tx é único para algum conjunto de entradas e saídas, ambos os quais refletem mudanças no estado do endereço. Note que as transações de Bitcoin também podem conter Scripts Taproot, que produzem saídas de transação que podem ser verificadas durante a verificação, frequentemente, rerodando o script usando as entradas da transação e os dados de testemunho do script, e validando as suas saídas.
Semelhante ao Bitcoin, o Ethereum armazena transações usando uma estrutura de dados personalizada (derivada de uma árvore de Merkle) chamada de Árvore Trie de Merkle Patricia(MPT). Esta estrutura de dados é projetada para atualizações rápidas e otimização em conjuntos de dados grandes. Naturalmente, isso ocorre porque o Ethereum tem significativamente mais entradas e saídas para gerenciar do que o Bitcoin.
O Máquina Virtual Ethereum(EVM) atua como uma máquina de estado global. O EVM é essencialmente um ambiente de computação distribuído gigantesco que suporta executávelcontratos inteligentes, cada um dos quais reserva seu próprio espaço de endereço na memória global. Como resultado, os clientes que desejam verificar o estado no Ethereum devem levar em consideração não apenas o resultado de uma transação (registos, código de retorno, etc.), mas também as alterações no estado global como resultado da transação.
Felizmente, o EVM faz uso inteligente de três estruturas de trie importantes, que armazenam suas raízes em cada cabeçalho de bloco.
Dado uma transação, um cliente pode provar a sua inclusão num bloco avaliando a raiz da árvore de transações (como no Bitcoin), o seu resultado avaliando a árvore de recibos e as alterações ao estado global avaliando a árvore de estado.
Uma das razões para a alta capacidade de processamento da Solana é o fato de não possuir a estrutura de multi-árvore que o Ethereum possui. Os líderes da Solana calculam árvores de Merkle ao criar blocos, mas elas têm uma estrutura diferente das do EVM. Infelizmente, aí reside o problema para os rollups baseados em SVM.
Solana merklizes transações em entradas, e pode haver várias entradas por slot; portanto, várias raízes de transação por bloco. Além disso, Solana calcula uma raiz de Merkle do estado da conta apenas uma vez por época (cerca de 2,5 dias), e esta raiz não está disponível no livro-razão.
Na verdade, os cabeçalhos de bloco do Solana não contêm nenhuma raiz de Merkle. Em vez disso, contêm o anterior e o atual blockhash, que é calculada através da Prova de HistóriaO algoritmo de Prova de História (PoH). A PoH requer que os validadores se registem continuamente "ticks" ao fazer hash de forma recursiva das entradas de bloco, que podem estar vazias ou conter lotes de transações. O tick final (hash) do algoritmo PoH é o blockhash desse bloco.
O problema com a Prova de História é que torna muito difícil provar o estado a partir de um blockhash. O Solana é projetado para transmitir hashes de PoH para manter seu conceito de tempo decorrido. A raiz da transação só está disponível para um pulso de PoH que continha uma entrada com transações, não o bloco como um todo, e não há raiz de estado armazenada em nenhuma entrada.
No entanto, existe outro hash que os clientes podem usar: o hash do banco. Por vezes referido como um hash de slot, os hashes do banco estão disponíveis no SlotHashes sysvarconta, que pode ser consultada por um cliente. Um hash de banco é criado para cada bloco (slot) a partir de um punhado de entradas:
Como se pode ver, o hash do banco está sobrecarregado com vários inputs de hash, o que adiciona complexidade para os clientes que tentam provar informações sobre transações ou estado. Além disso, apenas um hash de banco para um banco que realizou um "hash de contas de época" - o hash de todas as contas uma vez por época - terá essa raiz específica incluída nele. Além disso, a conta SlotHashes sysvar é truncada para os últimos 512 hashes de banco.
rede SOONé uma SVM L2 em cima do Ethereum. Ao integrar a merkleização em BREVE, priorizamos o uso de soluções comprovadas e bem estabelecidas em prol da estabilidade, em vez de reinventar a roda. Ao decidir qual estrutura de dados ou algoritmos de hashing usar, consideramos sua compatibilidade com os contratos L1 do Pilha de otimismo, sua capacidade de se integrar perfeitamente na arquitetura Solana e se podem alcançar alto desempenho sob o modelo de conta da Solana.
Descobrimos que à medida que o número de contas aumenta, o modelo de armazenamento Merkle Patricia Trie (MPT) baseado em LSM-Treeproduziria mais amplificação de leitura/escrita de disco, resultando em perdas de desempenho. No final, decidimos integrar o Erigon MPTao aproveitar o excelente trabalho em Rust feito pelo rETHequipa e adicionar suporte para o modelo de conta Solana.
Como mencionado anteriormente, o trie de estado do SOON é uma MPT construída para suportar contas Solana. Como tal, definimos um tipo de conta compatível com SVM para servir como os dados de cada nó folha.
struct TrieSolanaAccount {
lamports: u64,
dados: Vec
executável: bool,
rent_epoch: u64,
proprietário: Pubkey,
}
Para habilitar o módulo MPT a subscrever o estado mais recente das contas SVM em tempo real, introduzimos um notificador de conta. Durante a Fase Bancária, o notificador de conta informa o módulo MPT das alterações de estado da conta, e o MPT atualiza incrementalmente essas alterações na estrutura de trie.
É importante notar que o módulo MPT apenas atualiza suas subárvores a cada 50 slots e não calcula a raiz do estado no final de cada slot. Esta abordagem é adotada por duas razões. Primeiro, calcular a raiz do estado a cada slot afetaria significativamente o desempenho. Em segundo lugar, a raiz do estado só é necessária quando o proponente submete um outputRootpara o L1. Portanto, só precisa de ser calculado periodicamente, com base na frequência de submissão do proponente.
outputRoot = keccak256(versão, raiz_do_estado, raiz_de_retirada, hash_do_bloco_l2)
EM BREVE O módulo MPT mantém simultaneamente dois tipos de estruturas de trie: a Trie do Estado para o estado global e a Trie de Retirada para transações de retirada. O proponente gera periodicamente uma raiz de saída pelo estado raiz e raiz de retirada, e submete-a à L1.
Atualmente, o SOON calcula a raiz do estado e a raiz de retirada uma vez a cada 450 slots e anexa-a à blockchain L2. Como resultado, a consistência dos dados MPT em outros nós da rede pode ser garantida. No entanto, a estrutura de bloco do Solana não inclui um cabeçalho de bloco, o que significa que não há lugar para armazenar a raiz do estado. Vamos dar uma olhada mais de perto na estrutura básica da blockchain Solana e depois explorar como o SOON introduz o UniqueEntry para armazenar a raiz do estado.
A blockchain Solana é composta por slots, que são gerados pelo módulo PoH. Um slot contém múltiplas entradas, sendo que cada entrada inclui ticks e transações. Nas camadas de rede e armazenamento, um slot é armazenado e transmitido usando fragmentoscomo a menor unidade. Fragmentos podem ser convertidos para e a partir de entradas.
pub struct Entry {
O número de hashes desde o ID de entrada anterior.
pub num_hashes: u64,
/// O hash SHA-256num_hashes
após o ID de entrada anterior.
pub hash: Hash,
/// Uma lista não ordenada de transações que foram observadas antes do ID de entrada ser
/// gerado. Eles podem ter sido observados antes de um ID de Entrada anterior, mas foram
/// empurrado de volta para esta lista para garantir a interpretação determinística do livro-razão.
transações públicas: Vec
}
Seguimos a estrutura blockchain gerada pela PoH e mantivemos a estrutura de fragmentação, permitindo-nos reutilizar a camada de armazenamento existente, a camada de rede e o framework RPC da Solana. Para armazenar dados adicionais na blockchain L2, introduzimos o UniqueEntry. Esta característica permite-nos personalizar a carga da entrada e definir as regras de validação independentes para os dados.
pub const UNIQUE_ENTRY_NUM_HASHES_FLAG: u64 = 0x8000_0000_0000_0000;
/// A entrada exclusiva é um tipo de entrada especial. O que é útil quando precisamos de armazenar alguns dados em
/// blockstore but do not want to verify it.
///
/// A disposição de num_hashes
é:
/// |…1 bit…|…63 bit…|
/// \ _ _/
/// \ \
/// bandeira campo personalizado
pub trait UniqueEntry: Sized {
fn encode_to_entries(&self) -> Vec
FN decode_from_entries(
entradas: impl IntoIterator<Item = Entry>,
) -> Result
}
pub fn unique_entry(custom_field: u64, hash: Hash) -> Entry {
Entrada {
num_hashes: num_hashes(custom_field), hash, transactions: vec![],
}
}
pub fn num_hashes(custom_field: u64) -> u64 {
assert!(custom_field < (1 << 63));
UNIQUE_ENTRY_NUM_HASHES_FLAG | custom_field
}
pub fn is_unique(entry: &Entry) -> bool {
entry.num_hashes & UNIQUE_ENTRY_NUM_HASHES_FLAG != 0
}
Num UniqueEntry, num_hashes é usado como um layout de bits, onde o primeiro bit de sinal é usado para distinguir entre uma Entry e um Unique Entry, e os seguintes 63 bits são usados para definir o tipo de payload. O campo de hash serve como o payload, contendo os dados personalizados necessários.
Vamos ver um exemplo de utilização de três entradas únicas para armazenar o hash do slot, raiz do estado e raiz de retirada.
Entrada única de raiz MPT.
pub struct MptRoot {
pub slot: Slot,
pub state_root: B256,
pub withdrawal_root: B256,
}
impl UniqueEntry for MptRoot {
fn encode_to_entries(&self) -> Vec
deixe a entrada do slot = unique_entry(0, slot_to_hash(self.slot)); deixe a entrada da raiz do estado = unique_entry(1, self.state_root.0.into()); deixe a entrada da raiz de retirada = unique_entry(2, self.withdrawal_root.0.into()); vec![slot_entry, state_root_entry, withdrawal_root_entry]
}
fn decode_from_entries(
entradas: impl IntoIterator
) -> Result
deixe mut entradas = entradas.into_iter(); deixe entrada = entradas.next().ok_or(UniqueEntryError::NoMoreEntries)?; deixe slot = hash_to_slot(entrada.hash); deixe entrada = entradas.next().ok_or(UniqueEntryError::NoMoreEntries)?; deixe state_root = B256::from(entrada.hash.to_bytes()); deixe entrada = entradas.next().ok_or(UniqueEntryError::NoMoreEntries)?; deixe withdrawal_root = B256::from(entrada.hash.to_bytes()); Ok(MptRoot { slot, state_root, withdrawal_root, })
}
}
Uma vez que o UniqueEntry redefine a semântica de num_hashes, não pode ser processado durante a fase de verificação do PoH. Portanto, no início do processo de verificação, filtramos primeiro as entradas únicas e encaminhamo-las para um fluxo de verificação personalizado com base no seu tipo de carga útil. As entradas regulares restantes continuam através do processo de verificação original do PoH.
A ponte nativa é uma peça crucial da infraestrutura para soluções de Camada 2, responsável pela transmissão de mensagens entre a L1 e a L2. As mensagens da L1 para a L2 são chamadas de transações de depósito, enquanto as mensagens da L2 para a L1 são chamadas de transações de retirada.
As transações de depósito são tipicamente tratadas pelo pipeline de derivaçãoe apenas exigem que o utilizador envie uma única transação na L1. No entanto, as transações de levantamento são mais complexas e exigem que o utilizador envie três transações para completar o processo:
A prova de inclusão de uma transação de levantamento garante que o levantamento ocorreu no L2, aumentando significativamente a segurança da ponte canônica.
Na pilha OP, o hash da transação de retirada do usuário é armazenado na variável de estado correspondente aoOptimismPortalcontrato. A interface RPC eth_getProof é então usada para fornecer aos usuários uma prova de inclusão para uma transação de saque específica.
Ao contrário do EVM, os dados do contrato SVM são armazenados no campo de dados das contas, e todos os tipos de contas existem no mesmo nível hierárquico.
SOON introduziu um programa de ponte nativa: Bridge1111111111111111111111111111111111111. Sempre que um usuário inicia uma transação de saque, o programa de ponte gera um índice globalmente único para cada transação de saque e usa este índice como semente para criar um novo Conta Derivada do Programa(PDA) para armazenar a transação de levantamento correspondente.
pub struct RetiradaTransacao {
contador de levantamentos
pub nonce: U256,
/// usuário que deseja sacar
remetente público: Pubkey,
/// endereço do usuário em L1
alvo público: Endereço,
/// retirar montante em lamports
valor público: U256,
/// limite de gás em L1
pub gas_limit: U256,
/// dados de retirada em L1
dados de publicação: L1WithdrawalCalldata,
}
Definimos a estrutura WithdrawalTransaction para armazenar transações de saque no campo de dados do PDA.
Este design funciona de forma semelhante ao OP Stack. Assim que o outputRoot contendo a transação de levantamento for submetido ao L1, o utilizador pode submeter uma prova de inclusão para a transação de levantamento ao L1 para iniciar o período de desafio.
Depois que o proponente enviar o outputRoot para L1, significa que o estado de L2 foi resolvido. Se um desafiante detetar que o proponente enviou um estado incorreto, pode iniciar um desafio para proteger os fundos na ponte.
Um dos aspectos mais críticos na construção de um sistema à prova de falhas é permitir que a blockchain faça a transição do estado S1 para o estado S2 de forma stateless. Isso garante que o contrato do árbitro em L1 possa reproduzir as instruções do programa de forma stateless para realizar a arbitragem.
No entanto, no início deste processo, o desafiante deve provar que todas as entradas iniciais no estado S1 são válidas. Estas entradas iniciais incluem os estados das contas participantes (por exemplo, lamports, dados, proprietário, etc.). Ao contrário do EVM, o SVM separa naturalmente o estado da conta da computação. No entanto, API SVM da Anzapermite que as contas sejam passadas para o SVM através de um traço TransactionProcessingCallback, como mostrado abaixo.
pub trait TransactionProcessingCallback {
fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option
fn obter_dados_compartilhados_conta(&self, pubkey: &Pubkey) -> Opção
fn add_builtin_account(&self, _name: &str, _program_id: &Pubkey) {}
}
Assim, só precisamos de usar o estado S1 juntamente com as provas de inclusão das contas de entrada para verificar a validade dos inputs do programa de desafio.
EM BREVE marca um marco importante para o desenvolvimento do ecossistema do SVM. Ao integrar a merklização, EM BREVE aborda a falta de uma raiz de estado global da Solana, permitindo recursos essenciais como provas de inclusão para provas de falhas, saques seguros e execução sem estado.
Além disso, o design de merklização da SOON dentro da VM pode permitir clientes leves em cadeias baseadas em VM, mesmo que a própria Solana atualmente não suporte clientes leves. Até é possível que parte do design possa ajudar a trazer clientes leves para a cadeia principal também.
Usando Árvores de Patricia Merkle (MPT) para gestão de estado, a SOON alinha-se em breve com a infraestrutura da Ethereum, melhorando a interoperabilidade e promovendo soluções L2 baseadas em SVM. Esta inovação fortalece o ecossistema SVM, melhorando a segurança, a escalabilidade e a compatibilidade, ao mesmo tempo que fomenta o crescimento de aplicações descentralizadas.