DEV Community

Pedro Pietroluongo
Pedro Pietroluongo

Posted on

Os exemplos do repositório

Na série IPFS com o Nabu, os posts anteriores abriram o nó por dentro. Este fecha a série rodando os programas do módulo examples/ deste repositório. O nabu-chat é outro projeto. Aqui o código é examples/src/main/java/examples/.

A proposta

Ler EmbeddedIpfs.build mostra as peças. A proposta dos exemplos é ver duas dessas peças com um CID (Content Identifier, o identificador do conteúdo) na mão: um processo grava, o outro lê. Cada programa de linha de comando faz um só trabalho, em cima de um nó compartilhado. CLI significa command-line interface, o programa que se executa no terminal.

A Demonstração

DemoNode sobe esse nó. Gera uma identidade nova, guarda blocos num RamBlockstore e registros IPNS (InterPlanetary Name System) num RamRecordStore. RAM (Random Access Memory) aqui quer dizer a memória do processo: nada vai para disco. O listener é TCP (Transmission Control Protocol), uma porta só.

Sem --public, o bind é 127.0.0.1, o endereço que só a própria máquina alcança, e a lista de bootstrap é a que você passou em --bootstrap, muitas vezes vazia. Com --public, o bind é 0.0.0.0, todas as interfaces, e o bootstrap são os nós públicos de Config.defaultBootstrapNodes. Os dois modos se excluem: passar os dois lança erro. --public também liga provideBlocks só nos comandos que anunciam. O multiaddr impresso por advertise() é o que o outro processo cola em --bootstrap.

A ordem abaixo segue o que cada CLI pede da rede. Drop não usa DHT (Distributed Hash Table, a tabela de quem tem o CID): o outro processo ganha o multiaddr na saída e pede o bloco àquele par. Find anuncia o CID e o outro lado só precisa desse CID. Name move um nome IPNS. Chat manda HTTP (Hypertext Transfer Protocol) no stream /http. ApiBlocks fala com um daemon já de pé, pela API (Application Programming Interface) do post anterior.

Os scripts em examples/scripts/ sobem os dois processos e conferem a saída. No Windows:

powershell -ExecutionPolicy Bypass -File examples/scripts/all.ps1
Enter fullscreen mode Exit fullscreen mode

Há um par .ps1 e .sh por exemplo: chat, drop, find, name, api. Um script que passa imprime PASS e o nome. all.ps1 e all.sh imprimem PASS all no fim. Antes disso, mvn install na raiz publica o Nabu no Maven local, para o examples/pom.xml compilar. O script da API ainda precisa do jar com dependências, mvn package -DskipTests, porque ele sobe o processo Nabu por esse jar. Os outros quatro chamam mvn -f examples/pom.xml exec:java. O próprio script compila o módulo, então um ajuste no exemplo entra na execução seguinte sem um compile à parte.

Chat, Drop, Find e Name escutam uma porta efêmera, escolhida na hora. Não ocupam 4001 (swarm, as conexões entre pares), 5001 (API) nem 9100 (métricas). O script da API ocupa as três e disca os nós públicos de bootstrap, então a máquina precisa de rede até eles. Se a porta estiver tomada:

powershell -ExecutionPolicy Bypass -File examples/scripts/all.ps1 -SkipApi
Enter fullscreen mode Exit fullscreen mode

No shell, o equivalente é SKIP_API=1. A subida da API espera até três minutos pela linha Starting RPC API server. RPC (Remote Procedure Call) aqui é só o nome dessa linha de log: o servidor HTTP da API subiu. O repositório do daemon é target/example-tests/ipfs-repo, criado de novo a cada execução. Reusar um repositório travado deixa node.start() parado em Starting IPFS.... Os logs ficam em target/example-tests/. Um script que falha imprime a saída do nó e mata os processos que ele mesmo abriu. all tenta o script falho uma segunda vez e então para.

No Nabu

Drop grava um bloco raw e para. Raw, no CID, é o codec dos bytes opacos: não há links dentro do bloco. put chama DemoNode.start(false, lista vazia, false). O primeiro false desliga o anúncio na DHT. A lista vazia é o bootstrap. O último false prende o bind em 127.0.0.1. O bloco sai de DemoNode.payload. Se sobrou um caminho, lê o arquivo inteiro. Senão imprime Text to store: e usa uma linha da entrada. blockstore.put usa Cid.Codec.Raw. A saída traz Listening:, depois CID:, e a frase de que os mesmos bytes produzem esse CID. O outro processo cola o que vier em Other process: get <multiaddr> <cid>.

