如果你不知道 TheGraph 是什么,为什么 TheGraph 是有用的,可以读读那篇文章,在那篇文章详细解释了为什么需要 TheGraph 以及如何在中心化托管服务(Hosted Service)中使用它。 简短的说:区块链上的事件是一种非常有效的添加数据的方式,而不必将其存储在每个节点上(这很昂贵)。事件是通过使用bloom filter[6]来实现的,客户端能够解析区块和交易,以快速找到其要找的数据。但仍然需要解析大量的区块,所以一个替代方案是让服务器对这些数据进行索引并将其存储在数据库中。加上把GraphQL[7]作为一种非常方便的查询语言放在上面,这就是 The Graph 了,不过是作为一种中心化服务。 总之,The Graph 允许以一种更有效的方式查询区块链的数据。这在构建前端和显示区块链中发生的数据时是非常重要的,而不必将数据直接存储在智能合约中。 现在,The Graph 开始了一个新的去中心化的网络,也增加了更多的功能。托管服务(Hosted Service)将在 2023 年第一季度结束[8],所以现在是时候了解 The Graph 去中心化网络是如何工作的,如何使用它,以及作为开发者的你需要了解有哪些新功能。 向去中心化过渡最初 The Graph 只有一个中心化的托管服务,但从长远来看,这当然不是我们想要的。毕竟,如果你完全依赖一个中心化的服务器来查询数据,那么 Dapp 的意义何在。尽管这仍然比完全中心的基础设施要好,但我们仍然可以做得更好! 为了解决这个问题,The Graph 拥有自己的去中心化网络和自己的 GRT ERC-20 代币。 1. 协议角色- 消费者(comsumer):消费者是向索引器发送查询并为这项服务付费的人。消费者可以是直接的终端用户,例如正在使用 Dapp 或中间件服务的用户。
- 索引器(Indexer): 索引器是那些实际提供运行服务器、索引事件并将其存储在数据库中的服务的人。作为回报,他们将从消费者那里获得费用。
- 策展人(Curator):策展人将确定哪些是值得被索引的 subgraph(发出索引信号),并为特定的 subgraph 支付自己的 GRT。他们将根据联合曲线赚取一部分查询费用。策展人可能是 subgraph 的开发者,他想让自己的 subgraph 被索引。
- 委托人(Delegator):委托人可以质押 GRT 到索引器,以赚取其查询费用的分成 ,例如:无法运行索引器的人可以做委托人参与生态收益。
消费者可以根据运行时间和价格等因素,自由地选择使用哪个索引器。当然,一个 Dapp 甚至可以使用多个索引器以获得最高的安全性。 2. GRT 代币GRT 代币本身是用于质押: - 索引器用质押 GRT 的目的是为了抵制女巫攻击,以及允许对他们的不良行为进行罚款。索引器被期望其抵押 GRT 的比例(相对总质押)与他们为网络所做工作的比例相符。这不是强制的,而是来自于收集交易费用的设计,将质押比例和费用的函数返还给参与者,设计灵感来自于0x 设计[9]。
- 委托人将他们的 GRT 借给索引器,并收到部分查询费作为回报。他们不能因为索引器的不当行为而被削减他们的 GRT,而且每个索引器通过委托获得的 GRT 是有限制的。
以下是各方参与示意图: 3. 激励新 subgraph为了帮助引导那些还没有需求的新 subgraph,GRT 质押是通货膨胀的,新铸造的代币被给予那些对查询需求非常低的 subgraph 进行索引的索引器。为了确保索引器确实在做索引这些低需求 subgraph 的工作,有一个额外的机制叫做索引证明(POI)。POI 是对 subgraph 状态哈希的签名,索引器需要它来获得奖励。POI 被乐观地接受,但以后可以被罚没。在网络的第一个版本中,通过治理设置的仲裁员将决定这些争端。 subgraph 配置(称为清单)通常被上传到 IPFS。但是,当清单根本无法使用时,我们能做什么?那么就不可能验证这些 POI 了。对于这一点,有一个subgraph 可用性 Oracle。它将查看几个有名的 IPFS 端点,如果清单不可用,subgraph 就不能用于索引器获取奖励。 4. 支付通道常用的支付通道(状态通道技术)[10]是一种很好的支持规模支付方式。像 The Graph 那样为每个查询付费时,我们显然需要这样的东西。在 The Graph 中,支付通道有一个额外的安全层:WAVE。 WAVE 支付的几个部分: - Work(工作) :锁定小额付款,并对要进行的工作进行描述。
- Attestation(证明) :工作+签署的证明,可以乐观地解锁小额付款。
- Verification(验证) :通道外的证明,用于可能的惩罚。
- Expiration(过期) :锁定的小额支付可以过期。
查询验证 现在的问题是,你如何验证 The Graph 查询的正确性?最初在 The Graph 中,这是由链上争议解决程序处理的,通过仲裁决定。渔夫们将寻找不正确的查询,并将证明与保证金一起提交给仲裁。 在未来,与其依靠仲裁来解决查询纠纷,不如利用多项式承诺或 Merkle 树等技术的加密证明来保证查询的有效性。同样,即使是 POI 也可以使用类似于乐观 Rollup 的机制来自动验证。两者结合起来,就可以完全摆脱目前网络中比较集中的对仲裁的需要。 如何部署到 TheGraph 的去中心化网络Subgraph Studio[11]是迈向去中心化 TheGraph 的第一步,但它仍然包含中心化的组件。在其目前的设计中,支付是由 The Graph 团队直接处理的,你创建一个 API 密钥来查询数据。以后一旦实现了可验证的查询,用户将直接付款。 如果你有已经在托管服务上的 subgraph,可以看看这个迁移指南[12]。 - 进入https://thegraph.com/studio/,点击创建subgraph。
- $ npm install -g @graphprotocol/graph-cli。
- 在一个现有的项目内部署一个合约,例如:$ npx hardhat run scripts/deploy.ts --network rinkeby。
- $ graph init --studio <created-subgraph-name>。
- 如果 Etherscan 合约没有被验证,你也可以直接传递 ABI 文件,例如:artifacts/contracts/Game.sol/Game.json。
- 保存正确的 schema.graphql 和 schema.ts。
- $ graph auth --studio <my-subgraph-id> (见 Subgraph Studio UI)
- $ graph codegen && graph build。
- $ graph deploy --studio <my-subgraph-id>。
现在你可以在 Subgraph Studio 中点击 发布。要获得 Rinkeby testnet GRT,请到The Graph Discord[13] #roles,点击'T',然后到#testnet-faucet,输入!grt <自己的以太坊地址>。然后你就可以立即开始策展 subgraph 了。请注意,在写这篇文章的时候,还不支持通过去中心化网络对 Rinkeby 进行实际查询,但对于主网来说是支持的。 开发者需要了解的新功能- 新的 AssemblyScript 版本:Graph 已经更新了用于编写映射的 AssemblyScript。如果你习惯于旧版本,你可以遵循迁移指南[16]。
- 调试克隆(Forking):一个名为debug forking[17]的新功能允许克隆一个已部署的 subgraph 并改变其对特定块的映射。因此,如果你部署了一个新的 subgraph 而它失败了,现在你可以从该区块进行本地调试,而不是从头开始重新同步,浪费大量的时间。
- 新的区块链:虽然在去中心化网络上还不能使用,但 The Graph 已经为新公链增加了索引支持,最需要了解的是Cosmos[18]、NEAR[19] 和Arweave[20]。
- subgraph 映射单元测试:一个名为matchstick[21]的新单元测试功能可以测试 subgraph 映射,详情见下文。
单元测试 subgraph 映射要开始在现有的 subgraph 中进行单元测试。 - 安装 matchstick:
- $ yarn add --dev matchstick-as。
- 安装 Postgresql:
- $ brew install postgresql (Mac) 或
- $ sudo apt install postgresql(Linux)
- 或对于 Windows见文档[22]
- 在/tests文件夹中创建测试文件。
- 运行测试:$ graph test。
以下是之前的上一篇 TheGraph 文章[23]的 Bet 映射的单元测试例子: import {
assert,
describe,
clearStore,
test,
newMockEvent,
} from "matchstick-as/assembly/index";
import { BetPlaced } from "../generated/Game/Game";
import { Address, BigInt, ethereum } from "@graphprotocol/graph-ts";
import { handleBetPlaced } from "../src/mapping";
function createBetPlacedEvent(
player: string,
value: BigInt,
hasWon: boolean
): BetPlaced {
const mockEvent = newMockEvent();
const BetPlacedEvent = new BetPlaced(
mockEvent.address,
mockEvent.logIndex,
mockEvent.transactionLogIndex,
mockEvent.logType,
mockEvent.block,
mockEvent.transaction,
mockEvent.parameters,
null
);
BetPlacedEvent.parameters = new Array();
const playerParam = new ethereum.EventParam(
"player",
ethereum.Value.fromAddress(Address.fromString(player))
);
const valueParam = new ethereum.EventParam(
"value",
ethereum.Value.fromUnsignedBigInt(value)
);
const hasWonParam = new ethereum.EventParam(
"hasWon",
ethereum.Value.fromBoolean(hasWon)
);
BetPlacedEvent.parameters.push(playerParam);
BetPlacedEvent.parameters.push(valueParam);
BetPlacedEvent.parameters.push(hasWonParam);
BetPlacedEvent.transaction.from = Address.fromString(player);
return BetPlacedEvent;
}
describe("handleBetPlaced()", () => {
test("Should create a new Bet entity", () => {
const player = "0x7c812f921954680af410d86ab3856f8d6565fc69";
const hasWon = true;
const mockedBetPlacedEvent = createBetPlacedEvent(
player,
BigInt.fromI32(100),
hasWon
);
handleBetPlaced(mockedBetPlacedEvent);
const betId =
mockedBetPlacedEvent.transaction.hash.toHex() +
"-" +
mockedBetPlacedEvent.logIndex.toString();
// fieldEquals(entityType: string, id: string, fieldName: string, expectedVal: string)
assert.fieldEquals("Bet", betId, "id", betId);
assert.fieldEquals("Bet", betId, "player", player);
assert.fieldEquals("Bet", betId, "playerHasWon", "true");
assert.fieldEquals(
"Bet",
betId,
"time",
mockedBetPlacedEvent.block.timestamp.toString()
);
clearStore();
});
});
- 你可以首先使用newMockEvent创建模拟事件。
然后以你喜欢的方式修改模拟的事件。 然后从映射中调用处理事件函数。 断言新的状态符合预期。 事后清理存储。
你可以在https://github.com/soliditylabs/the-graph-studio-example 找到完整的例子。
本翻译由 Duet Protocol[24] 赞助支持。 原文:https://soliditydeveloper.com/thedecentralizedgraph
|