Executando um nó
O guia do operador para o zycordd. O protocolo que ele fala é a especificação de fio; esta página é sobre rodá-lo.
A versão de uma linha#
zycordd --devnet --dir ./devnet
Isso é um nó completo: ele valida cada bloco desde a gênese, serve pares, e responde a um
RPC somente leitura em 127.0.0.1:9420. Para entrar na rede que está de fato rodando, veja
Testnet pública — e note que ela precisa do binário
-randomx.
O que o nó não tem#
Não há material de chave no processo do nó, jamais. Ele não aceita seed, nem
frase secreta, nem arquivo de chave. A assinatura acontece no zcd, e o único endpoint de escrita
do nó — /submit — não concede autoridade alguma: um certificado submetido é
validado exatamente como um que chegasse de um desconhecido.
Também não há endpoint privilegiado, porque não há nada a privilegiar. Nenhuma chave pode pausar, atualizar, censurar ou emitir, então não há chamada a expor.
O limitador se baseia no par de transporte e nunca no X-Forwarded-For: um limitador
que confia em um cabeçalho que o cliente define é um limitador que o cliente desliga. Atrás de um proxy, toda
requisição portanto chega com o endereço do proxy, então o limite por cliente deixa de ser por cliente
e se torna um único balde compartilhado de 600 por minuto para todos ao mesmo tempo — e um único chamador
pode esvaziá-lo para todos eles. Isso é discutivelmente pior que nenhum limite, e nada na
resposta diz isso: os chamadores recebem um 429 simples quer tenham causado o problema, quer tenha sido outro.
A superfície somente leitura#
Qualquer coisa que observe a cadeia de fora — um explorador, um monitor, um indexador — a lê aqui. O nó entrega bytes e nunca interpretação.
| Endpoint | Responde |
|---|---|
/status, /head | Identidade do topo, altura, raiz de estado |
/block?height=N ou ?id=0x… | Um bloco em JSON, com canonical, orphaned e confirmations |
/block?…&format=ssz | O mesmo bloco como bytes SSZ canônicos, application/octet-stream |
/params | O conjunto de parâmetros ativo e sua raiz de consenso |
/cell, /balance | Estado do topo |
/fees | Taxas base, e os tetos elásticos em vigor |
/mempool | Contadores do pool; ?limit=N acrescenta até 1000 ids pendentes, do menor primeiro |
/network, /metrics | Formato das conexões e contadores |
/submit | A única escrita, e ela não concede nada |
O §8.1 do whitepaper faz os tetos de bytes, de gas e de certificados do bloco funções de
T, o alvo sequencial, e T é estado de consenso que o controlador de época move.
O /params portanto carrega apenas os valores de gênese —
block_byte_limit_genesis e afins — que são onde T começa e o
piso ao qual ele pode decair de volta, nunca o limite em vigor. Esses números permanecem reais
para sempre, então ler um deles como "o limite de tamanho do bloco" é errado silenciosamente, e por até a distância
entre o valor de gênese de 2,5 MB e o teto de capacidade de 8 MB. O /fees serve o T ao vivo
e os quatro tetos derivados dele, para que a derivação possa ser verificada em vez de presumida.
Por que os bytes importam#
O blake3("zcd/block/v1" ‖ header_bytes) é o id do bloco, então um observador que
rederiva ids a partir do que lhe foi servido ou concorda com a rede ou descobre imediatamente. É
também como os resultados por certificado são obtidos: se um certificado foi aplicado, sofreu omissão e foi
cobrado, ou foi descartado é computado dentro do fold e nunca persistido, porque uma
linha por certificado para sempre, em um armazenamento que vive em memória, não é algo que se possa pedir a um nó
que carregue. Um observador que queira os resultados faz o fold do bloco ele mesmo.
O que não está aqui, e não estará#
- Nenhuma iteração arbitrária de estado. Sem lista de endereços, sem varredura por prefixo, sem lista de ricos.
- Nenhum estado histórico.
/celle/balanceleem o topo; "saldo na altura H" implica um histórico retido que o nó não guarda. - Nenhuma chamada de administração, reindexação ou depuração, porque não há nada a privilegiar.
A agregação pertence a quem quer que esteja observando, computada uma vez na ingestão para seu próprio banco de dados.
Códigos de status#
As leituras respondem a GET e HEAD e recusam todo outro verbo com
405. Uma pergunta bem formada com resposta negativa — uma altura que a cadeia não
alcançou, um id desconhecido, o corpo de um bloco que perdeu uma reorganização — é 404; apenas uma
requisição malformada é 400. Um registro que o nó escreveu e não consegue mais ler de volta é
500, não 400: a falha é do disco do nó, e dizer a um chamador que sua
requisição era malformada o convida a parar de retentar uma requisição que sempre foi bem formada. Um poller
nunca deveria ter que ler prosa para distinguir ausência de erro, ou o próprio bug do bug do nó.
Reorganizações#
A busca por altura responde apenas para a cadeia canônica. Um bloco que perde uma reorganização mantém seu cabeçalho
e perde seu corpo, então seu id ainda resolve, volta marcado como orphaned, e ainda
carrega o vínculo com o pai de volta ao ponto de bifurcação — o que faz da busca por id o único
caminho seguro contra reorganizações.
Alcançabilidade, e por que importa mais do que parece#
O Zycord não faz travessia de NAT. Um nó com --listen em uma porta alcançável é
um lugar de onde outros podem fazer bootstrap, e a proporção de nós assim é a condição
falseável sobre a qual repousa toda a decisão de não fazer travessia.
# periphery: nothing to configure
zycordd --testnet --dir ./testnet
# core: bind a port, and make sure it is actually reachable
zycordd --testnet --dir ./testnet \
--listen 0.0.0.0:9421 --advertise <public-address>:9421
O --advertise recai sobre o --listen, então --listen
0.0.0.0:9421 sem --advertise publica 0.0.0.0:9421, que
ninguém consegue discar. Um endereço errado se propaga pela troca de pares e custa a cada nó que o tenta
uma discagem. Se você não conseguir encaminhar uma porta, deixe o --listen desligado e seja periferia.
Verificando se funcionou#
curl -s localhost:9420/network
{"enabled":true,"peers":8,"listening":true,"inbound":5,"outbound":3,"reachable":true}
listening: true com inbound: 0 depois que o nó está
no ar por alguns minutos significa que a porta não está de fato alcançável: o processo está vinculado e
esperando, e nada está chegando. Isso parece saudável em qualquer outra visão, e é por isso que este
endpoint existe.
Endereços de bootstrap a partir de um arquivo#
zycordd --testnet --dir ./testnet --peers-file peers.txt
Um endereço por linha; comentários com # e linhas em branco são ignorados, e a lista é
mesclada com os seeds embutidos da rede. O --peers recebe a mesma coisa como argumento
separado por vírgulas, e o --no-seeds descarta os embutidos mantendo os dois.
A interface da carteira, por um túnel ssh#
Um nó não guarda chave alguma e nunca guardará. A carteira é um processo separado, e em um servidor ela é o
zcd ui:
zcd ui --key wallet.json --no-open
Isso imprime uma URL e a serve em 127.0.0.1:9430. Ela vincula ao loopback e
recusa qualquer outra coisa, sem flag para sobrepor. O processo por trás desse listener guarda
uma chave privada destrancada e autentica com um token portador em uma URL, o que é adequado para um
socket que só a máquina local pode abrir e não é adequado para mais nada. Alcançá-lo de
outro lugar é trabalho do ssh, e o ssh é melhor nisso do que isto seria:
# on your own machine
ssh -L 9430:127.0.0.1:9430 <host>
Depois abra a URL que o servidor imprimiu. O token viaja no fragmento da URL,
então ele nunca chega ao servidor, a um log, ou a um cabeçalho Referer — trate a URL como
o segredo que ela é. Ela é nova a cada execução, e Ctrl-C apaga a chave e para de servir.
A ponta local do encaminhamento não precisa ser a porta 9430: a interface verifica o
hostname no cabeçalho Host e deliberadamente não a porta. O hostname é
o que importa — é ele que bloqueia o DNS rebinding, que é o ataque real contra um servidor em
loopback, já que qualquer página no seu navegador pode fazer requisições a 127.0.0.1 e a única
coisa que ela não pode forjar é o nome pelo qual foi acessada.
zcd uiO conselho acima sobre o RPC do nó é sobre uma superfície que não guarda chave alguma e não concede autoridade alguma. Esta guarda uma chave. Não existe versão de expô-la que seja uma boa ideia, e o túnel custa uma flag.
Duas outras formas que vale conhecer:
zcd ui --lockedinicia sem pedir uma frase secreta no terminal; o navegador pede em vez disso. Útil quando o terminal é compartilhado ou registrado em log.zcd ui --lock-after 5mencurta o travamento por ociosidade, que apaga a chave no lugar — o seed é sobrescrito, não meramente desreferenciado.
Identidade de par e anonimato#
O nó gera uma chave de par Ed25519 nova a cada início e nunca a escreve em disco. Ela não é derivada de nada — nem da sua carteira, nem de uma seed, nem de qualquer outra coisa na máquina. Reiniciar a rotaciona.
Se você está rodando um nó de uma forma que lhe importa, o que o desanonimiza não é a chave. É o endereço. Um nó de bootstrap que você roda em infraestrutura rastreável até você é um vetor de desanonimização que nenhuma higiene de chaves conserta.
Diretório de dados#
data/
chain/ blocks, state, and the write-ahead log
peers.json the persisted peer store
O armazenamento de pares é persistido de propósito: um nó que começa do zero após cada reinício dá a um atacante uma nova chance de preenchê-lo. Apagá-lo é seguro mas joga isso fora.
O nó sobrevive a ser morto a qualquer momento — é para isso que serve o write-ahead log, e isso é testado matando nós aleatoriamente sob caos de rede. Ele não sobrevive a ter seu diretório de dados editado por baixo dele: ao iniciar ele recomputa a raiz de estado e se recusa a rodar se a armazenada divergir.
Escolhendo uma rede#
| Flag | Rede | Chain id | Engine |
|---|---|---|---|
| (nenhuma) | mainnet | 1 | randomx-v1 |
--testnet | testnet pública | 2 | randomx-v1 |
--devnet | devnet local | 1337 | engine de desenvolvimento |
Os parâmetros são embutidos no binário, não lidos de um caminho. Todo
participante de uma rede pública precisa carregar os mesmos bytes ou eles não estão em uma mesma rede, e um
arquivo passado por --params é um arquivo que deriva. Uma rede diferente é uma rede
diferente: as duas se desconectam no handshake, e um diretório de dados contendo a cadeia errada é
recusado na inicialização em vez de silenciosamente misturado.
Recuperando um diretório de dados danificado#
O zycordd traz um caminho de reparo para um diretório de dados contra o qual um
nó se recusa a iniciar. Pare o nó primeiro — zycordd repair --dir ./data toma o
cadeado do diretório e se recusa enquanto um nó o estiver segurando — e então pergunte o que
aconteceu com --dry-run antes de mudar qualquer coisa. Só uma classe de dano é
reparável no lugar; para as outras o remédio é ressincronizar, e é a execução a seco que diz qual
delas você tem.