Um dos desafios do Ethereum é que, por padrão, o inchaço e a complexidade do protocolo blockchain aumentam ao longo do tempo. Isso acontece em dois lugares:
Para que o Ethereum se sustente a longo prazo, precisamos de uma forte contra-pressão contra essas duas tendências, reduzindo a complexidade e o inchaço ao longo do tempo. Mas ao mesmo tempo, precisamos preservar uma das principais propriedades que tornam as blockchains ótimas: sua permanência. Você pode colocar um NFT, uma mensagem de amor em dados de transação ou um contrato inteligente contendo um milhão de dólares na cadeia, entrar em uma caverna por dez anos, sair e encontrá-lo ainda lá esperando você ler e interagir com ele. Para que os dapps se sintam confortáveis em se tornarem totalmente descentralizados e removerem suas chaves de atualização, eles precisam ter confiança de que suas dependências não vão atualizar de uma forma que os quebre - especialmente a L1 em si.
O Purge, roteiro de 2023.
Equilibrar entre essas duas necessidades e minimizar ou reverter inchaço, complexidade e decadência, enquanto preserva a continuidade, é absolutamente possível se nos dedicarmos a isso. Os organismos vivos podem fazê-lo: enquanto a maioria envelhece com o tempo, alguns sortudos não. Até mesmo os sistemas sociais podem ter longevidade extrema. Em algumas ocasiões, o Ethereum já mostrou sucessos: a prova de trabalho desapareceu, o opcode SELFDESTRUCT está em grande parte desaparecido e os nós da cadeia de beacons já armazenam dados antigos por apenas seis meses. Descobrir esse caminho para o Ethereum de uma forma mais generalizada e avançar em direção a um resultado final estável a longo prazo é o desafio final da escalabilidade a longo prazo, sustentabilidade técnica e até mesmo segurança do Ethereum.
No momento em que este texto foi escrito, um nó Ethereum totalmente sincronizado requer aproximadamente 1,1 terabytesde espaço em disco para ocliente de execução, mais alguns centenas de gigabytes para o cliente de consenso. A grande maioria disto é história: dados sobre blocos históricos, transações e recibos, a maior parte dos quais tem muitos anos. Isto significa que o tamanho de um nó continua a aumentar em centenas de gigabytes a cada ano, mesmo que o limite de gás não aumente de todo.
Uma característica chave de simplificação do problema de armazenamento de histórico é que cada bloco aponta para o bloco anterior através de um link de hash (e outro estruturas) ter consenso sobre o presente é suficiente para ter consenso sobre a história. Desde que a rede tenha consenso sobre o último bloco, qualquer bloco ou transação histórica ou estado (saldo da conta, nonce, código, armazenamento) pode ser fornecido por qualquer ator único juntamente com uma prova de Merkle, e a prova permite a qualquer outra pessoa verificar a sua correção. Enquanto o consenso é um modelo de confiança N/2-de-N, a história é um modelo de confiança 1-de-N.
Isso abre muitas opções para como podemos armazenar o histórico. Uma opção natural é uma rede onde cada nó armazena apenas uma pequena porcentagem dos dados. É assim que as redes de torrent funcionam há décadas: enquanto a rede como um todo armazena e distribui milhões de arquivos, cada participante armazena e distribui apenas alguns deles. Talvez de forma contraintuitiva, essa abordagem não necessariamente diminui a robustez dos dados. Se, tornando a execução do nó mais acessível, pudermos chegar a uma rede com 100.000 nós, onde cada nó armazena aleatoriamente 10% do histórico, então cada dado seria replicado 10.000 vezes - exatamente o mesmo fator de replicação de uma rede de 10.000 nós onde cada nó armazena tudo.
Hoje, o Ethereum já começou a se afastar do modelo de todos os nós armazenando toda a história para sempre. Os blocos de consenso (ou seja, as partes relacionadas ao consenso de prova de participação) são armazenados apenas por cerca de 6 meses. Os blobs são armazenados apenas por cerca de 18 dias.EIP-4444pretende introduzir um período de armazenamento de um ano para blocos e recibos históricos. Um objetivo a longo prazo é ter um período harmonizado (que poderia ser ~18 dias) durante o qual cada nó é responsável por armazenar tudo, e depois ter uma rede peer-to-peer composta por nós Ethereum armazenando dados mais antigos de forma distribuída.
Os códigos de apagamento podem ser usados para aumentar a robustez mantendo o mesmo fator de replicação. Na verdade, os blobs já vêm codificados com apagamento para suportar a amostragem de disponibilidade de dados. A solução mais simples pode ser reutilizar esta codificação com apagamento e colocar os dados de bloco de execução e consenso nos blobs também.
O trabalho principal restante envolve a construção e integração de uma solução distribuída concreta para armazenar o histórico - pelo menos o histórico de execução, mas também o consenso e os blobs. As soluções mais fáceis para isso são (i) simplesmente introduzir uma biblioteca de torrent existente e (ii) uma solução nativa do Ethereum chamada a rede Portal. Uma vez que qualquer um destes seja introduzido, podemos activar o EIP-4444. O próprio EIP-4444 não requer uma hard fork, embora exija uma nova versão do protocolo de rede. Por esta razão, há valor em ativá-lo para todos os clientes ao mesmo tempo, porque caso contrário existem riscos de os clientes avariarem ao ligarem-se a outros nós a contar com o download da história completa mas na realidade não a receberem.
O principal compromisso envolve o quão duro tentamos tornar os dados históricos "antigos" disponíveis. A solução mais fácil seria simplesmente parar de armazenar a história antiga amanhã e confiar nos nós de arquivo existentes e em vários fornecedores centralizados para replicação. Isso é fácil, mas enfraquece a posição do Ethereum como um lugar para fazer registros permanentes. O caminho mais difícil, mas mais seguro, é primeiro construir e integrar a rede de torrent para armazenar a história de forma distribuída. Aqui, existem duas dimensões de "quão duro tentamos":
Uma abordagem maximalmente paranóica para (1) envolveriaprova de custódiana verdade, exigindo que cada validador de prova de participação armazene uma porcentagem do histórico e verifique regularmente criptograficamente se eles o fazem. Uma abordagem mais moderada é estabelecer um padrão voluntário para a porcentagem do histórico que cada cliente armazena.
Para (2), uma implementação básica envolve apenas pegar o trabalho que já é feito hoje: o Portal já armazena arquivos ERA contendo todo o histórico do Ethereum. Uma implementação mais completa envolveria realmente conectar isso ao processo de sincronização, para que, se alguém quisesse sincronizar um nó de armazenamento de histórico completo ou um nó de arquivo, eles pudessem fazê-lo mesmo se nenhum outro nó de arquivo existisse online, sincronizando diretamente da rede do Portal.
Reduzir os requisitos de armazenamento de histórico é possivelmente ainda mais importante do que a falta de estado se quisermos tornar extremamente fácil executar ou iniciar um nó: dos 1,1 TB que um nó precisa ter, ~300 GB são de estado, e os restantes ~800 GB são de histórico. A visão de um nó Ethereum rodando em um smartwatch e levando apenas alguns minutos para ser configurado só é alcançável se tanto a falta de estado quanto o EIP-4444 forem implementados.
Limitar o armazenamento de histórico torna também mais viável para as novas implementações de nós Ethereum suportarem apenas versões recentes do protocolo, o que lhes permite ser muito mais simples. Por exemplo, muitas linhas de código podem ser removidas com segurança agora que todas as ranhuras de armazenamento vazias criadas durante os ataques DoS de 2016 foramremovido. Agora que a mudança para o proof of stake é história antiga, os clientes podem remover com segurança todo o código relacionado ao proof-of-work.
Mesmo que removamos a necessidade de os clientes armazenarem o histórico, o requisito de armazenamento de um cliente continuará a crescer, em cerca de 50 GB por ano, devido ao crescimento contínuo do estado: saldos e nonces de conta, código de contrato e armazenamento de contrato. Os usuários podem pagar um custo único para impor um ônus nos clientes presentes e futuros do Ethereum para sempre.
O estado é muito mais difícil de “expirar” do que a história, porque o EVM é fundamentalmente projetado com base na suposição de que, uma vez que um objeto de estado é criado, ele estará sempre lá e pode ser lido por qualquer transação a qualquer momento. Se introduzirmos a falta de estado, há um argumento de que talvez esse problema não seja tão ruim: apenas uma classe especializada de construtores de blocos precisaria realmente armazenar o estado, e todos os outros nós (mesmolista de inclusãoprodução!) pode ser executado sem estado. No entanto, há um argumento de que não queremos depender muito do estado sem, e eventualmente podemos querer expirar o estado para manter o Ethereum descentralizado.
Hoje, quando você cria um novo objeto de estado (o que pode acontecer de três maneiras: (i) enviando ETH para uma nova conta, (ii) criando uma nova conta com código, (iii) definindo um slot de armazenamento previamente não utilizado), esse objeto de estado permanece no estado para sempre. O que queremos é que os objetos expirem automaticamente ao longo do tempo. O desafio-chave é fazer isso de uma maneira que cumpra três objetivos:
É fácil resolver o problema sem satisfazer esses objetivos. Por exemplo, você pode fazer com que cada objeto de estado também armazene um contador para sua data de expiração (que pode ser estendida pela gravação de ETH, o que pode acontecer automaticamente sempre que for lido ou gravado) e ter um processo que percorra o estado para remover objetos de estado expirados. No entanto, isso introduz computação extra (e até mesmo requisitos de armazenamento), e definitivamente não satisfaz o requisito de facilidade de uso. Os desenvolvedores também teriam dificuldade em raciocinar sobre casos de borda envolvendo valores de armazenamento às vezes redefinidos para zero. Se você fizer o contrato do temporizador de expiração em todo o contrato, isso torna a vida tecnicamente mais fácil para os desenvolvedores, mas torna a economia mais difícil: os desenvolvedores teriam que pensar em como "repassar" os custos contínuos de armazenamento para seus usuários.
Estes são problemas com os quais a comunidade de desenvolvimento central do Ethereum lutou por muitos anos, incluindo propostas como “aluguer de blockchain" e "regenesis“Eventualmente, combinámos as melhores partes das propostas e convergimos em duas categorias de 'soluções menos más conhecidas'.
As propostas de expiração parcial do estado funcionam todos segundo o mesmo princípio. Nós dividimos o estado em pedaços. Todo mundo armazena permanentemente o “mapa de nível superior” de quais pedaços estão vazios ou não vazios. Os dados dentro de cada pedaço só são armazenados se esses dados tiverem sido acessados recentemente. Existe um mecanismo de “ressurreição” onde, se um pedaço não estiver mais armazenado, qualquer pessoa pode trazer esses dados de volta fornecendo uma prova do que os dados eram.
As principais distinções entre essas propostas são: (i) como definimos "recentemente" e (ii) como definimos "pedaço"? Uma proposta concreta é EIP-7736, que se baseia no design de "caule e folha"introduzido para árvores Verkle (embora compatível com qualquer forma de apatridia, por exemplo, árvores binárias). Neste design, cabeçalho, código e slots de armazenamento que são adjacentes uns aos outros são armazenados sob o mesmo "caule". Os dados armazenados sob uma haste podem ser, no máximo, 256 * 31 = 7.936 bytes. Em muitos casos, todo o cabeçalho e código, e muitos slots de armazenamento de chaves, de uma conta serão todos armazenados sob a mesma haste. Se os dados sob uma determinada haste não forem lidos ou gravados por 6 meses, os dados não serão mais armazenados e, em vez disso, apenas um compromisso de 32 bytes ("stub") para os dados será armazenado. Transações futuras que acessam esses dados precisariam "ressuscitar" os dados, com uma prova que seria verificada em relação ao stub.
Existem outras maneiras de implementar uma ideia semelhante. Por exemplo, se a granularidade ao nível da conta não for suficiente, poderíamos criar um esquema em que cada fração 1/232 da árvore seja regida por um mecanismo semelhante ao caule e folha.
Isso é mais complicado por causa dos incentivos: um atacante poderia forçar os clientes a armazenar permanentemente uma quantidade muito grande de estado colocando uma quantidade muito grande de dados em uma única subárvore e enviando uma única transação a cada ano para 'renovar a árvore'. Se você fizer com que o custo de renovação seja proporcional (ou a duração de renovação inversamente proporcional) ao tamanho da árvore, então alguém poderia prejudicar outro usuário colocando uma quantidade muito grande de dados na mesma subárvore que eles. Poderíamos tentar limitar ambos os problemas tornando a granularidade dinâmica com base no tamanho da subárvore: por exemplo, cada 216 objetos de estado = 65536 consecutivos poderiam ser tratados como um 'grupo'. No entanto, essas ideias são mais complexas; a abordagem baseada no caule é simples e alinha os incentivos, porque geralmente todos os dados sob um caule estão relacionados à mesma aplicação ou usuário.
E se quisermos evitar qualquer crescimento permanente de estado, mesmo com conectores de 32 bytes? Este é um problema difícil por causa de @vbuterin/state_size_management#Conflitos_de_r ressurreição": conflitos de ressurreição: e se um objeto de estado for removido, mais tarde a execução do EVM coloca outro objeto de estado na exata mesma posição, mas depois alguém que se preocupa com o objeto de estado original volta e tenta recuperá-lo? Com expiração parcial do estado, o “stub” impede a criação de novos dados. Com expiração completa do estado, não podemos nos dar ao luxo de armazenar nem mesmo o stub.
O design baseado em períodos de endereço é a melhor ideia conhecida para resolver isso. Em vez de ter uma árvore de estado armazenando todo o estado, temos uma lista constantemente crescente de árvores de estado e qualquer estado que seja lido ou gravado é salvo na árvore de estado mais recente. Uma nova árvore de estado vazia é adicionada uma vez por período (pense: 1 ano). As árvores de estado mais antigas estão congeladas. Espera-se que os nós completos armazenem apenas as duas árvores mais recentes. Se um objeto de estado não for tocado por dois períodos e, portanto, cair em uma árvore expirada, ainda poderá ser lido ou gravado, mas a transação precisará provar uma prova de Merkle para isso - e, uma vez que o fizer, uma cópia será salva novamente na árvore mais recente.
Uma ideia-chave para tornar tudo isto amigável para utilizadores e desenvolvedores é o conceito de períodos de endereço. Um período de endereço é um número que faz parte de um endereço. Uma regra fundamental é que um endereço com o período de endereço N só pode ser lido ou escrito durante ou após o período N (ou seja, quando a lista da árvore de estado atingir o comprimento N). Se estiver a guardar um novo objeto de estado (por exemplo, um novo contrato ou um novo saldo ERC20), se certificar-se de colocar o objeto de estado num contrato cujo período de endereço seja N ou N-1, então pode guardá-lo imediatamente, sem necessidade de fornecer provas de que não havia nada antes. No entanto, quaisquer adições ou edições ao estado em períodos de endereço antigos requerem uma prova.
Este design preserva a maioria das propriedades atuais do Ethereum, é muito leve em computação extra, permite que as aplicações sejam escritas quase como estão hoje (os ERC20s precisarão ser reescritos, para garantir que os saldos dos endereços com período de endereço N sejam armazenados em um contrato filho que por si só tem um período de endereço N), e resolve o problema de "o usuário entra em uma caverna por cinco anos". No entanto, tem um grande problema: os endereços precisam ser expandidos além de 20 bytes para caber nos períodos de endereço.
Uma proposta é introduzir um novo formato de endereço de 32 bytes, que inclui um número de versão, um número de período de endereço e um hash expandido.
0x01000000000157aE408398dF7E5f4552091A69125d5dFcb7B8C2659029395bdF
O vermelho é um número de versão. Os quatro zeros coloridos a laranja aqui são destinados como espaço vazio, que poderia acomodar um número de partição no futuro. O verde é um número de período de endereço. O azul é um hash de 26 bytes.
O desafio chave aqui é a compatibilidade retroativa. Os contratos existentes são projetados em torno de endereços de 20 bytes e frequentemente usam técnicas de compactação de bytes apertadas que assumem explicitamente que os endereços têm exatamente 20 bytes de comprimento.@ipsilon/address-space-extension-exploration">Uma ideia para resolver isso envolve um mapa de tradução, onde contratos no estilo antigo interagindo com endereços no novo estilo veriam um hash de 20 bytes do endereço no novo estilo. No entanto, existem complexidades significativas envolvidas em tornar isso seguro.
Outra abordagem segue a direção oposta: proibimos imediatamente algumas subfaixas de endereços de tamanho 2128 (por exemplo, todos os endereços que começam com 0xffffffff) e, em seguida, usamos essa faixa para introduzir endereços com períodos de endereço e hashes de 14 bytes.
0xfffffff000169125d5dFcb7B8C2659029395bdF
O sacrifício-chave que esta abordagem faz, é que issointroduz riscos de segurança para endereços contrafactuais: endereços que possuem ativos ou permissões, mas cujo código ainda não foi publicado na cadeia. O risco envolve alguém criar um endereço que afirma ter um pedaço de código (ainda não publicado), mas também tem outro pedaço válido de código que gera o mesmo endereço. Calcular tal colisão requer 280hashes hoje; a contração do espaço de endereço reduziria esse número para um acessível 256hashes.
A área de risco chave, endereços contrafactuais que não são carteiras mantidas por um único proprietário, é um caso relativamente raro hoje, mas provavelmente se tornará mais comum à medida que entramos em um mundo multi-L2. A única solução é simplesmente aceitar esse risco, mas identificar todos os casos de uso comuns onde isso pode ser um problema e encontrar soluções alternativas eficazes.
Vejo quatro caminhos viáveis para o futuro:
Um ponto importante é que as questões difíceis em torno da expansão e contração do espaço de endereço terão que ser abordadas eventualmente, independentemente de as medidas de expiração do estado que dependem das alterações de formato do endereço serem ou não implementadas. Hoje, leva aproximadamente 280 hashes para gerar uma colisão de endereços, uma carga computacional que já é viável para atores extremamente bem dotados de recursos: uma GPU pode fazer cerca de 227hashes, portanto, executando por um ano, ele pode calcular 252, então todos ~2^30 GPUs no mundo poderia calcular uma colisão em ~1/4 de um ano, e FPGAs e ASICs poderiam acelerar isso ainda mais. No futuro, esses ataques tornar-se-ão abertos a cada vez mais pessoas. Por conseguinte, o custo real da implementação da expiração total do Estado pode não ser tão elevado como parece, uma vez que temos de resolver este problema de resolução muito difícil independentemente disso.
A expiração do estado potencialmente facilita as transições de um formato de árvore de estado para outro, porque não haverá necessidade de um procedimento de transição: você poderia simplesmente começar a fazer novas árvores usando um novo formato e, posteriormente, fazer um hard fork para converter as árvores mais antigas. Assim, embora a expiração do estado seja complexa, ela tem benefícios em simplificar outros aspectos do roteiro.
Uma das condições prévias chave de segurança, acessibilidade e neutralidade credívelé a simplicidade. Se um protocolo é bonito e simples, reduz a probabilidade de haver erros. Aumenta a probabilidade de novos desenvolvedores poderem entrar e trabalhar com qualquer parte dele. É mais provável que seja justo e mais fácil de defender contra interesses especiais. Infelizmente, os protocolos, como qualquer sistema social, por padrão, tornam-se mais complexos ao longo do tempo. Se não quisermos que o Ethereum entre num buraco negro de complexidade crescente, precisamos fazer uma das duas coisas: (i) parar de fazer alterações e ossificar o protocolo, (ii) ser capaz de realmente remover funcionalidades e reduzir a complexidade. Um caminho intermediário, de fazer menos alterações no protocolo e também remover pelo menos um pouco de complexidade ao longo do tempo, também é possível. Esta seção irá abordar como podemos reduzir ou remover a complexidade.
Não há uma grande correção única que possa reduzir a complexidade do protocolo; a natureza inerente do problema é que há muitas pequenas correções.
Um exemplo que já está quase concluído e pode servir como um modelo para lidar com os outros é o@vbuterinremoção do opcode SELFDESTRUCT. O opcode SELFDESTRUCT era o único opcode que podia modificar um número ilimitado de slots de armazenamento dentro de um único bloco, exigindo que os clientes implementassem significativamente mais complexidade para evitar ataques DoS. O objetivo original do opcode era permitir a limpeza voluntária do estado, permitindo que o tamanho do estado diminuísse ao longo do tempo. Na prática, muito poucos acabaram por usá-lo.o opcode foi enfraquecidopara permitir apenas contas autodestrutivas criadas na mesma transação no hardfork Dencun. Isso resolve o problema de DoS e permite uma simplificação significativa no código do cliente. No futuro, provavelmente faz sentido remover eventualmente o opcode completamente.
Alguns exemplos-chave de oportunidades de simplificação de protocolo identificadas até agora incluem as seguintes. Primeiro, alguns exemplos que estão fora do EVM; estes são relativamente não invasivos, e assim mais fáceis de obter consenso e implementar num prazo mais curto.
Agora, alguns exemplos que estão dentro do EVM:
O principal compromisso ao fazer esse tipo de simplificação de recurso é (i) o quanto simplificamos e o quão rapidamente versus (ii) compatibilidade com versões anteriores. O valor do Ethereum como uma cadeia vem do fato de ser uma plataforma onde você pode implantar um aplicativo e ter confiança de que ainda funcionará muitos anos a partir de agora. Ao mesmo tempo, é possível levar esse ideal longe demais e,parafrasear William Jennings Bryan"crucificar o Ethereum em uma cruz de compatibilidade reversa". Se houver apenas duas aplicações em todo o Ethereum que usem uma determinada funcionalidade, e uma delas não tem nenhum usuário há anos e a outra é quase completamente não utilizada e assegura um valor total de $57, então devemos simplesmente remover a funcionalidade, e se necessário pagar as vítimas $57 do nosso bolso.
O problema social mais amplo está em criar um pipeline padronizado para fazer alterações de compatibilidade reversa não emergenciais. Uma maneira de abordar isso é examinar e estender precedentes existentes, como o processo SELFDESTRUCT. O pipeline se parece com algo como o seguinte:
Deve haver um pipeline de vários anos entre o passo 1 e o passo 4, com informações claras sobre quais itens estão em que passo. Nesse ponto, há um trade-off entre o quão vigoroso e rápido é o pipeline de remoção de recursos, em comparação com ser mais conservador e colocar mais recursos em outras áreas de desenvolvimento de protocolo, mas ainda estamos longe da fronteira de Pareto.
Um conjunto importante de mudanças que foi proposto para a EVM é oFormato de Objeto EVM (EOF). EOF introduz um grande número de mudanças, como a proibição da observabilidade de gás, observabilidade de código (ou seja, sem CODECOPY), permitindo apenas saltos estáticos. O objetivo é permitir que o EVM seja atualizado de maneira mais robusta, preservando a compatibilidade com versões anteriores (pois o EVM pré-EOF ainda existirá).
Isso tem a vantagem de criar um caminho natural para adicionar novas funcionalidades do EVM e incentivar a migração para um EVM mais restritivo com garantias mais fortes. Tem a desvantagem de aumentar significativamente a complexidade do protocolo, a menos que possamos encontrar uma maneira de eventualmente descontinuar e remover o antigo EVM. Uma questão importante é: qual é o papel do EOF nas propostas de simplificação do EVM, especialmente se o objetivo é reduzir a complexidade do EVM como um todo?
Muitas das propostas de “melhoria” no resto do roteiro também são oportunidades para simplificar recursos antigos. Para repetir alguns exemplos acima:
Uma estratégia de simplificação do Ethereum mais radical é manter o protocolo como está, mas mover grandes partes dele de serem recursos do protocolo para serem código de contrato.
A versão mais extrema disso seria fazer com que o Ethereum L1 "tecnicamente" fosse apenas a cadeia beacon, e introduzir uma VM mínima (por exemplo. RISC-V, Cairo, ou algo ainda mais minimalista especializado em sistemas de provar) que permite a qualquer pessoa criar seu próprio rollup. A EVM então se tornaria o primeiro desses rollups. Ironicamente, esse é exatamente o mesmo resultado do propostas de ambiente de execução de 2019-20, embora os SNARKs tornem significativamente mais viável a sua implementação.
Uma abordagem mais moderada seria manter a relação entre a beacon chain e o ambiente de execução Ethereum atual, mas fazer uma troca no local da EVM. Poderíamos escolher RISC-V, Cairo ou outro VM para ser o novo 'Ethereum VM' oficial e, em seguida, converter forçadamente todos os contratos EVM em código de novo VM que interpreta a lógica do código original (compilando ou interpretando-o). Teoricamente, isso até poderia ser feito com o 'VM de destino' sendo uma versão do EOF.
Um dos desafios do Ethereum é que, por padrão, o inchaço e a complexidade do protocolo blockchain aumentam ao longo do tempo. Isso acontece em dois lugares:
Para que o Ethereum se sustente a longo prazo, precisamos de uma forte contra-pressão contra essas duas tendências, reduzindo a complexidade e o inchaço ao longo do tempo. Mas ao mesmo tempo, precisamos preservar uma das principais propriedades que tornam as blockchains ótimas: sua permanência. Você pode colocar um NFT, uma mensagem de amor em dados de transação ou um contrato inteligente contendo um milhão de dólares na cadeia, entrar em uma caverna por dez anos, sair e encontrá-lo ainda lá esperando você ler e interagir com ele. Para que os dapps se sintam confortáveis em se tornarem totalmente descentralizados e removerem suas chaves de atualização, eles precisam ter confiança de que suas dependências não vão atualizar de uma forma que os quebre - especialmente a L1 em si.
O Purge, roteiro de 2023.
Equilibrar entre essas duas necessidades e minimizar ou reverter inchaço, complexidade e decadência, enquanto preserva a continuidade, é absolutamente possível se nos dedicarmos a isso. Os organismos vivos podem fazê-lo: enquanto a maioria envelhece com o tempo, alguns sortudos não. Até mesmo os sistemas sociais podem ter longevidade extrema. Em algumas ocasiões, o Ethereum já mostrou sucessos: a prova de trabalho desapareceu, o opcode SELFDESTRUCT está em grande parte desaparecido e os nós da cadeia de beacons já armazenam dados antigos por apenas seis meses. Descobrir esse caminho para o Ethereum de uma forma mais generalizada e avançar em direção a um resultado final estável a longo prazo é o desafio final da escalabilidade a longo prazo, sustentabilidade técnica e até mesmo segurança do Ethereum.
No momento em que este texto foi escrito, um nó Ethereum totalmente sincronizado requer aproximadamente 1,1 terabytesde espaço em disco para ocliente de execução, mais alguns centenas de gigabytes para o cliente de consenso. A grande maioria disto é história: dados sobre blocos históricos, transações e recibos, a maior parte dos quais tem muitos anos. Isto significa que o tamanho de um nó continua a aumentar em centenas de gigabytes a cada ano, mesmo que o limite de gás não aumente de todo.
Uma característica chave de simplificação do problema de armazenamento de histórico é que cada bloco aponta para o bloco anterior através de um link de hash (e outro estruturas) ter consenso sobre o presente é suficiente para ter consenso sobre a história. Desde que a rede tenha consenso sobre o último bloco, qualquer bloco ou transação histórica ou estado (saldo da conta, nonce, código, armazenamento) pode ser fornecido por qualquer ator único juntamente com uma prova de Merkle, e a prova permite a qualquer outra pessoa verificar a sua correção. Enquanto o consenso é um modelo de confiança N/2-de-N, a história é um modelo de confiança 1-de-N.
Isso abre muitas opções para como podemos armazenar o histórico. Uma opção natural é uma rede onde cada nó armazena apenas uma pequena porcentagem dos dados. É assim que as redes de torrent funcionam há décadas: enquanto a rede como um todo armazena e distribui milhões de arquivos, cada participante armazena e distribui apenas alguns deles. Talvez de forma contraintuitiva, essa abordagem não necessariamente diminui a robustez dos dados. Se, tornando a execução do nó mais acessível, pudermos chegar a uma rede com 100.000 nós, onde cada nó armazena aleatoriamente 10% do histórico, então cada dado seria replicado 10.000 vezes - exatamente o mesmo fator de replicação de uma rede de 10.000 nós onde cada nó armazena tudo.
Hoje, o Ethereum já começou a se afastar do modelo de todos os nós armazenando toda a história para sempre. Os blocos de consenso (ou seja, as partes relacionadas ao consenso de prova de participação) são armazenados apenas por cerca de 6 meses. Os blobs são armazenados apenas por cerca de 18 dias.EIP-4444pretende introduzir um período de armazenamento de um ano para blocos e recibos históricos. Um objetivo a longo prazo é ter um período harmonizado (que poderia ser ~18 dias) durante o qual cada nó é responsável por armazenar tudo, e depois ter uma rede peer-to-peer composta por nós Ethereum armazenando dados mais antigos de forma distribuída.
Os códigos de apagamento podem ser usados para aumentar a robustez mantendo o mesmo fator de replicação. Na verdade, os blobs já vêm codificados com apagamento para suportar a amostragem de disponibilidade de dados. A solução mais simples pode ser reutilizar esta codificação com apagamento e colocar os dados de bloco de execução e consenso nos blobs também.
O trabalho principal restante envolve a construção e integração de uma solução distribuída concreta para armazenar o histórico - pelo menos o histórico de execução, mas também o consenso e os blobs. As soluções mais fáceis para isso são (i) simplesmente introduzir uma biblioteca de torrent existente e (ii) uma solução nativa do Ethereum chamada a rede Portal. Uma vez que qualquer um destes seja introduzido, podemos activar o EIP-4444. O próprio EIP-4444 não requer uma hard fork, embora exija uma nova versão do protocolo de rede. Por esta razão, há valor em ativá-lo para todos os clientes ao mesmo tempo, porque caso contrário existem riscos de os clientes avariarem ao ligarem-se a outros nós a contar com o download da história completa mas na realidade não a receberem.
O principal compromisso envolve o quão duro tentamos tornar os dados históricos "antigos" disponíveis. A solução mais fácil seria simplesmente parar de armazenar a história antiga amanhã e confiar nos nós de arquivo existentes e em vários fornecedores centralizados para replicação. Isso é fácil, mas enfraquece a posição do Ethereum como um lugar para fazer registros permanentes. O caminho mais difícil, mas mais seguro, é primeiro construir e integrar a rede de torrent para armazenar a história de forma distribuída. Aqui, existem duas dimensões de "quão duro tentamos":
Uma abordagem maximalmente paranóica para (1) envolveriaprova de custódiana verdade, exigindo que cada validador de prova de participação armazene uma porcentagem do histórico e verifique regularmente criptograficamente se eles o fazem. Uma abordagem mais moderada é estabelecer um padrão voluntário para a porcentagem do histórico que cada cliente armazena.
Para (2), uma implementação básica envolve apenas pegar o trabalho que já é feito hoje: o Portal já armazena arquivos ERA contendo todo o histórico do Ethereum. Uma implementação mais completa envolveria realmente conectar isso ao processo de sincronização, para que, se alguém quisesse sincronizar um nó de armazenamento de histórico completo ou um nó de arquivo, eles pudessem fazê-lo mesmo se nenhum outro nó de arquivo existisse online, sincronizando diretamente da rede do Portal.
Reduzir os requisitos de armazenamento de histórico é possivelmente ainda mais importante do que a falta de estado se quisermos tornar extremamente fácil executar ou iniciar um nó: dos 1,1 TB que um nó precisa ter, ~300 GB são de estado, e os restantes ~800 GB são de histórico. A visão de um nó Ethereum rodando em um smartwatch e levando apenas alguns minutos para ser configurado só é alcançável se tanto a falta de estado quanto o EIP-4444 forem implementados.
Limitar o armazenamento de histórico torna também mais viável para as novas implementações de nós Ethereum suportarem apenas versões recentes do protocolo, o que lhes permite ser muito mais simples. Por exemplo, muitas linhas de código podem ser removidas com segurança agora que todas as ranhuras de armazenamento vazias criadas durante os ataques DoS de 2016 foramremovido. Agora que a mudança para o proof of stake é história antiga, os clientes podem remover com segurança todo o código relacionado ao proof-of-work.
Mesmo que removamos a necessidade de os clientes armazenarem o histórico, o requisito de armazenamento de um cliente continuará a crescer, em cerca de 50 GB por ano, devido ao crescimento contínuo do estado: saldos e nonces de conta, código de contrato e armazenamento de contrato. Os usuários podem pagar um custo único para impor um ônus nos clientes presentes e futuros do Ethereum para sempre.
O estado é muito mais difícil de “expirar” do que a história, porque o EVM é fundamentalmente projetado com base na suposição de que, uma vez que um objeto de estado é criado, ele estará sempre lá e pode ser lido por qualquer transação a qualquer momento. Se introduzirmos a falta de estado, há um argumento de que talvez esse problema não seja tão ruim: apenas uma classe especializada de construtores de blocos precisaria realmente armazenar o estado, e todos os outros nós (mesmolista de inclusãoprodução!) pode ser executado sem estado. No entanto, há um argumento de que não queremos depender muito do estado sem, e eventualmente podemos querer expirar o estado para manter o Ethereum descentralizado.
Hoje, quando você cria um novo objeto de estado (o que pode acontecer de três maneiras: (i) enviando ETH para uma nova conta, (ii) criando uma nova conta com código, (iii) definindo um slot de armazenamento previamente não utilizado), esse objeto de estado permanece no estado para sempre. O que queremos é que os objetos expirem automaticamente ao longo do tempo. O desafio-chave é fazer isso de uma maneira que cumpra três objetivos:
É fácil resolver o problema sem satisfazer esses objetivos. Por exemplo, você pode fazer com que cada objeto de estado também armazene um contador para sua data de expiração (que pode ser estendida pela gravação de ETH, o que pode acontecer automaticamente sempre que for lido ou gravado) e ter um processo que percorra o estado para remover objetos de estado expirados. No entanto, isso introduz computação extra (e até mesmo requisitos de armazenamento), e definitivamente não satisfaz o requisito de facilidade de uso. Os desenvolvedores também teriam dificuldade em raciocinar sobre casos de borda envolvendo valores de armazenamento às vezes redefinidos para zero. Se você fizer o contrato do temporizador de expiração em todo o contrato, isso torna a vida tecnicamente mais fácil para os desenvolvedores, mas torna a economia mais difícil: os desenvolvedores teriam que pensar em como "repassar" os custos contínuos de armazenamento para seus usuários.
Estes são problemas com os quais a comunidade de desenvolvimento central do Ethereum lutou por muitos anos, incluindo propostas como “aluguer de blockchain" e "regenesis“Eventualmente, combinámos as melhores partes das propostas e convergimos em duas categorias de 'soluções menos más conhecidas'.
As propostas de expiração parcial do estado funcionam todos segundo o mesmo princípio. Nós dividimos o estado em pedaços. Todo mundo armazena permanentemente o “mapa de nível superior” de quais pedaços estão vazios ou não vazios. Os dados dentro de cada pedaço só são armazenados se esses dados tiverem sido acessados recentemente. Existe um mecanismo de “ressurreição” onde, se um pedaço não estiver mais armazenado, qualquer pessoa pode trazer esses dados de volta fornecendo uma prova do que os dados eram.
As principais distinções entre essas propostas são: (i) como definimos "recentemente" e (ii) como definimos "pedaço"? Uma proposta concreta é EIP-7736, que se baseia no design de "caule e folha"introduzido para árvores Verkle (embora compatível com qualquer forma de apatridia, por exemplo, árvores binárias). Neste design, cabeçalho, código e slots de armazenamento que são adjacentes uns aos outros são armazenados sob o mesmo "caule". Os dados armazenados sob uma haste podem ser, no máximo, 256 * 31 = 7.936 bytes. Em muitos casos, todo o cabeçalho e código, e muitos slots de armazenamento de chaves, de uma conta serão todos armazenados sob a mesma haste. Se os dados sob uma determinada haste não forem lidos ou gravados por 6 meses, os dados não serão mais armazenados e, em vez disso, apenas um compromisso de 32 bytes ("stub") para os dados será armazenado. Transações futuras que acessam esses dados precisariam "ressuscitar" os dados, com uma prova que seria verificada em relação ao stub.
Existem outras maneiras de implementar uma ideia semelhante. Por exemplo, se a granularidade ao nível da conta não for suficiente, poderíamos criar um esquema em que cada fração 1/232 da árvore seja regida por um mecanismo semelhante ao caule e folha.
Isso é mais complicado por causa dos incentivos: um atacante poderia forçar os clientes a armazenar permanentemente uma quantidade muito grande de estado colocando uma quantidade muito grande de dados em uma única subárvore e enviando uma única transação a cada ano para 'renovar a árvore'. Se você fizer com que o custo de renovação seja proporcional (ou a duração de renovação inversamente proporcional) ao tamanho da árvore, então alguém poderia prejudicar outro usuário colocando uma quantidade muito grande de dados na mesma subárvore que eles. Poderíamos tentar limitar ambos os problemas tornando a granularidade dinâmica com base no tamanho da subárvore: por exemplo, cada 216 objetos de estado = 65536 consecutivos poderiam ser tratados como um 'grupo'. No entanto, essas ideias são mais complexas; a abordagem baseada no caule é simples e alinha os incentivos, porque geralmente todos os dados sob um caule estão relacionados à mesma aplicação ou usuário.
E se quisermos evitar qualquer crescimento permanente de estado, mesmo com conectores de 32 bytes? Este é um problema difícil por causa de @vbuterin/state_size_management#Conflitos_de_r ressurreição": conflitos de ressurreição: e se um objeto de estado for removido, mais tarde a execução do EVM coloca outro objeto de estado na exata mesma posição, mas depois alguém que se preocupa com o objeto de estado original volta e tenta recuperá-lo? Com expiração parcial do estado, o “stub” impede a criação de novos dados. Com expiração completa do estado, não podemos nos dar ao luxo de armazenar nem mesmo o stub.
O design baseado em períodos de endereço é a melhor ideia conhecida para resolver isso. Em vez de ter uma árvore de estado armazenando todo o estado, temos uma lista constantemente crescente de árvores de estado e qualquer estado que seja lido ou gravado é salvo na árvore de estado mais recente. Uma nova árvore de estado vazia é adicionada uma vez por período (pense: 1 ano). As árvores de estado mais antigas estão congeladas. Espera-se que os nós completos armazenem apenas as duas árvores mais recentes. Se um objeto de estado não for tocado por dois períodos e, portanto, cair em uma árvore expirada, ainda poderá ser lido ou gravado, mas a transação precisará provar uma prova de Merkle para isso - e, uma vez que o fizer, uma cópia será salva novamente na árvore mais recente.
Uma ideia-chave para tornar tudo isto amigável para utilizadores e desenvolvedores é o conceito de períodos de endereço. Um período de endereço é um número que faz parte de um endereço. Uma regra fundamental é que um endereço com o período de endereço N só pode ser lido ou escrito durante ou após o período N (ou seja, quando a lista da árvore de estado atingir o comprimento N). Se estiver a guardar um novo objeto de estado (por exemplo, um novo contrato ou um novo saldo ERC20), se certificar-se de colocar o objeto de estado num contrato cujo período de endereço seja N ou N-1, então pode guardá-lo imediatamente, sem necessidade de fornecer provas de que não havia nada antes. No entanto, quaisquer adições ou edições ao estado em períodos de endereço antigos requerem uma prova.
Este design preserva a maioria das propriedades atuais do Ethereum, é muito leve em computação extra, permite que as aplicações sejam escritas quase como estão hoje (os ERC20s precisarão ser reescritos, para garantir que os saldos dos endereços com período de endereço N sejam armazenados em um contrato filho que por si só tem um período de endereço N), e resolve o problema de "o usuário entra em uma caverna por cinco anos". No entanto, tem um grande problema: os endereços precisam ser expandidos além de 20 bytes para caber nos períodos de endereço.
Uma proposta é introduzir um novo formato de endereço de 32 bytes, que inclui um número de versão, um número de período de endereço e um hash expandido.
0x01000000000157aE408398dF7E5f4552091A69125d5dFcb7B8C2659029395bdF
O vermelho é um número de versão. Os quatro zeros coloridos a laranja aqui são destinados como espaço vazio, que poderia acomodar um número de partição no futuro. O verde é um número de período de endereço. O azul é um hash de 26 bytes.
O desafio chave aqui é a compatibilidade retroativa. Os contratos existentes são projetados em torno de endereços de 20 bytes e frequentemente usam técnicas de compactação de bytes apertadas que assumem explicitamente que os endereços têm exatamente 20 bytes de comprimento.@ipsilon/address-space-extension-exploration">Uma ideia para resolver isso envolve um mapa de tradução, onde contratos no estilo antigo interagindo com endereços no novo estilo veriam um hash de 20 bytes do endereço no novo estilo. No entanto, existem complexidades significativas envolvidas em tornar isso seguro.
Outra abordagem segue a direção oposta: proibimos imediatamente algumas subfaixas de endereços de tamanho 2128 (por exemplo, todos os endereços que começam com 0xffffffff) e, em seguida, usamos essa faixa para introduzir endereços com períodos de endereço e hashes de 14 bytes.
0xfffffff000169125d5dFcb7B8C2659029395bdF
O sacrifício-chave que esta abordagem faz, é que issointroduz riscos de segurança para endereços contrafactuais: endereços que possuem ativos ou permissões, mas cujo código ainda não foi publicado na cadeia. O risco envolve alguém criar um endereço que afirma ter um pedaço de código (ainda não publicado), mas também tem outro pedaço válido de código que gera o mesmo endereço. Calcular tal colisão requer 280hashes hoje; a contração do espaço de endereço reduziria esse número para um acessível 256hashes.
A área de risco chave, endereços contrafactuais que não são carteiras mantidas por um único proprietário, é um caso relativamente raro hoje, mas provavelmente se tornará mais comum à medida que entramos em um mundo multi-L2. A única solução é simplesmente aceitar esse risco, mas identificar todos os casos de uso comuns onde isso pode ser um problema e encontrar soluções alternativas eficazes.
Vejo quatro caminhos viáveis para o futuro:
Um ponto importante é que as questões difíceis em torno da expansão e contração do espaço de endereço terão que ser abordadas eventualmente, independentemente de as medidas de expiração do estado que dependem das alterações de formato do endereço serem ou não implementadas. Hoje, leva aproximadamente 280 hashes para gerar uma colisão de endereços, uma carga computacional que já é viável para atores extremamente bem dotados de recursos: uma GPU pode fazer cerca de 227hashes, portanto, executando por um ano, ele pode calcular 252, então todos ~2^30 GPUs no mundo poderia calcular uma colisão em ~1/4 de um ano, e FPGAs e ASICs poderiam acelerar isso ainda mais. No futuro, esses ataques tornar-se-ão abertos a cada vez mais pessoas. Por conseguinte, o custo real da implementação da expiração total do Estado pode não ser tão elevado como parece, uma vez que temos de resolver este problema de resolução muito difícil independentemente disso.
A expiração do estado potencialmente facilita as transições de um formato de árvore de estado para outro, porque não haverá necessidade de um procedimento de transição: você poderia simplesmente começar a fazer novas árvores usando um novo formato e, posteriormente, fazer um hard fork para converter as árvores mais antigas. Assim, embora a expiração do estado seja complexa, ela tem benefícios em simplificar outros aspectos do roteiro.
Uma das condições prévias chave de segurança, acessibilidade e neutralidade credívelé a simplicidade. Se um protocolo é bonito e simples, reduz a probabilidade de haver erros. Aumenta a probabilidade de novos desenvolvedores poderem entrar e trabalhar com qualquer parte dele. É mais provável que seja justo e mais fácil de defender contra interesses especiais. Infelizmente, os protocolos, como qualquer sistema social, por padrão, tornam-se mais complexos ao longo do tempo. Se não quisermos que o Ethereum entre num buraco negro de complexidade crescente, precisamos fazer uma das duas coisas: (i) parar de fazer alterações e ossificar o protocolo, (ii) ser capaz de realmente remover funcionalidades e reduzir a complexidade. Um caminho intermediário, de fazer menos alterações no protocolo e também remover pelo menos um pouco de complexidade ao longo do tempo, também é possível. Esta seção irá abordar como podemos reduzir ou remover a complexidade.
Não há uma grande correção única que possa reduzir a complexidade do protocolo; a natureza inerente do problema é que há muitas pequenas correções.
Um exemplo que já está quase concluído e pode servir como um modelo para lidar com os outros é o@vbuterinremoção do opcode SELFDESTRUCT. O opcode SELFDESTRUCT era o único opcode que podia modificar um número ilimitado de slots de armazenamento dentro de um único bloco, exigindo que os clientes implementassem significativamente mais complexidade para evitar ataques DoS. O objetivo original do opcode era permitir a limpeza voluntária do estado, permitindo que o tamanho do estado diminuísse ao longo do tempo. Na prática, muito poucos acabaram por usá-lo.o opcode foi enfraquecidopara permitir apenas contas autodestrutivas criadas na mesma transação no hardfork Dencun. Isso resolve o problema de DoS e permite uma simplificação significativa no código do cliente. No futuro, provavelmente faz sentido remover eventualmente o opcode completamente.
Alguns exemplos-chave de oportunidades de simplificação de protocolo identificadas até agora incluem as seguintes. Primeiro, alguns exemplos que estão fora do EVM; estes são relativamente não invasivos, e assim mais fáceis de obter consenso e implementar num prazo mais curto.
Agora, alguns exemplos que estão dentro do EVM:
O principal compromisso ao fazer esse tipo de simplificação de recurso é (i) o quanto simplificamos e o quão rapidamente versus (ii) compatibilidade com versões anteriores. O valor do Ethereum como uma cadeia vem do fato de ser uma plataforma onde você pode implantar um aplicativo e ter confiança de que ainda funcionará muitos anos a partir de agora. Ao mesmo tempo, é possível levar esse ideal longe demais e,parafrasear William Jennings Bryan"crucificar o Ethereum em uma cruz de compatibilidade reversa". Se houver apenas duas aplicações em todo o Ethereum que usem uma determinada funcionalidade, e uma delas não tem nenhum usuário há anos e a outra é quase completamente não utilizada e assegura um valor total de $57, então devemos simplesmente remover a funcionalidade, e se necessário pagar as vítimas $57 do nosso bolso.
O problema social mais amplo está em criar um pipeline padronizado para fazer alterações de compatibilidade reversa não emergenciais. Uma maneira de abordar isso é examinar e estender precedentes existentes, como o processo SELFDESTRUCT. O pipeline se parece com algo como o seguinte:
Deve haver um pipeline de vários anos entre o passo 1 e o passo 4, com informações claras sobre quais itens estão em que passo. Nesse ponto, há um trade-off entre o quão vigoroso e rápido é o pipeline de remoção de recursos, em comparação com ser mais conservador e colocar mais recursos em outras áreas de desenvolvimento de protocolo, mas ainda estamos longe da fronteira de Pareto.
Um conjunto importante de mudanças que foi proposto para a EVM é oFormato de Objeto EVM (EOF). EOF introduz um grande número de mudanças, como a proibição da observabilidade de gás, observabilidade de código (ou seja, sem CODECOPY), permitindo apenas saltos estáticos. O objetivo é permitir que o EVM seja atualizado de maneira mais robusta, preservando a compatibilidade com versões anteriores (pois o EVM pré-EOF ainda existirá).
Isso tem a vantagem de criar um caminho natural para adicionar novas funcionalidades do EVM e incentivar a migração para um EVM mais restritivo com garantias mais fortes. Tem a desvantagem de aumentar significativamente a complexidade do protocolo, a menos que possamos encontrar uma maneira de eventualmente descontinuar e remover o antigo EVM. Uma questão importante é: qual é o papel do EOF nas propostas de simplificação do EVM, especialmente se o objetivo é reduzir a complexidade do EVM como um todo?
Muitas das propostas de “melhoria” no resto do roteiro também são oportunidades para simplificar recursos antigos. Para repetir alguns exemplos acima:
Uma estratégia de simplificação do Ethereum mais radical é manter o protocolo como está, mas mover grandes partes dele de serem recursos do protocolo para serem código de contrato.
A versão mais extrema disso seria fazer com que o Ethereum L1 "tecnicamente" fosse apenas a cadeia beacon, e introduzir uma VM mínima (por exemplo. RISC-V, Cairo, ou algo ainda mais minimalista especializado em sistemas de provar) que permite a qualquer pessoa criar seu próprio rollup. A EVM então se tornaria o primeiro desses rollups. Ironicamente, esse é exatamente o mesmo resultado do propostas de ambiente de execução de 2019-20, embora os SNARKs tornem significativamente mais viável a sua implementação.
Uma abordagem mais moderada seria manter a relação entre a beacon chain e o ambiente de execução Ethereum atual, mas fazer uma troca no local da EVM. Poderíamos escolher RISC-V, Cairo ou outro VM para ser o novo 'Ethereum VM' oficial e, em seguida, converter forçadamente todos os contratos EVM em código de novo VM que interpreta a lógica do código original (compilando ou interpretando-o). Teoricamente, isso até poderia ser feito com o 'VM de destino' sendo uma versão do EOF.