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.
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:
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.
Vamos revisar como o adaptador Ethereum atende aos requisitos da interface.
Primeiro, declaramos tipos específicos de blockchain e uma estrutura:
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:
Também é necessário implementar uma função construtora, como a função NewEthereumAdaptor(), abaixo:
Vamos explorar os métodos GetHeight() e Sign(). O primeiro recupera a altura do blockchain e o segundo implementa a assinatura bytes:
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:
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).
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.
SetOraclesToNebula() atualiza oracles do Nebula. A assinatura e a verificação da chave pública são obrigatórias:
SendConsulsToGravityContract() é semelhante, mas é usado para consuls e no contexto dos contratos Gravity.
SignConsuls(), assim como SignOracles (), são responsáveis por assinar os consuls / oracles atualizados. Ambos os métodos usam praticamente o mesmo algoritmo.
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.
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:
common/account nebula.go
Este módulo é responsável pela lógica do Nebula.
common/account/pubkey.go
Aqui, o comportamento em rede relacionado à instanciação do endereço da conta é especificado.
common/storage/consuls.go
Este módulo é responsável pela análise das consul Keys.
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
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/