PrincipalTutoriais

Como adicionar um novo blockchain ao Gravity

Veja os detalhes técnicos da implementação de um blockchain à rede Gravity.

Este artigo faz parte da documentação do protocolo Gravity e traz os aspectos técnicos da rede. Além de fornecer uma visão geral, iremos utilizar os exemplos das integrações Ethereum e BSC, para demonstrar o que um desenvolvedor pode encontrar ao integrar um novo blockchain na rede cross-chain Gravity.

Escrevendo Extensões

Para comportar um novo blockchain, é necessária uma sequência de vários commits no Gravity-Tech / gravity-core, além de outros repositórios Github que veremos ao longo deste artigo. As modificações na fonte podem ser divididas em quatro partes:

1. Lógica do Node Gravity Ledger (adaptador)

2. Lógica do Oracle Gravity

3. Contratos Gravity Core (Gravity-Tech/ gravity-core /contracts)

4. Gravity Deployer (Gravity-Tech/gateway-deployer)

É altamente recomendável abrir pull requests nos repositórios específicos, mencionados no guia durante a implementação da sua solução. Isso deve minimizar a reescrita do código-base. Se o seu objetivo é comportar a lógica application-layer na nova rede integrada, várias alterações precisam ser introduzidas no Gravity-Tech/gateway. Essa parte da lógica também inclui escrever um USER-SC (contrato inteligente do usuário) apropriado.

SuSy, um protocolo e interface para gateways de rede cruzada, foi o primeiro produto alfa desenvolvido com base no Gravity. Atualmente, ele conecta à rede de origem Waves com a rede de destino Binance Smart Chain. O SuSy mainnet alpha está disponível em susy.gravity.tech e fornece uma interface de front-end que permite aos usuários negociar tokens USDN da Waves para BSC. Os USDNs na BSC incorporam uma funcionalidade idêntica à do Ethereum, incluindo auto-staking, fornecimento flexível e recompensas de staking, automaticamente distribuídas entre todos os titulares.

Protocolo SuSy

O protocolo SuSy tem como base a confiança no oracle, que é um intermediário na transferência de informações de um blockchain para outro. Do ponto de vista técnico, ao implementar o oracle como um sistema descentralizado trustless, que é o que o protocolo Gravity faz, os gateways herdam essa característica trustless do Gravity. Outra característica da implementação do protocolo SuSy no protocolo do Oracle Gravity é a presença de abstrações e serviços de alto nível.

Além disso, para realizar uma negociação (swap) entre redes, de um token de um blockchain para o outro, nenhum token adicional é necessário, exceto para tokens nativos das redes blockchain correspondentes.

Abaixo está a descrição do algoritmo SuSy para transferência entre redes. Ele mostra uma transferência de um token da ORIGIN-CHAIN (rede de origem) para DESTINATION-CHAIN (rede de destino), onde será emitido como um token swT (wrapped, em português, empacotado) e enviado ao destinatário R em DESTINATION-CHAIN.

Image for post
Esquema de transferência do gateway SuSy entre as redes de origem e destino

Um usuário (S) interage com o contrato inteligente LU-PORT transferindo para ele uma quantidade (A) do token T e especificando o endereço público do destinatário em DESTINATION-CHAIN. O contrato inteligente do gateway cria automaticamente um SWAP-ID exclusivo e define o status registrado. Os fundos recebidos são bloqueados no contrato inteligente LU-PORT.

As informações sobre este evento são tratadas por extratores, serviços do protocolo que processam os dados recebidos e os comunicam ao Gravity. A partir do framework Gravity, o oracle move os dados hash sobre o novo SWAP-ID e as direções da negociação para o contrato de verificação (NEBULA-SC), no qual são verificadas as assinaturas dos validadores da rede Gravity e a legitimidade do contexto transferido.

Após a verificação, é chamada a transação SEND-DATA-TX, contendo um conjunto de dados e instruções para a emissão e envio de tokens swT ao destinatário (R).

