No Ethereum, os recursos eram, até recentemente, limitados e precificados usando um único recurso chamado "gás". O gás é uma medida da quantidade de "esforço computacional" necessário para processar uma transação ou bloco. O gás combina vários tipos de "esforço", principalmente:
Por exemplo, esta transaçãoque enviei custou um total de 47.085 gás. Isso é dividido entre (i) um "custo base" de 21000 gás, (ii) 1556 gás para os bytes nos dados de chamada incluídos como parte da transação, (iii) 16500 gás para leitura e escrita no armazenamento, (iv) gás 2149 para fazer um registo, e o restante para a execução do EVM. A taxa de transação que um usuário deve pagar é proporcional ao gás que a transação consome. Um bloco pode conter até um máximo de 30 milhões de gás, e os preços do gás são constantemente ajustados via o@vbuterin/eip-1559-faq">Mecanismo de segmentação EIP-1559, garantindo que, em média, os blocos contenham 15 milhões de gás.
Esta abordagem tem uma eficiência principal: porque tudo é fundido em um recurso virtual, leva a um design de mercado muito simples. Otimizar uma transação para minimizar custos é fácil, otimizar um bloco para coletar as taxas mais altas possíveis é relativamente fácil (não incluindoMEV) e não há incentivos estranhos que encorajem algumas transações a se agruparem com outras transações para economizar em taxas.
Mas essa abordagem também tem uma grande ineficiência: trata diferentes recursos como sendo mutuamente conversíveis, quando os limites reais subjacentes do que a rede pode lidar não são. Uma maneira de entender essa questão é olhar para este diagrama:
O limite de gás impõe uma restrição de
𝑥1∗𝑑𝑎𝑡𝑎+𝑥2∗c𝑜m𝑝𝑢t𝑎t𝑖𝑜n<𝑁
. A restrição de segurança subjacente real muitas vezes está mais próxima de
max(x1∗dados,x2∗computação) . Essa discrepância leva a que o limite de gás exclua desnecessariamente blocos realmente seguros, ou aceite blocos realmente inseguros, ou uma mistura de ambos. Se houver 𝑛 recursos que têm limites de segurança distintos, então o gás unidimensional reduz de forma plausível a taxa de transferência em até um fator de n . Por essa razão, há muito tempo há interesse no conceito de gás multi-dimensional e com EIP-4844na verdade, temos gás multi-dimensional trabalhando no Ethereum hoje. Esta postagem explora os benefícios desse abordagem e as perspectivas de aumentá-lo ainda mais. No início deste ano, o bloco médio era 150 kB em tamanho. Uma grande fração desse tamanho é dados de rollup: protocolos de camada 2armazenar dados na cadeia para segurança. Esses dados eram caros: mesmo que as transações em rollups custassem ~5-10x menos do que as transações correspondentes na Ethereum L1, mesmo esse custo era muito alto para muitos casos de uso. Por que não diminuir o custo de gás de calldata (atualmente 16 gás por byte não nulo e 4 gás por byte nulo), para tornar os rollups mais baratos? Nósfez isso antes, podemos fazer de novo. A resposta aqui é: o tamanho do pior caso de um bloco era 30,000,00016=1,875,000 bytes não nulos, e a rede já mal consegue lidar com blocos desse tamanho. Reduzir os custos em mais 4x elevaria o máximo para 7,5 MB, o que seria um risco enorme para a segurança. Esse problema acabou sendo resolvido introduzindo um espaço separado de dados amigáveis para rollup, conhecidos como "blobs", em cada bloco. Os dois recursos têm preços separados e limites separados: após o hard fork Dencun, um bloco Ethereum pode conter no máximo (i) 30 milhões de gás e (ii) 6 blobs, que podem conter ~125 kB de calldata cada. Ambos os recursos têm preços separados, ajustados por mecanismos de precificação semelhantes ao EIP-1559 separados, visando um uso médio de 15 milhões de gás e 3 blobs por bloco. Como resultado, os rollups se tornaram 100 vezes mais baratos, o volume de transações nos rollups aumentou mais de 3 vezes, e o tamanho máximo teórico do bloco foi aumentado apenas ligeiramente: de ~1,9 MB para ~2,6 MB. Taxas de transação em rollups, cortesia de growthepie.xyz. O fork Dencun, que introduziu blobs com preços multidimensionais, aconteceu em 13 de março de 2024. Em um futuro próximo, um problema semelhante surgirá em relação às provas de armazenamento para clientes sem estado. Clientes sem estado são um novo tipo de cliente que poderá verificar a cadeia sem armazenar muitos ou quaisquer dados localmente. Clientes sem estado fazem isso aceitando provas das partes específicas do estado Ethereum que as transações nesse bloco precisam acessar. Um cliente sem estado recebe um bloco, juntamente com provas que comprovam os valores atuais nas partes específicas do estado (por exemplo, saldos de contas, código, armazenamento) que a execução do bloco toca. Isso permite que um nó verifique um bloco sem ter nenhum armazenamento próprio. Uma leitura de armazenamento custa 2100-2600 gás, dependendo do tipo de leitura, e as escritas de armazenamento custam mais. Em média, um bloco faz algo como 1000 leituras e escritas de armazenamento (incluindo verificações de saldo ETH, chamadas SSTORE e SLOAD, leitura de código de contrato e outras operações). O máximo teórico, no entanto, é 30,000,0002,100=14,285 lê. A carga de largura de banda de um cliente sem estado é diretamente proporcional a esse número. Hoje, o plano é apoiar clientes sem estado ao mover o design da árvore de estado do Ethereum deÁrvores de Merkle PatriciaparaÁrvores Verkle. No entanto, as árvores Verkle não são resistentes a quantidades, e não são ótimas para as novas ondas de sistemas de prova STARK. Como resultado, muitas pessoas estão interessadas em apoiar clientes sem estado através de árvores binárias de Merkle e STARKsem vez disso - ou pulando Verkle completamente, ou atualizando alguns anos após a transição Verkle uma vez que os STARKs se tornem mais maduros. Provas STARK de ramos de árvore de hash binário têm muitas vantagens, mas têm a fraqueza chave de que as provas levam muito tempo para serem geradas: enquanto Árvores Verklepode provarmais de cem mil valores por segundo, STARKs baseados em hash normalmente conseguem provar apenas algumas milhares de hashes por segundo, e provar cada valor requer um “ramo” contendo muitos hashes. Dado os números que estão sendo projetados hoje a partir de sistemas de prova hiper-otimizados como BiniusePlonky3 e hashes especializados como Visão-Mark-32, parece provável que por algum tempo estaremos em um regime onde é prático provar 1.000 valores em menos de um segundo, mas não 14.285 valores. Blocos médios estariam bem, mas blocos de pior caso, potencialmente publicados por um atacante, quebrariam a rede. A maneira "padrão" como lidamos com esse cenário é a repricing: tornar a leitura de armazenamento mais cara para reduzir o máximo por bloco para algo mais seguro. No entanto, temosjáfeitoestemuitosvezes, e isso tornaria muitas aplicações muito caras para fazer isso novamente. Uma abordagem melhor seria o gás multidimensional: limitar e cobrar o acesso ao armazenamento separadamente, mantendo o uso médio em 1.000 acessos ao armazenamento por bloco, mas definindo um limite por bloco de, por exemplo, 2.000. Outro recurso que vale a pena considerar é o crescimento do tamanho do estado: operações que aumentam o tamanho do estado do Ethereum, que os nós completos precisarão manter a partir de então. A propriedade única do crescimento do tamanho do estado é que a justificativa para limitá-lo vem inteiramente do uso sustentado a longo prazo e não de picos. Portanto, pode haver valor em adicionar uma dimensão de gás separada para operações de aumento do tamanho do estado (por exemplo, de zero para não zero SSTORE, criação de contrato), mas com um objetivo diferente: poderíamos definir um preço flutuante para atingir um uso médio específico, mas não definir nenhum limite por bloco. Isso mostra uma das propriedades poderosas do gás multidimensional: nos permite perguntar separadamente as perguntas de (i) qual é o uso médio ideal e (ii) qual é o uso máximo por bloco seguro, para cada recurso. Em vez de definir os preços do gás com base nos máximos por bloco e deixar o uso médio seguir, nós temos 2𝑛 graus de liberdade para definir 2𝑛 parâmetros, ajustando cada um com base no que é seguro para a rede. Situações mais complicadas, como aquelas em que dois recursos têm considerações de segurança parcialmente aditivas, poderiam ser tratadas fazendo com que um opcode ou custo de recurso consumisse alguma quantidade de múltiplos tipos de gás (por exemplo, um SSTORE de zero para não zero poderia custar 5000 gás de prova para cliente sem estado e 20000 gás de expansão de armazenamento). Deixe 𝑥1 ser o custo de gás de dados e 𝑥2 ser o custo de gás da computação, então em um sistema de gás unidimensional podemos escrever o custo de gás de uma transação: gás=𝑥1∗𝑑𝑎𝑡𝑎+𝑥2∗𝑐𝑜𝑚𝑝𝑢𝑡𝑎𝑡𝑖𝑜𝑛 Neste esquema, definimos em vez disso o custo de gás de uma transação como: gás=máx(x1*dados,x2*computação) Isto é, em vez de uma transação ser cobrada por dados mais computação, a transação é cobrada com base em qual dos dois recursos consome mais. Isso pode ser facilmente estendido para cobrir mais dimensões (por exemplo, 𝑚𝑎𝑥(…,𝑥3∗𝑠𝑡𝑜𝑟𝑎𝑔𝑒_𝑎𝑐𝑐𝑒𝑠𝑠) ). Deve ser fácil ver como isso melhora a taxa de transferência enquanto preserva a segurança. A quantidade máxima teórica de dados em um bloco ainda é 𝐺𝐴𝑆𝐿𝐼𝑀𝐼𝑇𝑥1 , exatamente o mesmo que no esquema de gás unidimensional. Da mesma forma, a quantidade máxima teórica de computação é 𝐺𝐴𝑆𝐿𝐼𝑀𝐼𝑇𝑥2 , novamente exatamente o mesmo que no esquema de gás unidimensional. No entanto, o custo de gás de qualquer transação que consuma tanto dados quanto computação diminui. Esta é aproximadamente o esquema empregado no proposto EIP-7623, para reduzir o tamanho máximo do bloco e, ao mesmo tempo, aumentar ainda mais a contagem de blobs. O mecanismo preciso no EIP-7623 é um pouco mais complicado: ele mantém o preço atual do calldata de 16 gás por byte, mas adiciona um "preço mínimo" de 48 gás por byte; uma transação paga o maior de (16 bytes + execution_gas) e (48 bytes). Como resultado, o EIP-7623 diminui o calldata máximo teórico da transação em um bloco de ~1,9 MB para ~0,6 MB, mantendo os custos da maioria das aplicações inalterados. O benefício desta abordagem é que é uma mudança muito pequena em relação ao esquema de gás unidimensional atual e, portanto, é muito fácil de implementar. Há duas desvantagens: Eu argumentaria que uma regra no estilo EIP-7623, tanto para dados de transação quanto para outros recursos, pode trazer benefícios grandes o suficiente para valer a pena, mesmo apesar dessas desvantagens. No entanto, se e quando estivermos dispostos a investir um esforço de desenvolvimento (significativamente maior), existe uma abordagem mais ideal. Vamos primeiro recapitular como funciona o EIP-1559 "regular". Vamos nos concentrar na versão que foi introduzida no EIP-4844 para blobs, porque é matematicamente mais elegante. Nós rastreamos um parâmetro, excess_blobs. Durante cada bloco, nós definimos: excess_blobs <— max(excess_blobs + len(block.blobs) - TARGET, 0) Onde TARGET = 3. Ou seja, se um bloco tiver mais blobs do que o alvo, excess_blobs aumenta e, se um bloco tiver menos do que o alvo, ele diminui. Em seguida, definimos blob_basefee = exp(excess_blobs / 25.47), onde exp é uma aproximação da função exponencial 𝑒𝑥𝑝(𝑥)=2.71828𝑥 . Isto é, sempre que excess_blobs aumenta em cerca de ~25, a taxa base do blob aumenta por um fator de ~2.7. Se os blobs ficarem muito caros, o uso médio diminui e o excess_blobs começa a diminuir, fazendo com que o preço caia novamente automaticamente. O preço de um blob se ajusta constantemente para garantir que, em média, os blocos estejam meio cheios - ou seja, que contenham em média 3 blobs cada. Se houver um pico de uso a curto prazo, então o limite é acionado: cada bloco pode conter no máximo 6 blobs e, nesse caso, as transações podem competir entre si, aumentando suas taxas de prioridade. No caso normal, no entanto, cada blob só precisa pagar a blob_basefee mais uma pequena taxa de prioridade extra como incentivo para ser incluído. Esse tipo de precificação existia no Ethereum para gás há anos: um mecanismo muito semelhante foi introduzido com @vbuterin/eip-1559-faq">EIP-1559 de volta em 2020. Com o EIP-4844, agora temos dois preços separados flutuantes para gás e para blobs. Taxa base de gás ao longo de uma hora em 08-05-2024, em gwei. Fonte: ultrasound.money. Em princípio, poderíamos adicionar mais taxas separadas para leitura de armazenamento e outros tipos de operações, embora com uma ressalva que vou expandir na próxima seção. Para os usuários, a experiência é notavelmente semelhante à atual: em vez de pagar uma taxa básica, você paga duas taxas básicas, mas sua carteira pode abstrair isso de você e apenas mostrar a taxa esperada e a taxa máxima que você pode esperar pagar. Para os construtores de blocos, na maioria das vezes, a estratégia ideal é a mesma de hoje: incluir qualquer coisa que seja válida. A maioria dos blocos não está cheia - nem em gásnemem manchas. O caso desafiador é quando há gás suficiente ou bastantes blobs para exceder o limite do bloco, e o construtor precisa potencialmente resolver um problema da mochila multidimensionalpara maximizar seu lucro. No entanto, mesmo que existam algoritmos de aproximação bastante bons, os ganhos de criar algoritmos proprietários para otimizar lucros neste caso são muito menores do que os ganhos de fazer o mesmo com MEV. Para os desenvolvedores, o principal desafio é a necessidade de redesenhar recursos do EVM e sua infraestrutura circundante, que atualmente é projetada em torno de um preço e um limite, em um design que acomode múltiplos preços e múltiplos limites. Um problema para os desenvolvedores de aplicativos é que a otimização se torna ligeiramente mais difícil: em alguns casos, você não pode mais dizer inequivocamente que A é mais eficiente que B, porque se A usar mais calldata e B usar mais execução, então A pode ser mais barato quando calldata é barato e mais caro quando calldata é caro. No entanto, os desenvolvedores ainda seriam capazes de obter resultados razoavelmente bons otimizando com base nos preços médios históricos de longo prazo. Há um problema que não apareceu com blobs, e não aparecerá com EIP-7623 ou mesmo com uma implementação de precificação multidimensional 'completa' para calldata, mas aparecerá se tentarmos precificar separadamente os acessos ao estado, ou qualquer outro recurso: limites de gás em chamadas subsidiárias. Os limites de gás na EVM existem em dois lugares. Primeiro, cada transação define um limite de gás, que limita a quantidade total de gás que pode ser usada nessa transação. Em segundo lugar, quando um contrato chama outro contrato, a chamada pode definir seu próprio limite de gás. Isso permite que contratos chamem outros contratos nos quais não confiam e ainda garantam que terão gás suficiente para realizar outras computações após essa chamada. Um rastro de uma transação de abstração de conta, onde uma conta chama outra conta e dá ao chamado uma quantidade limitada de gás, para garantir que a chamada externa possa continuar mesmo que o chamado consuma todo o gás que lhe foi atribuído. O desafio é: tornar o gás multidimensional entre diferentes tipos de execução parece que exigiria sub-chamadas para fornecer múltiplos limites para cada tipo de gás, o que exigiria uma mudança realmente profunda no EVM e não seria compatível com as aplicações existentes. Esta é uma das razões pelas quais propostas de gás multidimensionais frequentemente param em duas dimensões: dados e execução. Os dados (seja calldata de transação ou blobs) são apenas atribuídos fora do EVM e, portanto, nada dentro do EVM precisa mudar para tornar calldata ou blobs com preços separados. Podemos pensar em uma solução estilo “EIP-7623” para este problema. Aqui está uma implementação simples: durante a execução, cobrar 4x mais pelas operações de armazenamento; para simplificar a análise, digamos 10000 gás por operação de armazenamento. No final da transação, reembolsar min(7500 * operações_de_armazenamento, gás_de_execução). O resultado seria que, após subtrair o reembolso, um usuário é cobrado: execução_gás + 10000 armazenamento_operacional - min(7500 operacoes_de_armazenamento, gas_de_execucao) Que equivale a: max(execution_gás + 2500operações de armazenamento, 10000storage_operations) Isso espelha a estrutura do EIP-7623. Outra maneira de fazer é rastrear as operações de armazenamento e o gás de execução em tempo real e cobrar 2500 ou 10000, dependendo de quanto max(execução_gas + 2500operações de armazenamento, 10000storage_operations) aumenta no momento em que o opcode é chamado. Isso evita a necessidade de transações alocarem gás em excesso, pois a maior parte será devolvida por meio de reembolsos. Não obtemos permissão detalhada para subchamadas: uma subchamada poderia consumir toda a 'permissão' de uma transação para operações baratas de armazenamento. Mas obtemos algo suficientemente bom, onde um contrato que faz uma subchamada pode definir um limite e garantir que, uma vez que a subchamada termine de executar, a chamada principal ainda tenha gás suficiente para fazer qualquer pós-processamento necessário. A solução de precificação multidimensional completa mais fácil que consigo pensar é: tratamos os limites de gás de sub-chamada como sendo proporcionais. Ou seja, suponha que haja 𝑘 diferentes tipos de execução e cada transação define um limite multidimensional 𝐿1…𝐿𝑘 Suponha que, no ponto atual da execução, o gás restante seja 𝑔1…𝑔𝑘 Suponha que um opcode CALL seja chamado, com limite de gás de chamada sub 𝑆 . Deixe 𝑠1=𝑆 , e então 𝑠2=𝑠1𝑔1∗𝑔2 , 𝑠3=𝑠1𝑔1∗𝑔3 , e assim por diante. Isto é, tratamos o primeiro tipo de gás (realisticamente, execução VM) como sendo uma espécie de “unidade de conta” privilegiada e, em seguida, atribuímos os outros tipos de gás de modo que a sub-chamada obtenha a mesma porcentagem de gás disponível em cada tipo. Isso é um pouco feio, mas maximiza a compatibilidade com versões anteriores. Se quisermos tornar o esquema mais “neutro” entre diferentes tipos de gás, ao custo de sacrificar a compatibilidade com versões anteriores, poderíamos simplesmente fazer com que o parâmetro de limite de gás da sub-chamada represente uma fração (por exemplo, [1…63] / 64) do gás restante no contexto atual). Em qualquer caso, no entanto, vale ressaltar que, uma vez que você comece a introduzir gás de execução multidimensional, o nível inerente de feiura aumenta, e isso parece difícil de evitar. Portanto, nossa tarefa é fazer um trade-off complicado: aceitamos um pouco mais de feiura no nível do EVM, a fim de desbloquear com segurança ganhos significativos de escalabilidade L1, e, se sim, qual proposta específica funciona melhor para a economia do protocolo e os desenvolvedores de aplicativos? Muito provavelmente, não é nenhuma das que mencionei acima, e ainda há espaço para pensar em algo mais elegante e melhor.Blobs: gás multidimensional em Dencun
Clientes sem estado e gás multidimensional
Gás multidimensional de forma mais geral
Máximo por transação: a maneira mais fraca, mas mais fácil de obter gás multidimensional
Multidimensional EIP-1559: a estratégia mais difícil, mas ideal
Preços multidimensionais, o EVM e chamadas secundárias
Aviso legal:
分享
目录
No Ethereum, os recursos eram, até recentemente, limitados e precificados usando um único recurso chamado "gás". O gás é uma medida da quantidade de "esforço computacional" necessário para processar uma transação ou bloco. O gás combina vários tipos de "esforço", principalmente:
Por exemplo, esta transaçãoque enviei custou um total de 47.085 gás. Isso é dividido entre (i) um "custo base" de 21000 gás, (ii) 1556 gás para os bytes nos dados de chamada incluídos como parte da transação, (iii) 16500 gás para leitura e escrita no armazenamento, (iv) gás 2149 para fazer um registo, e o restante para a execução do EVM. A taxa de transação que um usuário deve pagar é proporcional ao gás que a transação consome. Um bloco pode conter até um máximo de 30 milhões de gás, e os preços do gás são constantemente ajustados via o@vbuterin/eip-1559-faq">Mecanismo de segmentação EIP-1559, garantindo que, em média, os blocos contenham 15 milhões de gás.
Esta abordagem tem uma eficiência principal: porque tudo é fundido em um recurso virtual, leva a um design de mercado muito simples. Otimizar uma transação para minimizar custos é fácil, otimizar um bloco para coletar as taxas mais altas possíveis é relativamente fácil (não incluindoMEV) e não há incentivos estranhos que encorajem algumas transações a se agruparem com outras transações para economizar em taxas.
Mas essa abordagem também tem uma grande ineficiência: trata diferentes recursos como sendo mutuamente conversíveis, quando os limites reais subjacentes do que a rede pode lidar não são. Uma maneira de entender essa questão é olhar para este diagrama:
O limite de gás impõe uma restrição de
𝑥1∗𝑑𝑎𝑡𝑎+𝑥2∗c𝑜m𝑝𝑢t𝑎t𝑖𝑜n<𝑁
. A restrição de segurança subjacente real muitas vezes está mais próxima de
max(x1∗dados,x2∗computação) . Essa discrepância leva a que o limite de gás exclua desnecessariamente blocos realmente seguros, ou aceite blocos realmente inseguros, ou uma mistura de ambos. Se houver 𝑛 recursos que têm limites de segurança distintos, então o gás unidimensional reduz de forma plausível a taxa de transferência em até um fator de n . Por essa razão, há muito tempo há interesse no conceito de gás multi-dimensional e com EIP-4844na verdade, temos gás multi-dimensional trabalhando no Ethereum hoje. Esta postagem explora os benefícios desse abordagem e as perspectivas de aumentá-lo ainda mais. No início deste ano, o bloco médio era 150 kB em tamanho. Uma grande fração desse tamanho é dados de rollup: protocolos de camada 2armazenar dados na cadeia para segurança. Esses dados eram caros: mesmo que as transações em rollups custassem ~5-10x menos do que as transações correspondentes na Ethereum L1, mesmo esse custo era muito alto para muitos casos de uso. Por que não diminuir o custo de gás de calldata (atualmente 16 gás por byte não nulo e 4 gás por byte nulo), para tornar os rollups mais baratos? Nósfez isso antes, podemos fazer de novo. A resposta aqui é: o tamanho do pior caso de um bloco era 30,000,00016=1,875,000 bytes não nulos, e a rede já mal consegue lidar com blocos desse tamanho. Reduzir os custos em mais 4x elevaria o máximo para 7,5 MB, o que seria um risco enorme para a segurança. Esse problema acabou sendo resolvido introduzindo um espaço separado de dados amigáveis para rollup, conhecidos como "blobs", em cada bloco. Os dois recursos têm preços separados e limites separados: após o hard fork Dencun, um bloco Ethereum pode conter no máximo (i) 30 milhões de gás e (ii) 6 blobs, que podem conter ~125 kB de calldata cada. Ambos os recursos têm preços separados, ajustados por mecanismos de precificação semelhantes ao EIP-1559 separados, visando um uso médio de 15 milhões de gás e 3 blobs por bloco. Como resultado, os rollups se tornaram 100 vezes mais baratos, o volume de transações nos rollups aumentou mais de 3 vezes, e o tamanho máximo teórico do bloco foi aumentado apenas ligeiramente: de ~1,9 MB para ~2,6 MB. Taxas de transação em rollups, cortesia de growthepie.xyz. O fork Dencun, que introduziu blobs com preços multidimensionais, aconteceu em 13 de março de 2024. Em um futuro próximo, um problema semelhante surgirá em relação às provas de armazenamento para clientes sem estado. Clientes sem estado são um novo tipo de cliente que poderá verificar a cadeia sem armazenar muitos ou quaisquer dados localmente. Clientes sem estado fazem isso aceitando provas das partes específicas do estado Ethereum que as transações nesse bloco precisam acessar. Um cliente sem estado recebe um bloco, juntamente com provas que comprovam os valores atuais nas partes específicas do estado (por exemplo, saldos de contas, código, armazenamento) que a execução do bloco toca. Isso permite que um nó verifique um bloco sem ter nenhum armazenamento próprio. Uma leitura de armazenamento custa 2100-2600 gás, dependendo do tipo de leitura, e as escritas de armazenamento custam mais. Em média, um bloco faz algo como 1000 leituras e escritas de armazenamento (incluindo verificações de saldo ETH, chamadas SSTORE e SLOAD, leitura de código de contrato e outras operações). O máximo teórico, no entanto, é 30,000,0002,100=14,285 lê. A carga de largura de banda de um cliente sem estado é diretamente proporcional a esse número. Hoje, o plano é apoiar clientes sem estado ao mover o design da árvore de estado do Ethereum deÁrvores de Merkle PatriciaparaÁrvores Verkle. No entanto, as árvores Verkle não são resistentes a quantidades, e não são ótimas para as novas ondas de sistemas de prova STARK. Como resultado, muitas pessoas estão interessadas em apoiar clientes sem estado através de árvores binárias de Merkle e STARKsem vez disso - ou pulando Verkle completamente, ou atualizando alguns anos após a transição Verkle uma vez que os STARKs se tornem mais maduros. Provas STARK de ramos de árvore de hash binário têm muitas vantagens, mas têm a fraqueza chave de que as provas levam muito tempo para serem geradas: enquanto Árvores Verklepode provarmais de cem mil valores por segundo, STARKs baseados em hash normalmente conseguem provar apenas algumas milhares de hashes por segundo, e provar cada valor requer um “ramo” contendo muitos hashes. Dado os números que estão sendo projetados hoje a partir de sistemas de prova hiper-otimizados como BiniusePlonky3 e hashes especializados como Visão-Mark-32, parece provável que por algum tempo estaremos em um regime onde é prático provar 1.000 valores em menos de um segundo, mas não 14.285 valores. Blocos médios estariam bem, mas blocos de pior caso, potencialmente publicados por um atacante, quebrariam a rede. A maneira "padrão" como lidamos com esse cenário é a repricing: tornar a leitura de armazenamento mais cara para reduzir o máximo por bloco para algo mais seguro. No entanto, temosjáfeitoestemuitosvezes, e isso tornaria muitas aplicações muito caras para fazer isso novamente. Uma abordagem melhor seria o gás multidimensional: limitar e cobrar o acesso ao armazenamento separadamente, mantendo o uso médio em 1.000 acessos ao armazenamento por bloco, mas definindo um limite por bloco de, por exemplo, 2.000. Outro recurso que vale a pena considerar é o crescimento do tamanho do estado: operações que aumentam o tamanho do estado do Ethereum, que os nós completos precisarão manter a partir de então. A propriedade única do crescimento do tamanho do estado é que a justificativa para limitá-lo vem inteiramente do uso sustentado a longo prazo e não de picos. Portanto, pode haver valor em adicionar uma dimensão de gás separada para operações de aumento do tamanho do estado (por exemplo, de zero para não zero SSTORE, criação de contrato), mas com um objetivo diferente: poderíamos definir um preço flutuante para atingir um uso médio específico, mas não definir nenhum limite por bloco. Isso mostra uma das propriedades poderosas do gás multidimensional: nos permite perguntar separadamente as perguntas de (i) qual é o uso médio ideal e (ii) qual é o uso máximo por bloco seguro, para cada recurso. Em vez de definir os preços do gás com base nos máximos por bloco e deixar o uso médio seguir, nós temos 2𝑛 graus de liberdade para definir 2𝑛 parâmetros, ajustando cada um com base no que é seguro para a rede. Situações mais complicadas, como aquelas em que dois recursos têm considerações de segurança parcialmente aditivas, poderiam ser tratadas fazendo com que um opcode ou custo de recurso consumisse alguma quantidade de múltiplos tipos de gás (por exemplo, um SSTORE de zero para não zero poderia custar 5000 gás de prova para cliente sem estado e 20000 gás de expansão de armazenamento). Deixe 𝑥1 ser o custo de gás de dados e 𝑥2 ser o custo de gás da computação, então em um sistema de gás unidimensional podemos escrever o custo de gás de uma transação: gás=𝑥1∗𝑑𝑎𝑡𝑎+𝑥2∗𝑐𝑜𝑚𝑝𝑢𝑡𝑎𝑡𝑖𝑜𝑛 Neste esquema, definimos em vez disso o custo de gás de uma transação como: gás=máx(x1*dados,x2*computação) Isto é, em vez de uma transação ser cobrada por dados mais computação, a transação é cobrada com base em qual dos dois recursos consome mais. Isso pode ser facilmente estendido para cobrir mais dimensões (por exemplo, 𝑚𝑎𝑥(…,𝑥3∗𝑠𝑡𝑜𝑟𝑎𝑔𝑒_𝑎𝑐𝑐𝑒𝑠𝑠) ). Deve ser fácil ver como isso melhora a taxa de transferência enquanto preserva a segurança. A quantidade máxima teórica de dados em um bloco ainda é 𝐺𝐴𝑆𝐿𝐼𝑀𝐼𝑇𝑥1 , exatamente o mesmo que no esquema de gás unidimensional. Da mesma forma, a quantidade máxima teórica de computação é 𝐺𝐴𝑆𝐿𝐼𝑀𝐼𝑇𝑥2 , novamente exatamente o mesmo que no esquema de gás unidimensional. No entanto, o custo de gás de qualquer transação que consuma tanto dados quanto computação diminui. Esta é aproximadamente o esquema empregado no proposto EIP-7623, para reduzir o tamanho máximo do bloco e, ao mesmo tempo, aumentar ainda mais a contagem de blobs. O mecanismo preciso no EIP-7623 é um pouco mais complicado: ele mantém o preço atual do calldata de 16 gás por byte, mas adiciona um "preço mínimo" de 48 gás por byte; uma transação paga o maior de (16 bytes + execution_gas) e (48 bytes). Como resultado, o EIP-7623 diminui o calldata máximo teórico da transação em um bloco de ~1,9 MB para ~0,6 MB, mantendo os custos da maioria das aplicações inalterados. O benefício desta abordagem é que é uma mudança muito pequena em relação ao esquema de gás unidimensional atual e, portanto, é muito fácil de implementar. Há duas desvantagens: Eu argumentaria que uma regra no estilo EIP-7623, tanto para dados de transação quanto para outros recursos, pode trazer benefícios grandes o suficiente para valer a pena, mesmo apesar dessas desvantagens. No entanto, se e quando estivermos dispostos a investir um esforço de desenvolvimento (significativamente maior), existe uma abordagem mais ideal. Vamos primeiro recapitular como funciona o EIP-1559 "regular". Vamos nos concentrar na versão que foi introduzida no EIP-4844 para blobs, porque é matematicamente mais elegante. Nós rastreamos um parâmetro, excess_blobs. Durante cada bloco, nós definimos: excess_blobs <— max(excess_blobs + len(block.blobs) - TARGET, 0) Onde TARGET = 3. Ou seja, se um bloco tiver mais blobs do que o alvo, excess_blobs aumenta e, se um bloco tiver menos do que o alvo, ele diminui. Em seguida, definimos blob_basefee = exp(excess_blobs / 25.47), onde exp é uma aproximação da função exponencial 𝑒𝑥𝑝(𝑥)=2.71828𝑥 . Isto é, sempre que excess_blobs aumenta em cerca de ~25, a taxa base do blob aumenta por um fator de ~2.7. Se os blobs ficarem muito caros, o uso médio diminui e o excess_blobs começa a diminuir, fazendo com que o preço caia novamente automaticamente. O preço de um blob se ajusta constantemente para garantir que, em média, os blocos estejam meio cheios - ou seja, que contenham em média 3 blobs cada. Se houver um pico de uso a curto prazo, então o limite é acionado: cada bloco pode conter no máximo 6 blobs e, nesse caso, as transações podem competir entre si, aumentando suas taxas de prioridade. No caso normal, no entanto, cada blob só precisa pagar a blob_basefee mais uma pequena taxa de prioridade extra como incentivo para ser incluído. Esse tipo de precificação existia no Ethereum para gás há anos: um mecanismo muito semelhante foi introduzido com @vbuterin/eip-1559-faq">EIP-1559 de volta em 2020. Com o EIP-4844, agora temos dois preços separados flutuantes para gás e para blobs. Taxa base de gás ao longo de uma hora em 08-05-2024, em gwei. Fonte: ultrasound.money. Em princípio, poderíamos adicionar mais taxas separadas para leitura de armazenamento e outros tipos de operações, embora com uma ressalva que vou expandir na próxima seção. Para os usuários, a experiência é notavelmente semelhante à atual: em vez de pagar uma taxa básica, você paga duas taxas básicas, mas sua carteira pode abstrair isso de você e apenas mostrar a taxa esperada e a taxa máxima que você pode esperar pagar. Para os construtores de blocos, na maioria das vezes, a estratégia ideal é a mesma de hoje: incluir qualquer coisa que seja válida. A maioria dos blocos não está cheia - nem em gásnemem manchas. O caso desafiador é quando há gás suficiente ou bastantes blobs para exceder o limite do bloco, e o construtor precisa potencialmente resolver um problema da mochila multidimensionalpara maximizar seu lucro. No entanto, mesmo que existam algoritmos de aproximação bastante bons, os ganhos de criar algoritmos proprietários para otimizar lucros neste caso são muito menores do que os ganhos de fazer o mesmo com MEV. Para os desenvolvedores, o principal desafio é a necessidade de redesenhar recursos do EVM e sua infraestrutura circundante, que atualmente é projetada em torno de um preço e um limite, em um design que acomode múltiplos preços e múltiplos limites. Um problema para os desenvolvedores de aplicativos é que a otimização se torna ligeiramente mais difícil: em alguns casos, você não pode mais dizer inequivocamente que A é mais eficiente que B, porque se A usar mais calldata e B usar mais execução, então A pode ser mais barato quando calldata é barato e mais caro quando calldata é caro. No entanto, os desenvolvedores ainda seriam capazes de obter resultados razoavelmente bons otimizando com base nos preços médios históricos de longo prazo. Há um problema que não apareceu com blobs, e não aparecerá com EIP-7623 ou mesmo com uma implementação de precificação multidimensional 'completa' para calldata, mas aparecerá se tentarmos precificar separadamente os acessos ao estado, ou qualquer outro recurso: limites de gás em chamadas subsidiárias. Os limites de gás na EVM existem em dois lugares. Primeiro, cada transação define um limite de gás, que limita a quantidade total de gás que pode ser usada nessa transação. Em segundo lugar, quando um contrato chama outro contrato, a chamada pode definir seu próprio limite de gás. Isso permite que contratos chamem outros contratos nos quais não confiam e ainda garantam que terão gás suficiente para realizar outras computações após essa chamada. Um rastro de uma transação de abstração de conta, onde uma conta chama outra conta e dá ao chamado uma quantidade limitada de gás, para garantir que a chamada externa possa continuar mesmo que o chamado consuma todo o gás que lhe foi atribuído. O desafio é: tornar o gás multidimensional entre diferentes tipos de execução parece que exigiria sub-chamadas para fornecer múltiplos limites para cada tipo de gás, o que exigiria uma mudança realmente profunda no EVM e não seria compatível com as aplicações existentes. Esta é uma das razões pelas quais propostas de gás multidimensionais frequentemente param em duas dimensões: dados e execução. Os dados (seja calldata de transação ou blobs) são apenas atribuídos fora do EVM e, portanto, nada dentro do EVM precisa mudar para tornar calldata ou blobs com preços separados. Podemos pensar em uma solução estilo “EIP-7623” para este problema. Aqui está uma implementação simples: durante a execução, cobrar 4x mais pelas operações de armazenamento; para simplificar a análise, digamos 10000 gás por operação de armazenamento. No final da transação, reembolsar min(7500 * operações_de_armazenamento, gás_de_execução). O resultado seria que, após subtrair o reembolso, um usuário é cobrado: execução_gás + 10000 armazenamento_operacional - min(7500 operacoes_de_armazenamento, gas_de_execucao) Que equivale a: max(execution_gás + 2500operações de armazenamento, 10000storage_operations) Isso espelha a estrutura do EIP-7623. Outra maneira de fazer é rastrear as operações de armazenamento e o gás de execução em tempo real e cobrar 2500 ou 10000, dependendo de quanto max(execução_gas + 2500operações de armazenamento, 10000storage_operations) aumenta no momento em que o opcode é chamado. Isso evita a necessidade de transações alocarem gás em excesso, pois a maior parte será devolvida por meio de reembolsos. Não obtemos permissão detalhada para subchamadas: uma subchamada poderia consumir toda a 'permissão' de uma transação para operações baratas de armazenamento. Mas obtemos algo suficientemente bom, onde um contrato que faz uma subchamada pode definir um limite e garantir que, uma vez que a subchamada termine de executar, a chamada principal ainda tenha gás suficiente para fazer qualquer pós-processamento necessário. A solução de precificação multidimensional completa mais fácil que consigo pensar é: tratamos os limites de gás de sub-chamada como sendo proporcionais. Ou seja, suponha que haja 𝑘 diferentes tipos de execução e cada transação define um limite multidimensional 𝐿1…𝐿𝑘 Suponha que, no ponto atual da execução, o gás restante seja 𝑔1…𝑔𝑘 Suponha que um opcode CALL seja chamado, com limite de gás de chamada sub 𝑆 . Deixe 𝑠1=𝑆 , e então 𝑠2=𝑠1𝑔1∗𝑔2 , 𝑠3=𝑠1𝑔1∗𝑔3 , e assim por diante. Isto é, tratamos o primeiro tipo de gás (realisticamente, execução VM) como sendo uma espécie de “unidade de conta” privilegiada e, em seguida, atribuímos os outros tipos de gás de modo que a sub-chamada obtenha a mesma porcentagem de gás disponível em cada tipo. Isso é um pouco feio, mas maximiza a compatibilidade com versões anteriores. Se quisermos tornar o esquema mais “neutro” entre diferentes tipos de gás, ao custo de sacrificar a compatibilidade com versões anteriores, poderíamos simplesmente fazer com que o parâmetro de limite de gás da sub-chamada represente uma fração (por exemplo, [1…63] / 64) do gás restante no contexto atual). Em qualquer caso, no entanto, vale ressaltar que, uma vez que você comece a introduzir gás de execução multidimensional, o nível inerente de feiura aumenta, e isso parece difícil de evitar. Portanto, nossa tarefa é fazer um trade-off complicado: aceitamos um pouco mais de feiura no nível do EVM, a fim de desbloquear com segurança ganhos significativos de escalabilidade L1, e, se sim, qual proposta específica funciona melhor para a economia do protocolo e os desenvolvedores de aplicativos? Muito provavelmente, não é nenhuma das que mencionei acima, e ainda há espaço para pensar em algo mais elegante e melhor.Blobs: gás multidimensional em Dencun
Clientes sem estado e gás multidimensional
Gás multidimensional de forma mais geral
Máximo por transação: a maneira mais fraca, mas mais fácil de obter gás multidimensional
Multidimensional EIP-1559: a estratégia mais difícil, mas ideal
Preços multidimensionais, o EVM e chamadas secundárias
Aviso legal: