Whitepaper Urbit Explicado


O whitepaper da Urbit é bastante complexo e desafiador, principalmente para pessoas sem conhecimento técnico.

Portanto, tentaremos explicá-lo de forma simples e descomplicada, passo a passo, respeitando a ordem em que o whitepaper foi construído.

Como são muitos os termos que serão novidade para você, o início da leitura pode parecer difícil, mas garanto que depois de ler dois ou três parágrafos as coisas vão começar a ficar mais claras, pois vários termos se repetem e são usados novamente em contextos diferentes. Em outras palavras, explicaremos o mesmo conceito de maneiras diferentes ao longo do texto. Assim, se algum ponto não ficou claro, provavelmente será esclarecido ao longo do texto.

Você está pronto? Então vamos começar!


Resumo

O Whitepaper é intitulado “Urbit: A Solid-State Interpreter” e este termo é repetido no Resumo:

Um “solid-state interpreter” (SSI) é um interpretador que também é um banco de dados ACID. Uma “operating function” (OF) é um sistema operacional de propósito geral definido como uma função pura de seu fluxo de entrada. Urbit é um SSI definido como OF. Funcionalmente, Urbit é uma pilha de execução completa. VM, compilador, sistema operacional, rede, servidor web e aplicativos principais são 30kloc; o sistema está em versão alfa com uma rede bastante estável e funcional.

Vamos detalhar passo a passo o que tudo isso significa:

Solid-state interpreter(SSI): Em Urbit, um SSI é como uma mistura entre um programa que pode entender e executar código (um intérprete) e um tipo de banco de dados muito seguro (um banco de dados ACID) que pode manter os dados seguros e correto mesmo quando as coisas dão errado. ACID significa Atomicidade, Consistência, Isolamento e Durabilidade, recursos essenciais para manter a integridade dos dados. O termo “estado sólido” é uma metáfora aqui, dizendo que todo o sistema funciona de maneira previsível e confiável, muito parecido com dispositivos eletrônicos sem partes móveis. Atomicidade, uma parte do ACID, significa que em uma série de ações, tudo deve dar certo ou toda a transação é cancelada. Isso mantém o banco de dados em um estado consistente mesmo se algo der errado durante a transação.

“Operating function(OF): Isso significa que todo o comportamento do sistema pode ser previsto a partir de suas entradas. O sistema não possui nenhum estado oculto ou efeitos colaterais: ele se comporta de maneira determinística.

“Urbit é um SSI definido como um OF“: Esta frase combina os conceitos acima. O Urbit é um sistema que interpreta e executa código, mantendo a robustez e o determinismo de um banco de dados ACID, e se comporta como uma função determinística de suas entradas.

“Urbit é uma pilha de execução completa“: Isso significa que o Urbit inclui todas as camadas necessárias para ir do código bruto ao software em execução. Isso normalmente inclui uma máquina virtual (para fornecer um ambiente isolado para a execução do código), um compilador (para traduzir o código de alto nível em instruções de máquina), um sistema operacional (para gerenciar recursos), uma rede (para comunicação) e um servidor web (para hospedar e entregar conteúdo web).

“VM, compilador, sistema operacional, rede, servidor web e aplicativos principais são 30kloc“: afirma que todo o sistema, incluindo a máquina virtual, compilador, sistema operacional, rede, servidor web e aplicativos principais, é implementado em 30.000 linhas de código (“kloc” significa “milhares de linhas de código”). Isso é surpreendentemente pequeno para um sistema tão completo, o que é digno de nota.

“O sistema está em versão alfa com uma rede bastante estável e funcional“: O termo “versão alfa” refere-se a um estágio inicial de desenvolvimento em que o software pode ter recursos completos, mas ainda contém bugs ou carece de otimização. Isso geralmente não é estável o suficiente para uso geral, mas os autores também mencionam que a rede está “funcional” e “bastante estável”, sugerindo que ela está funcionando e operacional, embora potencialmente com alguns problemas ainda sendo resolvidos. É importante considerar que o whitepaper foi escrito em 2016 e esta frase se refere ao estágio do projeto naquele momento.

