DesenvolvimentoPrincipal

Rede de desenvolvedores do Gravity apresenta framework de testes

Devnet do Gravity apresenta framework de testes para integração fácil e rápida de blockchains.

A versão de desenvolvedores do Protocolo Gravity, lançada em setembro, foi desenvolvida para testar atualizações e manter a sincronização com as redes, que também passam por atualizações regularmente. Anteriormente, apresentamos o gateway SuSy, para transferência de tokens entre blockchains diferentes. Agora, estamos executando testes para permitir uma integração rápida dos blockchains ao Gravity. Neste artigo iremos explorar o framework para testes de integração e o fluxo que oferecemos aos desenvolvedores para adicionar novos blockchains ao Gravity. Nos próximos posts da série iremos descrever imagens para o lançamento de um node em uma plataforma na nuvem ou em qualquer provedor de nuvem personalizado.

TERMOS-CHAVE

O framework de integração, lançado no dia 30 de setembro, tem como objetivo testar diferentes aspectos do gateway SuSy. A rede Gravity fornece uma base confiável para que os gateways permaneçam descentralizados e trustless. Transferência de ativos é a principal funcionalidade da comunicação entre blockchains diferentes.

Vamos começar apresentando a terminologia chave que usaremos para explicar a interação Gravity-SuSy:

  • ORIGIN-CHAIN (“cadeia de origem”): a rede blockchain na qual origina-se a transferência, ou seja, nesta rede, os tokens são bloqueados e desbloqueados.
  • DESTINATION-CHAIN (“cadeia de destino”): o blockchain para o qual a transferência da ORIGIN-CHAIN é feita. É nessa rede que ocorre a emissão e queima do token wrapped (“encapsulado”).
  • B-PORT é um contrato inteligente na DESTINATION-CHAIN, ​​que implementa a funcionalidade de emissão e queima do token ‘wrapped’ (encapsulado).
  • LU-PORT é um contrato inteligente na ORIGIN-CHAIN ​​que bloqueia e desbloqueia o token original
  • NEBULA-SC é uma das principais unidades arquitetônicas do protocolo Gravity, um contrato inteligente que aceita e verifica dados de oráculos da rede. A unidade também implementa verificações de relevância de dados (blockchain height), disponibilidade de assinaturas criptográficas apropriadas e regras de assinatura com limite para dados transmitidos.
  • USER-SC outra unidade arquitetônica principal do protocolo Gravity. É um contrato inteligente que aceita dados verificados no NEBULA-SC e produz uma ação que faz parte de um aplicativo customizado. No caso do gateway SuSy, LU-PORT e IB-PORT são exemplos de USER-SC.
  • PULSE-TX é uma transação que irá transferir o hash dos dados para o NEBULA-SC, com as assinaturas necessárias para verificação e registro.
  • SEND-DATA-TX é uma transação para transferência de dados verificados e registrados do NEBULA-SC para o USER-SC.
Framework de testes de integração

O framework de testes de integração busca, como o próprio nome diz, testar integrações de novos blockchains, nos quais os dados são fornecidos pela rede Gravity. Desse modo, simplifica-se o processo de fazer modificações para lógica do USER-SC ou nas regras do NEBULA-SC, sem a necessidade de implantar e atualizar uma rede Gravity totalmente personalizada. O framework ajuda a resolver a tarefa de configuração inicial e funcionalidade dos nodes Gravity, que não estão diretamente relacionados aos blockchains de destino. O fluxo de desenvolvimento padrão para integração de um novo blockchain consiste em quatro etapas:

1) Implementar no blockchain os contratos SYSTEM-SC e NEBULA-SC;

2) Criar um conjunto idêntico de testes de integração, para testar os contratos no blockchain;

3) Rodar os testes de integração, para verificar se os contratos estão implementados corretamente;

4) Adicionar a integração do blockchain de destino na interface IBlockchainAdaptor do Gravity .

O framework apresentado hoje pode facilitar muito a segunda e a terceira etapa da integração ao blockchain-alvo. O princípio básico por trás de qualquer teste de integração implementado no Gravity é o seguinte:

Para testar uma funcionalidade específica, que pode ser, por exemplo, uma parte da lógica do NEBULA-SC no blockchain Ethereum, que implementa a verificação de dados, seria necessário implantar um node Ethereum, implantar contratos, simular um estado necessário e chamar várias operações de verificação, esperando uma resposta definitiva e bem específica ao final do teste.

