Um dos desafios do Ethereum é que, por padrão, o inchaço e a complexidade do protocolo de qualquer 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 excelentes: sua permanência. Você pode colocar um NFT, uma mensagem de amor nos 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 que você leia e interaja com ele. Para que os dapps se sintam confortáveis em se tornar completamente descentralizados e remover suas chaves de atualização, eles precisam ter confiança de que suas dependências não vão ser atualizadas de uma forma que os prejudique - especialmente a L1 em si.
A Purge, roteiro de 2023.
Equilibrar entre essas duas necessidades, e minimizar ou reverter a inflação, complexidade e decadência, enquanto preservamos a continuidade, é absolutamente possível se nos dedicarmos a isso. Os organismos vivos podem fazer isso: enquanto a maioria envelhece com o tempo, alguns sortudos não. Mesmo os sistemas sociais podem tem longevidade extrema. Em algumas ocasiões, o Ethereum já mostrou sucessos: a prova de trabalho se foi, o opcode SELFDESTRUCT está quase extinto, e os nós da cadeia de beacon já armazenam dados antigos por apenas seis meses. Descobrir esse caminho para o Ethereum de forma mais generalizada e avançar em direção a um resultado final estável a longo prazo é o desafio supremo da escalabilidade a longo prazo, sustentabilidade técnica e até segurança do Ethereum.
No momento da redação deste texto, um nó Ethereum totalmente sincronizado requeraproximadamente 1,1 terabytesde espaço em disco para ocliente de execução, mais algumas centenas de gigabytes para o cliente de consenso. A grande maioria disso é história: dados sobre blocos históricos, transações e recibos, a maior parte dos quais tem muitos anos. Isso significa que o tamanho de um nó continua aumentando em centenas de gigabytes a cada ano, mesmo se o limite de gás não aumentar.
Um recurso de simplificação-chave do problema de armazenamento de histórico é que, como cada bloco aponta para o bloco anterior por meio de um link de hash (eoutro 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 histórico, transação 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 que qualquer outra pessoa verifique sua correção. Embora o consenso seja 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 diminui necessariamente 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 parte dos dados seria replicada 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 todo o histórico para sempre. Blocos de consenso (ou seja, as partes relacionadas ao consenso de participação) são armazenados apenas por ~6 meses. Blobs são armazenados apenas por ~18 dias.EIP-4444O objetivo é 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 de cerca de 18 dias) durante o qual cada nó é responsável por armazenar tudo e, em seguida, 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. De fato, os blobs já vêm com códigos de apagamento para suportar a amostragem de disponibilidade de dados. A solução mais simples pode ser reutilizar esse código de apagamento e colocar os dados de bloco de execução e consenso em 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 eventualmente o consenso e os blobs. As soluções mais simples para isso são (i) simplesmente introduzir uma biblioteca de torrents existente e (ii) uma solução nativa do Ethereum chamada gate.a rede Portal. Uma vez que qualquer um deles seja introduzido, podemos ativar o EIP-4444. O próprio EIP-4444 não requer um hard fork, embora exija uma nova versão de protocolo de rede. Por essa razão, há valor em habilitá-lo para todos os clientes ao mesmo tempo, porque caso contrário há riscos de mau funcionamento dos clientes ao se conectarem a outros nós esperando fazer o download de todo o histórico, mas na verdade não o obtendo.
O principal trade-off 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 em nós de arquivo existentes e vários provedores 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 torrent para armazenar a história de maneira distribuída. Aqui, há duas dimensões de 'quão duro tentamos':
Uma abordagem maximamente paranóica para (1) envolveriaprova de custódia: na verdade, exigindo que cada validador de prova de participação armazene uma certa porcentagem de histórico e verifique regularmente criptograficamente se eles o fazem. Uma abordagem mais moderada é estabelecer um padrão voluntário para qual porcentagem de histórico cada cliente armazena.
Para (2), uma implementação básica envolve apenas pegar o trabalho que já é feito hoje: 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 Portal.
Reduzir os requisitos de armazenamento de histórico é possivelmente ainda mais importante do que a ausência de estado, se quisermos tornar extremamente fácil executar ou iniciar um nó: dos 1,1 TB necessários para um nó, cerca de 300 GB são de estado, e os restantes 800 GB são de histórico. A visão de um nó Ethereum funcionando em um relógio inteligente e levando apenas alguns minutos para ser configurado só é possível se a ausência de estado e o EIP-4444 forem implementados.
Limitar o armazenamento de histórico também torna mais viável que novas implementações de nós Ethereum suportem apenas versões recentes do protocolo, o que permite que sejam muito mais simples. Por exemplo, muitas linhas de código podem ser removidas com segurança agora que todos os espaços de armazenamento vazios criados durante os ataques DoS de 2016 foram...removido. Agora que a transição 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 contas, código de contrato e armazenamento de contrato. Os usuários podem pagar um custo único para impor um fardo aos clientes presentes e futuros do Ethereum para sempre.
O estado é muito mais difícil de “expirar” do que o histórico, porque o EVM é fundamentalmente projetado com a 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 ausência de estado, há um argumento de que talvez esse problema não seja tão ruim assim: apenas uma classe especializada de construtores de blocos precisaria armazenar o estado de fato, e todos os outros nós (mesmo lista de inclusãoprodução!) pode ser executado sem estado. No entanto, há um argumento de que não queremos depender muito do estado de não ter 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 fica no estado para sempre. O que queremos, em vez disso, é que os objetos expirem automaticamente com o tempo. O desafio chave é fazer isso de uma maneira que alcance três objetivos:
É fácil resolver o problema sem atingir 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 gravando 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ê tornar o contrato de temporizador de expiração amplo, 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.
Esses são problemas com os quais a comunidade de desenvolvimento central do Ethereum lutou por muitos anos, incluindo propostas como “aluguel de blockchain“ e “regenesis". Eventualmente, combinamos as melhores partes das propostas e convergimos em duas categorias de "soluções conhecidas menos ruins":
As propostas de expiração parcial do estado funcionam todos com o mesmo princípio. Dividimos o estado em pedaços. Todos armazenam 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ão armazenados apenas 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 'chunk'? Uma proposta concreta é EIP-7736, que se baseia no design “stem-and-leaf”introduzido para árvores Verkle (embora compatível com qualquer forma de estado sem estado, por exemplo, árvores binárias). Nesse design, cabeçalho, código e slots de armazenamento que são adjacentes entre si são armazenados sob o mesmo “ramo”. Os dados armazenados embaixo de um ramo podem ter no máximo 256 * 31 = 7.936 bytes. Em muitos casos, todo o cabeçalho e código, e muitos slots de armazenamento-chave, de uma conta serão armazenados sob o mesmo ramo. Se os dados embaixo de um determinado ramo não forem lidos ou gravados por 6 meses, os dados não são mais armazenados, e apenas um compromisso de 32 bytes (“stub”) para os dados é 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 de 1/232 da árvore seja governada por um mecanismo semelhante haste-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. Pode-se tentar limitar ambos os problemas tornando a granularidade dinâmica com base no tamanho da subárvore: por exemplo, cada 216 = 65536 objetos de estado consecutivos poderiam ser tratados como um 'grupo'. No entanto, essas ideias são mais complexas; a abordagem baseada em 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 de estado permanente, mesmo com stubs de 32 bytes? Este é um problema difícil por causa de @vbuterin/state_size_management#Conflitos_de_r ressurreição": e se um objeto de estado for removido, a execução do EVM posteriormente coloca outro objeto de estado na mesma posição exata, mas depois disso alguém que se importa com o objeto de estado original volta e tenta recuperá-lo? Com a expiração parcial do estado, o "stub" impede a criação de novos dados. Com a expiração completa do estado, não podemos nos dar ao luxo de armazenar nem mesmo o "stub".
O design baseado em período de endereço é a ideia mais conhecida para resolver isso. Em vez de ter uma árvore de estado armazenando todo o estado, temos uma lista de árvores de estado em constante crescimento, e qualquer estado que é lido ou escrito é 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 são sólidas congeladas. Espera-se que os nós completos armazenem apenas as duas árvores mais recentes. Se um objeto de estado não foi tocado por dois períodos e, portanto, cai em uma árvore expirada, ele ainda pode ser lido ou gravado, mas a transação precisaria provar uma prova de Merkle para ele - e uma vez que isso aconteça, uma cópia será salva na árvore mais recente novamente.
Uma ideia-chave para tornar tudo isso amigável para usuários 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 importante é que um endereço com período de endereço N só pode ser lido ou gravado durante ou após o período N (ou seja, quando a lista de árvores de estado atinge o comprimento N). Se você estiver salvando um novo objeto de estado (por exemplo, um novo contrato ou um novo saldo ERC20), se certificar de colocar o objeto de estado em um contrato cujo período de endereço seja N ou N-1, você pode salvá-lo imediatamente, sem precisar fornecer provas de que não havia nada lá antes. No entanto, qualquer adição ou edição de estado em períodos de endereço mais antigos requer uma prova.
Este design preserva a maioria das propriedades atuais do Ethereum, é muito leve em termos de computação extra, permite que as aplicações sejam escritas quase como são hoje (ERC20s precisarão ser reescritas, para garantir que os saldos de endereços com período de endereço N sejam armazenados em um contrato filho que também tem período de endereço N) e resolve o problema de "o usuário entra em uma caverna por cinco anos". No entanto, ele 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 de laranja aqui são destinados como espaço vazio, que poderia caber um número de shard no futuro. O verde é um número de período de endereço. O azul é um hash de 26 bytes.
O desafio aqui é a compatibilidade retroativa. Contratos existentes são projetados em torno de endereços de 20 bytes e muitas vezes usam técnicas de compactação de byte apertado 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 estilo novo veriam um hash de 20 bytes do endereço no estilo novo. No entanto, existem complexidades significativas envolvidas em tornar isso seguro.
Outra abordagem segue a direção oposta: imediatamente proibimos uma subfaixa 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 é queintroduz 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 criando 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 faz hash para 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 2 muito acessível56hashes.
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 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 políticas de expiração de estado que dependem de alterações no formato do endereço serem ou não implementadas. Hoje, leva aproximadamente 280 hashes para gerar uma colisão de endereço, uma carga computacional que já é viável para atores extremamente bem dotados de recursos: uma GPU pode fazer cerca de 227 hashes, então, executando por um ano, ele pode calcular 252, então todos ~2^30 GPUs no mundopoderia calcular uma colisão em ~1/4 de ano, e FPGAs e ASICs poderiam acelerar isso ainda mais. No futuro, tais ataques estarão disponíveis para cada vez mais pessoas. Assim, o custo real de implementar a expiração completa do estado pode não ser tão alto quanto parece, já que temos que resolver esse problema de endereço muito desafiador de qualquer maneira.
Fazer a expiração de estado potencialmente torna as transições de um formato de árvore de estado para outro mais fácil, porque não haverá necessidade de um procedimento de transição: você pode simplesmente começar a fazer novas árvores usando um novo formato e, mais tarde, fazer um hard fork para converter as árvores mais antigas. Assim, embora a expiração do estado seja complexa, ela tem benefícios na simplificação de outros aspectos do roteiro.
Uma das condições prévias fundamentais de segurança, acessibilidade e neutralidade credívelé a simplicidade. Se um protocolo é bonito e simples, reduz a chance de haver bugs. Aumenta a chance de novos desenvolvedores serem capazes de 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 com o tempo. Se não quisermos que o Ethereum entre em um buraco negro de complexidade cada vez maior, precisamos fazer uma das duas coisas: (i) parar de fazer mudanças e ossificar o protocolo, (ii) ser capaz de realmente remover recursos e reduzir a complexidade. Um caminho intermediário, de fazer menos mudanças no protocolo e também remover pelo menos um pouco de complexidade ao longo do tempo, também é possível. Esta seção falará sobre como podemos reduzir ou remover complexidade.
Não há uma grande solução única que possa reduzir a complexidade do protocolo; a natureza inerente do problema é que há muitas pequenas soluções.
Um exemplo que está principalmente concluído e pode servir como um modelo de como lidar com os outros é o @vbuterin/selfdestruct">remoção do opcode SELFDESTRUCT. O opcode SELFDESTRUCT era o único opcode que poderia 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 de 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 usando.opcode foi nerfadopara permitir apenas contas auto-destrutivas criadas na mesma transação na atualização 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 a opcode completamente.
Alguns exemplos-chave de oportunidades de simplificação de protocolo que foram identificadas até agora incluem o seguinte. Primeiro, alguns exemplos que estão fora do EVM; estes são relativamente não invasivos e, portanto, mais fáceis de obter consenso e implementar em um prazo mais curto.
Agora, alguns exemplos que estão dentro do EVM:
O principal tradeoff ao fazer esse tipo de simplificação de recurso é (i) o quanto simplificamos e o quão rápido vs (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 a confiança de que ele ainda funcionará por muitos anos. 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 utilizam um determinado recurso, e uma delas não tem usuários há anos e a outra é quase completamente não utilizada e assegura um total de $57 de valor, então devemos simplesmente remover o recurso e, se necessário, pagar às vítimas $57 do próprio bolso.
O problema social mais amplo está em criar um pipeline padronizado para fazer alterações que quebram a compatibilidade reversa, mas não são de emergência. Uma maneira de abordar isso é examinar e estender precedentes existentes, como o processo SELFDESTRUCT. O pipeline parece algo como o seguinte:
Deve haver um pipeline de vários anos entre a etapa 1 e a etapa 4, com informações claras sobre quais itens estão em qual etapa. Nesse ponto, há um equilíbrio entre o quão vigoroso e rápido é o pipeline de remoção de recursos, em comparação com ser mais conservador e investir mais recursos em outras áreas de desenvolvimento do protocolo, mas ainda estamos longe da fronteira de Pareto.
Um conjunto importante de alterações que foi proposto para o EVM é oFormato do Objeto EVM (EOF). EOF introduz um grande número de mudanças, como proibir a observabilidade de gás, observabilidade de código (ou seja, sem CODECOPY), permitindo apenas saltos estáticos. O objetivo é permitir que a EVM seja atualizada de forma mais forte, preservando a compatibilidade com versões anteriores (pois a 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 grande questão é: qual papel o EOF desempenha 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 mais radical do Ethereum é manter o protocolo como está, mas mover grandes partes dele de serem recursos do protocolo para ser código de contrato.
A versão mais extrema disso seria fazer com que o Ethereum L1 fosse "tecnicamente" apenas a cadeia de beacons e introduzir uma VM mínima (por exemplo.RISC-V, Cairo, ou algo ainda mais minimalista especializado em provar sistemas) que permite a qualquer outra pessoa criar seu próprio rollup. A EVM então se tornaria o primeiro desses rollups. Ironicamente, esse é exatamente o mesmo resultado que o propostas de ambiente de execução de 2019-20, embora SNARKs tornem significativamente mais viável a implementação real.
Uma abordagem mais moderada seria manter a relação entre a cadeia de faróis e o ambiente de execução atual do Ethereum como está, mas fazer uma troca no local da EVM. Poderíamos escolher RISC-V, Cairo ou outra VM para ser a nova 'VM oficial do Ethereum' e, em seguida, converter forçadamente todos os contratos EVM em código da nova VM que interpreta a lógica do código original (compilando ou interpretando-o). Teoricamente, isso poderia até ser feito com a '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 de qualquer 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 excelentes: sua permanência. Você pode colocar um NFT, uma mensagem de amor nos 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 que você leia e interaja com ele. Para que os dapps se sintam confortáveis em se tornar completamente descentralizados e remover suas chaves de atualização, eles precisam ter confiança de que suas dependências não vão ser atualizadas de uma forma que os prejudique - especialmente a L1 em si.
A Purge, roteiro de 2023.
Equilibrar entre essas duas necessidades, e minimizar ou reverter a inflação, complexidade e decadência, enquanto preservamos a continuidade, é absolutamente possível se nos dedicarmos a isso. Os organismos vivos podem fazer isso: enquanto a maioria envelhece com o tempo, alguns sortudos não. Mesmo os sistemas sociais podem tem longevidade extrema. Em algumas ocasiões, o Ethereum já mostrou sucessos: a prova de trabalho se foi, o opcode SELFDESTRUCT está quase extinto, e os nós da cadeia de beacon já armazenam dados antigos por apenas seis meses. Descobrir esse caminho para o Ethereum de forma mais generalizada e avançar em direção a um resultado final estável a longo prazo é o desafio supremo da escalabilidade a longo prazo, sustentabilidade técnica e até segurança do Ethereum.
No momento da redação deste texto, um nó Ethereum totalmente sincronizado requeraproximadamente 1,1 terabytesde espaço em disco para ocliente de execução, mais algumas centenas de gigabytes para o cliente de consenso. A grande maioria disso é história: dados sobre blocos históricos, transações e recibos, a maior parte dos quais tem muitos anos. Isso significa que o tamanho de um nó continua aumentando em centenas de gigabytes a cada ano, mesmo se o limite de gás não aumentar.
Um recurso de simplificação-chave do problema de armazenamento de histórico é que, como cada bloco aponta para o bloco anterior por meio de um link de hash (eoutro 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 histórico, transação 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 que qualquer outra pessoa verifique sua correção. Embora o consenso seja 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 diminui necessariamente 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 parte dos dados seria replicada 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 todo o histórico para sempre. Blocos de consenso (ou seja, as partes relacionadas ao consenso de participação) são armazenados apenas por ~6 meses. Blobs são armazenados apenas por ~18 dias.EIP-4444O objetivo é 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 de cerca de 18 dias) durante o qual cada nó é responsável por armazenar tudo e, em seguida, 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. De fato, os blobs já vêm com códigos de apagamento para suportar a amostragem de disponibilidade de dados. A solução mais simples pode ser reutilizar esse código de apagamento e colocar os dados de bloco de execução e consenso em 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 eventualmente o consenso e os blobs. As soluções mais simples para isso são (i) simplesmente introduzir uma biblioteca de torrents existente e (ii) uma solução nativa do Ethereum chamada gate.a rede Portal. Uma vez que qualquer um deles seja introduzido, podemos ativar o EIP-4444. O próprio EIP-4444 não requer um hard fork, embora exija uma nova versão de protocolo de rede. Por essa razão, há valor em habilitá-lo para todos os clientes ao mesmo tempo, porque caso contrário há riscos de mau funcionamento dos clientes ao se conectarem a outros nós esperando fazer o download de todo o histórico, mas na verdade não o obtendo.
O principal trade-off 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 em nós de arquivo existentes e vários provedores 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 torrent para armazenar a história de maneira distribuída. Aqui, há duas dimensões de 'quão duro tentamos':
Uma abordagem maximamente paranóica para (1) envolveriaprova de custódia: na verdade, exigindo que cada validador de prova de participação armazene uma certa porcentagem de histórico e verifique regularmente criptograficamente se eles o fazem. Uma abordagem mais moderada é estabelecer um padrão voluntário para qual porcentagem de histórico cada cliente armazena.
Para (2), uma implementação básica envolve apenas pegar o trabalho que já é feito hoje: 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 Portal.
Reduzir os requisitos de armazenamento de histórico é possivelmente ainda mais importante do que a ausência de estado, se quisermos tornar extremamente fácil executar ou iniciar um nó: dos 1,1 TB necessários para um nó, cerca de 300 GB são de estado, e os restantes 800 GB são de histórico. A visão de um nó Ethereum funcionando em um relógio inteligente e levando apenas alguns minutos para ser configurado só é possível se a ausência de estado e o EIP-4444 forem implementados.
Limitar o armazenamento de histórico também torna mais viável que novas implementações de nós Ethereum suportem apenas versões recentes do protocolo, o que permite que sejam muito mais simples. Por exemplo, muitas linhas de código podem ser removidas com segurança agora que todos os espaços de armazenamento vazios criados durante os ataques DoS de 2016 foram...removido. Agora que a transição 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 contas, código de contrato e armazenamento de contrato. Os usuários podem pagar um custo único para impor um fardo aos clientes presentes e futuros do Ethereum para sempre.
O estado é muito mais difícil de “expirar” do que o histórico, porque o EVM é fundamentalmente projetado com a 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 ausência de estado, há um argumento de que talvez esse problema não seja tão ruim assim: apenas uma classe especializada de construtores de blocos precisaria armazenar o estado de fato, e todos os outros nós (mesmo lista de inclusãoprodução!) pode ser executado sem estado. No entanto, há um argumento de que não queremos depender muito do estado de não ter 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 fica no estado para sempre. O que queremos, em vez disso, é que os objetos expirem automaticamente com o tempo. O desafio chave é fazer isso de uma maneira que alcance três objetivos:
É fácil resolver o problema sem atingir 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 gravando 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ê tornar o contrato de temporizador de expiração amplo, 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.
Esses são problemas com os quais a comunidade de desenvolvimento central do Ethereum lutou por muitos anos, incluindo propostas como “aluguel de blockchain“ e “regenesis". Eventualmente, combinamos as melhores partes das propostas e convergimos em duas categorias de "soluções conhecidas menos ruins":
As propostas de expiração parcial do estado funcionam todos com o mesmo princípio. Dividimos o estado em pedaços. Todos armazenam 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ão armazenados apenas 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 'chunk'? Uma proposta concreta é EIP-7736, que se baseia no design “stem-and-leaf”introduzido para árvores Verkle (embora compatível com qualquer forma de estado sem estado, por exemplo, árvores binárias). Nesse design, cabeçalho, código e slots de armazenamento que são adjacentes entre si são armazenados sob o mesmo “ramo”. Os dados armazenados embaixo de um ramo podem ter no máximo 256 * 31 = 7.936 bytes. Em muitos casos, todo o cabeçalho e código, e muitos slots de armazenamento-chave, de uma conta serão armazenados sob o mesmo ramo. Se os dados embaixo de um determinado ramo não forem lidos ou gravados por 6 meses, os dados não são mais armazenados, e apenas um compromisso de 32 bytes (“stub”) para os dados é 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 de 1/232 da árvore seja governada por um mecanismo semelhante haste-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. Pode-se tentar limitar ambos os problemas tornando a granularidade dinâmica com base no tamanho da subárvore: por exemplo, cada 216 = 65536 objetos de estado consecutivos poderiam ser tratados como um 'grupo'. No entanto, essas ideias são mais complexas; a abordagem baseada em 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 de estado permanente, mesmo com stubs de 32 bytes? Este é um problema difícil por causa de @vbuterin/state_size_management#Conflitos_de_r ressurreição": e se um objeto de estado for removido, a execução do EVM posteriormente coloca outro objeto de estado na mesma posição exata, mas depois disso alguém que se importa com o objeto de estado original volta e tenta recuperá-lo? Com a expiração parcial do estado, o "stub" impede a criação de novos dados. Com a expiração completa do estado, não podemos nos dar ao luxo de armazenar nem mesmo o "stub".
O design baseado em período de endereço é a ideia mais conhecida para resolver isso. Em vez de ter uma árvore de estado armazenando todo o estado, temos uma lista de árvores de estado em constante crescimento, e qualquer estado que é lido ou escrito é 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 são sólidas congeladas. Espera-se que os nós completos armazenem apenas as duas árvores mais recentes. Se um objeto de estado não foi tocado por dois períodos e, portanto, cai em uma árvore expirada, ele ainda pode ser lido ou gravado, mas a transação precisaria provar uma prova de Merkle para ele - e uma vez que isso aconteça, uma cópia será salva na árvore mais recente novamente.
Uma ideia-chave para tornar tudo isso amigável para usuários 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 importante é que um endereço com período de endereço N só pode ser lido ou gravado durante ou após o período N (ou seja, quando a lista de árvores de estado atinge o comprimento N). Se você estiver salvando um novo objeto de estado (por exemplo, um novo contrato ou um novo saldo ERC20), se certificar de colocar o objeto de estado em um contrato cujo período de endereço seja N ou N-1, você pode salvá-lo imediatamente, sem precisar fornecer provas de que não havia nada lá antes. No entanto, qualquer adição ou edição de estado em períodos de endereço mais antigos requer uma prova.
Este design preserva a maioria das propriedades atuais do Ethereum, é muito leve em termos de computação extra, permite que as aplicações sejam escritas quase como são hoje (ERC20s precisarão ser reescritas, para garantir que os saldos de endereços com período de endereço N sejam armazenados em um contrato filho que também tem período de endereço N) e resolve o problema de "o usuário entra em uma caverna por cinco anos". No entanto, ele 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 de laranja aqui são destinados como espaço vazio, que poderia caber um número de shard no futuro. O verde é um número de período de endereço. O azul é um hash de 26 bytes.
O desafio aqui é a compatibilidade retroativa. Contratos existentes são projetados em torno de endereços de 20 bytes e muitas vezes usam técnicas de compactação de byte apertado 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 estilo novo veriam um hash de 20 bytes do endereço no estilo novo. No entanto, existem complexidades significativas envolvidas em tornar isso seguro.
Outra abordagem segue a direção oposta: imediatamente proibimos uma subfaixa 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 é queintroduz 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 criando 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 faz hash para 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 2 muito acessível56hashes.
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 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 políticas de expiração de estado que dependem de alterações no formato do endereço serem ou não implementadas. Hoje, leva aproximadamente 280 hashes para gerar uma colisão de endereço, uma carga computacional que já é viável para atores extremamente bem dotados de recursos: uma GPU pode fazer cerca de 227 hashes, então, executando por um ano, ele pode calcular 252, então todos ~2^30 GPUs no mundopoderia calcular uma colisão em ~1/4 de ano, e FPGAs e ASICs poderiam acelerar isso ainda mais. No futuro, tais ataques estarão disponíveis para cada vez mais pessoas. Assim, o custo real de implementar a expiração completa do estado pode não ser tão alto quanto parece, já que temos que resolver esse problema de endereço muito desafiador de qualquer maneira.
Fazer a expiração de estado potencialmente torna as transições de um formato de árvore de estado para outro mais fácil, porque não haverá necessidade de um procedimento de transição: você pode simplesmente começar a fazer novas árvores usando um novo formato e, mais tarde, fazer um hard fork para converter as árvores mais antigas. Assim, embora a expiração do estado seja complexa, ela tem benefícios na simplificação de outros aspectos do roteiro.
Uma das condições prévias fundamentais de segurança, acessibilidade e neutralidade credívelé a simplicidade. Se um protocolo é bonito e simples, reduz a chance de haver bugs. Aumenta a chance de novos desenvolvedores serem capazes de 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 com o tempo. Se não quisermos que o Ethereum entre em um buraco negro de complexidade cada vez maior, precisamos fazer uma das duas coisas: (i) parar de fazer mudanças e ossificar o protocolo, (ii) ser capaz de realmente remover recursos e reduzir a complexidade. Um caminho intermediário, de fazer menos mudanças no protocolo e também remover pelo menos um pouco de complexidade ao longo do tempo, também é possível. Esta seção falará sobre como podemos reduzir ou remover complexidade.
Não há uma grande solução única que possa reduzir a complexidade do protocolo; a natureza inerente do problema é que há muitas pequenas soluções.
Um exemplo que está principalmente concluído e pode servir como um modelo de como lidar com os outros é o @vbuterin/selfdestruct">remoção do opcode SELFDESTRUCT. O opcode SELFDESTRUCT era o único opcode que poderia 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 de 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 usando.opcode foi nerfadopara permitir apenas contas auto-destrutivas criadas na mesma transação na atualização 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 a opcode completamente.
Alguns exemplos-chave de oportunidades de simplificação de protocolo que foram identificadas até agora incluem o seguinte. Primeiro, alguns exemplos que estão fora do EVM; estes são relativamente não invasivos e, portanto, mais fáceis de obter consenso e implementar em um prazo mais curto.
Agora, alguns exemplos que estão dentro do EVM:
O principal tradeoff ao fazer esse tipo de simplificação de recurso é (i) o quanto simplificamos e o quão rápido vs (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 a confiança de que ele ainda funcionará por muitos anos. 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 utilizam um determinado recurso, e uma delas não tem usuários há anos e a outra é quase completamente não utilizada e assegura um total de $57 de valor, então devemos simplesmente remover o recurso e, se necessário, pagar às vítimas $57 do próprio bolso.
O problema social mais amplo está em criar um pipeline padronizado para fazer alterações que quebram a compatibilidade reversa, mas não são de emergência. Uma maneira de abordar isso é examinar e estender precedentes existentes, como o processo SELFDESTRUCT. O pipeline parece algo como o seguinte:
Deve haver um pipeline de vários anos entre a etapa 1 e a etapa 4, com informações claras sobre quais itens estão em qual etapa. Nesse ponto, há um equilíbrio entre o quão vigoroso e rápido é o pipeline de remoção de recursos, em comparação com ser mais conservador e investir mais recursos em outras áreas de desenvolvimento do protocolo, mas ainda estamos longe da fronteira de Pareto.
Um conjunto importante de alterações que foi proposto para o EVM é oFormato do Objeto EVM (EOF). EOF introduz um grande número de mudanças, como proibir a observabilidade de gás, observabilidade de código (ou seja, sem CODECOPY), permitindo apenas saltos estáticos. O objetivo é permitir que a EVM seja atualizada de forma mais forte, preservando a compatibilidade com versões anteriores (pois a 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 grande questão é: qual papel o EOF desempenha 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 mais radical do Ethereum é manter o protocolo como está, mas mover grandes partes dele de serem recursos do protocolo para ser código de contrato.
A versão mais extrema disso seria fazer com que o Ethereum L1 fosse "tecnicamente" apenas a cadeia de beacons e introduzir uma VM mínima (por exemplo.RISC-V, Cairo, ou algo ainda mais minimalista especializado em provar sistemas) que permite a qualquer outra pessoa criar seu próprio rollup. A EVM então se tornaria o primeiro desses rollups. Ironicamente, esse é exatamente o mesmo resultado que o propostas de ambiente de execução de 2019-20, embora SNARKs tornem significativamente mais viável a implementação real.
Uma abordagem mais moderada seria manter a relação entre a cadeia de faróis e o ambiente de execução atual do Ethereum como está, mas fazer uma troca no local da EVM. Poderíamos escolher RISC-V, Cairo ou outra VM para ser a nova 'VM oficial do Ethereum' e, em seguida, converter forçadamente todos os contratos EVM em código da nova VM que interpreta a lógica do código original (compilando ou interpretando-o). Teoricamente, isso poderia até ser feito com a 'VM de destino' sendo uma versão do EOF.