Unindo todos os conceitos que acabamos de aprender, podemos resumir o Resumo como: Urbit é um sistema altamente determinístico e compacto que combina a robustez de um banco de dados ACID com a previsibilidade de uma função pura, e inclui todas as ferramentas que você precisa para ir de código para execução.

Não se preocupe se alguns pontos ainda não estiverem claros, pois iremos revisitar vários desses conceitos ao longo do texto.


Introdução ao Urbit

Após este breve resumo, o whitepaper apresenta “o que é Urbit” usando 6 abordagens diferentes:

Funcionalmente:

Isso se refere ao que o Urbit faz e aos componentes dos quais é composto. Aqui estão as partes principais:

Sistema Operacional (Arvo): É como o chefe do computador. Ele gerencia a memória, os processos e todo o software e hardware do computador. Também permite que você se comunique com o computador sem saber falar a linguagem do computador.

Linguagem Funcional (Hoon): Esta é uma linguagem de programação que o Urbit usa. Em linguagens funcionais, o valor de saída de uma função depende apenas dos argumentos que são inseridos na função. Isso o torna diferente dos idiomas que dependem de um estado local ou global, que pode mudar com o tempo.

Máquina Virtual (Nock): Este é um programa que age como um computador no qual você pode executar outros programas. É “virtual” porque opera apenas em software executado em um computador físico real.

Protocolo de rede (Ames): Este é o sistema que a Urbit usa para enviar dados pela rede. Os dados são enviados em pequenos pacotes que são remontados em seu destino.

Sistema de Controle de Versão (Clay): É como uma máquina do tempo. Ele acompanha todas as alterações feitas no código-fonte de um sistema. Se você cometer um erro, poderá voltar no tempo e comparar as versões anteriores do código para ajudar a corrigir o erro, minimizando a interrupção de todos os membros da equipe.

Formalmente:

Isso se refere a como Urbit é definido em um sentido matemático ou lógico. Urbit é descrito como uma “função operacional”. Em termos simples, você pode pensar em uma função como uma máquina que recebe certas coisas (entrada), faz algo com elas e depois devolve outra coisa (saída). Uma “função operacional” para Urbit significa que toda a vida do sistema Urbit pode ser descrita como uma função que recebe um fluxo de entrada (uma sequência de dados) e produz uma saída. Essa função é um interpretador, o que significa que traduz instruções de alto nível em um formato que pode ser executado ou executado. O interpretador começa com uma linguagem inicial, que então inicia um kernel inicial (o núcleo do sistema do computador). O interessante sobre o Urbit é que tudo, exceto a função de ciclo de vida, pode ser atualizado a partir do código-fonte no fluxo de entrada. Isso significa que a maioria das partes do Urbit pode melhorar ou se atualizar com o tempo.

Com segurança:

O Urbit pretende ser mais simples e rigoroso do que os sistemas atuais, aprendendo com os erros do passado no design de software. O objetivo é evitar a complexidade e os problemas de segurança associados à semântica de computação atual.

Estruturalmente:

O Urbit é executado em um servidor de sistema operacional normal (como Linux ou macOS) e interage com o usuário, outros nós do Urbit e a Internet.

Produtivamente:

O Urbit foi projetado para funcionar como um “servidor pessoal”, substituindo vários serviços da Web por aplicativos auto-hospedados em um servidor pessoal. Ele foi projetado para ser mais simples de usar do que o modelo da web atual. No modelo atual, os usuários fazem solicitações a servidores centralizados. No Urbit, todo usuário é um servidor.

Praticamente:

No momento da redação deste artigo, o Urbit estava em um teste alfa semi-fechado. Este não é mais o caso.


Seção 2: Destaques e novidades

A seção 2 do whitepaper fornece uma visão geral dos vários componentes e recursos da plataforma Urbit. Ele mergulha no funcionamento interno da arquitetura de Urbit, discutindo especificamente quatro componentes principais: Nock, Hoon, Arvo e Ames. Então, vamos dividir cada um deles em termos mais simples:

Nock (intérprete combinador): Nock é o nível fundamental do Urbit, funcionando como sua máquina virtual. Pense nisso como a linguagem base na qual tudo no Urbit é construído. Ele foi projetado para ser super simples e imutável. Um “valor Nock” ou “noun” é um número de qualquer tamanho ou um par desses “nouns”. O conceito-chave aqui é que Nock é como um sistema de computação básico que só sabe fazer as coisas mais simples, como contar. Leia este artigo para mais detalhes.

