Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fibos-tracker 使用指南 #6

Open
WxSimon opened this issue Apr 25, 2019 · 0 comments
Open

fibos-tracker 使用指南 #6

WxSimon opened this issue Apr 25, 2019 · 0 comments
Labels

Comments

@WxSimon
Copy link

WxSimon commented Apr 25, 2019

fibos-tracker 使用指南

什么是 fibos-tracker

  • fibos-tracker 是一个 FIBOS 区块链数据 API 服务框架,基于 fib-app 框架实现
  • 提供对 FIBOS 区块数据的 emitter 监听事件
  • 提供 http 服务,支持 GraphQL 调用
  • 支持使用 ORM 模型 定制自己的数据模型 model,自定义数据表以及自定义 hook 监听数据

为什么使用 fibos-tracker

在 FIBOS 中,进行链下数据存储的方式有两种:

1 配合 history 插件进行链上查询。
2 使用 mongodb 插件将链上数据存储到 mongodb 中。

但是这两个插件在日常使用中都存在少许问题,比如 history 插件需要配合高配置的服务器使用,而 mongodb 的数据不能够定制化存储,数据查询起来也非常费时。而 fibos-tracker 的出现就是为了解决 history 插件和 mongodb 插件的链下数据存储问题。

fibos-tracker 配合 fibos 独有的 emitter 插件,能够将链上的数据实时推送到客户端。由于 emitter 插件只做链上数据推送功能,开启后并不会增大节点的性能消耗,同时在 fibos-tracker 得到链上推送的数据后可以根据业务的需求,自定义数据存储引擎(Mysql、SQLite)、自定义数据存储。

如何使用 fibos-tracker

1 环境支持

fibos 版本: v1.3.1.7+

  • 快速安装:curl -s https://fibos.io/download/installer.sh | sh

存储支持:Mysql、SQLite

2 安装 fibos-tracker

fibos --init
fibos --install fibos-tracker

3 全局配置

配置名 描述 默认值
DBconnString 数据存储引擎配置 使用 SQLite 存储引擎
isFilterNullBlock 是否过滤空块 true
isSyncSystemBlock 是否存储默认数据 false

实例:

const Tracker = require("fibos-tracker");

Tracker.Config.DBconnString = "mysql://root:[email protected]/fibos_chain"; //使用Mysql数据存储引擎

Tracker.Config.isFilterNullBlock = false; //存储空块

Tracker.Config.isSyncSystemBlock = true; //存储默认数据

一个小栗子

学习了解 fibos-trakcer 之后,让我们开始动手编写一个可以定制同步 FIBOS TESTNET 网络区块数据的应用。

  • 代码目录结构如下:

    ├── genesis.json    //genesis 文件
    ├── hooks.js        //自定义存储数据文件
    ├── index.js        //节点配置文件
    ├── server.js       //http 服务文件   
    ├── node_modules
    └── package.json
    

    实例代码地址: fibos-tracker-demo

  • index.js 配置测试网启动节点代码如下:

    const fibos = require("fibos");
    const Tracker = require("fibos-tracker");
    
    const CONFIG = {
        node_dir: "./node",
        DBconnString: "mysql://root:[email protected]/chain_data"
    }
        
    fibos.config_dir = CONFIG.node_dir;
    fibos.data_dir = CONFIG.node_dir;
    
    fibos.load("http", {
        "http-server-address": "0.0.0.0:8871",
        "access-control-allow-origin": "*",
        "http-validate-host": false,
        "verbose-http-errors": true
    });
    
    fibos.load("net", {
        'p2p-listen-endpoint': "0.0.0.0:9876",
        "p2p-peer-address": ["p2p.testnet.fo:80"]
    });
    
    fibos.load("producer");
    fibos.load("chain", {
        "delete-all-blocks": true,
        "genesis-json": "./genesis.json"
    });
    
    fibos.load("chain_api");
    fibos.load("emitter");
    
    Tracker.Config.DBconnString = CONFIG.DBconnString;
    const tracker = new Tracker();
    
    tracker.use(require("./hooks.js"));
    
    tracker.emitter(fibos);
    
    fibos.start();
    

    CONFIG 内的存在两个配置: node_dir: 区块数据存储位置,DBconnString: 数据存储连接字符串。

  • genesis.json 文件:

    {
    "initial_timestamp": "2018-08-01T00:00:00.000",
    "initial_key": "FO6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
    "initial_configuration": {
        "max_block_net_usage": 1048576,
        "target_block_net_usage_pct": 1000,
        "max_transaction_net_usage": 524288,
        "base_per_transaction_net_usage": 12,
        "net_usage_leeway": 500,
        "context_free_discount_net_usage_num": 20,
        "context_free_discount_net_usage_den": 100,
        "max_block_cpu_usage": 200000,
        "target_block_cpu_usage_pct": 1000,
        "max_transaction_cpu_usage": 150000,
        "min_transaction_cpu_usage": 100,
        "max_transaction_lifetime": 3600,
        "deferred_trx_expiration_window": 600,
        "max_transaction_delay": 3888000,
        "max_inline_action_size": 4096,
        "max_inline_action_depth": 4,
        "max_authority_depth": 6
    },
    "initial_chain_id": "68cee14f598d88d340b50940b6ddfba28c444b46cd5f33201ace82c78896793a"
    }
    
  • hooks.js 文件:

    let defines = [db => { }];
    let hooks = {};
    
    module.exports = {
        defines: defines,
        hooks: hooks
    }
    