Se o teste for bem-sucedido, então todos os estágios da integração implementada, começando pela lógica que implementa a leitura do blockchain (extração) e terminando com a verificação de dados na rede, também são considerados bem-sucedidos.

Neste artigo, vamos usar o exemplo de como trabalhar com a estrutura de teste de integração, tendo como base as transferências entre blockchains feitas pelo gateway SuSy.

Revisão – SuSY Gateway

Para começar, vamos ver como funciona, no gateway, um algoritmo para transferência de tokens entre blockchains diferentes, usando como exemplo, uma transferência da ORIGIN-CHAIN para DESTINATION-CHAIN, no qual um token (T) será emitido como um token wrapped empacotado (swT) e enviado ao destinatário R (recipient) em DESTINATION-CHAIN.

Gravity Protocol

Como mostrado acima, um remetente S (sender) interage com o contrato inteligente LU-PORT (Lock-Unlock) transferindo para ele uma quantidade A (amount) 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 (id de transferência) 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 registradas pelos extratores, serviço da rede Gravity que processa os dados recebidos e os comunica à Gravity. A partir do framework Gravity, o oráculo move os dados hash sobre o novo SWAP-ID e os direciona 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 emissão e envio de tokens ao destinatário R (recipient). Da mesma forma, os dados sobre este evento são tratados pelos oráculos da rede Gravity e, caso a execução seja bem-sucedida, o status “processado” é 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 finalizado.

Na direção oposta, para transferir o token swT da DESTINATION CHAIN (destino) para a ORIGIN CHAIN (origem) 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.

Para uma visão mais detalhada dos principais conceitos do gateway SuSy, consulte o white paper.

Visão geral do framework de integração

O repositório de testes de integração contém verificações de várias das funções do gateway SuSy descritas acima, cobrindo especialmente a funcionalidade das portas IB e LU, que são exemplos de USER-SC. Os testes para os contratos são escritos em Golang, usando uma biblioteca denominada testing. No código de teste principal, as funções são declaradas para criar conexões com o node Ethereum, ler configurações e implantar contratos. Para saber como executar os testes, verifique no repositório o Readme. Abaixo estão alguns exemplos das funções de teste implementadas:

 

/**
  "addresses", "config", "ethConnection" 
  and contract variables are 
  top-scope variables used throughout the whole test
**/

/**
  Read config.
**/
func ReadConfig() {
	var err error
	config, err = helpers.LoadConfiguration()
    if err != nil {
        log.Fatal(err)
    }
	addresses, err = helpers.LoadAddresses()
    if err != nil {
        log.Fatal(err)
    }
}

/**
  Connect to ethereum node
**/
func ConnectClient() bool {
    conn, err := ethclient.Dial(config.Endpoint)
    if err != nil {
        log.Fatal(err)
        return false
    }
    ethConnection = conn
    return true
}

