万向区块链技术研究报告 | The Graph技术调研

区块链技术迅猛发展,新想法、新概念、新名词层出不穷。万向区块链因此推出“技术研究报告”专栏,定期与大家分享在区块链行业创新及热门技术方面的研究成果,带领大家第一时间研究学习新技术,紧跟技术发展趋势,探索发掘技术的应用价值。

本期技术研究将带大家了解区块链数据索引及查询协议The Graph。

本文作者:万向区块链通用架构技术部 杜超

一、概念

1.1、什么是 The Graph

The Graph是一个去中心化的协议,用于索引和查询区块链的数据,从以太坊开始。它使查询难以直接查询的数据成为可能。

The Graph网络对Web3的查询层和API层进行了去中心化,消除了dApp开发者目前面临的取舍难题:到底是开发一个高性能应用,还是开发一个完全去中心化的应用。

目前,开发者可以在自己的基础架构上运行一个Graph节点,也可以在我们的托管服务上开发一个。其中,开发者构建和部署从Web3数据源提取数据并为其编制索引的子图。目前已经有许多领先的以太坊项目创建了子图,包括Uniswap、ENS、DAOstack、Synthetix和Moloch等。在The Graph网络中,任何索引器都能够通过抵押Graph代币(GRT)参与到网络中,并在提供查询服务的过程赚取费用和通货膨胀奖励。

用户则按照使用次数进行付费,使用日益增长的索引器,此做法证明了供需规律也适用于该协议提供的服务。

Graph可以让数据查询变得简单易操作。任何人都可以构建和发布开放的API,使数据易于访问。

1.2 The Graph网络中的协议角色

协议中包括以下角色:

  • Consumers: 用户向索引器支付查询费用。他们通常是终端用户,但也可能是集成The Graph的网络服务或中间件。
  • Indexers:  检索人是The Graph的运行节点。其动力是赚取财务奖励。
  • Curators: 策展人使用GRT代币来指明哪些子图值得索引。他们通常是开发者,也可能是支持他们在使用服务的终端用户,或者纯粹出于经济动机的一种角色。策展人通过自己手中的GRT代码为某些subgraph投票,也叫信号标记,使这些子图更容易被搜索到。
  • Delegators: 委托人向某个Indexer质押GRT代币,赚取一部分通货膨胀奖励和费用,他们无需亲自运行一个Graph节点。这类角色主要出于经济动机。
  • Fisherman: 执法者时刻检查查询响应是否正确,以此保护网络。渔夫动机是利他的,因此The Graph将率先为网络提供执法者服务。
  • Arbitrator: 在争议解决期间,仲裁员决定是否对索引器进行罚没。他们可能出于经济或利他动机。
  • 前四种是网络中利己的,是比较市场化的角色。后两种是利他的,一般由The Graph负责或引入其它机制。

为了鼓励索引器为新子图建立索引,策展人必须介入并发出信号标记,表明新子图值得索引。

1.3Graph代币在协议中主要用途

  • 索引器质押:索引器质押Graph代币,以便自身被查询市场所发现,同时在执行工作过程中提供经济安全。
  • 策展人信号:策展人将Graph代币质押到策展市场中,预测哪些API对网络具备价值,他们因为正确预测而获得奖励。

二、工作原理

2.1 GRAPH是如何工作的

Graph基于子图描述索引以太坊数据的内容和方法,称为子图清单。子图描述定义了子图相关的智能合约,这些合约中所定义的事件,以及如何将事件数据映射到The Graph将存储在其数据库中的数据。

下图给出了关于数据流的更多细节,一旦一个子图清单被部署,将转化为处理以太坊事务。

  1. Transactions:去中心化应用程序通过智能合约上的交易向以太坊添加数据。
  1. Events: 智能合约在处理交易时发出一个或多个事件。
  1. Graph node: 图节点持续扫描以太坊的新块和它们可能包含的子图数据。
  1. Mapping: 图节点在这些块中为你的子图找到以太坊事件,并运行你提供的映射处理程序。映射是一个WASM模块,用于创建或更新Graph Node存储的数据实体,以响应以太坊事件。
  1. Store: 通过映射的数据被保存到节点上
  1. Queries: 分散的应用程序使用节点的GraphQL端点向Graph Node查询从区块链索引的数据。

2.2The Graph 架构

The Graph网络包含以太坊上的智能合约,以及链下运行的各种其他服务和客户端。

三、如何索引合约事件

3.1、合约代码部署

JavaScript

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract GravatarRegistry {

  event NewGravatar(uint id, address owner, string displayName, string imageUrl);

  event UpdatedGravatar(uint id, address owner, string displayName, string imageUrl);

  struct Gravatar {

    address owner;

    string displayName;

    string imageUrl;

  }

  Gravatar[] public gravatars;

  mapping (uint => address) public gravatarToOwner;

  mapping (address => uint) public ownerToGravatar;

  function createGravatar(string memory _displayName, string memory _imageUrl) public {

    require(ownerToGravatar[msg.sender] == 0);

    gravatars.push(Gravatar(msg.sender, _displayName, _imageUrl));

    uint id = gravatars.length - 1;

    gravatarToOwner[id] = msg.sender;

    ownerToGravatar[msg.sender] = id;

    emit NewGravatar(id, msg.sender, _displayName, _imageUrl);

  }

  function updateGravatarName(string memory _displayName) public {

    require(ownerToGravatar[msg.sender] != 0);

    require(msg.sender == gravatars[ownerToGravatar[msg.sender]].owner);

    uint id = ownerToGravatar[msg.sender];

    gravatars[id].displayName = _displayName;

    emit UpdatedGravatar(id, msg.sender, _displayName, gravatars[id].imageUrl);

  }

}

