Arquitetura
O companheiro de engenharia do whitepaper — como o nó de referência realiza o projeto, e as decisões por trás dele. Explicativo, não normativo.
O protocolo são os arquivos de parâmetros e os vetores dourados, mais as regras nomeadas onde quer que estejam definidas; a especificação de fio carrega os requisitos da camada de pares. Esta página explica essa superfície e registra as decisões por trás dela. Onde ela divergir da superfície normativa, a superfície normativa vence e este texto é corrigido. O companheiro de engenharia completo é docs/ARCHITECTURE.md.
Dois predicados, dois motores#
A vida de um certificado, de ponta a ponta:
wallet network every full node
+------------+ gossip +--------------+ +---------------------+
| build cert | ---------> | cert topic | ------------> | STATELESS PIPELINE |
| (reads, | | (TLS gossip) | | V1..V8, batch sigs, |
| writes, | +--------------+ | native re-exec |
| sigs, seq, | | -> mark VALID |
| deposit) | +----------+----------+
+------------+ | mempool
v
miner (any node) +---------------------+
+-------------------+ block | FOLD (sequential) |
| assemble ordered | --------> | F-rules per cert: |
| hash list + bodies| gossip | APPLY / SKIP / DROP |
| + RandomX solve | | -> new state |
+-------------------+ +---------------------+
A validade é sem estado e paralela: roda uma vez por certificado por nó, antes dos blocos e independentemente deles. A aplicabilidade é com estado e sequencial: roda dentro do fold na posição comprometida do certificado. O minerador não executa nada; ele ordena hashes que já viu validados e resolve proof of work. O fold é um laço apertado sobre um conjunto de trabalho em memória — comparar, somar, escrever.
Princípios de engenharia#
| Princípio | |
|---|---|
| P1 | O fold é sagrado. A função de transição de estado vive num único pacote puro, sem E/S, sem relógios, sem goroutines, sem iteração de mapas e sem ponto flutuante. É o único código cujos bugs são impossíveis de corrigir depois do fato. Todo o resto no nó é encanamento substituível. |
| P2 | Determinismo vence desempenho. Qualquer otimização que arrisque não determinismo é rejeitada em código de consenso. O desempenho pertence ao pipeline sem estado, onde é seguro. |
| P3 | Sem chaves de administração, sem RPC privilegiada. Não existe caminho de código pelo qual qualquer chave possa pausar, atualizar, emitir ou reorganizar. Se não está nas regras do fold, não existe. |
| P4 | Superfície de consenso pequena. core/ não importa nada fora da biblioteca padrão. O resto do nó pode usar bibliotecas do ecossistema; o núcleo não pode. |
| P5 | Especificação primeiro. Os vetores de referência são o protocolo. O código Go é uma implementação de referência; uma implementação independente que passa nos vetores é um par, não um fork. É isso que permite que o mantenedor venha a ser ninguém em particular. |
| P6 | Reproduzível desde a v0.1. Toolchain do Go fixado, -trimpath, dependências fixadas. A confiança se desloca do binário para o código, que é a única confiança que um autor anônimo pode oferecer. |
| P7 | Um binário só, liberado por altura. A máquina, as operações de caução e o registro de sequenciadores são compilados em cada release e recusados abaixo da sua altura de ativação — Validate exige h1_vm ≥ h1_bond e exige que h1_vm caia num limite de época. Uma era chega porque a cadeia alcançou um número, nunca porque pediram aos operadores que atualizassem. |
P3 é o princípio que um leitor cético deve pressionar com mais força, e a tesouraria é onde pressioná-lo: a gênese não contém chave alguma nem caminho de gasto algum, então não há privilégio a deter, delegar ou roubar. O quórum de 3 de 5 da Era 2 é fixado por um hard fork futuro — o mesmo mecanismo social de qualquer outra mudança de consenso, sujeito à mesma recusa, e capaz de mover exatamente uma célula mesmo então. Um quórum que só aparece se a rede concordar em inscrevê-lo, e que então detém uma célula, é uma regra de gasto. Uma chave de administração é uma que existe antes de alguém consentir e alcança tudo.
Primitivas criptográficas#
| Papel | Escolha | Por quê |
|---|---|---|
| Assinaturas | Ed25519 | Verificação em lote (a rampa para GPU), sem maleabilidade, chaves minúsculas. Regras estritas fixadas na gênese: codificações canônicas exigidas para a chave pública e R, a chave pública livre de torção e não de ordem baixa, e verificação sem cofator. |
| Hashing | BLAKE3 | Ids de certificado e de bloco, endereços, raiz de estado. Rápido o bastante para fazer hash na velocidade de linha da retransmissão; amigável a paralelismo para a raiz de estado da época. |
| Proof of work | RandomX | Otimizado para CPU. O único cgo na árvore, atrás de uma build tag, e ausente de um build sem ela. pow_engine está na raiz de consenso, então um binário com a engine errada se recusa a iniciar em vez de aceitar a prova errada. |
| Separação de domínio | obrigatória | Todo hash é blake3(tag ‖ payload). As assinaturas assinam sobre o chain id e a raiz de consenso, o que mata o replay tanto entre redes quanto entre duas encarnações de uma mesma rede. |
A rejeição de torção é o que torna o caminho em lote seguro. Uma chave de ordem mista não é de ordem pequena, então nenhuma lista de bloqueio a alcança, e é exatamente onde um verificador em lote com cofator e um verificador único sem cofator divergem. Com a chave e o R no subgrupo de ordem prima os dois são comprovadamente equivalentes — então um verificador em lote pode usar cofator desde que aplique as mesmas regras de codificação e de torção antes de agrupar em lote. Essa obrigação é o preço da escolha, e o verificador em lote ainda não existe.
Codificação canônica e identificadores#
Todos os objetos de consenso são contêineres SSZ: uma única codificação canônica de bytes, sem ordenação de mapas, sem ambiguidade de campos opcionais, deslocamentos fixos para parsing parcial barato, e merkleização nativa.
cert_id = blake3("zcd/certid/v1" || ssz(certificate with an empty signature list))
cert_exemplar = blake3("zcd/cert/v1" || ssz(certificate))
block_id = blake3("zcd/block/v1" || ssz(header))
Os dois primeiros são digests diferentes respondendo a perguntas diferentes, e uma implementação que use um onde o outro cabe está monetariamente quebrada. O id responde esta autorização já foi cobrada; o hash do exemplar responde estes bytes provam isso. Os dois nunca compartilham uma chave.
As assinaturas ficam fora da preimagem do id porque uma assinatura é uma demonstração aleatorizada: o assinante escolhe o nonce, cada nonce produz outra assinatura válida e perfeitamente canônica sobre o mesmo corpo, e nenhum verificador pode checar qual foi usado. Se estivessem dentro, uma autorização teria ilimitadamente muitos ids, cada um cobrável, cada um produzível por qualquer um dos seus assinantes exigidos à revelia da autoridade dos demais.
Um lance fora do id seria um lance que qualquer um em trânsito poderia reescrever — inflado para queimar o saldo do assinante pela taxa base, ou zerado para manter o certificado fora de todo bloco. O que um certificado paga faz parte do que seu assinante autorizou, então isso entra no hash e é assinado. Uma mudança de codificação posterior que "tire a taxa do corpo assinado por flexibilidade de retransmissão" pareceria uma otimização e seria um vetor de roubo.
A regra que se segue: faça o parse, não valide duas vezes. A decodificação impõe a forma canônica, então um objeto decodificado é estruturalmente válido por construção e os motores de regras nunca rechecam a forma.
O modelo de células#
Uma célula é o valor em um slot. Os valores de célula são inteiros de 256 bits sem sinal armazenados em big-endian em 32 bytes. Células ausentes leem como zero — zero é ausência, o que é um requisito de consenso e não uma conveniência de implementação: isso mantém a raiz de estado uma função do estado em vez do histórico que o produziu.
Separadamente, o protocolo mantém um registro de endereços gastos: um conjunto de consenso permanente de endereços de uso único cuja autoridade de assinatura foi queimada.
Addr = version || blake3("zcd/addr/v1" || version || payload)[:31]
| Versão | Tipo | Autorização de débito |
|---|---|---|
0x01 | usuário de uso único | Assinatura do dono; todo certificado que debite precisa carregar também um MARK_SPENT explícito. Depois que ele se aplica, toda leitura e escrita sob o endereço falha para sempre. |
0x02 | usuário persistente | Assinatura do dono; reutilizável para sempre. Nunca pode entrar no registro de gastos. |
0x03 | ativo | Governado pelas células de autoridade imutáveis do ativo. |
0x00 | protocolo | Somente pelo fold: beacon de época, células de taxa base, anel de maturidade do coinbase, célula da tesouraria. |
0x04 | reservado — célula de valor oculto (Era S) | Inalcançável na Era 0. |
O 0x04 é reservado agora em vez de alocado depois, porque um valor oculto
precisa ser distinguível de um comum pelo seu endereço: um compromisso de Pedersen
e um saldo de 256 bits têm ambos 32 bytes, então um delta com guarda apontado por erro ou malícia para um
slot de compromisso faria o fold somar um inteiro a uma codificação de ponto de curva — aritmética
que passa por toda verificação e deixa uma célula que ninguém jamais poderá gastar. Esta tabela é congelada na gênese,
então o byte é reivindicado aqui e deixado inalcançável.
A entrada do registro nunca é compactada. Os valores de célula sob um endereço gasto podem ser podados após o horizonte de desfazer, mas a entrada que registra o endereço como gasto é o que impede que ele seja ressuscitado. É estado de consenso somente-anexação, cerca de 33 bytes por endereço, para sempre — o problema em aberto que o protocolo assume honestamente, compartilhado estruturalmente com todo design de conjunto de nullifiers.
Validade sem estado, e a lei de cobrança#
As regras V rodam sobre cada certificado, em paralelo, antes da admissão no mempool e durante a verificação de blocos, e não exigem estado algum: forma canônica e chain id; cada assinatura verificando sobre a raiz de assinatura; autorização derivável somente do certificado; as leituras declaradas iguais ao que o programa deriva; e o destino do reembolso conferido contra o que o próprio certificado queima.
A lei de cobrança do sistema é uma frase, e é a coisa a que se agarrar:
Uma assinatura, no máximo uma cobrança, nunca em uma posição que seu assinante não pudesse evitar.
Esta especificação acrescenta um termo ao vocabulário do whitepaper: descarte, um não-evento sem cobrança. Um certificado que chega à aplicação com seu depósito já consumido é descartado — sem cobrança, sem ser marcado como visto, livre para reenvio contra um depósito novo — então usuários honestos não perdem nada em disputas sobre sua própria célula de depósito.
Organização do repositório#
zycord/
spec/ parameter sets, golden vectors, library images <- THE PROTOCOL
core/ consensus-critical; standard library only (P4)
types/ crypto/ ssz/ u256/ state/ validity/ fold/ params/ genesis/
cevm/ the certificate-adapted EVM; vendored interpreter, pure Go
stdlib/ the pre-deployed library, its addresses and code hashes
pow/randomx/ the mainnet engine: vendored C++, cgo, behind a build tag
node/ storage/ chain/ verify/ mempool/ miner/ p2p/ sync/ rpc/ stratum/
wallet/ key management, certificate builders (reference; not consensus)
contracts/ the reference contracts, in Solidity
sim/ simulator, fuzz harnesses, differential refold, chaos soak
cmd/ zycordd, zcd
desktop/ the wallet in a native window — a separate Go module
docs/ architecture, protocol, operating guide, whitepaper
As setas de dependência apontam apenas para dentro — node → core,
wallet → core, nunca o inverso — e nada dentro de core/
alcança fora de core/ e da biblioteca padrão. Uma exceção, nomeada e imposta:
core/pow/randomx é o único cgo na árvore, e compila apenas sob a build
tag, então toda build sem a tag continua sendo apenas biblioteca padrão e não precisa de toolchain C. A CI
roda a verificação que impõe isso, porque a verificação de terceiros faz grep em caminhos de módulo e cgo não
tem nenhum — o que deixou a regra imposta por ninguém até que ela fosse acrescentada.
Como é testado#
- Vetores de referência. Toda regra de fold, de bloco e de validade tem casos positivos e negativos na forma
(pre-state, block) → (post-state | invalid, outcomes, fees). A suíte é o contrato de compatibilidade para implementações independentes. - A suíte de assédio. Reinclusão de certificados aplicados e omitidos, inclusão expirada, inclusão com lance abaixo, cadeias dependentes sob embaralhamento do proponente, ciclos de queima e reembolso, tempestades de crédito de terceiros, disputas na fronteira do teto de emissão.
- Baseados em propriedades. Determinismo do fold sob permutação da ordem do proponente, comutatividade de deltas, tolerância a ABA, e conservação — incluindo depósitos em trânsito, o anel de maturidade e a célula da tesouraria, já que uma verificação de conservação que omita a tesouraria reporta todo bloco como criador de valor.
- Diferenciais. Uma segunda implementação do fold, deliberadamente ingênua, escrita para ser óbvia em vez de rápida, submetida a fuzzing contra a real. Divergência é bloqueadora de release.
- Simulação adversarial. Tempestades de omissão, disputas por drenagem de depósito, mineradores que entupem com descartes, tortura por reorganização, manipulação de timestamp, cenários de retransmissão eclipse-lite. As configurações de cenário são versionadas; as execuções são reproduzíveis por seed.
- Concorrência, deliberadamente. Todo componente que mais de uma goroutine toca tem um teste que é concorrente, no formato que o processo realmente usa.
-racenuma suíte de uma única goroutine não mede nada e reporta sucesso. - O teste de imersão sob caos. Processos de nó reais sobre sockets reais atrás de um proxy injetando latência, jitter, perda e partições, com nós mortos por
SIGKILLao acaso. Esta é a superfície que encontrou a corrida de dados que toda a suíte-racedeixou passar.
A gênese é um artefato, não uma cerimônia#
O zcd genesis emite o bloco de gênese — estado vazio, células de beacon inicializadas,
registro de gastos vazio, sem alocações de qualquer tipo — e seu id. O lançamento
anunciado se compromete, semanas antes, com a tag do código, o hash dos parâmetros, o id de gênese, e o
horário de lançamento. Qualquer um pode reconstruir os quatro em milissegundos, a partir do código-fonte, em qualquer máquina.
Não há mais nada em que confiar.