Como se não tivéssemos blockchain

Como usamos o contrato inteligente para construir um sistema para selecionar os melhores projetos tecnológicos no IT MTS? E em que "armadilhas" caímos, mas conseguimos escapar, provando no final que é possível manter um registro distribuído em dispositivos móveis!



Por que era necessário um sistema baseado em blockchain?


Vamos começar desde o começo. A MTS tem uma longa tradição - escolher o melhor projeto tecnológico realizado em um ano e premiar sua equipe. A equipe recebe prêmios, respeito e fama. Ao longo dos anos, vários projetos se tornaram vencedores: de sistemas de telecomunicações altamente carregados a sistemas de inteligência artificial.

A seleção do melhor projeto sempre ocorreu em várias etapas:

  • Equipes se aplicam
  • A votação de respeitados especialistas técnicos é realizada
  • Após especialistas, os projetos são selecionados pelos gerentes
  • Após a conclusão de todas as etapas, o grande chefe escolhe o melhor projeto.

Decidimos que esse esquema não é transparente o suficiente para os participantes e pensamos: por que não dar a todos os especialistas da empresa a oportunidade de escolher o melhor projeto tecnológico? 


Se implementarmos esta oportunidade diretamente no telefone, veremos a classificação atual dos projetos e quem votará em quem - isso garantirá total transparência do processo.

Lemos vários artigos sobre blockchain e a ideia de construir um sistema de registro distribuído firmemente estabelecido em nossas cabeças. Mas e se aplicarmos o contrato inteligente aqui ?

Fomos atraídos pelas seguintes propriedades:

  • abertura - não existe um servidor único onde você possa manipular informações;
  • as informações colocadas em um registro distribuído permanecem lá para sempre;
  • a informação não pode ser falsificada (bem ... praticamente)

Não blockchain




O próprio Blockchain não deve ser usado para essas eleições. Mas e se pegarmos os protocolos para construir consenso em sistemas distribuídos e aplicá-los na construção de consenso nas relações humanas?

Dado que teremos que trabalhar em uma rede aberta, precisamos nos proteger de ataques não bizantinos e da substituição de informações nos dispositivos dos usuários.

Quais são as alternativas


O protocolo mais famoso é o PAXOS. Falta um líder explícito e todas as alterações passam por um commit de duas fases. No início de cada alteração, a proposta ocorre. Se foi bem sucedido, Aceitar é enviado.


Um recurso do algoritmo pode ser chamado de usar timers globais para determinar qual solicitação foi gerada anteriormente e que o nó que faz alterações deve se comunicar com todos os nós da rede. Leia mais sobre o algoritmo aqui .

O algoritmo é usado em muitos lugares, por exemplo, no DBMS Cassandra. Não gostamos desse protocolo em termos da complexidade de sua implementação para a tarefa. Mas a segunda opção veio até nós - é a RAFT. De fato, esta é uma evolução do protocolo PAXOS com um líder claro.

O protocolo simplificado pode ser descrito da seguinte maneira:
· Está sendo construída uma rede de dispositivos que se conhecem;
· Os dispositivos escolhem entre si um líder que toma “decisões importantes” (por exemplo, adicionando uma entrada ao registro ou alterando a composição da rede);
· O dispositivo líder é responsável pela distribuição de informações por toda a rede, para que em todos os lugares sejam idênticas;
· Assim que o líder deixar de lidar com seus deveres - escolha um novo líder.

Leia sobre o protocolo aqui .

Nossa implementação


O que estamos fazendo e por que o mundo é outra bicicleta? 


Quase todos os usuários do nosso registro têm dispositivos móveis que estão quase sempre ativados e quase sempre online, e que os utilizam quase sempre; portanto, decidimos executar o algoritmo de registro de distribuição em dispositivos móveis e não na infraestrutura do servidor, como outras implementações conhecidas. .

Vamos ver em que armadilhas entramos, mas conseguimos escapar ...

Armadilha número 1: "Meu endereço não é o lar nem a rua"


