Na série IPFS com o Nabu, o bloco já tem um CID (Content Identifier, o identificador do conteúdo). Falta nomear quem o guarda e onde esse par pode ser encontrado agora.
A proposta
Um CID diz o que o bloco é. Não diz quem o tem. A rede precisa de outra identidade: a do processo que fala libp2p, a pilha de rede par a par. Esse processo muda de IP (Internet Protocol, o endereço da máquina), abre outra porta e atravessa um NAT (Network Address Translation, a reescrita de endereços que o roteador faz entre a rede de casa e a internet).
A proposta é separar as duas coisas. O nome do par sobrevive à mudança de rede. O endereço de rede é uma lista que se atualiza.
A Demonstração
A identidade é um par de chaves. A chave pública vira o PeerId. A chave privada assina o que só aquele par pode afirmar, entre outras coisas o registro IPNS (InterPlanetary Name System) de um post seguinte. O Nabu gera Ed25519 por padrão, um esquema de assinatura de chave pequena e rápida. Um PeerId também pode nascer de RSA (Rivest–Shamir–Adleman, o esquema clássico de chave pública). Os dois cabem no mesmo formato porque o PeerId é um multihash da chave pública: o tipo da chave e o digest viajam juntos. Quem vê o PeerId sabe qual algoritmo esperar na hora de verificar uma assinatura.
O lugar onde o par escuta é um multiaddr, uma sequência de protocolos escritos num só texto. /ip4/203.0.113.4/tcp/4001 diz IPv4, TCP (Transmission Control Protocol, o fluxo confiável) e a porta 4001. /ip4/0.0.0.0/udp/4001/quic-v1 diz "escute em todas as interfaces", UDP (User Datagram Protocol) e QUIC versão 1. O 0.0.0.0 é um bind local: serve para abrir o socket, e não é um destino que o resto da internet disca. O mesmo PeerId anuncia vários multiaddrs ao mesmo tempo. Trocar de rede troca a lista. O PeerId fica.
Como a lista muda, cada nó guarda um caderno de endereços: PeerId para os multiaddrs vistos por último. A lista chega de três jeitos. O outro par diz, no protocolo identify, em quais endereços escuta. Uma conexão que nós mesmos discamos traz o endereço que acabou de funcionar. A DHT (Distributed Hash Table), mais adiante, devolve endereços quando perguntamos por um par.
O identify também devolve o observed address, o endereço que o outro par observou em nós. Um nó atrás de NAT usa isso para descobrir o IP externo. O post sobre entrar na rede continua a partir daí.
No Nabu
HostBuilder.generateIdentity, em src/main/java/org/peergos/HostBuilder.java, cria a chave e deriva o PeerId.
public HostBuilder generateIdentity() {
return setPrivKey(Ed25519Kt.generateEd25519KeyPair().getFirst());
}
public HostBuilder setPrivKey(PrivKey privKey) {
this.privKey = privKey;
this.peerId = PeerId.fromPubKey(privKey.publicKey());
return this;
}
generateIdentity pede um par Ed25519 à biblioteca e guarda só a metade privada. setPrivKey deriva a pública e chama PeerId.fromPubKey. A partir daí getPeerId() e getPrivateKey() devolvem os dois lados. setIdentity(byte[]) faz o caminho inverso: KeyKt.unmarshalPrivateKey lê os bytes já gravados e passa por setPrivKey.
O processo standalone persiste isso em IdentitySection, no arquivo de config. O JSON (JavaScript Object Notation) tem PeerID em base58, o alfabeto que o IPFS usa para PeerIds, e PrivKey em base64. fromJson decodifica os dois. Na subida, EmbeddedIpfs.build chama setIdentity(identity.privKeyProtobuf) e compara builder.getPeerId() com identity.peerId. Se divergirem, lança PeerId invalid e o nó não sobe. A chave e o identificador anunciado têm de ser o mesmo par.
O caderno padrão é RamAddressBook, passado no construtor do HostBuilder. Ele vive na memória do processo. Protocolos que precisam dele implementam AddressBookConsumer. No fim de build, cada protocolo da lista recebe setAddressBook. Bitswap e Kademlia fazem isso, e repassam o caderno aos motores internos.
O identify está no handler de conexão de HostBuilder.build, registrado para toda conexão nova. O fluxo, na ordem do código:
- Lê o PeerId remoto da sessão já cifrada,
connection.secureSession().getRemoteId(). Nesse ponto o handshake já amarrou a chave. - Consulta o caderno. Se a lista daquele par está vazia, ele é novo.
- Se fomos nós que discamos (
connection.isInitiator()), grava o endereço remoto dessa conexão comaddAddrs. Esse endereço acabou de ser discável. - Abre um stream do protocolo identify e chama
id. - Dos
listenAddrsque o remoto devolve, grava a lista no caderno. Esses são os endereços em que ele escuta, não a porta efêmera de uma conexão que chegou de fora. Guardar a porta de origem deixaria no caderno um destino que ninguém escuta. - Se a resposta traz observed address, entrega a
ReachabilityManager.observeAddress, junto com o hash do par que observou. - Se a lista de protocolos do remoto contém
/ipfs/kad/1.0.0e este nó ainda não o tinha discado, abre um stream Kademlia. A tabela de roteamento cresce com quem chega.
O que fica de fora: o caderno não é um banco. Reiniciar o processo esquece os endereços aprendidos. A identidade, no processo Nabu, sobrevive porque está no config. Um EmbeddedIpfs montado pela aplicação guarda a chave só se a aplicação a passar de novo em IdentitySection.
Nesta série
O anterior é O que é IPFS. O próximo é Transportes, criptografia e multiplexação.
Top comments (0)