部署合约:

网络: Rinkeby network

合约地址:0x0a89E2093d371A1AC387346C63b462BE3aD40F00

3.2the graph注册subgraph

the graph studio

Bash

# 1、INSTALL GRAPH CLI USING NPM

npm install -g @graphprotocol/graph-cli

# OR USING YARN

yarn global add @graphprotocol/graph-cli

# 2、INITIALIZE SUBGRAPH

graph init --studio kktest

# 3、AUTHENTICATE IN CLI

graph auth  --studio d2a22d5c11******b7ef6

# 4、BUILD SUBGRAPH

cd kktest

graph codegen && graph build

# 5、DEPLOY SUBGRAPH

graph deploy --studio kktest

3.3、初始化subgraph配置

TheGraph中定义如何为数据建立索引,称为Subgraph,它包含三个组件:

  1. Manifest 清单(subgraph.yaml) - 定义配置项
  1. Schema 模式(schema.graphql) - 定义数据
  1. Mapping 映射(mapping.ts) - 定义事件到数据的转换

在kktest目录下会生成 subgraph.yaml 文件和 schema.graphq 文件

subgraph.yaml

YAML

specVersion: 0.0.1

schema:

 file: ./schema.graphql

dataSources:

 - kind: ethereum

  name: GravatarRegistry

  network: rinkeby

  source:

   address: "0x0a89E2093d371A1AC387346C63b462BE3aD40F00"

   abi: GravatarRegistry

  mapping:

   kind: ethereum/events

   apiVersion: 0.0.5

   language: wasm/assemblyscript

   entities:

    - NewGravatar

    - UpdatedGravatar

   abis:

    - name: GravatarRegistry

     file: ./abis/GravatarRegistry.json

   eventHandlers:

    - event: NewGravatar(uint256,address,string,string)

     handler: handleNewGravatar

    - event: UpdatedGravatar(uint256,address,string,string)

     handler: handleUpdatedGravatar

   file: ./src/mapping.ts

3.4 定义模式(schema.graphql)

编写自己的模式 schema.graphql,模式是GraphQL数据定义。允许我们定义实体及其类型,这里我们在schema.graphql定义一个Gravatar实体:

Haskell

type Gravatar @entity {

  id: ID!

  owner: Bytes!

  displayName: String!

  imageUrl: String!

}

3.5、定义映射(mapping.ts)

TheGraph中的映射文件定义了如何将传入事件转换为实体的函数。它用TypeScript的子集AssemblyScript编写。因此可以将其编译为WASM(WebAssembly),以更高效,更便携式地执行映射。

需要定义subgraph.yaml文件中每个handler函数,因此在我们的例子中,我们需要实现函数:handleNewGravatar 及 handleUpdatedGravatar。

graph codegen 可以生成解析事件的代码及模式实体代码,生成代码src/mapping.ts。

TypeScript

import { NewGravatar, UpdatedGravatar } from '../generated/Gravity/Gravity'

import { Gravatar } from '../generated/schema'

export function handleNewGravatar(event: NewGravatar): void {

  let gravatar = new Gravatar(event.params.id.toHex())

  gravatar.owner = event.params.owner

  gravatar.displayName = event.params.displayName

  gravatar.imageUrl = event.params.imageUrl

  gravatar.save()

}

export function handleUpdatedGravatar(event: UpdatedGravatar): void {

  let id = event.params.id.toHex()

  let gravatar = Gravatar.load(id)

  if (gravatar == null) {

    gravatar = new Gravatar(id)

  }

  gravatar.owner = event.params.owner

  gravatar.displayName = event.params.displayName

  gravatar.imageUrl = event.params.imageUrl

  gravatar.save()

}

在handler函数,我们使用事件的ID 创建Gravatar实体。并使用相应的字段填充数据,最后需要.save()来存储实体。

3.6、部署 Subgraph

Bash

graph deploy \

    --debug \

    --node https://api.thegraph.com/deploy/ \

    --ipfs https://api.thegraph.com/ipfs/ \

    kktest

3.7DAPP查询数据

JavaScript

import { createClient } from 'urql'

const APIURL = 'https://api.thegraph.com/subgraphs/name/username/subgraphname'

const tokensQuery = `

  query {

    tokens {

      id

      tokenID

      contentURI

      metadataURI

    }

  }

`

const client = createClient({

  url: APIURL,

})

const data = await client.query(tokensQuery).toPromise()

四、FAQ

  1. the graph 网络上一个indexer最小需要质押要求?

一个indexer最小质押数设置为100K GRT。

  1. indexer的收来源是什么?

查询费用回扣 —— 为网络上的查询提供服务的费用。

索引奖励 —— 通过每年3%的协议膨胀产生,索引奖励被分配给索引人员,他们正在为网络的子图部署进行索引。

五、参考文档

  1. the graph官网
  1. the graph studio
  1. define a subgraph
  1. 如何索引合约事件
  1. 深入理解Graph (上)
  1. 深入理解Graph(下)
  1. The Graph Network in Depth

猜你喜欢

转载自blog.csdn.net/Venachain/article/details/123405751
今日推荐