WEB3DEV

Cover image for Construindo um Sistema de Controle de Versão Descentralizado Simples com IPFS + Textile
Paulo Gio
Paulo Gio

Posted on

Construindo um Sistema de Controle de Versão Descentralizado Simples com IPFS + Textile

Usando Threads e Esquemas do Textile para fazer (documentar) história

https://miro.medium.com/max/1100/1*OEQw9ah9fH2WvpqbwSCetg.gif

Quando começamos a construir as APIs de Threads e Esquemas do Textile, tínhamos em mente o backup e o compartilhamento de fotos. Threads permitem que grupos privados postem fotos e interajam em uma rede descentralizada, mantendo controle total sobre seu próprio conteúdo. Mas é claro que eles podem facilitar o compartilhamento, a coordenação e o armazenamento seguros de muitos tipos de dados em uma rede descentralizada. Então, ao longo do caminho, começamos a pensar em arquivos de forma mais geral e como o Textile poderia fornecer acesso mais fácil/melhor a arquivos estruturados no IPFS. Acabamos desenvolvendo um 'banco de dados' altamente flexível, offline, seguro e descentralizado, que é útil para uma variedade de produtos de dados descentralizados. Para realmente enfatizar a flexibilidade e a utilidade dos frameworks, começamos a brincar com diferentes casos de uso e aplicações em demonstrações. Na demonstração de hoje, usaremos um Thread do Textile e um simples esquema JSON para construir um sistema simples de controle de versão/histórico de documentos.

Nosso objetivo é criar um histórico simples de atualizações de documentos que possa ser rastreado, rebobinado e, se desejarmos, potencialmente bifurcado e mesclado. Pense no rastreamento de alterações em uma postagem de blog ou em um ambiente colaborativo de edição de documentos (veja os Próximos Passos abaixo) ou até mesmo em um feed de mídia social de microblog. Enquanto estamos fazendo isso, vamos garantir que nosso histórico de documentos funcione entre um conjunto de pares descentralizados/distribuídos. Vamos nos concentrar em documentos JSON para começar e demonstraremos testes com vários pares na linha de comando. Basicamente, estamos criando um feed de documentos colaborativo descentralizado! A estrutura do nosso tutorial será:

  • Configuração (criando pares para brincar)
  • Defina nossa estrutura de dados
  • Crie nosso thread (compartilhado)
  • Convide um (alguns) colega(s)
  • Transmita alguns dados via conexão p2p direta
  • Deleite-se com a glória de um teste bem-sucedido

Configuração

Primeiro, vamos criar dois pares para testar…

textile wallet init
textile wallet accounts "lista de palavras mnemônicas ..." -d 2
textile init -s <account-0-seed>
textile init -s <account-1-seed> -r /Users/carson/.tex2/repo
Enter fullscreen mode Exit fullscreen mode

Em seguida, vamos editar o arquivo de configuração do nosso segundo par para especificar diferentes portas a serem usadas para as APIs (estou usando o VSCode, você pode usar o editor de texto que desejar ou usar nossa nova ferramenta de configuração):

code ~/.tex2/repo/textile
Enter fullscreen mode Exit fullscreen mode

Para este exemplo, apenas incrementei ligeiramente os números das portas. Isso nos permitirá fazer referência aos diferentes pares (que chamarei de Par 1 e Par 2) por seus URLs de API… mais sobre isso daqui a pouco.

...
   "Addresses": {
       "API": "127.0.0.1:40602",
       "CafeAPI": "127.0.0.1:40602",
       "Gateway": "127.0.0.1:5052"
   },
...
Enter fullscreen mode Exit fullscreen mode

Com essas mudanças em vigor, podemos iniciar o Par 1:

textile daemon
Enter fullscreen mode Exit fullscreen mode

E em um terminal separado, inicie o Par 2. Observe que estou especificando um diretório de repositório específico aqui.

textile daemon --repo-dir=/Users/carson/.tex2/repo/
Enter fullscreen mode Exit fullscreen mode

Esquema JSON