Hoon (linguagem funcional digitada): Hoon é uma linguagem de programação construída sobre o Nock. É como o tradutor que permite que a programação mais complexa seja traduzida em operações simples que Nock entende. O Hoon foi projetado para ser mais concreto e menos abstrato do que muitas linguagens de programação, tornando-o mais simples de aprender e usar. Ele também lida com coisas como validar dados recebidos de uma rede não confiável, garantindo que eles se encaixem no formato esperado.

Arvo (kernel não preemptivo): Arvo é o sistema operacional do Urbit, como Windows ou macOS são para um PC. É a parte do Urbit que gerencia todo o resto, incluindo o registro de eventos passados (semelhante aos logs de transações na computação tradicional). Ele pode atualizar qualquer parte do sistema em tempo real e salvar as alterações em um ‘snapshot’ ou registro das alterações. O Arvo também é capaz de gerenciar ‘eventos’ para evitar que a complexidade aumente de forma incontrolável. Uma de suas características é o Clay, um sistema de armazenamento de dados que é como uma versão digitada do Git, um popular sistema de controle de versão.

Ames (protocolo de pacotes P2P): Ames é a camada de rede do Urbit. Ele fornece uma maneira para que diferentes partes do Urbit se comuniquem com segurança umas com as outras, da mesma forma que a Internet permite que os computadores troquem informações. Atribui uma identidade única a cada entidade e permite a comunicação encriptada, garantindo segurança e privacidade.


Seção 3: The solid-state interpreter (SSI)

Já estamos na página 8 do whitepaper. Aqui, começaremos a detalhar alguns conceitos que aprendemos superficialmente no início do whitepaper, como “solid-state interpreter”.

Em essência, um SSI é um novo design para software de sistema que visa ser mais confiável e previsível, enquanto ainda é capaz de lidar com uma ampla gama de cálculos.

O SSI é considerado um “stateful packet transceiver”. Um transceptor de pacotes é um dispositivo ou componente que recebe e envia pacotes de informações. Nesse contexto, o SSI atua como um “chip” virtual que realiza as funções de recebimento e envio de pacotes dentro da rede Urbit.

O estado do SSI refere-se à sua configuração ou condição interna em um determinado momento. À medida que o SSI recebe e envia pacotes, ele processa e executa instruções, o que pode fazer com que seu estado interno mude. Essas mudanças de estado refletem a progressão do SSI através de diferentes tarefas ou operações dentro do sistema Urbit.

Persistência Uniforme: Esta seção fala sobre o sistema de armazenamento de um SSI. Ao contrário dos sistemas tradicionais que diferenciam entre “memória” (rápida, mas volátil) e “disco” (lento, mas estável), o armazenamento de um SSI é “uniformemente persistente”, o que significa que é uma estrutura de dados estável. O documento compara isso a uma implementação de hardware de persistência uniforme que poderia usar tecnologias como NVDIMMs, mas menciona que o Urbit atualmente usa um mecanismo de log-snapshot como um banco de dados padrão em hardware típico. O sistema nunca “reinicia” no sentido tradicional, o que significa que não apaga os dados de forma imprevisível.

Rede de pacotes independente da fonte: um SSI só interage com o mundo externo por meio de “rede de pacotes não confiável e insegura”. Ele recebe os pacotes (unidades de dados enviados pela internet) e os processa, mas não se importa de onde vieram (sua “fonte”) — a única coisa que importa é o conteúdo do pacote. Esta seção faz uma distinção importante entre um barramento, que transporta comandos, e uma rede, que transporta pacotes. Ao contrário dos comandos, os pacotes são como “fatos” que podem ser descartados ou repetidos sem alterar as informações que transmitem.

Determinismo de alto nível: Isso significa que o SSI sempre se comporta de maneira previsível com base em seu estado atual e nas entradas que recebe. É determinístico no “nível semântico”, o que significa que toda a operação é mais como uma chamada de função, em vez de ser baseada em ciclos de CPU individuais e imagens de memória. Ele garante que a interrupção do SSI não produza um estado imprevisível.

