Gravity Dev Net : 통합 테스트 프레임 워크
Tags: Framework, Gravity, Waves, Blockchain

Gravity의 개발자 네트워크 출시에 대한 일련의 발표를 이어 가면서 새로운 타겟 체인의 빠르고 쉬운 통합을 지원하는 통합 테스트 프레임 워크를 제시합니다.

Gravity 데모 출시 에 대한 기사 에서 언급했듯이 프로토콜의 devnet 버전은 다가오는 Gravity 업데이트의 공개 테스트와 정기적으로 업데이트되는 통합 네트워크와의 동기화를위한 호환성 유지를 위해 설계된 9 월에 출시 될 예정입니다.

이전에 우리 는 토큰을위한 크로스 체인 게이트웨이 인 SuSy 를 소개했습니다 . 오늘, 우리는 Gravity 네트워크에 빠른 타겟 체인 통합을 가능하게하는 통합 테스트 프레임 워크를 공개합니다. 시리즈의 다음 기사에서는 원 클릭 배포 클라우드 플랫폼 또는 사용자 지정 클라우드 제공 업체에서 노드를 시작하기위한 이미지를 설명합니다.

이번 발표에서는 오픈 소스 Gravity 개발자에게 제공하는 새로운 타겟 체인을 추가하는 흐름 인 통합 테스트 프레임 워크를 살펴볼 것입니다.

핵심 용어

오늘 발표 된 통합 프레임 워크는 Gravity를 기반으로하는 블록 체인에 구애받지 않는 교차 체인 자산 전송 게이트웨이 프로토콜 인 SuSy의 다양한 기능을 테스트하는 데 중점을 둡니다. 게이트웨이의 신뢰할 수있는 기반으로서 Gravity는 신뢰할 수없고 분산 된 상태를 유지할 수 있도록합니다. 교차 체인 통신에 필요한 중요한 기능은 가장 기본적인 절차 인 교차 체인 자산 스왑입니다.

Gravity, SuSy 및 이들의 상호 작용을 설명하는 데 사용할 주요 용어를 소개하는 것으로 시작하겠습니다.

ORIGIN-CHAIN ​​: 전송이 시작되는 블록 체인 네트워크. 즉,이 네트워크에서 토큰은 잠기고 잠금 해제됩니다.

DESTINATION-CHAIN ​​: ORIGIN-CHAIN에서 전송되는 블록 체인입니다. 래핑 된 토큰의 발행 및 소각이이 네트워크에서 이루어집니다.

IB-PORT는 포장 된 토큰의 발행 및 소각 기능을 구현하는 DESTINATION-CHAIN의 스마트 계약입니다.

LU-PORT는 원래 토큰을 잠그고 잠금 해제하는 ORIGIN-CHAIN의 스마트 계약입니다.

NEBULA-SC는 Gravity 오라클의 데이터를 수락하고 확인하는 스마트 계약 인 Gravity 프로토콜의 주요 아키텍처 단위 중 하나입니다. 데이터 관련성 (블록 체인 높이) 검사, 적절한 암호화 서명의 가용성 및 전송 된 데이터에 대한 임계 서명 규칙을 구현합니다.

USER-SC는 Gravity 프로토콜의 주요 아키텍처 단위 중 하나입니다. NEBULA-SC에서 확인 된 데이터를 수락하고 사용자 지정 응용 프로그램의 일부인 작업을 생성하는 스마트 계약입니다. SuSy의 경우 LU-PORT와 IB-PORT가 USER-SC의 예입니다.

PULSE-TX는 확인 및 등록에 필요한 서명을 사용하여 데이터에서 NEBULA-SC로 해시를 전송하는 트랜잭션입니다.

SEND-DATA-TX는 NEBULA-SC에서 확인 및 등록한 데이터를 USER-SC로 전송하는 거래입니다.

통합 테스트 프레임 워크

통합 테스트 프레임 워크는 Gravity 네트워크에서 데이터를 제공하는 새로운 Gravity 타겟 체인의 통합을 작성하고 테스트 할 목적으로 설계되었습니다. 전체 사용자 지정 Gravity 네트워크를 배포하고 업데이트 할 필요없이 USER-SC의 로직 또는 NEBULA-SC의 규칙을 간단하게 수정할 수 있습니다. 프레임 워크는 타겟 체인과 직접 관련이없는 Gravity 노드의 초기 구성 및 기능 작업을 해결하는 데 도움이됩니다.

새 대상 체인을 통합하기위한 표준 개발 흐름은 다음 4 단계로 구성됩니다.

특정 블록 체인에서 SYSTEM-SC 및 NEBULA-SC 계약을 구현합니다 . 블록 체인에서 계약을 테스트 하기 위해 동일한 통합 테스트 세트를 만듭니다 . 통합 테스트를 실행하여 계약이 올바르게 구현되었는지 확인하십시오. Gravity Core 인터페이스 IBlockchainAdaptor에 대상 체인 통합을 추가합니다 .

오늘 제시된 프레임 워크는 타겟 체인 통합의 2d 및 3d 단계를 크게 촉진 할 수 있습니다. Gravity 내에서 구현 된 통합 테스트의 핵심 기본 원칙은 다음과 같습니다.

예를 들어 데이터 검증을 구현하는 이더 리움 블록 체인에서 NEBULA-SC 로직의 일부일 수있는 특정 기능을 테스트하려면 이더 리움 노드를 배포하고 계약을 배포하고 필요한 상태를 모의해야합니다. 테스트가 끝날 때 명확하고 잘 지정된 답변을 기대하면서 여러 검증 작업을 호출합니다.