Da mesma forma, todos os dados sobre este evento são tratados pelos oracles da rede Gravity e, caso a execução seja bem-sucedida, o status “processed” é definido. Depois de atingir um certo número de blocos em que a probabilidade de uma bifurcação é mínima, pode ser necessário definir o status finalized.

Na direção oposta, para transferir o token swT da DESTINATION CHAIN para a ORIGIN CHAIN e destravar T no contrato LU PORT, o procedimento é semelhante. A única diferença está nas transações finais, ou seja, a queima do token swT no IB PORT e o desbloqueio do token T no LU PORT são revertidos.

No que diz respeito à extensão do SuSy dApp para outros blockchains, o USER-SC desempenha o papel das portas IB (Issue-Burn) e LU (Lock-Unlock). Os métodos USER-SC devem ser invocados pelo NEBULA-SC. Portanto, para ter um aplicativo funcional no protocolo, é necessário:

1. Fornecer novos NEBULA-SC e SYSTEM-SC (contratos Gravity) dentro do Gravity-Tech / gravity-core / contracts.

2. Criar um USER-SC personalizado, que seja consistente com o seu NEBULA-SC personalizado.

Recomendações sobre a extensão do Gravity Core

Para garantir a compatibilidade com uma nova rede, é necessário um conjunto de contratos inteligentes separados. Existem dois tipos de contratos: aqueles usados pelo Gravity Core e contratos específicos de aplicação (por exemplo, SuSy).

Aqui estão alguns exemplos de um IB port e um LU port para Ethereum e Waves:

Os dois novos contratos inteligentes devem ser implementados na nova rede blockchain com a seguinte funcionalidade:

1. O contrato inteligente deve comportar uma call externa da função attachEventData com os seguintes parâmetros: Token ID (opcional, pois diferentes tokens terão seus próprios gateways), Amount, Receiver

2. Apenas uma das 5 contas de administrador deve ser capaz de chamar esse método.

3. Após attachEventData, o Receiver (receptor)deve receber os “wrapped tokens

4. Os titulares de wrapped tokens devem ser capazes de enviar tokens para o endereço do gateway, que deve acionar API (RPC) calls.

Para as instruções 1–4 acima, uma API aberta e pública deve ser implementada.

Depois de criar os contratos, eles precisam ser compilados em bytecode. Os arquivos de bytecode compilados devem ser colocados no diretório “/abi”. Considere verificar o diretório “contracts” dentro do repositório Gravity-Tech/gateway para ver exemplos do código-fonte dos contratos de gateway.

Para adicionar uma implementação para uma nova rede, ela deve ser primeiramente suportada pela própria Rede Gravity. Depois de concluir os contratos inteligentes, é necessário implementar adaptadores de núcleo (core) dentro do repositório gravity-core. A arquitetura do aplicativo Gravity Core é modular, sem qualquer acoplamento rígido. Isso garante a facilidade de estender componentes separados. A princípio, é aceito o uso de qualquer linguagem de programação ou biblioteca, mas no geral é preferencial estender o código-fonte Go, fornecendo implementações para os métodos exigidos, em vez de reescrever do zero.

Para ter uma ideia mais clara das mudanças necessárias para o Gravity Core, analise o pull request “BSC support” para a extensão do node Gravity Ledger. Mais adiante neste artigo, ilustraremos cada parte das alterações descritas do pull request acima.

Pontos principais a serem lembrados:

  • Observar o parâmetro BlocksInterval para a rede específica (necessário para uma “pulsação” correta da rede).
  • Incluir uma implementação dos adaptadores, interface IBlockchainAdaptor, que é a parte mais descritiva de cada rede específica.
  • Tentar iniciar um nó e implantar contratos em um customnet para testar seu adaptador recém-escrito. Após o teste, crie um pull request para Gravity-Tech/gravity-core.
Visão geral das mudanças necessárias com base nas integrações BSC e Ethereum 
cmd/gravity /commands /ledger.go

Na função createApp(), um construtor para instanciação de um novo adaptador deverá ser fornecido:

blockchain

Considere armazenar sua implementação do adaptador em

common/adaptors/[chain_name].go

A interface IBlockchainAdaptor é uma parte importante que está relacionada à comunicação com a rede específica.