Drop put [file]
Drop get <multiaddr> <cid>
Enter fullscreen mode Exit fullscreen mode

get sobe outro DemoNode do mesmo jeito, chama remember no multiaddr e fetch com o conjunto daquele único PeerId, timeout de 30 segundos. remember faz addAddrs no caderno. fetch chama ipfs.getBlocks com addToLocal verdadeiro. A linha final é Fetched <cid> from <peerId>. A DHT não entra, porque a lista de pares não está vazia.

Find é o anúncio. put sobe com provideBlocks verdadeiro e, depois do put raw, chama dht.provideBlock com o CID e PeerAddresses.fromHost, e espera o future. A frase na saída diz que o outro lado só precisa do CID, e pede para deixar o processo vivo: o provedor precisa continuar discável. get sobe com provideBlocks falso, lê o CID e chama findProviders pedindo 5 provedores. Imprime cada PeerId e os endereços. remember grava esses endereços no caderno, pulando provedor sem endereço. Em seguida fetch vai com a lista de pares vazia. A busca do bloco passa de novo pela DHT, agora com os endereços já conhecidos, no caminho do BitswapBlockService que tenta os conectados e depois os provedores. Sem registro, a linha é No provider records. Com --public, os dois lados usam a DHT pública. Sem ele, get imprime Start the other process with: put --bootstrap mais o multiaddr local, e os dois formam uma DHT de dois nós.

Name publica IPNS. O comentário da classe pede para subir resolve primeiro e passar o multiaddr dele a publish --bootstrap. publish sobe com provideBlocks verdadeiro. Cada linha da entrada vira um bloco raw. O valor do registro são os bytes UTF-8 do CID em texto, não os bytes do bloco. O sequence começa em 0 e incrementa antes de cada publishValue, então o primeiro anúncio sai com sequence 1. O TTL (Time To Live, a vida que o registro declara) é 24 horas, a constante TTL_HOURS. A saída de cada linha é Publishing sequence ..., o CID e IPNS record stored on N peers., onde N é o inteiro que publishValue devolve, a quantidade de pares que guardaram o registro. resolve sobe sem anunciar, pede o PeerId do publicador e aceita resolve, get <cid> ou quit. resolve chama resolveRecords com minResults 1, imprime IPNS sequence e o CID, e busca o bloco. Sem --public, o conjunto de pares é só o publicador e o timeout é 30 segundos. Com --public, o conjunto vai vazio, o timeout sobe para 120 segundos e a DHT entra. Sem registro, a linha é No IPNS record yet.

Chat sobe com provideBlocks falso e registra o handler incoming. O primeiro prompt pede um PeerId ou um multiaddr, e remember grava o endereço. Cada linha seguinte vira um DefaultFullHttpRequest POST para /, com o texto no corpo. p2pHttp precisa estar presente. dial usa o host, o PeerId e os multiaddrs, e send espera a resposta. Falha de envio imprime Could not send:. O handler remoto lê o corpo e imprime received:. O script chat.ps1 sobe o primeiro sem argumentos e o segundo com --bootstrap no endereço que o primeiro imprimiu.

ApiBlocks é cliente, não nó. O padrão é o multiaddr /ip4/127.0.0.1/tcp/5001. Um argumento só, se começa com /, substitui o multiaddr. Se não começa, é o arquivo a guardar. Dois argumentos são o multiaddr e o arquivo. Sem arquivo, o programa lê uma linha, Text to store:. NabuClient chama id, imprime PeerId:, faz block/put em raw, imprime CID:, faz block/get e compara os bytes. Diferença lança block/get returned different bytes. Sucesso imprime block/get matches block/put. Depois chama dht/findprovs. Sem provedor, a linha é dht/findprovs: no providers. Com provedor, imprime PeerId e endereços.

O que fica de fora: os exemplos não cobrem S3, filtro, UPnP (Universal Plug and Play) nem relay. O blockstore é RAM. Matar o processo apaga blocos e registros. O script da API é o único que fala com a rede pública de bootstrap. Os outros quatro ficam entre si.

Nesta série

O anterior é HTTP entre pares. Este é o último post.

Top comments (0)