Agora, vamos criar um Thread que usaremos para testar as coisas. Como vamos enviar e consolidar atualizações de documentos de maneira incremental, criaremos um novo Thread com um Esquema que suporte dados JSON. Isso é bem simples de configurar, exigindo apenas que usemos a biblioteca mill do /json e algumas outras chaves secundárias para controlar a fixação e a encriptação (manteremos as coisas em texto simples por enquanto). Para alguns detalhes sobre Threads em geral, confira esse link. E para uma breve introdução aos Esquemas, dê uma olhada nesta demonstração/exemplo anterior antes de prosseguir.

{
   "pin": true,
   "plaintext": true,
   "mill": "/json",
   "json_schema": ...
}
Enter fullscreen mode Exit fullscreen mode

Observe a entrada json_schema (atualmente vazia), que é onde definimos a real estrutura JSON, esperada para nossos dados de Thread. Ela é baseada no vocabulário do Esquema JSON para anotar e validar documentos JSON. Portanto, nosso Esquema de Thread do Textile conterá o esquema JSON, que podemos usar para validar os dados JSON de entrada antes de adicioná-lo a um Thread. Isso é muito bom, porque garante que os dados do Thread do Textile permaneçam estruturados, tornando mais fácil para os desenvolvedores de aplicativos confiarem que os dados adicionados a um Thread estão em conformidade com o modelo de dados esperado.

Patches JSON

Novamente, como enviaremos atualizações de documentos, queremos um Esquema JSON que suporte a modificação de documentos JSON. A especificação do Patch JSON, conforme descrito na RFC 6902, é uma dessas especificações:

O Patch JSON define uma estrutura de documento JSON para expressar uma sequência de operações a serem aplicadas a um documento JavaScript Object Notation (JSON)…

Isso é praticamente perfeito para o nosso caso de uso, embora calcular e aplicar patches de mesclagem JSON RFC7396 também possa ser muito útil…

De qualquer forma, precisaremos de uma representação em Esquema JSON de nossa especificação do Patch JSON, o que pode ser um pouco trabalhoso para criar… exceto que alguém já fez isso por nós! Você pode pegá-lo na 'loja' de Esquemas JSON. Para tornar todo esse processo agradável e reprodutível, veja como (em outro Terminal) podemos pegar o esquema e criar um esquema de Threads do Textile usando a biblioteca jq que usamos em um post anterior:

wget http://json.schemastore.org/json-patch -O json-patch.json
jq '{"pin": true, "plaintext": true, "mill": "/json", "json_schema": .}' json-patch.json > patch-schema.json
Enter fullscreen mode Exit fullscreen mode

Criando um Thread

Com o arquivo patch-schema.json pronto, crie um Thread usando o esquema acima. Vamos mantê-lo aberto (--open) e chamá-lo de json-patch:

textile threads add --open --schema=patch-schema.json json-patch
Enter fullscreen mode Exit fullscreen mode

Em seguida, encontraremos o id de nossos outros pares e os convidaremos para o Thread do par 1:

textile peer --api=http://127.0.0.1:40602 # imprime peer-id do Par 2
textile invites create -t <thread-id> -p <peer-id>
Enter fullscreen mode Exit fullscreen mode

Podemos então usar o Par 2 para aceitar o convite (você pode usar o mesmo terminal, porque estamos especificando o host da API diretamente):

textile invites accept --api=http://127.0.0.1:40602 <invite-id>
Enter fullscreen mode Exit fullscreen mode

Agora a saída de seus dois daemons em execução deve se parecer com isto (Par 1):

Textile daemon version v1.0.0
Repo:    /Users/carsonfarmer/.textile/repo
API:     127.0.0.1:40600
Gateway: 127.0.0.1:5050
PeerID:  <peer-one-id>
Account: <account-0-id>
12 Dec 18 18:26 UTC  <peer-one> added JOIN update to thread <blah>
12 Dec 18 18:41 UTC  <peer-two> added JOIN update to thread <blah>
12 Dec 18 18:41 UTC  <peer-two> joined <blah>
Enter fullscreen mode Exit fullscreen mode

e isto (Par 2):

Textile daemon version v1.0.0
Repo: /Users/carsonfarmer/.textile2/repo/
API: 127.0.0.1:40602
Gateway: 127.0.0.1:5052
PeerID: <peer-two-id>
Account: <account-1-id>
12 Dec 18 18:40 UTC <peer-one> invited you to join <blah>
12 Dec 18 18:26 UTC <peer-one> added JOIN update to thread <blah>
12 Dec 18 18:41 UTC <peer-two> added JOIN update to thread <blah>
Enter fullscreen mode Exit fullscreen mode