“De repente”, descobriu-se que, para construir uma rede P2P que implementa um registro distribuído usando o protocolo RAFT para replicação de dados, cada dispositivo pode se comunicar, o que significa que é um cliente e um servidor. Portanto, precisamos de um endereço IP "branco" público para cada telefone celular (pode não ser).

O número de IPv4s reais é muito limitado, portanto, as operadoras de telecomunicações usam a tecnologia NAT (Network Address Translation) no modo PAT (Port Address Translation), convertendo vários endereços IP da rede interna (que são distribuídos aos assinantes) em um endereço IP público externo. Portanto, a capacidade de aceitar conexões de entrada da Internet é excluída.

A boa notícia é que há muito IPv6! 


Temos suporte para IPv6 incluído no pacote base. Além disso, todos os telefones modernos são compatíveis com IPv6, e a operadora atribui ao assinante um endereço v6 "branco" público. Nossa escolha é IPv6.

Armadilha número 2: Todo mundo foi dormir


Ao contrário dos servidores, os celulares ainda às vezes desligam. Encontramos isso ao testar o primeiro protótipo. Além disso, o endereço IPv6 fornecido pelo operador é público, mas não estático, cada nova sessão de comunicação é um novo endereço. O endereço do dispositivo móvel pode mudar a qualquer momento. E se não houver um único telefone com o endereço conhecido por nós na rede, ele simplesmente deixará de existir (não há nada com o qual conectar-se para "aumentá-lo"). Portanto, tivemos que violar nossa regra de “não servidor” até certo ponto. Criamos um nó especial na nuvem com um endereço conhecido estático. Sua tarefa é lembrar / atualizar a composição da rede e não desligar. Ou seja, é um site comum, ninguém apenas vota nele e, voltando-se para ele, você sempre pode obter a lista atual de endereços de todos os participantes da rede.

3: 



Era necessário resolver de alguma forma o problema, para que nem todos pudessem votar em projetos, mas quem precisasse. A primeira ideia foi a seguinte: manter um banco de dados de números de telefone especializados. Mas eles recusaram, porque não queriam dar ao aplicativo o direito de acessar essas informações.
Como resultado, eles simplificaram tudo: decidiram fornecer a cada voz, cada entrada do registro, uma assinatura digital nas curvas elípticas da moda, que determinarão a autenticidade do registro. Um serviço WEB foi colocado na rede corporativa, que, mediante autorização de domínio, determinou o especialista e gerou um código QR exclusivo para ele com as chaves de criptografia pública e privada (é claro, no lado do cliente). O especialista digitalizou o código do aplicativo e se conectou. Depois disso, a versão atual do registro "rolou" em seu telefone e a oportunidade de votar apareceu.

4: Android 
 



Durante o teste, foi quase "repentinamente" revelado que alguns usuários são proprietários de modelos conhecidos de dispositivos móveis executando o sistema operacional iOS. E ficou claro que nosso software de registro deveria ser executado em plataformas diferentes. Observamos a linguagem de programação Kotlin, que não é apenas “moderna, elegante, jovem”, mas também multiplataforma .

O conceito de multiplataforma no Kotlin implica que há parte do código comum e específico da plataforma, mas como os recursos de nossa equipe são limitados, nos propusemos a odiosa tarefa de usar uma única versão do código fonte para todas as plataformas! Obviamente, o módulo executável deve ser nativo para cada plataforma. Kotlin foi capaz disso.

Não antes de dizer que acabou! Temos um SourceSet único com código-fonte, do qual coletamos binários para todas as plataformas (!), Usando o "depende" fitOn . Legal? Muito legal!

Armadilha número 5: o tráfego para celular não é gratuito 



Como podemos implementar com mais eficiência a interação entre os nós da rede para não gastar todo o tráfego do assinante e não consumir a bateria do dispositivo móvel? Assumimos que a rede pode consistir em 1000 ou mais dispositivos! A opção mais óbvia é usar UDP em vez de TCP, por exemplo, no procedimento de seleção "líder" ou ao enviar pulsações sem dados. O UDP é mais econômico porque utiliza um modelo simples de transferência de dados, sem “handshakes” e confirmações. Bem! O quê mais? Obviamente, E / S assíncrona!

Lemos atentamente a documentação do Kotlin Native.