/**
  Bind required contracts
**/
func BindContracts() {
	var err error
	nebulaContract, err = nebula.NewNebula(common.HexToAddress(addresses.Nebula), ethConnection)
    if err != nil {
        log.Fatal(err)
    }
	nebulaReverseContract, err = nebula.NewNebula(common.HexToAddress(addresses.NebulaReverse), ethConnection)
    if err != nil {
        log.Fatal(err)
    }
	ibportContract, err = ibport.NewIBPort(common.HexToAddress(addresses.IBPort), ethConnection)
    if err != nil {
        log.Fatal(err)
    }
	luportContract, err = luport.NewLUPort(common.HexToAddress(addresses.LUPort), ethConnection)
    if err != nil {
        log.Fatal(err)
    }

O signData é uma das funções mais importantes para verificar a lógica dos oráculos, no blockchain-alvo que está sendo implementado

func signData (dataHash [32]byte, validSignsCount int, isReverse bool) (* big.Int)

Ele coleta assinaturas e permite a assinatura de uma versão hash dos dados transmitidos para um NEBULA-SC. Por convenção, todos os conjuntos de testes adicionais que cobrem vários aspectos da funcionalidade começam com a palavra Teste.

/**
"the Pulse - is a transaction that will transfer hash from data to NEBULA-SC with
necessary signatures for verification and registration." - SuSy WP.
**/

/**
1. Generate pulse ID.
2. Check that pulse ID has been signed. (5 signs required)
3. Check existence in nebula contract
4. Check that pulse data matches the one we generated
**/
func TestPulseSaved(t *testing.T) {
d := Random32Byte()
pulseId := signData(d, 5, false)
if pulseId == nil {
t.Error("can't send signed data")
} else {
pulseData, err := nebulaContract.Pulses(nil, pulseId)

if err != nil {
t.Error("can't get pulse hash")
}

if d != pulseData.DataHash {
t.Error("data mismatch")
}
}
}

/**
Check Pulse for 3 signs validness
**/
func TestPulseCorrect3(t *testing.T) {
d := Random32Byte()
if signData(d, 3, false) == nil {
t.Error("can't send signed data")
}
}

/**
Check Pulse for 2 signs validness
**/
func TestPulseInCorrect2(t *testing.T) {
d := Random32Byte()
if signData(d, 2, false) != nil {
t.Error("transaction 2/3 valid sigs should be rejected")
}
}

/**
Data signing function.
**/
func signData(dataHash [32]byte, validSignsCount int, isReverse bool) (*big.Int) {
var r [5][32]byte
var s [5][32]byte
var v [5]uint8

privateKey, err := crypto.HexToECDSA(config.OraclePK[0])
if err != nil {
log.Fatal(err)
}

publicKey := privateKey.Public()
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
log.Fatal("error casting public key to ECDSA")
}

fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
nonce, err := ethConnection.PendingNonceAt(context.Background(), fromAddress)
if err != nil {
log.Fatal(err)
}

gasPrice, err := ethConnection.SuggestGasPrice(context.Background())
if err != nil {
log.Fatal(err)
}

auth := bind.NewKeyedTransactor(privateKey)
auth.Nonce = big.NewInt(int64(nonce))
auth.Value = big.NewInt(0) // in wei
auth.GasLimit = uint64(300000) // in units
auth.GasPrice = gasPrice

for position, validatorKey := range config.OraclePK {
validatorEthKey, _ := crypto.HexToECDSA(validatorKey)
sign, _ := crypto.Sign(dataHash[:], validatorEthKey)

copy(r[position][:], sign[:32])
copy(s[position][:], sign[32:64])

if (position < validSignsCount) {
v[position] = sign[64] + 27
} else {
v[position] = 0 // generate invalid signature
}
}

var tx *types.Transaction

if !isReverse {
tx, err = nebulaContract.SendHashValue(auth, dataHash, v[:], r[:], s[:])
if err != nil {
return nil
}
} else {
tx, err = nebulaReverseContract.SendHashValue(auth, dataHash, v[:], r[:], s[:])
if err != nil {
return nil
}
}

receipt, err := bind.WaitMined(context.Background(), ethConnection, tx)
if err != nil {
log.Fatal(err)
}

if len(receipt.Logs) < 1 {
return nil
}

if !isReverse {
pulseEvent, err := nebulaContract.NebulaFilterer.ParseNewPulse(*receipt.Logs[0])
if err != nil {
log.Fatal(err)
}
return pulseEvent.PulseId
} else {
pulseEvent, err := nebulaReverseContract.NebulaFilterer.ParseNewPulse(*receipt.Logs[0])
if err != nil {
log.Fatal(err)
}
return pulseEvent.PulseId
}
}

Por exemplo, a função TestPulseInCorrect2 verifica se pelo menos ⅔ assinaturas estão presentes, o que é necessário para validar o valor passado.

Portanto, este artigo apresentou uma visão geral do framework de testes de integração, que permite implementar e testar contratos Gravity no blockchain de destino. Acreditamos que este framework pode facilitar a experiência de desenvolvimento para colaboradores externos, garantindo uma expansão mais rápida do ecossistema Gravity.

Nos próximos artigos sobre Gravity Dev Net, examinaremos mais de perto os aspectos práticos de trabalhar com testes de integração ao conectar- se a um novo blockchain, bem como integração de fluxo em uma rede Gravity em funcionamento.

Para mais informações, você pode ler o whitepaper do Gravity, visitar o site, entrar para a comunidade no Twitter ou Telegram ou entrar em contato através do email press@gravity.tech


Faça parte da comunidade Waves Brasil!

Telegram
Twitter
Facebook
Instagram