Decidibilidade, Determinismo e Interrupção: O artigo fala sobre um equilíbrio entre a completude Turing (a capacidade de resolver qualquer problema de computação) e o comportamento determinístico/repetível. Apesar do SSI ser Turing completo, ainda assim pode ser interrompido com segurança. O texto também menciona algumas técnicas para gerenciar cálculos de execução longa e lidar com loops infinitos.

Em resumo, um SSI pode ser pensado como um “chip” que recebe e envia pacotes de dados, às vezes alterando seu estado como resultado. Ele nunca perde dados e não “reinicializa” conceitualmente. Ele opera como um sistema operacional e registra seu fluxo de eventos (sequência cronológica de eventos ou ações executadas pelo SSI) em um cluster para determinismo de baixa latência, salvando periodicamente seu estado em uma imagem instantânea.

Logging refere-se ao processo de gravação ou captura de eventos ou dados para futura referência ou análise.

Um cluster refere-se a um grupo de computadores ou servidores interconectados trabalhando juntos para executar uma tarefa específica. Nesse caso, o SSI registra seu fluxo de eventos em um cluster, indicando que envia os dados do evento gravado para uma coleção de servidores ou recursos de computação.

Baixa latência refere-se a atrasos mínimos ou tempos de resposta curtos. O determinismo, neste contexto, refere-se à propriedade de ser previsível ou ter uma relação de causa e efeito. Ao registrar seu fluxo de eventos em um cluster, o SSI visa obter determinismo de baixa latência. Isso significa que os eventos registrados podem ser acessados e analisados de forma rápida e confiável, permitindo um comportamento previsível e determinístico dentro do sistema Urbit.


Seção 4: The operating function (OP)

Uma função operacional é uma função única e imutável que determina como um computador opera. É como um conjunto de regras ou instruções que o computador segue para processar informações. No contexto do Urbit, essa função é uma “função de ciclo de vida” que usa um histórico de entradas para determinar o estado atual do sistema.

Função do ciclo de vida:

A função de ciclo de vida em Urbit, denotada como L, é essencialmente um mapeamento do histórico de entradas recebidas pelo sistema para o estado atual do sistema. Na prática, isso significa que o estado de um nó na rede Urbit em um determinado momento é determinado inteiramente pelas entradas que ele recebeu no passado.

No contexto de um sistema stateful (um sistema que acompanha seu estado), uma função de transição (T) normalmente seria usada. Uma função de transição é uma regra que pega o estado atual e uma entrada e produz um novo estado e saída. No entanto, no Urbit, a saída não faz parte da definição fundamental do sistema, pois é apenas uma sugestão de que um pacote pode ser enviado, não uma garantia. Além disso, como as regras do sistema são congeladas (não mudam), o Urbit usa uma função de ciclo de vida (L) que usa todo o histórico de entradas para determinar o estado atual.

Fase larval:

Quando um novo nó é criado na rede Urbit, ele passa por um “estágio larval” onde é alimentado com uma série de pacotes “larvais” que o preparam para a vida na rede. Esta etapa permite que o sistema estabeleça a identidade do nó e faça o upload de qualquer código necessário para ele. Embora esse estágio seja essencial, não é considerado trapaça porque a função de ciclo de vida (L) continua sendo a única parte não atualizável do sistema. Depois que a identidade do nó é estabelecida, ela não pode ser atualizada. Se uma nova identidade for necessária, um novo nó deverá ser criado.

Modelo de etapa:

No Urbit, o sistema não apenas processa pacotes (pedaços de dados enviados pela rede), mas também comandos do sistema operacional host (SO). Essas partes de entrada são chamadas de “etapas”. Embora os pacotes possam ser ignorados, os comandos devem ser processados. Se uma etapa não puder ser processada ou for interrompida, ela pode ser substituída por um comando de erro, que permite ao sistema rastrear erros e tratá-los adequadamente.

Quebra de simetria e segredos:

Para diferenciar diferentes nós (naves) na rede, o sistema introduz entropia ou segredos. Estes são exclusivos para cada nó e quebram a simetria.

“Quebra de simetria” e “Segredos” têm a ver com estabelecer a identidade única de um nó (referido como uma “nave”) na rede.

