Element App 正式上线

SubQuery开发者指南丨映射(Mapping)

SubQuery中文站
个人专栏
热度: 17840
一文带你了解映射(Mapping)

NEW

映射函数定义了如何将链数据转换为我们之前在 schema.graphql 文件中定义的优化 GraphQL 实体。

 

映射是用称为 AssemblyScript 的 TypeScript 子集编写的,它可以编译为 WASM (WebAssembly)。

 

  • 映射在 src/mappings 目录中定义,作为为函数导出
  • 这些映射也被导出到 src/index.ts
  • 映射文件在映射处理程序下的 project.yaml 中引用

 

映射函数分为三类:块处理程序、事件处理程序和调用处理程序。

 

块处理程序(Block Handler)

 

每次将新块附加到 Substrate 链时,你都可以使用块处理程序来捕获信息,例如块编号。 为了实现这一点,每个块调用一次定义过的 BlockHandler 。

 

import {SubstrateBlock} from "@subql/types"; export async function handleBlock(block: SubstrateBlock): Promise<void> { // Create a new StarterEntity with the block hash as it's ID const record = new starterEntity(block.block.header.hash.toString()); record.field1 = block.block.header.number.toNumber(); await record.save(); }

SubstrateBlock 是 signedBlock 的扩展接口类型,但还包括 specVersion 和 timestamp。

 

事件处理程序(Event Handler)

 

当一些事件包含在一个新块中时,你可以使用事件处理程序来捕获信息。 该事件是默认 Substrate 运行时间组成部分,一个块可能包含多个事件。

 

处理过程中,事件处理程序将接收一个 Substrate 事件作为参数,带有事件已键入的输入和输出。 任何类型的事件都会触发映射,导致产生捕获数据源的活动。 你应该在清单中使用映射过滤器(Mapping Filtershttps://doc.subquery.network/create/manifest.html#mapping-filters)来过滤事件,减少索引数据所需的时间,并提高映射性能。

 

import {SubstrateEvent} from "@subql/types"; export async function handleEvent(event: SubstrateEvent): Promise<void> { const {event: {data: [account, balance]}} = event; // Retrieve the record by its ID const record = new starterEntity(event.extrinsic.block.block.header.hash.toString()); record.field2 = account.toString(); record.field3 = (balance as Balance).toBigInt(); await record.save();

 

SubstrateEvent 是 EventRecord 的扩展接口类型,除了事件数据之外,它还包括一个 id(这个事件所属的块)和这个块的外来内部信息。

 

调用处理程序(Call Handler)

 

当你想要捕获一些 Substrate 外来上链信息时,会使用到调用处理程序。

 

export async function handleCall(extrinsic: SubstrateExtrinsic): Promise<void> { const record = new starterEntity(extrinsic.block.block.header.hash.toString()); record.field4 = extrinsic.block.timestamp; await record.save(); }

SubstrateExtrinsic(substrate 外来上链信息), 扩展 GenericExtrinsic(泛型外来上链信息) 它被分配一个 id(该外来上链信息所属的块),提供外部属性扩展这个块之间的事件。 此外,它还记录了外来上链信息的成功状态。

 

查询状态(Query States)

 

我们的目标是覆盖用户用于映射处理程序的所有数据源(不仅仅是上述三种接口事件类型)。因

此,我们公开了一些@polkadot/api 接口以增加功能。

 

这些是我们目前支持的接口:

api.query.<module>.<method>() (打开新窗口/opens new window)

will query the current block./将查询当前块。

api.query.<module>.<method>() 将查询当前块。

api.query.<module>.<method>.multi() (opens new window/打开新窗口/)

will make multiple queries of the same type at the current block.

api.query.<module>.<method>.multi() 将在当前块进行多个相同类型的查询。

api.queryMulti() (opens new window/打开新窗口)

will make multiple queries of different types at the current block.

api.queryMulti() 将在当前块进行不同类型的多个查询。

 

这些是我们目前不支持的接口:

  • api.tx.*
  • api.derive.*
  • api.query.<module>.<method>.at
  • api.query.<module>.<method>.entriesAt
  • api.query.<module>.<method>.entriesPaged
  • api.query.<module>.<method>.hash
  • api.query.<module>.<method>.keysAt
  • api.query.<module>.<method>.keysPaged
  • api.query.<module>.<method>.range
  • api.query.<module>.<method>.sizeAt

 

在我们的验证器阈值示例中查看使用此 API 的示例(打开新窗口)https://github.com/subquery/tutorials-validator-threshold 示例适用案例

 

RPC 调用(RPC calls)

 

我们还支持一些 API RPC 方法,这些方法是远程调用,允许映射函数与实际节点、查询和提交进行交互。 SubQuery 的一个核心前提是它是确定性的,因此,为了保持结果一致,我们只允许历史 RPC 调用。

 

JSON-RPC 中的文档(打开新窗口)https://polkadot.js.org/docs/substrate/rpc/#rpc,提供一些将 BlockHash 作为输入参数的方法(例如 at?: BlockHash),这些方法现在是允许的。 我们还修改了这些方法以默认采用当前索引块哈希。

 

对于自定义Substrate链 RPC 调用,请参阅用法 https://doc.subquery.network/create/mapping/#usage

 

模块和库(Modules and Libraries)

 

为了提高 SubQuery 的数据处理能力,我们允许 NodeJS 的一些内置模块在沙箱中运行映射函数,并允许用户调用第三方库。

 

请注意,这是一项实验性功能,你可能会遇到可能对您的映射功能产生负面影响的错误或问题。 请通过在 GitHub(https://github.com/subquery/subql)中创建问题来报告发现的任何错误。

 

内置模块(Built-in modules)

 

目前,我们允许使用以下 NodeJS 模块:assert、buffer、crypto、util 和 path。

 

我们建议只导入您需要的必需方法,而不是导入整个模块。 这些模块中的某些方法可能具有不受支持的依赖项,并且会在导入时失败。


import {hashMessage} from "ethers/lib/utils"; //Good way import {utils} from "ethers" //Bad way export async function handleCall(extrinsic: SubstrateExtrinsic): Promise<void> { const record = new starterEntity(extrinsic.block.block.header.hash.toString()); record.field1 = hashMessage('Hello'); await record.save(); }

第三方库(Third-party libraries)

 

由于我们沙箱中虚拟机的限制,目前我们只支持由 CommonJS 编写的第三方库。

 

我们还支持像 @polkadot/* 这样的混合库,它默认使用 ESM。 但是,如果任何其他库依赖于 ESM 格式的任何模块,虚拟机将不会编译并显示错误。

 

自定义Substrate链(Custom Substrate Chains)

 

SubQuery 可用于任何基于 Substrate 的链,而不仅仅是 Polkadot 或 Kusama。

 

您可以使用自定义的基于 Substrate 的链,我们提供了使用 @polkadot/typegen 自动导入类型、接口和其他方法的工具

 

在以下部分中,我们使用我们的 kitty (https://github.com/subquery/tutorials-kitty-chain) 示例解释集成过程。

 

准备(Preparation)

 

在项目 src 文件夹下创建一个新目录 api-interfaces 以存储所有必需和生成的文件。 我们还创建了一个 api-interfaces/kitties 目录,因为我们想从 kitties 模块在 API 中添加修饰。

 

元数据(Metadata)

 

我们需要元数据来生成实际的 API 端点。 在 kitty 示例中,我们使用了来自本地测试网的端点,它提供了其他类型的端点。 按照 PolkadotJS 元数据设置中的步骤操作,来从其 HTTP 端点检索一个节点的元数据。

 

curl -H "Content-Type: application/json" -d '{"id":"1", "jsonrpc":"2.0", "method": "state_getMetadata", "params":[]}' http://localhost:9933

或利用websocat形成websocket端

 

//Install the websocat brew install websocat //Get metadata echo state_getMetadata | websocat 'ws://127.0.0.1:9944' --jsonrpc

然后,复制黏贴输出到 JSON 文件,在我们 kitty 示例中,我们创建了 api-interface/kitty.json.

 

键入定义(Type definitions)

 

我们假定用户知道具体类型和来自区块链的RPC支持,并在清单中定义。

使用类型设置,我们创建:

  • src/api-interfaces/definitions.ts - this exports all the sub-folder definitions

export { default as kitties } from './kitties/definitions';

  • src/api-interfaces/kitties/definitions.ts - type definitions for the kitties module

 

export default { // custom types types: { Address: "AccountId", LookupSource: "AccountId", KittyIndex: "u32", Kitty: "[u8; 16]" }, // custom rpc : api.rpc.kitties.getKittyPrice rpc: { getKittyPrice:{ description: 'Get Kitty price', params: [ { name: 'at', type: 'BlockHash', isHistoric: true, isOptional: false }, { name: 'kittyIndex', type: 'KittyIndex', isOptional: false } ], type: 'Balance' } } }

 

包(Packages)

 

  • 在package.json文件中,确保加上 @polkadot/typegen 作为开发依赖项,加上 @polkadot/api 作为常规依赖项 (最好是相同版本)。我们还需要 ts-node 作为开发依赖项帮助我们运行脚本。

 

  • 我们加上 脚本运行2种类型:generate:defs 和metadata generate:meta 生成器 (此命令下,元数据可使用这些类型)。

 

下面是package.json的简化版本。确保脚本的包名是正确的,目录是有效的。

 

{ "name": "kitty-birthinfo", "scripts": { "generate:defs": "ts-node --skip-project node_modules/.bin/polkadot-types-from-defs --package kitty-birthinfo/api-interfaces --input ./src/api-interfaces", "generate:meta": "ts-node --skip-project node_modules/.bin/polkadot-types-from-chain --package kitty-birthinfo/api-interfaces --endpoint ./src/api-interfaces/kitty.json --output ./src/api-interfaces --strict" }, "dependencies": { "@polkadot/api": "^4.9.2" }, "devDependencies": { "typescript": "^4.1.3", "@polkadot/typegen": "^4.9.2", "ts-node": "^8.6.2" } }

键入生成(Type generation)

 

现在已完成准备,可以生成类型和元数据。运行以下命令:

 

# Yarn to install new dependencies yarn # Generate types yarn generate:defs

每个模块文件夹(eg /kitties),应有已生成的types.ts 可以定义按照该模块定义的所有接口,且文件 index.ts可以全部将他们导出。

 

# Generate metadata yarn generate:meta

该命令将生成元数据和新的针对 API 的 api- 函数。由于我们不想使用内置的 API, 我们需要在我们的 tsconfig.json.添加明确的覆盖来取代他们。更新后,配置种的路径如是(无备注):

 

{ "compilerOptions": { // this is the package name we use (in the interface imports, --package for generators) */ "kitty-birthinfo/*": ["src/*"], // here we replace the @polkadot/api augmentation with our own, generated from chain "@polkadot/api/augment": ["src/interfaces/augment-api.ts"], // replace the augmented types with our own, as generated from definitions "@polkadot/types/augment": ["src/interfaces/augment-types.ts"] } }

用法(Usage)

 

现在,在映射函数中,我们可以展示元数据和类型是如何装饰API的。RPC端点将支持我们上述模块和方法。要使用自定义rpc调用,请参阅自定义链 rpc 调用一节

 

export async function kittyApiHandler(): Promise<void> { //return the KittyIndex type const nextKittyId = await api.query.kitties.nextKittyId(); // return the Kitty type, input parameters types are AccountId and KittyIndex const allKitties = await api.query.kitties.kitties('xxxxxxxxx',123) logger.info(`Next kitty id ${nextKittyId}`) //Custom rpc, set undefined to blockhash const kittyPrice = await api.rpc.kitties.getKittyPrice(undefined,nextKittyId); }

如果你想将这个项目发布到我们的资源管理器中,请在 src/api-interfaces 中该包含生成文件。

 

自定义链 rpc 调用(Custom chain rpc calls)

 

为了支持自定义链 RPC 调用,我们必须手动为 typesBundle 注入 RPC 定义,允许按规格进行配置。你可以在 project.yml 中定义 typesBundle。请记住,只支持历史类型的调用。

 

... types: { "KittyIndex": "u32", "Kitty": "[u8; 16]", } typesBundle: { spec: { chainname: { rpc: { kitties: { getKittyPrice:{ description: string, params: [ { name: 'at', type: 'BlockHash', isHistoric: true, isOptional: false }, { name: 'kittyIndex', type: 'KittyIndex', isOptional: false } ], type: "Balance", } } } } } }

声明:本文为入驻“火星号”作者作品,不代表火星财经官方立场。
转载请联系网页底部:内容合作栏目,邮件进行授权。授权后转载时请注明出处、作者和本文链接。 未经许可擅自转载本站文章,将追究相关法律责任,侵权必究。
提示:投资有风险,入市须谨慎,本资讯不作为投资理财建议。
免责声明:作为区块链信息平台,本站所提供的资讯信息不代表任何投资暗示,本站所发布文章仅代表个人观点,与火星财经官方立场无关。虚拟货币不具有法定货币等同的法律地位,参与虚拟货币投资交易存在法律风险。火星财经反对各类代币炒作,请投资者理性看待市场风险。
语音技术由科大讯飞提供