Assinando atualizações

Agora vamos configurar as coisas para que o Par 2 possa ouvir as atualizações do Thread e tomar as medidas adequadas (alternativamente, o Par 2 pode simplesmente verificar periodicamente as atualizações: textile ls -t &lt;thread-id>). Enquanto isso, o Par 1 será responsável por enviar atualizações de documentos para o Thread como Patches JSON. Eu escrevi um script em Python super simples que permitirá que o Par 2 assine atualizações e aplique essas atualizações a um documento JSON padrão (vazio):

import json
import requests
from jsonpatch import apply_patchapi_base = "http://127.0.0.1:40602/api/v0/"
sub_path = "sub/<thread-id>"
doc = {}# Primeiro, acesse a API de assinatura de "transmissão" com requests.get(api_base + sub_path, stream=True) como linhas:
   # Para cada resposta do
   for line in lines.iter_lines():
       # Se houver alguns dados
       if line:
           # Analisa e percorre a estrutura JSON
           update = json.loads(line)
           if update["block"]["type"] == "FILES":
               for f in update["info"]["files"]:
                   h = f["file"]["hash"]
                   # Use a API ipfs para obter os dados reais
                   r = requests.get(api_base + "ipfs/" + h)
                   # Aplique o patch no doc...
                   apply_patch(doc, r.json(), in_place=True)
                   print(doc)
Enter fullscreen mode Exit fullscreen mode

Com isso em execução, o Par 2 terminará com uma série de Patches JSON, que eles aplicam automaticamente à “cópia funcional” local de um documento JSON. Aqui está o desdobramento:

  • As primeiras linhas são importações (pip install jsonpatch requests em primeiro) e configurações dos caminhos da API;
  • Estamos usando os pontos de extremidade sub e ipfs da nossa API do daemon do Par 2;
  • Para cada resposta da API de transmissão sub, pegue os dados adicionados do Patch Json e aplique-os ao nosso documento JSON local e imprima o resultado.

Como o próprio Thread é um registro das atualizações, você também pode usar a API textile ls para listar uma variedade de atualizações e computar a versão corrigida com elas. Isso permitiria que você criasse novamente o documento em qualquer ponto de sua história! Os parâmetros de deslocamento (offset) e limite (limit) permitem controlar de onde você irá começar a listar e quantas atualizações listar por resposta.

textile ls --offset=<update-id> --limit=100 -t <thread-id>
Enter fullscreen mode Exit fullscreen mode

E aqui está uma função Python simples que permite que você faça isso (não incluí limit aqui, mas você pode adicioná-lo):

def recreate_doc(thread_id, offset=None):
   headers = {
       "X-Textile-Opts": "thread={}".format(thread_id)
   }
   if offset is not None:
       headers["X-Textile-Opts"] += ",offset={}".format(offset)
   b = requests.get(api_base + "files", headers=headers)
   doc = {}
   for f in b.json():
       for f in update["files"]:
           h = f["file"]["hash"]
           r = requests.get(api_base + "ipfs/" + h)
           patch = r.json()
           apply_patch(doc, patch, in_place=True)
   return doc
Enter fullscreen mode Exit fullscreen mode

Como alternativa, já que você instalou o jsonpatch, você pode fazer algo assim na linha de comando:

textile sub -t <thread-id> | jq -r --unbuffered '.info.files[].file.hash' | xargs -L 1 sh -c 'textile cat $0 | jsonpatch doc.json > tmp.json && cp tmp.json doc.json && cat doc.json'
Enter fullscreen mode Exit fullscreen mode

Isso canaliza todas as atualizações do Patch JSON da API de assinatura por meio de jq, xargs e até jsonpatch para manter um documento JSON atualizado (doc.json) e depois concatenar (cat) (veja, eu disse que teríamos gatos 😉) ao console!

Envie as atualizações!

Ok, agora é finalmente hora de testar as coisas. A coisa mais fácil a fazer é enviar alguns documentos do Patch JSON bem formatados para o Thread do Par 1:

echo '[{"op":"add", "path":"/name/first", "value":"Carson"}]' > patch.json
textile add patch.json -t <thread-id>
Enter fullscreen mode Exit fullscreen mode