테스트가 성공하면 블록 체인에서 읽기 (추출)를 구현하는 로직에서 시작하여 온 체인 데이터 검증으로 끝나는 구현 된 통합의 모든 단계도 성공한 것으로 간주되어야합니다. 프레임 워크를 사용하여 디버깅 된 코드는 특정 타겟 체인과 스마트 계약을 메인 Gravity 네트워크에 통합하는 데 재사용 할 수 있습니다.

이 기사에서는 SuSy 기반 교차 체인 전송을 예로 사용하여 통합 테스트 프레임 워크 작업을 살펴 ​​보겠습니다.

SuSy 게이트웨이 요약

우선, 토큰 (T)이 래핑 된 토큰으로 발행되는 ORIGIN-CHAIN에서 DESTINATION-CHAIN으로의 전송을 예로 들어 SuSy에서 토큰에 대한 교차 체인 전송 알고리즘이 어떻게 작동하는지 살펴 보겠습니다. (swT) 및 DESTINATION-CHAIN의 수신자 R에게 전송됩니다.

위와 같이 발신자 (S)는 T 토큰의 금액 (A)을 전송하고 DESTINATION-CHAIN에 수신자의 공용 주소를 지정하여 LU-PORT (Lock-Unlock) 스마트 계약과 상호 작용합니다. 게이트웨이 스마트 계약은 자동으로 고유 한 SWAP-ID를 생성하고 등록 상태를 설정합니다. 수신 된 자금은 LU-PORT 스마트 계약에서 차단됩니다.

이 이벤트에 대한 정보는 수신 된 데이터를 처리하고 Gravity에 전달하는 Gravity 네트워크의 서비스 인 추출기에 의해 등록됩니다. Gravity 프레임 워크에서 오라클은 새로운 SWAP-ID 및 지침에 대한 해시 된 데이터를 Gravity 네트워크 유효성 검사기의 서명과 전송 된 컨텍스트의 합법성을 확인하는 확인 계약 (NEBULA-SC)으로 이동합니다.

확인시 SEND-DATA-TX 트랜잭션이 호출되어 수신자 (R)에게 토큰을 발행하고 보내는 데 필요한 데이터 및 지침을 포함합니다.

마찬가지로이 이벤트에 대한 데이터는 Gravity 네트워크 오라클에 의해 처리되며 성공적인 실행에 따라 “처리됨”상태가 설정됩니다. 포크 가능성이 최소 인 특정 블록 수에 도달 한 후 최종 상태를 설정해야 할 수 있습니다.

반대로 DESTINATION CHAIN에서 ORIGIN CHAIN으로 swT 토큰을 전송하고 LU PORT 계약에서 T를 잠금 해제하는 절차는 비슷합니다. 유일한 차이점은 최종 트랜잭션에 있습니다. 즉, IB PORT에서 swT 토큰을 태우고 LU PORT에서 T 토큰을 잠금 해제하는 것이 반전됩니다.

주요 SuSy 개념에 대한 자세한 개요는 SuSy 백서를 참조하십시오 .

통합 프레임 워크 개요

통합 테스트 — 저장소는 SuSy 게이트웨이의 각종 기능의 검증은 USER-SC의 일례 인 IB 및 LU 포트의 기능을 덮는 특히 상술 포함한다. 계약 테스트 는 testing 이라는 범용 라이브러리를 사용하여 Golang 으로 작성됩니다 . 메인 테스트 코드에서 Ethereum 노드에 대한 연결 생성, 구성 읽기 및 계약 배포를위한 함수가 선언됩니다. 테스트를 실행하는 방법을 찾으려면 저장소 내의 Readme를 확인하십시오 . 다음은 구현 된 테스트 기능의 몇 가지 예입니다.

/**

"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)

}

}

구현되는 대상 체인에서 오라클의 로직을 확인하는 가장 중요한 기능 중 하나는 signData입니다 .

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

서명을 수집하고 NEBULA-SC로 전송 된 데이터의 해시 버전에 서명 할 수 있습니다.

관례 적으로 기능의 다양한 측면을 다루는 모든 추가 테스트 스위트는 테스트라는 단어로 시작합니다.

/**

"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

}

}

예를 들어 TestPulseInCorrect2 함수는 전달 된 값의 유효성을 검사하는 데 필요한 최소 ⅔ 서명이 있는지 확인합니다.

요약하면이 기사에서는 새로운 타겟 체인에서 Gravity 계약을 구현하고 테스트 할 수있는 통합 테스트 프레임 워크의 개요를 제공했습니다. 우리는이 프레임 워크가 외부 협력자들의 개발 경험을 촉진하여 Gravity 생태계의 더 빠른 확장을 보장 할 수 있다고 믿습니다. Gravity Dev Net에 대한 다가오는 기사에서는 새로운 타겟 체인을 연결할 때 통합 테스트 작업의 실제적인 측면과 작동중인 Gravity 네트워크로의 흐름 통합에 대해 자세히 살펴볼 것입니다.

자세한 내용은 Gravity 백서를 읽거나 Gravity 웹 사이트를 방문 하거나 Twitter 또는 Telegram 에서 커뮤니티에 가입 하거나 press@gravity.tech에 문의 할 수 있습니다.

출처원문 : https://medium.com/gravity-protocol/gravity-dev-net-integration-tests-framework-d73db8a6bc14

작성자 : Gravity Protocol

번역 : WaveFred