“Quebra de simetria” refere-se ao processo de diferenciar cada nó de todos os outros. Em uma rede perfeitamente simétrica, todos os nós seriam idênticos e indistinguíveis. Mas, para que uma rede funcione adequadamente, cada nó precisa de uma identidade única – é preciso haver uma maneira de diferenciá-los. É aqui que entra a “quebra de simetria”: é o processo de atribuir uma identidade única a cada nó.

A “entropia” ou “segredos” mencionados estão relacionados a esse processo. Na teoria da informação e na ciência da computação, “entropia” geralmente se refere ao grau de aleatoriedade ou incerteza em um conjunto de dados. Quando o whitepaper fala sobre o uso de “entropia” para quebrar a simetria, está se referindo ao uso de dados aleatórios para gerar uma identidade única para cada nó. Isso pode envolver, por exemplo, a geração de um número aleatório ou string que serve como identificador do nó.

Esses “segredos” são informações únicas usadas para estabelecer a identidade de um nó. O texto sugere que esses segredos precisam ser entregues como pacotes para o nó, mas não pela rede (já que não seriam mais segredos se fossem transmitidos publicamente). Isso é feito durante o “estágio larval”, fase em que o nó é configurado e preparado para participar da rede.

Os segredos desempenham um papel vital porque ajudam a garantir que cada nó tenha uma identidade única. Isso é crucial para o bom funcionamento da rede: você precisa ser capaz de endereçar nós específicos, autenticar suas identidades e garantir uma comunicação segura entre os nós. Os segredos, portanto, são a base da identidade e segurança de um nó dentro da rede.

Esses segredos existem devido à necessidade de segurança e identificação exclusiva em um ambiente de rede. Ao atribuir um segredo exclusivo a cada nó, o Urbit pode garantir que os nós possam se autenticar e se comunicar com segurança. Uma vez estabelecida, esta identidade não pode ser atualizada ou alterada, preservando a integridade do nó dentro da rede.

Interações com o sistema operacional host:

Apesar de seu isolamento do sistema operacional host, o interpretador (o componente do sistema que processa entradas e produz saídas) pode processar comandos do sistema operacional. Em resposta, pode sugerir ações. Essa interação não envolve as definições semânticas (significados) do interpretador e do SO.

O modelo Urbit é complexo, pois envolve uma nova abordagem para projetar e operar uma rede peer-to-peer. Ele visa minimizar a complexidade de sua função principal (a função de ciclo de vida) e, em vez disso, oferece funcionalidade mais avançada (como uma linguagem de programação de alto nível ou um sistema operacional) por meio de pacotes pela rede. Esse design garante que cada nó seja isolado e seu estado só possa ser alterado pelo seu proprietário, aumentando a segurança e a privacidade da rede.


Seção 5: A função de ciclo de vida do Urbit

Continuando com o assunto da função operacional, o whitepaper apresenta dois conceitos importantes no Urbit: Nouns e Nock.

Aqui está uma analogia simplificada: imagine o Urbit como um jogo, em que os nouns são as peças do jogo e o Nock é o livro de regras que determina como você pode mover as peças.

No contexto da função de ciclo de vida, os nouns são os estados e as entradas, e o Nock fornece as regras para a transição de um estado para outro com base em uma entrada.

O estágio larval é o estágio inicial da função de ciclo de vida em que o sistema define a semântica básica e a identidade de um nó. Isso é feito alimentando o nó com uma sequência de entradas especialmente criadas (os pacotes “larvais”), que são processadas de acordo com as regras definidas por Nock para produzir o estado inicial do nó.

A função de transição T incorporada à função de ciclo de vida L é o conjunto de regras específicas que descreve como atualizar o estado com base em uma determinada entrada. A função de transição não está explicitamente definida aqui, mas entende-se que ela faz parte da definição do Nock.

Para resumir: a função de ciclo de vida L no Urbit usa o histórico de entradas recebidas por um nó para determinar seu estado atual. Isso é feito usando o Nock, que fornece as regras fundamentais de computação do sistema, e o conceito de nouns, que são as unidades básicas de dados no Urbit.

Vamos agora ser um pouco mais específicos sobre a definição de cada um desses termos.

Nouns:

No Urbit, o termo “noun” é usado para se referir à unidade mais básica de dados. Há dois tipos de nouns: átomos e células.