Isso produzirá o documento JSON atualizado no script de assinatura Python do Par 2. Podemos então enviar mais alguns patches e ver o documento se acumular diante de nossos olhos:

echo '[{"op":"add", "path":"/name", "value":{}}]' > patch.json
textile add patch.json -t <thread-id>echo '[{"op":"add", "path":"/name/first", "value":"Carson"}]' > patch.json
textile add patch.json -t <thread-id>echo '[{"op":"add", "path":"/name/last", "value":"Farmer"}]' > patch.json
textile add patch.json -t <thread-id>echo '[{"op":"add", "path":"/bio", "value":"Carson é humano"}]' > patch.json
textile add patch.json -t <thread-id>echo '[{"op":"add", "path":"/website", "value":"https://carsonfarmer.com"}]' > patch.json
textile add patch.json -t <thread-id>
Enter fullscreen mode Exit fullscreen mode

E podemos até editar/modificar entradas anteriores!

echo '[{"op":"replace", "path":"/bio", "value":"Carson é provavelmente um humano"}]' > patch.json
textile add patch.json -t <thread-id>
Enter fullscreen mode Exit fullscreen mode

Você deve produzir uma saída semelhante a esta no console Python do Par 2:

https://miro.medium.com/max/1100/1*OEQw9ah9fH2WvpqbwSCetg.gif

O script à esquerda envia patches para o comando 'textile add', e o script à direita é o primeiro script Python descrito acima.

Você também pode criar os diffs diretamente de dois documentos JSON e fazê-lo dessa maneira (novamente usando uma ferramenta instalada com jsonpath):

jsondiff original.json modified.json > patch.json
textile add patch.json -t <thread-id>
Enter fullscreen mode Exit fullscreen mode

Isso tudo é realmente apenas para nossa diversão e mostra alguns exemplos interessantes para você pensar sobre o Textile e os tipos de fluxos de trabalho e canalizações de dados que você pode criar com Threads, Esquemas e todas as ferramentas que o Textile fornece. Mas há muito mais para explorar, construir e projetar…

Próximos passos

Um sistema de controle de versão linear pessoal simples é muito bom, mas não vai mudar a forma como você versiona e interage com os documentos. Esta postagem não aborda coisas como Patches JSON desatualizados ou problemas potenciais semelhantes, mas eles podem ser resolvidos com um conjunto de Transformações Operacionais (OT) do Patch JSON ou uma implementação CRDT adequada (na qual estamos trabalhando!). Este post também não aborda a colaboração entre pares. Os Threads foram projetados com esse tipo de aplicação (estado compartilhado entre vários membros do grupo), portanto, seria uma extensão óbvia. Na verdade, isso é apenas o que faremos em um post futuro!

Durante o desenvolvimento da nossa mais recente biblioteca textile-go, encontramos muitos dos conceitos que surgem quando falamos sobre edição colaborativa, transformações operacionais, patching, enfileiramento etc. aplicados a uma ampla gama de interações descentralizadas. Então, na verdade, já começamos a adicionar muitas das ideias e soluções acima ao textile-go! Embora este post tenha sido escrito mais rusticamente, em breve teremos recursos completos de OT e CRDT incorporados diretamente no Textile. Com esses tipos de ferramentas no lugar, o céu é o limite do que você pode começar a construir em cima do Textile…

Falando nisso, esperamos que você tenha gostado desta rápida demonstração/tutorial. Continuaremos a publicar mais deles à medida que continuamos a lançar nossas novas APIs e ferramentas, então deixe-nos saber o que você pensa! Você pode entrar em contato pelo Twitter ou Slack ou nos chamar de lado na próxima vez que nos vir em uma conferência ou evento. Estamos felizes em fornecer informações, pensamentos e detalhes sobre onde estamos levando este trabalho. Enquanto isso, não se esqueça de conferir nossos repositórios do GitHub, para código e PRs, que mostram nossas implementações atuais e antigas. Tentamos garantir que todo o nosso desenvolvimento aconteça de modo aberto, para que você possa ver as coisas à medida que elas se desenvolvem. Além disso, não deixe de se inscrever em nossa lista de espera, onde você pode obter acesso antecipado ao Textile Photos, a bela interface móvel das ferramentas multiplataforma do Textile.

Tutorial original escrito por Carson Farmer. Traduzido por Paulinho Giovannini.

Top comments (0)