A Máquina Virtual Solana (SVM) está se tornando 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 de sua raiz de estado global. Isso representa desafios significativos para os sistemas de rollup, onde as raízes de estado global são críticas para garantir a integridade, permitir provas de fraude e apoiar operações entre camadas.
Em um rollup, o proponente envia a raiz do estado L2 (raiz de Merkle) para a Camada-1 (L1) periodicamente, estabelecendo checkpoints para a cadeia L2. Esses 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 desse mecanismo, já que os participantes podem fornecer provas de inclusão para verificar entradas válidas durante disputas. Além disso, as árvores de Merkle aprimoram a segurança das pontes canônicas, permitindo que os usuários gerem provas de inclusão para transações de saque, 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. SOON se integra com a Prova de História usando entradas únicas para incorporar diretamente raízes de estado em blockchains baseados em SVM. Com essa inovação, a pilha SOON pode suportar novas consolidações baseadas 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 girou em torno de como e quando Solana merklizaria seu estado.
No final, essa decisão criou desafios significativos para os clientes na comprovação do estado global, bem como na inclusão de transações e verificação simples de pagamentos (SPV). A falta de uma raiz de estado consistentemente hashada que representa o estado merklizado do SVM apresenta 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 em um bloco usando um árvore de Merkle, cuja raiz é armazenada no cabeçalho do bloco. O protocolo Bitcoin irá hash os inputs e outputs de uma transação (bem como alguns metadados) em um 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.
Esse processo de verificação também valida o estado, já que o ID da Tx é único para algum conjunto de entradas e saídas, ambos refletindo 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 rodando novamente o script usando as entradas da transação e os dados de testemunha do script, e validando contra suas saídas.
Similar ao Bitcoin, o Ethereum armazena transações usando uma estrutura de dados personalizada (derivada de uma árvore de Merkle) chamada de Árvore de Patricia Merkle(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) age como uma máquina de estados global. O EVM é essencialmente um ambiente de computação distribuída gigantesco que suporta executáveiscontratos 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 (logs, código de retorno, etc.) mas também as mudanças 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.
Dada uma transação, um cliente pode provar sua inclusão em um bloco avaliando a raiz do trie de transações (como o Bitcoin), seu resultado avaliando o trie de recibos e as mudanças no estado global avaliando o trie de estado.
Um dos motivos para a alta taxa de transferência da Solana é o fato de que ela não possui a estrutura de árvore múltipla que o Ethereum possui. Os líderes da Solana calculam árvores de Merkle ao criar blocos, mas elas são estruturadas de forma diferente das que estão dentro do EVM. Infelizmente, aí reside o problema para rollups baseados em SVM.
Solana merkliza 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, o 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 da Solana não contêm nenhuma raiz de Merkle. Em vez disso, contêm o anterior e o atual blockhash, que é calculado através do Solana's Prova de História (PoH) algoritmo. O PoH exige que os validadores registrem continuamente "ticks" fazendo hash recursivo de 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 bloco hash. Solana é projetado para transmitir hashes de PoH a fim de manter seu conceito de tempo decorrido. A raiz da transação só está disponível para um tick de PoH que continha uma entrada com transações, e 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. Às vezes referido como hash de slot, os hashes bancários 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 do sysvar SlotHashes é truncada para os últimos 512 hashes de banco.
rede SOON é um SVM L2 em cima do Ethereum. Ao integrar a merklização no SOON, 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 hash 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/gravação de disco, resultando em perdas de desempenho. No final, decidimos integrar o Erigon MPTao construir a partir do excelente trabalho de Rust feito pelorETHequipe e adicionando suporte para o modelo de conta Solana.
Como mencionado anteriormente, o trie de estado do SOON é um MPT construído 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 se inscrever no estado mais recente das contas SVM em tempo real, introduzimos um notificador de conta. Durante a Etapa Bancária, o notificador de conta informa o módulo MPT sobre as alterações no estado da conta, e o MPT atualiza incrementalmente essas alterações na estrutura de trie.
É importante observar que o módulo MPT atualiza apenas suas subárvores a cada 50 slots e não calcula a raiz do estado ao final de cada slot. Esse método é adotado por duas razões. Primeiro, calcular a raiz do estado a cada slot impactaria significativamente o desempenho. Segundo, a raiz do estado só é necessária quando o proponente envia um outputRootpara o L1. Portanto, só precisa ser calculado periodicamente, com base na frequência de envio do proponente.
outputRoot = keccak256(versão, state_root, withdraw_root, l2_block_hash)
O módulo MPT SOON mantém simultaneamente dois tipos de estruturas de trie: a Trie de 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 a submete ao L1.
Atualmente, o SOON calcula a raiz do estado e a raiz de retirada uma vez a cada 450 slots e a anexa à blockchain L2. Como resultado, a consistência dos dados do MPT entre outros nós na 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 várias entradas, sendo que cada entrada inclui ticks e transações. Nas camadas de rede e armazenamento, um slot é armazenado e transmitido utilizando fragmentoscomo a menor unidade. Fragmentos podem ser convertidos em e de entradas.
pub struct Entry {
O número de hashes desde a 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 que o ID de entrada fosse
/// gerados. 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 de pub: Vec
}
Seguimos a estrutura blockchain gerada por PoH e mantemos a estrutura de fragmentos, o que nos permite reutilizar a camada de armazenamento, a camada de rede e o framework RPC existentes da Solana. Para armazenar dados adicionais na blockchain L2, introduzimos o UniqueEntry. Essa característica nos permite personalizar a carga útil de 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 única é um tipo de entrada especial. O que é útil quando precisamos armazenar alguns dados em
/// blockstore mas não deseja verificá-lo.
///
/// A disposição de num_hashes
é:
/// |…1 bit…|…63 bit…|
/// \ __/
/// \ \
/// bandei 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 | campo_personalizado
}
pub fn is_unique(entrada: &Entrada) -> bool {
entry.num_hashes & UNIQUE_ENTRY_NUM_HASHES_FLAG != 0
}
Em um 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 63 bits seguintes são usados para definir o tipo de payload. O campo de hash serve como o payload, contendo os dados personalizados necessários.
Vejamos um exemplo de uso de três entradas exclusivas para armazenar o hash do slot, a raiz do estado e a raiz de retirada.
/// Entrada única da raiz MPT.
pub struct MptRoot {
pub slot: Slot,
pub state_root: B256,
pub withdrawal_root: B256,
}
impl UniqueEntry para MptRoot {
fn encode_to_entries(&self) -> Vec
let slot_entry = unique_entry(0, slot_to_hash(self.slot)); let state_root_entry = unique_entry(1, self.state_root.0.into()); let withdrawal_root_entry = 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 as entradas mutáveis = entradas.into_iter (); deixe a entrada = entradas.next ().ok_or (UniqueEntryError::NoMoreEntries)?; deixe o slot = hash_to_slot (entrada.hash); deixe a entrada = entradas.next ().ok_or (UniqueEntryError::NoMoreEntries)?; deixe a raiz do estado = B256::from (entrada.hash.to_bytes ()); deixe a entrada = entradas.next ().ok_or (UniqueEntryError::NoMoreEntries)?; deixe a raiz do saque = B256::from (entrada.hash.to_bytes ()); Ok (MptRoot { slot, state_root, withdrawal_root, })
}
}
Como o UniqueEntry redefine a semântica de num_hashes, não pode ser processado durante a etapa de verificação do PoH. Portanto, no início do processo de verificação, primeiro filtramos as entradas únicas e as direcionamos para um fluxo de verificação personalizado com base em seu tipo de carga útil. As entradas regulares restantes continuam pelo 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 o L1 e o L2. As mensagens do L1 para o L2 são chamadas de transações de depósito, enquanto as mensagens do L2 para o L1 são chamadas de transações de retirada.
As transações de depósito são normalmente tratadas pelo pipeline de derivaçãoe só exigem que o usuário envie uma única transação no L1. No entanto, as transações de saque são mais complexas e exigem que o usuário envie três transações para concluir o processo:
A prova de inclusão de uma transação de saque garante que o saque ocorreu no L2, aumentando significativamente a segurança da ponte canônica.
Na pilha OP, o hash da transação de saque do usuário é armazenado na variável de estado correspondente ao Portal de Otimismo contrato. 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 nativo: 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 uma semente para criar um novo Conta Derivada do Programa(PDA) para armazenar a transação de retirada correspondente.
pub struct RetiradaTransação {
/// contador de retirada
pub nonce: U256,
/// usuário que deseja sacar
remetente do pub: Pubkey,
/// endereço do usuário em L1
pub target: Endereço,
/// retirar a quantidade em lamports
valor público: U256,
/// limite de gás em L1
pub gas_limit: U256,
/// dados de chamada de retirada em L1
pub data: L1WithdrawalCalldata,
}
Definimos a estrutura de WithdrawalTransaction para armazenar transações de saque no campo de dados do PDA.
Este design funciona de forma semelhante ao Stack OP. Uma vez que o outputRoot contendo a transação de saque é enviada para o L1, o usuário pode enviar uma prova de inclusão para a transação de saque para o L1 para iniciar o período de desafio.
Depois que o proponente envia a outputRoot para L1, isso significa que o estado de L2 foi resolvido. Se um desafiante detectar que o proponente enviou um estado incorreto, eles podem iniciar um desafio para proteger os fundos na ponte.
Um dos aspectos mais críticos da construção de um sistema à prova de falhas é permitir que o blockchain faça a transição do estado S1 para o estado S2 de forma sem estado. Isso garante que o contrato de árbitro na L1 possa reexecutar as instruções do programa de forma sem estado para realizar a arbitragem.
No entanto, no início desse processo, o desafiante deve provar que todas as entradas iniciais no estado S1 são válidas. Essas 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 por meio de um traço TransactionProcessingCallback, conforme mostrado abaixo.
pub trait Callback de Processamento de Transação {
fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option
fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option
fn add_builtin_account(&self, _name: &str, _program_id: &Pubkey) {}
}
Assim, só precisamos usar o estado S1 juntamente com as provas de inclusão das contas de entrada para verificar a validade das entradas do programa desafiante.
SOON está marcando um marco importante para o desenvolvimento do ecossistema SVM. Ao integrar a merklização, SOON aborda a falta de uma raiz de estado global da Solana, permitindo recursos essenciais como provas de inclusão para provas de falhas, retiradas seguras e execução sem estado.
Além disso, o design de merklização do SOON dentro do SVM pode permitir clientes leves em cadeias baseadas em SVM, mesmo que a Solana em si atualmente não suporte clientes leves. É até possível que parte do design possa auxiliar na implementação de clientes leves na cadeia principal também.
Usando Árvores de Merkle Patricia (MPT) para gerenciamento de estado, O Gate se alinha em BREVE com a infraestrutura da Ethereum, aprimorando a interoperabilidade e avançando soluções SVM baseadas em L2. Essa inovação fortalece o ecossistema SVM ao melhorar a segurança, escalabilidade e compatibilidade, ao mesmo tempo que promove o crescimento de aplicativos descentralizados.
Compartilhar
A Máquina Virtual Solana (SVM) está se tornando 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 de sua raiz de estado global. Isso representa desafios significativos para os sistemas de rollup, onde as raízes de estado global são críticas para garantir a integridade, permitir provas de fraude e apoiar operações entre camadas.
Em um rollup, o proponente envia a raiz do estado L2 (raiz de Merkle) para a Camada-1 (L1) periodicamente, estabelecendo checkpoints para a cadeia L2. Esses 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 desse mecanismo, já que os participantes podem fornecer provas de inclusão para verificar entradas válidas durante disputas. Além disso, as árvores de Merkle aprimoram a segurança das pontes canônicas, permitindo que os usuários gerem provas de inclusão para transações de saque, 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. SOON se integra com a Prova de História usando entradas únicas para incorporar diretamente raízes de estado em blockchains baseados em SVM. Com essa inovação, a pilha SOON pode suportar novas consolidações baseadas 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 girou em torno de como e quando Solana merklizaria seu estado.
No final, essa decisão criou desafios significativos para os clientes na comprovação do estado global, bem como na inclusão de transações e verificação simples de pagamentos (SPV). A falta de uma raiz de estado consistentemente hashada que representa o estado merklizado do SVM apresenta 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 em um bloco usando um árvore de Merkle, cuja raiz é armazenada no cabeçalho do bloco. O protocolo Bitcoin irá hash os inputs e outputs de uma transação (bem como alguns metadados) em um 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.
Esse processo de verificação também valida o estado, já que o ID da Tx é único para algum conjunto de entradas e saídas, ambos refletindo 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 rodando novamente o script usando as entradas da transação e os dados de testemunha do script, e validando contra suas saídas.
Similar ao Bitcoin, o Ethereum armazena transações usando uma estrutura de dados personalizada (derivada de uma árvore de Merkle) chamada de Árvore de Patricia Merkle(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) age como uma máquina de estados global. O EVM é essencialmente um ambiente de computação distribuída gigantesco que suporta executáveiscontratos 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 (logs, código de retorno, etc.) mas também as mudanças 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.
Dada uma transação, um cliente pode provar sua inclusão em um bloco avaliando a raiz do trie de transações (como o Bitcoin), seu resultado avaliando o trie de recibos e as mudanças no estado global avaliando o trie de estado.
Um dos motivos para a alta taxa de transferência da Solana é o fato de que ela não possui a estrutura de árvore múltipla que o Ethereum possui. Os líderes da Solana calculam árvores de Merkle ao criar blocos, mas elas são estruturadas de forma diferente das que estão dentro do EVM. Infelizmente, aí reside o problema para rollups baseados em SVM.
Solana merkliza 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, o 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 da Solana não contêm nenhuma raiz de Merkle. Em vez disso, contêm o anterior e o atual blockhash, que é calculado através do Solana's Prova de História (PoH) algoritmo. O PoH exige que os validadores registrem continuamente "ticks" fazendo hash recursivo de 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 bloco hash. Solana é projetado para transmitir hashes de PoH a fim de manter seu conceito de tempo decorrido. A raiz da transação só está disponível para um tick de PoH que continha uma entrada com transações, e 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. Às vezes referido como hash de slot, os hashes bancários 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 do sysvar SlotHashes é truncada para os últimos 512 hashes de banco.
rede SOON é um SVM L2 em cima do Ethereum. Ao integrar a merklização no SOON, 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 hash 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/gravação de disco, resultando em perdas de desempenho. No final, decidimos integrar o Erigon MPTao construir a partir do excelente trabalho de Rust feito pelorETHequipe e adicionando suporte para o modelo de conta Solana.
Como mencionado anteriormente, o trie de estado do SOON é um MPT construído 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 se inscrever no estado mais recente das contas SVM em tempo real, introduzimos um notificador de conta. Durante a Etapa Bancária, o notificador de conta informa o módulo MPT sobre as alterações no estado da conta, e o MPT atualiza incrementalmente essas alterações na estrutura de trie.
É importante observar que o módulo MPT atualiza apenas suas subárvores a cada 50 slots e não calcula a raiz do estado ao final de cada slot. Esse método é adotado por duas razões. Primeiro, calcular a raiz do estado a cada slot impactaria significativamente o desempenho. Segundo, a raiz do estado só é necessária quando o proponente envia um outputRootpara o L1. Portanto, só precisa ser calculado periodicamente, com base na frequência de envio do proponente.
outputRoot = keccak256(versão, state_root, withdraw_root, l2_block_hash)
O módulo MPT SOON mantém simultaneamente dois tipos de estruturas de trie: a Trie de 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 a submete ao L1.
Atualmente, o SOON calcula a raiz do estado e a raiz de retirada uma vez a cada 450 slots e a anexa à blockchain L2. Como resultado, a consistência dos dados do MPT entre outros nós na 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 várias entradas, sendo que cada entrada inclui ticks e transações. Nas camadas de rede e armazenamento, um slot é armazenado e transmitido utilizando fragmentoscomo a menor unidade. Fragmentos podem ser convertidos em e de entradas.
pub struct Entry {
O número de hashes desde a 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 que o ID de entrada fosse
/// gerados. 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 de pub: Vec
}
Seguimos a estrutura blockchain gerada por PoH e mantemos a estrutura de fragmentos, o que nos permite reutilizar a camada de armazenamento, a camada de rede e o framework RPC existentes da Solana. Para armazenar dados adicionais na blockchain L2, introduzimos o UniqueEntry. Essa característica nos permite personalizar a carga útil de 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 única é um tipo de entrada especial. O que é útil quando precisamos armazenar alguns dados em
/// blockstore mas não deseja verificá-lo.
///
/// A disposição de num_hashes
é:
/// |…1 bit…|…63 bit…|
/// \ __/
/// \ \
/// bandei 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 | campo_personalizado
}
pub fn is_unique(entrada: &Entrada) -> bool {
entry.num_hashes & UNIQUE_ENTRY_NUM_HASHES_FLAG != 0
}
Em um 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 63 bits seguintes são usados para definir o tipo de payload. O campo de hash serve como o payload, contendo os dados personalizados necessários.
Vejamos um exemplo de uso de três entradas exclusivas para armazenar o hash do slot, a raiz do estado e a raiz de retirada.
/// Entrada única da raiz MPT.
pub struct MptRoot {
pub slot: Slot,
pub state_root: B256,
pub withdrawal_root: B256,
}
impl UniqueEntry para MptRoot {
fn encode_to_entries(&self) -> Vec
let slot_entry = unique_entry(0, slot_to_hash(self.slot)); let state_root_entry = unique_entry(1, self.state_root.0.into()); let withdrawal_root_entry = 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 as entradas mutáveis = entradas.into_iter (); deixe a entrada = entradas.next ().ok_or (UniqueEntryError::NoMoreEntries)?; deixe o slot = hash_to_slot (entrada.hash); deixe a entrada = entradas.next ().ok_or (UniqueEntryError::NoMoreEntries)?; deixe a raiz do estado = B256::from (entrada.hash.to_bytes ()); deixe a entrada = entradas.next ().ok_or (UniqueEntryError::NoMoreEntries)?; deixe a raiz do saque = B256::from (entrada.hash.to_bytes ()); Ok (MptRoot { slot, state_root, withdrawal_root, })
}
}
Como o UniqueEntry redefine a semântica de num_hashes, não pode ser processado durante a etapa de verificação do PoH. Portanto, no início do processo de verificação, primeiro filtramos as entradas únicas e as direcionamos para um fluxo de verificação personalizado com base em seu tipo de carga útil. As entradas regulares restantes continuam pelo 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 o L1 e o L2. As mensagens do L1 para o L2 são chamadas de transações de depósito, enquanto as mensagens do L2 para o L1 são chamadas de transações de retirada.
As transações de depósito são normalmente tratadas pelo pipeline de derivaçãoe só exigem que o usuário envie uma única transação no L1. No entanto, as transações de saque são mais complexas e exigem que o usuário envie três transações para concluir o processo:
A prova de inclusão de uma transação de saque garante que o saque ocorreu no L2, aumentando significativamente a segurança da ponte canônica.
Na pilha OP, o hash da transação de saque do usuário é armazenado na variável de estado correspondente ao Portal de Otimismo contrato. 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 nativo: 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 uma semente para criar um novo Conta Derivada do Programa(PDA) para armazenar a transação de retirada correspondente.
pub struct RetiradaTransação {
/// contador de retirada
pub nonce: U256,
/// usuário que deseja sacar
remetente do pub: Pubkey,
/// endereço do usuário em L1
pub target: Endereço,
/// retirar a quantidade em lamports
valor público: U256,
/// limite de gás em L1
pub gas_limit: U256,
/// dados de chamada de retirada em L1
pub data: L1WithdrawalCalldata,
}
Definimos a estrutura de WithdrawalTransaction para armazenar transações de saque no campo de dados do PDA.
Este design funciona de forma semelhante ao Stack OP. Uma vez que o outputRoot contendo a transação de saque é enviada para o L1, o usuário pode enviar uma prova de inclusão para a transação de saque para o L1 para iniciar o período de desafio.
Depois que o proponente envia a outputRoot para L1, isso significa que o estado de L2 foi resolvido. Se um desafiante detectar que o proponente enviou um estado incorreto, eles podem iniciar um desafio para proteger os fundos na ponte.
Um dos aspectos mais críticos da construção de um sistema à prova de falhas é permitir que o blockchain faça a transição do estado S1 para o estado S2 de forma sem estado. Isso garante que o contrato de árbitro na L1 possa reexecutar as instruções do programa de forma sem estado para realizar a arbitragem.
No entanto, no início desse processo, o desafiante deve provar que todas as entradas iniciais no estado S1 são válidas. Essas 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 por meio de um traço TransactionProcessingCallback, conforme mostrado abaixo.
pub trait Callback de Processamento de Transação {
fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option
fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option
fn add_builtin_account(&self, _name: &str, _program_id: &Pubkey) {}
}
Assim, só precisamos usar o estado S1 juntamente com as provas de inclusão das contas de entrada para verificar a validade das entradas do programa desafiante.
SOON está marcando um marco importante para o desenvolvimento do ecossistema SVM. Ao integrar a merklização, SOON aborda a falta de uma raiz de estado global da Solana, permitindo recursos essenciais como provas de inclusão para provas de falhas, retiradas seguras e execução sem estado.
Além disso, o design de merklização do SOON dentro do SVM pode permitir clientes leves em cadeias baseadas em SVM, mesmo que a Solana em si atualmente não suporte clientes leves. É até possível que parte do design possa auxiliar na implementação de clientes leves na cadeia principal também.
Usando Árvores de Merkle Patricia (MPT) para gerenciamento de estado, O Gate se alinha em BREVE com a infraestrutura da Ethereum, aprimorando a interoperabilidade e avançando soluções SVM baseadas em L2. Essa inovação fortalece o ecossistema SVM ao melhorar a segurança, escalabilidade e compatibilidade, ao mesmo tempo que promove o crescimento de aplicativos descentralizados.