Um átomo é um número inteiro sem sinal, o que significa que ele pode representar qualquer número inteiro não negativo.

Uma célula é um par de substantivos. É semelhante a uma pequena estrutura de dados que pode conter duas partes de dados. Essas partes podem ser quaisquer dois nouns, portanto, podem ser dois átomos, duas células ou um átomo e uma célula.

Os substantivos não carregam nenhuma informação de tipo com eles porque o Urbit espera um sistema de tipos estáticos em um nível mais alto. Uma linguagem estaticamente tipada é aquela em que o tipo de uma variável (ou seja, o tipo de dados que ela pode conter) é verificado no momento da compilação, antes de o programa ser executado. Isso significa que o tipo de uma variável é fixo depois de declarado e não pode ser alterado. As linguagens comuns com tipagem estática incluem Java, C e C++. Por outro lado, as linguagens dinamicamente tipadas, como Python ou JavaScript, permitem que você altere o tipo de uma variável mesmo depois de ela ter sido definida.

Portanto, um átomo é apenas um número, e uma célula é apenas uma estrutura que contém dois nouns. O sistema não sabe inerentemente o que esses números ou estruturas representam (por exemplo, se um determinado átomo representa um ID de usuário, um registro de data e hora ou algo completamente diferente).

É nesse ponto que o sistema de tipagem estática entra em ação. Nas camadas de nível superior do sistema Urbit, esses substantivos recebem significado por meio da digitação estática. Por exemplo, na linguagem de alto nível do Urbit, Hoon, você pode declarar que um determinado átomo deve ser usado como um carimbo de data/hora e, a partir desse ponto, o sistema sabe que deve tratar esse átomo como um carimbo de data/hora.

Para resumir, as unidades básicas de dados do Urbit (nouns) são simples e não têm significado inerente. Elas podem representar qualquer dado, mas precisam de um sistema de digitação estática para dar-lhes contexto e significado. Espera-se que a digitação estática seja feita em um nível mais alto no sistema Urbit, na linguagem de programação Hoon.

Nock:

O Nock é essencialmente a “linguagem de máquina” do Urbit. É um intérprete simples definido por um conjunto de regras de redução. Essas regras descrevem como avaliar ou “reduzir” expressões no Nock. O objetivo do Nock é definir as regras fundamentais de computação do sistema.

A sintaxe do Nock tem a seguinte aparência: [ a 7 [[7 [0 1] b ] 0 1] c ]


Finalizando o whitepaper

As seções 6 e 7 falam sobre detalhes das linguagens nock e hoon, como operadores, funções e instruções. Em resumo, nock é a linguagem de máquina e hoon é a linguagem de programação usada na prática pelos desenvolvedores que criam aplicativos no Urbit.

Da mesma forma, as seções 8, 9 e 10 fornecem detalhes técnicos sobre o kernel do Arvo e uma de suas vanes: Ames. Recomendamos a leitura destes artigos: “O que é Arvo” e “O que é Ames” para uma compreensão simplificada desses elementos.

Vamos encerrar este artigo com uma citação direta do final do whitepaper, que compara diretamente o Urbit com outros projetos:

“Muitos SOs e interpretadores históricos se aproximaram do ideal SSI, mas falharam na persistência, no determinismo ou em ambos. No departamento de sistemas operacionais, o armazenamento clássico de nível único é o IBM AS/400. O NewtonOS era um produto de remessa com persistência em nível de linguagem. Muitos interpretadores orientados a imagens (por exemplo, Lisps e Smalltalks) também são do tipo SSI, mas geralmente não são transacionais nem deterministas. E, é claro, muitos bancos de dados são transacionais e determinísticos, mas sua função de ciclo de vida não é um interpretador de uso geral.”

Isso deixa claro que o Urbit é um projeto único.

Hoje, quase 10 anos após o lançamento do whitepaper, o Urbit já é uma plataforma funcional com centenas de aplicativos e milhares de usuários. O protocolo continua a melhorar e a ganhar adoção. Esperamos que este artigo tenha sido útil para revelar um pouco mais sobre essa tecnologia fascinante. Você pode se aprofundar em mais detalhes sobre cada componente da rede Urbit neste curso gratuito.