Autor: Giulio Rebuffo
Introdução
A especificação da API SSZ Engine propõe a substituição da codificação JSON-RPC por SSZ (Simple Serialize) binário para a API Ethereum Engine – a interface entre os clientes da camada de consenso (CL) e da camada de execução (EL).
Hoje, cada mensagem entre CL e EL é codificada em JSON: cada byte de cada transação, blob e retirada é codificado em hexadecimal e empacotado em JSON. Para pequenas cargas como forkchoiceUpdatedestá tudo bem. Para engine_getPayloadV5 – que retorna o completo ExecutionPayload mais BlobsBundle — a sobrecarga de codificação torna-se um contribuinte significativo para bloquear a latência de propagação.
Transporte
Ambos os transportes coexistem na mesma porta Engine API (padrão 8551):
| Transporte | Tipo de conteúdo | Ponto final |
|---|---|---|
| JSON-RPC | application/json |
POST / |
| RESTO SSZ | application/octet-stream |
POST /engine/v{N}/{resource} |
Com o PeerDAS e o fork Fulu aumentando a contagem de blobs de 6 para 72 (alvo 48), os tamanhos de carga útil por meio da API do mecanismo crescerão significativamente. Esta postagem apresenta benchmarks de codificação de um devnet Kurtosis ativo executando três implementações EL com instrumentação de transporte SSZ.
Metodologia
Configuração do desenvolvedor
Um devnet Kurtosis foi implantado com 12 pares EL+CL — cada combinação de três clientes EL (Geth, Erigon, Nethermind) e quatro clientes CL (Prysm, Lighthouse, Teku, Lodestar) — todos executando imagens Docker personalizadas com suporte de transporte SSZ e instrumentação de codificação.
Parâmetros de rede: slots de 6 segundos, garfo Fulu na gênese, 72 bolhas no máximo, 48 bolhas alvo32 validadores por nó. Carga de transação gerada com spamoor a 200 EOA tx/s mais transações blob (6 sidecars cada).
Medição
Cada EL foi instrumentado para codificar cada GetPayloadV5 resposta em ambos SSZ e JSON, registrando o tamanho e a hora de cada um:
GetPayloadV5 encoding: SSZ=9,930,470 bytes in 19,700 us | JSON=19,906,846 bytes in 59,531 us | ratio=2.0x size, 3.0x time blobs=72
Resultados
getPayload Tempo de codificação em 72 Blobs (Fulu Max)
Piores tempos de codificação observados por par EL+CL em 72 blobs (~9,9 MB SSZ / ~19,9 MB JSON) após 17 horas de operação contínua devnet:
| CL\EL | Mente Infernal | Geth | Erígon |
|---|---|---|---|
| Prisma | SSZ 5 ms / JSON 103 ms (20,6×) | SSZ 19 ms / JSON 180 ms (9,3×) | SSZ 15 ms / JSON 63 ms (4,2×) |
| Farol | SSZ 8 ms / JSON 114 ms (14,3×) | SSZ 24 ms / JSON 181 ms (7,5×) | SSZ 6 ms / JSON 82 ms (12,7×) |
| Teku | SSZ 14 ms / JSON 120 ms (8,6×) | SSZ 28 ms / JSON 358 ms (12,8×) | SSZ 13 ms / JSON 81 ms (6,1×) |
| Estrela Polar | SSZ 8 ms / JSON 91 ms (11,4×) | SSZ 36 ms / JSON 446 ms (12,3×) | SSZ 31 ms / JSON 101 ms (3,3×) |
Essas são medições do pior caso (tempos de codificação observados mais altos) em uma execução de 17 horas – os tempos típicos do mundo real são mais baixos, mas a latência do pior caso é o que importa para os prazos de propagação de blocos.
Principais observações:
- A codificação SSZ permanece abaixo de 36 ms em todos os 12 pares no pior caso – consistentemente rápida, independentemente da implementação.
- A codificação JSON de pior caso varia de 63 ms a 446 ms – altamente variável entre implementações EL.
- Geth tem a maior sobrecarga JSON (180–446 ms), tornando a aceleração do SSZ mais dramática (7,5–12,8×).
- A codificação JSON do pior caso do Nethermind varia de 91 a 120 ms, com SSZ 8,6 a 20,6 × mais rápido.
- Erigon fica no meio (63–101 ms JSON), com aceleração SSZ de 3,3–12,7×.
Tamanho do fio
Consistentemente em todas as cargas que transportam blobs:
| Carga útil | Tamanho SSZ | Tamanho JSON | Razão |
|---|---|---|---|
| Bloco de 72 bolhas | ~9,9MB | ~19,9MB | 2,0× |
| Bloco de 24 bolhas | ~3,3MB | ~6,6 MB | 2,0× |
| Bloco de 6 bolhas | ~837KB | ~1,68MB | 2,0× |
A proporção 2× é estrutural: a codificação hexadecimal JSON dobra cada byte (0xff → "0xff" = 4 caracteres).
Outros métodos de API do mecanismo
Nem todas as chamadas da API do mecanismo se beneficiam igualmente do SSZ. Os três métodos chamados de cada slot são forkchoiceUpdated, newPayloade getPayload. Aqui está como eles se comparam em termos de tamanhos de solicitação/resposta e tempos de codificação (todas as medidas do par Erigon + Prysm):
Tamanhos de mensagens:
| Método | Direção | Tamanho SSZ | Tamanho JSON |
|---|---|---|---|
forkchoiceUpdated |
Solicitação CL → EL | 100–200 bilhões | 300–600 a.B. |
forkchoiceUpdated |
Resposta EL → CL | 49-57B | ~200 bilhões |
newPayload |
Solicitação CL → EL | 603 B – 46 KB | 1,4KB – 92KB |
getPayload |
Resposta EL → CL | 837 KB – 9,9 MB | 1,68 MB – 19,9 MB |
Tempos de codificação:
| Método | Hora SSZ | Hora JSON |
|---|---|---|
forkchoiceUpdated |
<1 µs | <1 µs |
newPayload |
15–45 µs | 60–4.978 µs |
getPayload |
1,1–25,6ms | 16–211ms |
forkchoiceUpdated é minúsculo em ambas as direções – menos de 200 bytes. A diferença de sobrecarga entre SSZ e JSON é insignificante nesta escala.
newPayload carrega o ExecutionPayload (transações, saques), mas não blobs – aqueles que se propagam através da camada de fofoca. Mesmo em um bloco mainnet de alto rendimento (cerca de 1.500 transações), newPayload teria aproximadamente 200–400 KB em SSZ. Nesses tamanhos, a sobrecarga de codificação é bem inferior a 1 ms, independentemente do formato.
getPayload é o discrepante. Ele retorna o completo ExecutionPayload mais o BlobsBundle (compromissos, provas e todos os dados do blob). Com 72 blobs, esta resposta única é 9,9 MB em SSZ (19,9 MB em JSON) — ordens de magnitude maiores do que qualquer outra mensagem da API do mecanismo. É aqui que a sobrecarga de codificação se torna mensurável em milissegundos e é por isso que a especificação de transporte SSZ concentra seu impacto aqui.
Conclusão
A API do mecanismo getPayload resposta é a maior mensagem trocada entre CL e EL em cada slot. Com 72 blobs, ele carrega aproximadamente 9,9 MB de dados em SSZ (19,9 MB em JSON) e essa única etapa de codificação pode levar de 63 ms a 446 ms em JSON, dependendo do cliente, consumindo diretamente o orçamento de propagação de bloco.
A codificação SSZ da mesma carga leva de 5 a 36 ms em todos os 12 pares EL + CL testados. A aceleração varia de 3× a 20× dependendo do par, com o tempo SSZ do pior caso ainda bem abaixo do tempo JSON do melhor caso para a maioria das implementações.
A redução do tamanho do fio é consistente em 2× em todos os tamanhos de carga útil – uma consequência estrutural da eliminação da codificação hexadecimal. Para um bloco de 72 blobs, são aproximadamente 10 MB salvos por slot no link CL↔EL.
O custo de adoção é baixo: ambos os transportes coexistem na mesma porta, JSON-RPC continua sendo o padrão e SSZ é opcional por meio de negociação de tipo de conteúdo. Já existem implementações para Geth, Erigon, Nethermind, Prysm, Lighthouse, Teku e Lodestar.
Reprodução
Configuração de devnet de curtose
participants:
# 3 ELs × 4 CLs = 12 pairs
- el_type: geth
cl_type: prysm
supernode: true
validator_count: 32
- el_type: geth
cl_type: lodestar
supernode: true
validator_count: 32
- el_type: geth
cl_type: teku
supernode: true
validator_count: 32
- el_type: geth
cl_type: lighthouse
supernode: true
validator_count: 32
- el_type: erigon
cl_type: prysm
supernode: true
validator_count: 32
- el_type: erigon
cl_type: lodestar
supernode: true
validator_count: 32
- el_type: erigon
cl_type: teku
supernode: true
validator_count: 32
- el_type: erigon
cl_type: lighthouse
supernode: true
validator_count: 32
- el_type: nethermind
cl_type: prysm
supernode: true
validator_count: 32
- el_type: nethermind
cl_type: lodestar
supernode: true
validator_count: 32
- el_type: nethermind
cl_type: teku
supernode: true
validator_count: 32
- el_type: nethermind
cl_type: lighthouse
supernode: true
validator_count: 32
network_params:
seconds_per_slot: 6
fulu_fork_epoch: 0
bpo_1_max_blobs: 72
bpo_1_target_blobs: 48
additional_services:
- spamoor
spamoor_params:
spammers:
- scenario: eoatx
config:
throughput: 200
max_pending: 400
max_wallets: 200
- scenario: blob-combined
config:
throughput: 40
max_pending: 80
sidecars: 6
kurtosis run github.com/ethpandaops/ethereum-package --args-file ssz_bench_net.yml
Implementações
Fontesethresear