Para todos os destinos baseados em Unix ou Windows (incluindo Android e iPhone), fornecemos a lib da plataforma posix. Ele contém ligações para a implementação da plataforma do padrão POSIX.

Também lemos atentamente a documentação padrão do POSIX e encontramos uma função incrível que nos permite processar eventos de soquete no modo sem bloqueio! Tendo mergulhado de cabeça no maravilhoso mundo da corotina, soquetes e C Interop, conseguimos realizar um transporte muito eficiente. Super!

E de que forma enviar dados? 



Claro CBOR!

Um formato de dados binários compacto, que, por sorte, é implementado na biblioteca de várias plataformas kotlinx.serialization . Simplesmente fantástico!

Armadilha número 6: serialização 



Desta vez, ocorreu inesperadamente que a kotlinx.serialization não está no androidNative (no androidJvm, é claro que existe). Caros colegas do JetBrains, confirmaram que, no momento, não estão construindo uma biblioteca para o androidNative, e antes do lançamento do Kotlin 1.4, não havia mais lugar para essa tarefa no roteiro. 


O que fazer? Se a montanha não vai para Mohammed, Mohammed vai para a montanha!

Nós mesmos compilamos o kotlinx.serialization para todas as plataformas, incluindo androidNative! O mais surpreendente é que funcionou! 


Armadilha número 7: onde armazenar o log? 



Obviamente, no armazenamento de valor-chave incorporado, mas em qual? Escolhemos lmdbx pela compactação do código, velocidade, multiplataforma e falta de um arquivo WAL. Esta biblioteca é desenvolvida pelos profissionais da Positive Techlologies e é originária da lendária biblioteca LMDB de um dos autores do OpenLDAP Howard Chu. E isso, por sua vez, está enraizado na implementação da árvore B + de Martin Hedenfalk. A propósito, fora da caixa, a biblioteca não foi construída para o androidNative. Coletamos cuidadosamente todos os erros e os autores prontamente forneceram correções - pelas quais agradecemos especialmente a eles!

Armadilha número 8: Interoperabilidade C 



Juntar tudo provou ser uma tarefa não trivial. Além dos soquetes lmdbx e posix, integramos bibliotecas para gerar / validar assinaturas digitais em curvas elípticas e calcular o SHA256 usando o incrível mecanismo C Interop . Em palavras simples - de um aplicativo nativo no Kotlin, você pode chamar a função da biblioteca C, incluindo ponteiros para ponteiros e outras magias, tudo isso parece um pouco estranho.

Por exemplo, chamando getaddrinfo para obter sockaddr.



Como você gosta de Ilon Mask?

Vincular a biblioteca C ao executável nativo do Kotlin é uma missão separada, que também conseguimos concluir, mas não sem muletas. Formamos dinamicamente o arquivo def diretamente no script de construção para indicar o caminho correto para as bibliotecas em relação ao diretório raiz do projeto e, em seguida, substituímos seu descritor (arquivo def) na seção cinterops. No próprio arquivo def, apenas o caminho absoluto é determinado, o que não só pode ter um formato diferente se a montagem for executada em SO diferente, mas, na verdade, nas máquinas locais dos desenvolvedores, é claro, também podem diferir, o que obviamente leva a um erro durante o link.

Sobre a eleição


As principais eleições que realizamos durante o dia. Um pouco mais de 20 especialistas participaram de testes usando nossa rede. Foram avaliados 21 projetos em 5 categorias, ou seja, mais de 100 inscrições com votos para projetos foram adicionadas ao registro.

Conclusão


Como resultado desse pequeno projeto de pesquisa, pudemos provar que é possível manter um registro distribuído em dispositivos móveis! Isso abre muitas possibilidades para o uso dessa tecnologia em dispositivos IoT para organizar a computação de computação em borda. À nossa frente, ainda estamos aguardando testes de carga, vulnerabilidade a ataques e tolerância a falhas. Mas acreditamos que teremos sucesso!

Autores do artigo: arquitetos e desenvolvedores do Centro de P&D da MTS Dmitry Dzyuba, Alexey Vasilenko e Semen Nevrev

All Articles