Image for post

Vamos revisar como o adaptador Ethereum atende aos requisitos da interface.

Primeiro, declaramos tipos específicos de blockchain e uma estrutura:

Image for post

A implementação do Ethereum contém:

1. A Função WithEthereumGravityContract() que permite a instanciação com base no GravityContractAddress. Ela é invocada na função createApp, mencionada anteriormente.

2. Função EthAdapterWithGhClient(), usada para lançar um oracle Gravity:

Image for post

Também é necessário implementar uma função construtora, como a função NewEthereumAdaptor(), abaixo:

Image for post

Vamos explorar os métodos GetHeight() e Sign(). O primeiro recupera a altura do blockchain e o segundo implementa a assinatura bytes:

Image for post

WaitTx deve bloquear a thread. Um simples padrão for-select permite lidar com casos em que é necessário aguardar uma transação. Atenção: queryTicker é usado aqui para evitar um vazamento goroutine, de forma que o tempo limite de 3 segundos é hardcoded:

Image for post

PubKey() resolve a chave pública do adaptador. ValueType() instancia um Nebula-SC usando um ABI correspondente. Em seguida, ela chama DataType() a fim de resolver o tipo de valor com o qual a Nebula trabalha (Gravity comporta os tipos Byte, Int, String).

Image for post

A implementação AddPulse() contém muitos detalhes técnicos. Mais importante ainda, esse método:

1. Verifica se a contagem de pulsos do Nebula e o valor de BFT são mantidos até o estado de contrato inteligente. Se algum dos valores não existir, o método retornará uma string vazia.

2. Verifica se um número correto de validadores (> = valor BFT) assinou a mensagem.

O método SendValueToSubs() é responsável por enviar valores para os assinantes do Nebula, que tem uma relação one-to-many com os assinantes.

Image for post

SetOraclesToNebula() atualiza oracles do Nebula. A assinatura e a verificação da chave pública são obrigatórias:

Image for post

SendConsulsToGravityContract() é semelhante, mas é usado para consuls e no contexto dos contratos Gravity.

Image for post

SignConsuls(), assim como SignOracles (), são responsáveis por assinar os consuls / oracles atualizados. Ambos os métodos usam praticamente o mesmo algoritmo.

Image for post

LastPulseId() resolve o id do último pulso do Nebula, que é o ID da última ação realizada pelos oracles do Nebula. Este método é crucial para os oracles do Gravity, pois é comumente usado em iteradores que comparam pulsos atuais e anteriores.

LastRound() determina a ação mais recente na rede Gravity. Basicamente, uma rodada é um índice específico de altura do blockchain, onde a rede Gravity alterou o estado por meio de atualização de oracles / consuls, atualização de score ou envio de pulso.

RoundExist() verifica a existência de uma determinada rodada.

Image for post

common/ account/chain.go

Nesse arquivo, uma constante deve ser instanciada. Em outros lugares, onde as constantes existentes são mencionadas, esta nova constante também deve ser adicionada:

blockchain

common/account nebula.go

Este módulo é responsável pela lógica do Nebula.

blockchain

common/account/pubkey.go

Aqui, o comportamento em rede relacionado à instanciação do endereço da conta é especificado.

blockchain

common/storage/consuls.go

Este módulo é responsável pela análise das consul Keys.

blockchain

O componente Oracle
oracle/node/node.go

Uma parte crucial do Gravity Oracle. node.go, assim como de todo o módulo oracle / node, descreve as implementações existentes e configura as restrições oracle.

Leitura adicional

1. https://github.com/Gravity-Tech/gateway

2. https://github.com/Gravity-Tech/gravity-core

3. https://github.com/Gravity-Tech/gateway-deployer

4. WP Protocolo Gravity

5. WP SuSy – https://arxiv.org/pdf/2008.13515.pdf

Para mais informações sobre o Protocolo Gravity, visite o site, siga o Twitter e junte-se à comunidade do Telegram ou entre em contato através do email http://press@gravity.tech/


Faça parte da comunidade Waves Brasil!

Telegram
Twitter
Facebook
Instagram