tl; dr: Divida um bloco El (= carga útil) em vários mini -bloqueios (“pedaços”) Do orçamento fixo de gás (por exemplo 2**24 = 16.77M
) que se propaga independentemente como carros laterais. Cada parcela carrega o pré -estado necessário para executar sem estado e se compromete com seu diferencial pós -estado. Pedaços são ordenados, mas pode ser executado totalmente independentemente em paralelo. CL se compromete com o conjunto de cabeçalhos de pedaços; Sidecars carregam corpos e provas de inclusão.
A validação se torna mais um fluxo contínuo.
Motivação
Hoje, os blocos são objetos grandes e monolíticos que se tornarão ainda maiores no futuro. A validação requer que o recebimento do bloco antes que a execução possa começar. Isso cria gargalos de latência na propagação e execução de blocos.
Depois que o bloco é recebido pela rede P2P, as transações são executadas sequencialmente. Não podemos começar a validar durante o download ou a execução paralela.
As mensagens na camada P2P geralmente são compactadas usando o Snappy. O formato de bloco de Snappy que é usado no Ethereum não pode ser transmitido. Assim, precisamos cortar o bloco em pedaços antes compressão.
Com as listas de acesso ao nível de bloco EIP-7928: a situação melhora, mas ainda estamos aguardando o download terminar antes de iniciar a validação do bloco. Com 4 núcleos, obtemos o seguinte gráfico de Gantt:
Em vez disso, podemos stream blocos como pedaços:
- Cada pedaço contém ≤
2**24
gás de transações.- Também se pode ter o tamanho do pedaço aumentando geometricamente (
2**22
Assim,2**23
…,2**25
) em gás. Isso nos daria latências variadas para pedaços, permitindo uma melhor paralelização – mas não tenho certeza de que valeria a complexidade.
- Também se pode ter o tamanho do pedaço aumentando geometricamente (
- Transações permanecer ordenado. Os pedaços são indexados e ordenados, mas independente um do outro, para que eles possam ser validados em paralelo. Ainda assim, o pós-estado de Chunk 0 é o pré-estado do pedaço 1.
- (opcional) Cada pedaço carrega o estado em que precisa ser executado sem estado.
Isso muda a validação de “Faça o download do bloco completo e depois processe ”→“ Processe ao receber o restante.”
Alterações da camada de execução
Estendemos o formato El Block para apoiar o Chunking:
class ELHeader:
parent_hash: Hash32
fee_recipient: Address
block_number: uint64
gas_limit: uint64
timestamp: uint64
extra_data: ByteList(MAX_EXTRA_DATA_BYTES)
prev_randao: Bytes32
base_fee_per_gas: uint256
parent_beacon_block_root: Root
blob_gas_used: uint64
excess_blob_gas: uint64
transactions_root: Root
state_root: Root
receipts_root: Root
logs_bloom: Bloom
gas_used: Uint
withdrawals_root: Root
block_access_list_hash: Bytes32
# New fields
chunk_count: int # >= 0
Há não compromisso para os pedaços individuais no cabeçalho El. Nós apenas adicionamos a contagem de pedaços a ele. As saídas de execução (state_root
Assim, logs_bloom
Assim, receipts_root
Assim, gas_used
) deve ser o mesmo que o valor no último pedaço (aplica -se à raiz do estado e nas retiradas da raiz) ou a raiz após agregar os valores do pedaço (aplica -se a transações, recibos, logs, gás usado e lista de acesso ao bloco).
Pedaços de execução
Os pedaços nunca são colocados na cadeia; Apenas suas raízes estão comprometidas.
Os pedaços contêm os campos que normalmente esperávamos no corpo do bloco El. As transações são divididas em pedaços com um limite de 2**24
gás por pedaço. Os saques devem ser incluídos apenas no último pedaço. Listas de acesso ao nível do bloco espelhado, pedaços vêm com sua própria lista de acesso a pedaços e pode-se adicionar adicionalmente valores pré-estadual aos pedaços, desbloqueando a apatridia.
class Chunk:
header: ChunkHeader
transactions: List(Tx)
withdrawals: List(Withdrawal) # only in chunk at index -1
chunk_access_list: List(ChunkAccessList)
pre_state_values: List((Key, Value)) # optional
Cada pedaço vem com um cabeçalho, incluindo o índice de pedaços. As transações são ordenadas por chunk.header.index
e seu índice no pedaço. Os compromissos com a saída de execução de cada pedaço estão incluídos no cabeçalho.
class ChunkHeader:
index: int
txs_root: Root
post_state_root: Root
receipts_root: Root
logs_bloom: Bloom
gas_used: uint64
withdrawals_root: Root
chunk_access_list_root: Root
pre_state_values_root: Root # optional
Para impedir que os proponentes dividam seus blocos em muitos pedaços, o protocolo pode fazer cumprir que os pedaços devem estar pelo menos metade completa (\ geq \ frac {chunk \ _gas \ _limit} {2}) OU chunk.header.index == len(beaconBlock.chunk_roots)
(= Último pedaço naquele bloco).
Alterações na camada de consenso
Beacon blocks rastrear pedaços com novos campos:
class BeaconBlockBody:
...
chunk_roots: List(ChunkRoot, MAX_CHUNKS_PER_BLOCK) # SSZ roots of chunks
class ExecutionPayloadHeader:
...
chunk_count: int
O CL recebe os pedaços de execução do EL por meio de um novo ChunkBundle
contêiner que inclui o cabeçalho El e os pedaços (= semelhante a Blobs).
O CL calcula raízes de pedaços usando o SSZ’s hash_tree_root
e os coloca no corpo do bloqueio do farol.
Design Sidecar
Pedaços são transportados Sidecars:
class ExecutionChunkSidecar:
index: uint64 # chunk index
chunk: ByteList(MAX_CHUNK_SIZE) # Opaque chunk data
signed_block_header: SignedBeaconBlockHeader
chunk_root_inclusion_proof: Vector(Bytes32, PROOF_DEPTH)
A camada de consenso garante chunk_roots
(= semelhante a Blobs).
Networking
O proponente fofoca apenas o bloco de farol leve com compromissos (chunk_count
Assim, chunk_headers_root
) no normal beacon_block
tópico, enquanto os dados de execução pesados são transmitidos separadamente como ExecutionChunkSidecar
s de outro lado X sub -redes paralelas (beacon_chunk_sidecar_{0..X}
), deduzido por (block_root, index)
.
Inicialmente, todos os nós devem assinar todas as sub -redes e custódia de todos os pedaços. Embora isso ainda não reduza os requisitos de largura de banda/armazenamento, ele permite os benefícios imediatos da paralelização. A custódia parcial pode ser adicionada em uma atualização futura quando o mecanismo básico for comprovado e/ou a provação de ZK se tornar viável.
Escolha do garfo
A escolha do garfo exige que todos os sidcars estejam disponíveis e validados com sucesso antes que um bloco seja considerado válido. O bloqueio do farol com o chunk_roots
propaga-se rapidamente, mas o bloco só se torna elegível para a garfo, uma vez que cada pedaço foi recebido e comprovado por inclusão contra a raiz. O bloco Beacon ainda contém o cabeçalho El com todos os compromissos necessários (=comprometendo -se com saídas de bloqueio pai e execução). O que sabíamos como Bloquear o corpo No El fica vazio neste design.
Benefícios
- Validação de streaming: A execução pode iniciar enquanto outras partes do bloco ainda estão baixando ou ocupadas carregando do disco. Os pedaços são independentes (se pré-estatal fornecidos) ou confiam na lista de acesso a pedaços (com diferenças de estado no nível de pedaços) e o pré-bloqueio; Várias CPUs/núcleos podem validar pedaços simultaneamente; Distribua o uso da largura de banda sobre o slot em vez de explosões de início de slot.
- Provando simplificado: O ZK Provers pode paralelizar a comprovação de múltiplos pedaços ao mesmo tempo, beneficiando -se da independência de pedaços.
- Amizade sem estado: Como um único pedaço é menor que um bloco, podemos considerar adicionar valores pré-estados, de modo que não há necessidade de acesso ao estado local. Um meio termo prático é incluir valores pré-estados apenas em pedaços
0
garantindo que pelo menos um pedaço sempre possa ser executado enquanto o nó carrega o estado necessário para outros pedaços do disco em cache. - Extensibilidade futura: Caminho claro para integrar a prova de ZK em pedaços ou optar por execução fragmentada.
Espaço de design
Tamanho do pedaço
2**24
O gás (~ 16,7m) emergiu como um tamanho de pedaço natural:
- Tamanho máximo da transação: A partir de Fusaka (EIP-7825),
2**24
é o tamanho máximo da transação possível. - Blocos atuais: Blocos de gás de 45m se dividem naturalmente em ~ 3 pedaços, proporcionando paralelismo imediato
- Blocos futuros: Escalas bem – 100m a gás blocos teriam ~ 6 pedaços
Validador
- Engine de execução divide o bloco em pedaços internamente (opaco para CL) e os passa para o CL através de um
ExecutionChunkBundle
. - Propositor Envolva cada pedaço em um carro lateral com prova de inclusão. O proponente também calcula a raiz da árvore de hash de cada pedaço e os coloca no corpo do bloqueio do farol.
- Publicação acontece em paralelo em todas as sub -redes
- Atestadores Espere por todos os pedaços e valide -os antes de votar
Construtores
Os proponentes podem publicar pedaços à medida que terminarem, e os validadores podem começar a validá -los mesmo antes de receber o bloco de beacon. Como os pedaços contêm o cabeçalho do bloco de farol assinado e uma prova de inclusão contra ele, pode -se validar (= executar) pedaços quando eles entram, confiando em sua fonte (= propositor).
Perguntas abertas e trabalho futuro
Tamanhos progressivos de pedaços?
A idéia de aumentar geometricamente tamanhos de pedaços (2**22
Assim, 2**23
…, 2**25
) parece benéfico, mas acrescenta complexidade. O primeiro pedaço pode ser menor (5m de gás) com valores pré-estatal para execução imediata, enquanto os pedaços posteriores são maiores. Esta continua sendo uma área para experimentação.
Caminho de custódia parcial
Embora a implementação inicial exija custódia total, a arquitetura suporta naturalmente a custódia parcial:
- Nós poderia custódia apenas Y sub -redes de x
- Os mecanismos de reconstrução (semelhantes ao DAS) podem recuperar pedaços ausentes
Compatível com EPBS e execução tardia
À primeira vista, o design proposto parece compatível com o EIP-7732 e o EIP-7886. Sob EPBS, as raízes de pedaços provavelmente se moveriam para o ExecutionPayloadEnvelope
e colocamos uma raiz adicional sobre as raízes de pedaços no ExecutionPayloadHeader
. O PTC não apenas teria que verificar se uma única carga útil do EL está disponível, mas também que todos os pedaços são. Isso não é muito diferente dos blobs.
As vantagens da escala de bloqueio e validação independente com limites de gás mais altos e podem contribuir para reduzir o pico no consumo de largura de banda do nó.
Fontesethresear