如何 hook 链上数据

假设我们存在这么一个业务场景:
我需要记录和一个账号相关的所有资金流水,即和这个账号之间的所有转账操作。那么基于这个业务需求,我们可以在 hooks.js 中定制自己需要存储的数据表结构和 hook 数据的方式。

  • 根据业务定义表结构
let defines = [db => {
    return db.define('transfer', {
        from: {
            required: true,
            type: "text",
            size: 12
        },
        to: {
            required: true,
            type: "text",
            size: 12
        },
        quantity: {
            required: true,
            type: "text",
            size: 256
        },
        memo: {
            type: "text",
            size: 256
        }
    }, {});
}];

可以看到,定义了一个名为 transfer 的表。表中的字段如下:

字段 类型 备注 实例
from String 转账发起方 fiboscouncil
to String 转账接收方 fibos
quantity String 转账数量 1.0000 FO
memo String 转账附言 hello world
默认字段 - - -
id Number 自增长id 1
createdAt Date 记录创建时间
updatedAt Date 记录更新时间
  • 根据业务自定义hook数据

从上面的业务需求出发,我们需要将所有和我转账相关的链上记录都存储下来,所以我们需要监听链上所有 transfer 动作记录,同时在 action 的data中,需要判断 from、to 为我的动作记录。代码如下:

let account = "fibos";

let hooks = {
    "eosio.token/transfer": (db, messages) => {
        let Transfer = db.models.transfer;
        try {
            db.trans(() => {
                messages.forEach((m) => {
                    let data = m.act.data;
                    if (data.from !== account || data.to !== account) return;
                    Transfer.createSync(data);
                });
            });
        } catch (e) {
            console.error("eosio.token/transfer Error:", e);
        }
    }
}

从上面代码中可以看到,我们存储数据的条件为:转账的 from 为 "fibos" 账号或者转账的 to 为 "fibos" 账号。当条件满足时,直接将 data 内的数据存储到 Transfer 表中。

经过上面两个步骤,我们已经使用 fibos-tracker 完成了一个简单的区块链数据定制存储,下面就让程序跑起来吧。
在当前目录下执行: fibos index.js。执行完成后,Tracker 节点开始同步测试网的区块数据,过程中所有和 "fibos" 这个账号的转账记录都将保存到 Transfer 表中。

如何查询存储的数据

完成上面两个步骤后,我们的需求数据都已经保存到了Mysql中。但是我们往往需要将数据提供给前端展示,所以需要提供出一个 HTTP server。这个时候我们可以直接使用 fib-app 的特性,使用 graphql 来进行查询。

  • 编写HTTP server

新建文件 server.js,我们主要使用 fib-app 提供HTTP服务供前端使用 graphql 来进行数据查询,实例代码如下:

const http = require("http");
const Tracker = require("fibos-tracker");

Tracker.Config.DBconnString = "mysql://root:[email protected]/chain_data"
const tracker = new Tracker();
    
tracker.use(require("./hooks.js"));

let httpServer = new http.Server("", 8080, [
	(req) => {
		req.session = {};
	}, {
		'/app': tracker.app,
		"*": [function(req) {}]
	},
	function(req) {}
]);

httpServer.crossDomain = true;
httpServer.run(() => { });

console.notice("http server run port: 8080");

保存完毕后,执行 fibos server.js 便启动了服务,其中服务的端口为:8080,此时终端打出 http server run port: 8080 证明服务启动正常。

  • 数据查询

1 使用 FIBOS GraphQL 客户端查询

const http = require("http");

let graphql = function(body) {
	return http.post(`http://127.0.0.1:8080/1.0/app/`, {
		headers: {
			'Content-Type': 'application/graphql'
		},
		body: body
	});
}

2 Web GraphQL 查询

let graphql = function(body) {
    $.ajax({
        type: "POST",
        url: "http://127.0.0.1:8080/1.0/app",
        data: body,
        headers: {
            "Content-Type": "application/graphql"
        },
        success: (res) => {
            console.log("success");
        },
        error: (res) => {
            console.log("error");
        }
    });
}

请求体:「获取所有记录」

{
    find_transfer(
		where:{}
	   ) {
        id
        from
        to
        quantity
        memo
     }
}`

框架说明

fibos-tracker 默认 DB 说明

为了完善区块数据存储,框架会默认存储 blocks、transactions 以及 actions 的基础数据,三张表的数据结构如下:

blocks 表数据

字段 类型 备注
id Number 自增长id
block_num Number 区块高度
block_time Date 区块时间
producer_block_id String 区块hash
producer String 区块 producer
status String 可逆状态
createdAt Date 记录创建时间
updatedAt Date 记录更新时间

transactions 表数据

字段 类型 备注
id Number 自增长id
trx_id String 交易hash
rawData JSON 原始数据
block_id String 区块高度(关联blocks)
createdAt Date 记录创建时间
updatedAt Date 记录更新时间

actions 表数据

字段 类型 备注
id Number 自增长id
contract_name String 合约名称
action String 动作名称
authorization Array 授权用户
data JSON 交易data
transaction_id Number 交易事务id(关联 transactions表)
parent_id Number 上级action id(关联 actions 表)
createdAt Date 记录创建时间
updatedAt Date 记录更新时间

fibos-tracker 内部逻辑

fibos-tracker 的数据存储是配合 FIBOS 的插件 emitter 来进行实现的。当开启 emitter 插件的节点进行同步时,FIBOS 会将 transaction、block 、irreversible_block 数据通过 emitter 插件推送到客户端,而 fibos-tracker 需要做的就是将插件推送过来的数据拼装后存储。

tracker.use 介绍
tracker.use 属于 fibos-tracker 中重要的api,与 fib-app 的支持,可以实现自定义 hook 数据监听,使用 ORM 模型自定义 DB 数据存储,实例如下:

tracker.use({
	defines: [(db) => {
		// ORM DB Define
	}, (db) => {
		// ORM DB Define
	}],
	hooks: {
		"eosio.token/transfer": (db, messages) => {
			// hook Tracker messages
		},
		"eosio/newaccount": (db, messages) => {
			// hook Tracker messages
		}
	}
});

可以看到在 tracker.use 中接受的对象数据类型为:

{
    defines: [],
    hooks: {}
}

其中 defines 中定义的为 ORM 模型数据表,这就意味着我们可以定义很多数据表结构满足我们的业务需求,而 hooks 主要实现的为数据的过滤,其中过滤规则如下:

hooks 过滤规则:

  • 过滤某个合约:如:eosio
  • 过滤某个合约的某个动作:如:eosio/newaccount

hooks 的 messages 数据说明:
为了方便 hooks 业务研发,传递 messages 时做了优化:

  • 满足过滤规则的所有 action 合并成数组传递
  • 数组内每一个满足过滤规则的 action 包含 本层 action 以下所有 inline_action,并且如果存在上层 action,将携带 parent 属性,标识上层 parent 的 action 数据。

注:每层 parent_id 是该层 action 上级的 DB 自增长 id。

举一个返回示例结构:

[
  {
    "inline_traces": [
      {
        "parent": ... parent_id => 1
        "inline_traces": [
          {
            "parent": ... parent_id => 2
                "parent": ... parent_id => 1
          },
          {
            "parent": ... parent_id => 2
                "parent": ... parent_id => 1
          }
        ]
      }
    ]
  }
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants