yjjnls/Notes

View on GitHub
block chain/Basic/exchange.md

Summary

Maintainability
Test Coverage
# 数字货币交易所
- [数字货币交易所](#%E6%95%B0%E5%AD%97%E8%B4%A7%E5%B8%81%E4%BA%A4%E6%98%93%E6%89%80)
    - [传统交易所的运作流程](#%E4%BC%A0%E7%BB%9F%E4%BA%A4%E6%98%93%E6%89%80%E7%9A%84%E8%BF%90%E4%BD%9C%E6%B5%81%E7%A8%8B)
    - [数字资产交易平台](#%E6%95%B0%E5%AD%97%E8%B5%84%E4%BA%A7%E4%BA%A4%E6%98%93%E5%B9%B3%E5%8F%B0)
        - [数字货币交易平台的三种类型](#%E6%95%B0%E5%AD%97%E8%B4%A7%E5%B8%81%E4%BA%A4%E6%98%93%E5%B9%B3%E5%8F%B0%E7%9A%84%E4%B8%89%E7%A7%8D%E7%B1%BB%E5%9E%8B)
    - [中心化模式下的数字货币交易平台](#%E4%B8%AD%E5%BF%83%E5%8C%96%E6%A8%A1%E5%BC%8F%E4%B8%8B%E7%9A%84%E6%95%B0%E5%AD%97%E8%B4%A7%E5%B8%81%E4%BA%A4%E6%98%93%E5%B9%B3%E5%8F%B0)
        - [两套账本](#%E4%B8%A4%E5%A5%97%E8%B4%A6%E6%9C%AC)
        - [数字货币交易所包含哪些系统模块](#%E6%95%B0%E5%AD%97%E8%B4%A7%E5%B8%81%E4%BA%A4%E6%98%93%E6%89%80%E5%8C%85%E5%90%AB%E5%93%AA%E4%BA%9B%E7%B3%BB%E7%BB%9F%E6%A8%A1%E5%9D%97)
            - [各自模块的特征](#%E5%90%84%E8%87%AA%E6%A8%A1%E5%9D%97%E7%9A%84%E7%89%B9%E5%BE%81)
        - [涉及的技术栈](#%E6%B6%89%E5%8F%8A%E7%9A%84%E6%8A%80%E6%9C%AF%E6%A0%88)
            - [交易过程](#%E4%BA%A4%E6%98%93%E8%BF%87%E7%A8%8B)
    - [数字货币钱包服务](#%E6%95%B0%E5%AD%97%E8%B4%A7%E5%B8%81%E9%92%B1%E5%8C%85%E6%9C%8D%E5%8A%A1)
        - [数字货币钱包的分类](#%E6%95%B0%E5%AD%97%E8%B4%A7%E5%B8%81%E9%92%B1%E5%8C%85%E7%9A%84%E5%88%86%E7%B1%BB)
        - [扫描区块技术 Block scan](#%E6%89%AB%E6%8F%8F%E5%8C%BA%E5%9D%97%E6%8A%80%E6%9C%AF-block-scan)
        - [区块浏览器](#%E5%8C%BA%E5%9D%97%E6%B5%8F%E8%A7%88%E5%99%A8)
        - [数字货币钱包服务](#%E6%95%B0%E5%AD%97%E8%B4%A7%E5%B8%81%E9%92%B1%E5%8C%85%E6%9C%8D%E5%8A%A1)


先来说说交易模式,交易模式分为两种:场内交易和场外交易。
1.   场内交易,又称成交单优先模式,指的是有交易场所将买卖双方聚集在一起,进行竞价交易的交易方式。这种交易方式中,交易场所负责用户的 **资产托管、交易撮合、资产结算、履约担保**等功能。
2.   场外交易,又称报价单优先模式,指的是买卖方不通过第三方而直接成为交易对手的交易方式。

这里介绍的交易所主要是第一种交易模式,即场内交易模式。

## 传统交易所的运作流程
区块链领域的交易所与传统金融的结构十分相似,但也存在了一些区别,为了讲清楚这种区别,我先简述一下证券交易的结构。

传统一般有四种角色,分别是投资者,在国内大部分是散户,其次是证券公司,接着是交易场所,这里的交易场所仅指场内交易场所,最后是证监会。它们的关系如下图。

![](https://github.com/yjjnls/blockchain-tutorial-cn/blob/master/img/27.1.png)

**通常投资者在证券公司开户,然后投资者委托证券公司下单,证券公司接受投资者下单并收取一定的佣金,然后买卖单由证券公司在交易场所进行交易,这个过程叫做委托,投资者是委托者,证券公司是受托者。**

换句话说,**普通投资者是无法直接接触交易所的**,这是因为交易所采取会员制,只有成为交易所的会员且有交易席位,**才可以接收其他投资者的委托**。

而这里交易所是非盈利性的事业法人,它接受国家证券主管机关证券委员会及证监会的领导、管理和监督。证券交易所本身并不参与证券交易,不能决定证券价格。

所以对于投资者来说,只和证券公司有直接联系,这里的流程可以简述为:

1.  投资者到证券公司开户,然后下单给证券公司;
2.  证券公司再将这些订单传递到交易所,订单在交易所完成撮合成交;
3.  接着交易所将订单和成交数据传到证券登记结算公司,结算公司据此进行资金和证券账户的结算;
4.  结算结果再回传给证券公司,证券公司通过自己的平台显示给投资者,例如成交信息,账户余额等等。

## 数字资产交易平台
数字货币交易平台也叫做数字资产交易平台,主要为人们提供交易数字货币或数字资产撮合交易服务。

这种交易市场是一种典型的买单、卖单交易市场,所以这些平台以场内交易为主,通过提供买单卖单的撮合服务然后赚取手续费,这个过程中也发展出了层次丰富的金融工具,例如杠杆交易。

![](https://github.com/yjjnls/blockchain-tutorial-cn/blob/master/img/27.2.png)

人们提供了买、卖交易数字资产的过程,这个过程是一种数字货币交换到另外一种数字货币的过程。

这种交易市场通常是数字货币对数字货币的,当然这显然不够,所以这就催生了法币对数字货币的交易,这也是中心化交易平台的由来,目前流行的法币与数字货币的交易有美元、欧元、日元、澳元等。

目前市场上,中心化的交易平台占据了绝大部分。从功能上来看, **`中心化交易所`一般都会提供以下业务模块:充提数字货币、`资产托管、交易撮合、资产结算`、杠杆交易、KYC 等**。

上面我们介绍过传统交易平台的结构,借此我们也可以归纳比较出数字货币平台的与传统交易平台的优劣势。

**传统交易所依赖券商,属于金字塔型结构,用户聚集在券商处,资金托管在银行,证券登记结算在第三方机构,本质上交易场所只提供了撮合、监管辅助、信息辅助等功能。**

而由于数字货币的去中心化特性,数字货币交易平台属于扁平型结构,资产管理本身就内置在交易所内部。

所以任何一个交易平台都不需要第三方机构登记结算数字货币,这带来了业务架构上的易部署特性,用户流量和资金流量最终全部汇聚在数字货币交易所。

如果我们从这些业务模块出发,结合技术架构来看,中心化的数字货币交易平台更接近互联网应用的技术架构,它本质上是金融交易系统与互联网网络服务相结合,具有以下优势特性:

1.  能够支持海量高并发实时撮合交易 ;
2.  用户终端全平台支持,从桌面到移动端不一而足 ;
3.  数字资产的超高流动性;
4.  平台可以获取超高的利润;
5.  价值发现功能。

虽然利润和流量都超大,但中心化交易平台也面临了一些天花板,例如动不动就被爆出被黑事件,这也着实让用户提心吊胆,所以我归纳了一些如下缺点:

1.  **内幕操作**,交易所相当于是一个黑盒,内部操作不透明带来了巨大风险;
2.  **监管缺失**,信息披露不完善,项目方跑路风险;
3.  **持仓风险**,用户的资金完全托管在交易所;
4.  **趋利优先**,区块链本身的发展是在盈利之后才会被考虑的;
5.  **交易所破产倒闭风险**。

总的来说,数字货币交易所目前是区块链与现实的接触面最大的接触点,也是数字金融中的最重要一环。

### 数字货币交易平台的三种类型
其实在数字货币交易领域,交易平台是不必局限在中心化这一种实现方案的,这由于交易所也是由很多业务模块组成的,它是 **把某些模块进行去中心化,而不是整体去中心化**,所以实际上它可以有三种实现类型。

*   模式一:中心化模式,**资金管理系统与区块链账本各自记账**,包含充币提币的过程,需要人工干涉。模式一的代表有很多,现在市场上主流的交易所主要的有 Binance、Bittrex、Bitfinex、Gate.io、Huobi.pro、Okex、Kraken 等,它们为数字货币和数字资产的发展提供了全球化、7*24 小时不间断的流动性,也就是上文我们介绍的数字货币交易平台。
*   模式二:半中心化模式,**选择把资产托管、订单簿维护、交易撮合、清结算四大步骤中的一个或多个放在链上进行**。例如交易撮合与行情模块是中心化的,而资产是在区块链上直接结算的。代表有 0x Project、Keyber Network 这些前沿的项目。
*   模式三:完全去中心化模式 ,比模式二更进一步,所有模块都是去中心化的,包括`交易撮合`和行情部分。代表有`比特股和 EtherDelta`。

模式二和模式三我们统称为`去中心化交易所`。去中心化交易所模式简单,它只需要承担主要的 **资产托管、撮合交易及资产清算**。而不需要承担像中心化交易所所需要承担的`非交易的功能`像 **账户体系、KYC、法币兑换**等,这是目前区块链数字资产领域研究的一个前沿方向。

去中心化交易所,一般都采用 **智能合约方式编写交易撮合和清结算逻辑**,并且将合约代码开源出来供所有人查看。这样的话,代码是公开的,又是运行在链上,就可以在一定程度上保障用户的资金安全,防止内部交易。

但是,去中心化交易所也存在不稳定因素。为了获得用户信心,合约代码是需要开源的,谁都可以拿这份代码资金单独部署一套交易所,长期下去会导致多个交易平台出现,多个交易平台的竞争将导致市场流动性的分割,进而导致单个交易市场的深度不足,影响到参与用户的交易机会成本,也会带来价格不稳定的因素。
​
同时,去中心化交易所因为区块链本身的特性和不同去中心化交易所的技术方案不同,目前很难在技术层面集中解决 **去中心化交易所带来的流动性分割的问题**,一般是通过市场自动平衡价格的力量来解决。
​
此外,去中心化交易所的困难在于:交易所,特别是支持高频交易、量化交易等高级金融功能的交易所,它产生的数据量是非常大的,它对数据的一致性,及时性的要求是非常高的,这与目前各种区块链实现的特性从本质上就是冲突的。
​
​从另一个角度来看,虽然去中心化交易所是一个很好的方向,但可能会脱离掉用户的真实需求,正如我前面谈论“去中心化”这个概念所说,终端用户也许根本不关心平台是否是去中心化的。在这里,用户的主要诉求体现在下面几点:

1.  用户终端产品的界面操作流畅、响应速度快、用户体验好;
2.  资产结算速度快,如果存在充币提币的话取决于审核过程;
3.  订单簿挂单的深度要好,可以快速成交订单;
4.  托管的资产安全性高,如果有的话。

以上四点,目前模式一的交易平台的体验完胜模式二和模式三,这也是中心化交易平台流行的主要原因。

## 中心化模式下的数字货币交易平台
### 两套账本
数字货币交易平台的技术基本沿用了金融交易技术中的系统架构,只是把原来针对 **法币和证券**(或平台代币)的部分,也就是我们通常称作 **资金管理系统**的部分,完全替换为针对数字货币的数字货币管理系统,换句话说,就是换了一套内部账本。

然而我们知道,区块链本身也是用来记账的,也算作一种金融账本,所以 **一套内部账本,一套区块链本身的账本**,这里就出现了两套账本,如何管理这两套账本,就是资金管理系统的首要任务。

![](https://github.com/yjjnls/blockchain-tutorial-cn/blob/master/img/28.1.png)

(图示 数字货币交易所)

解释一下这张图,图的左边表示了多个区块链账本,右边的数字货币交易所有自己的内部账本,这两套账本是独立的。

交易所内部的账本记录的是交易 Trade,这个交易是由用户挂单,接着被撮合引擎撮合成交而产生的,而区块链账本上的交易 Transaction,是当且仅当用户发起充币提币请求并被执行时,才会产生的。

这两种交易都用了中文“交易”来表示,但是它们所属的语境不同,前者的交易表示的是金融交易语境下的资产交换,也就是 Deal;后者表示的是区块链上的技术概念,表示资产转移的一次记账过程,上述特意用英文以表区别,希望你能够区分。

### 数字货币交易所包含哪些系统模块
一个数字货币交易所的后端其实至少有四部分构成:**Web 业务逻辑系统、交易撮合系统、运营后台管理系统、资金管理系统**。资金管理系统其实就是刚才说到的内部账本。

![](https://github.com/yjjnls/blockchain-tutorial-cn/blob/master/img/28.2.png)

1.  Web 业务逻辑系统:这个模块通常包含了用户账户模块、登录网关、账户安全管理、KYC 认证、行情推送等等,这个模块更偏向用户,也与通常的电商账户系统十分类似。
2.  交易撮合系统:这个模块是一个交易所的核心模块,为所有的用户提供订单撮合。
3.  运营后台管理:这个模块是一个交易所运营人员使用的系统,交易所内部人员才能访问。
4.  资金管理系统:这里的资金管理其实包含了三部分,第一部分是 **法币的支付网关**,可能需要对接银行或第三方支付机构;第二部分就是 **数字货币钱包管理**,它提供了大部分主流数字货币的支付功能;第三部分是 **用户持仓信息**,所谓持仓就是用户持有多少数字货币,这个是记录在数据库中的,不需要与区块链保持一致,但是要求交易所的总账是平的。

#### 各自模块的特征
*   Web 业务系统与我们常见的电商系统无异,主要是用户账户以及简单的业务逻辑,重点是可扩展性,业务要求比较弹性。
*   交易撮合系统本质上是一个高并发的计算系统,特点是系统性能高和稳定性好,其中订单队列可以是编程语言中的数据容器,也可以是内存数据库。
*   运营后台系统在整个交易所生命周期的早期并不凸显重要性,但是运营后台系统恰恰是交易所中后期发展的核心系统,重点在数据准确,要求网络安全性高和可扩展性好。
*   资金管理系统包含用户持仓状态,以及数字货币钱包服务,它是一个交易平台中安全性要求最高的系统,资金管理系统往往要搭配一个内存数据库,其中数字货币钱包服务也可以拆出来做成独立子系统,甚至可以改造成整个公司的内部区块浏览器,因为钱包服务需要设计成多个钱包实例,并统一所有的币种钱包接口。
下图是一个交易所可能的结构

![](https://github.com/yjjnls/blockchain-tutorial-cn/blob/master/img/28.3.png)

上图中,MatchingGroup 相当于是交易撮合系统;Web-Group 相当于 Web 业务逻辑系统;Back-office 相当于后台管理系统;AssetsManagement 相当于是资金管理系统。

### 涉及的技术栈
如果我们再将刚才提到的各个模块细分,会看到以下的功能。

![](https://github.com/yjjnls/blockchain-tutorial-cn/blob/master/img/28.4.png)

按照上图的细分功能,我们可以得到哪些技术支持一目了然。

首先是 Server 需要数据库作为支撑,其次是 Restful API 作为基础通讯协议,并且集成钱包相关的技术,撮合引擎为 Sever 提供撮合服务。

在这里面,例如需要 SMS 系统,所以可以使用云服务中的 SMS 组件,这些都可以是成熟的通用组件技术。我们可以发现中心化交易使用的技术与互联网技术并无不同。

把这些通用组件塞到下图中各个层次和大模块当中,所以最终一个交易所的详细架构可能是下图的样子。

![](https://github.com/yjjnls/blockchain-tutorial-cn/blob/master/img/28.5.png)

首先是存储,持久存储通常可以选择 MySQL,撮合相关的模块由于要避免接触磁盘 IO,所以需要为撮合模块提供 Redis 类型的内存存储,二者需要保证最终一致性。

撮合和行情部分,几乎与传统技术无异,行情推送可以类比到其他推送系统,只是频率更高,一般首选 Websocket 技术。

**这与传统互联网应用的最大区别里主要是数字货币钱包管理**,这块完全是新的内容,对安全性、易用性提出了相当高难度的挑战,**这里也是交易所资金托管的根本**,所以如何管理好大量数字货币,往往要结合运营、内部管理制度、冷热钱包技术一起才能做好交易所的资金管理。

那么用户是如何挂单的,又是如何产生区块链交易的呢?我们来看一看。

#### 交易过程
用户 A 拿 0.01BTC 换取了 B 的 10 个 ETP 的过程究竟是什么样的呢,我来举一个例子。

1.  用户 A 挂 10ETP 买单,出价 0.01BTC 经过 Web 业务系统进入撮合系统订单簿 ETP-BTC 买单队列,等待撮合成交,同时资金管理系统冻结 0.01BTC。

2.  用户 B 挂 10ETP 卖单,出价 0.01BTC 经过 Web 业务系统进入撮合系统订单簿 ETP-BTC 卖单队列,与步骤 1 中 A 的订单撮合匹配成功,**生成 Trade**,同时 **资金管理系统结算对应资产**,B 的资产变化为增加 0.01BTC 并减少 10ETP, A 增加 10ETP 并减少 0.01BTC。

3.  成交 Trade 以及资产变化通过资金管理系统写 RDB 数据库,形成成交记录,同时更新行情,数据库记录可供用户和运营后台管理系统查询。 **要注意的是这一步并不是登记到区块链上**。

>交易所本身所产生的交易(trade)是由挂单撮合而来,可以认为是**交易所内部记账**,是一个中心化的账本(通常是RDB数据库)。  
>当且仅当用户发起充值/提现请求时,才会产生区块链交易(transaction),这时候才可以上区块浏览器上查询到交易。

4.  用户 B 经过 Web 业务系统发起提币请求,请求提取 10ETP 进入自己的数字货币钱包,这个请求进入 **资金管理系统**,交易所运营人员可通过运营后台观察到这笔请求,运营人员审核用户 B 的信息,比如实名认证是否正常等。

5.  提币请求进入 **运营系统**后,如果通过审核,则 **资金管理系统**会冻结用户 B 的 10ETP,同时将提币请求发起给数字货币钱包服务系统,也就是 WalletGroup,子系统发起区块链上的交易 x(Transaction), 等待交易被打包,并根据更新提币审核状态,供用户查看。(交易所内部钱包的开发营运人员或拿到保存私钥机器权限的黑客,能挪动用户的资产)
​

6.  数字货币钱包服务根据区块信息查询交易 x 是否被打包,如果已经打包,则 **资金管理系统**将完全把用户 B 被冻结的 10ETP 直接抹成 0,更新提币状态最终为完成,提供区块交易 ID 以供用户和运营后台系统进行查询。

在步骤 3 中,我们可以看到用户所持有的资产,相当于是交易所对用户的负债,但这也只是数据库中的一个数字,并不是真正的链上资产。

在步骤 6 中,我们看到区块链上的“交易”与步骤 3 中的“交易”完全不是一个概念,同时用户的资产是否安全,完全取决于交易平台的技术是否安全,对交易所是否信任。

再来看看充值阶段。简单来说,充值是与提币相反的过程,不同的是,充值不需要审核,一般数字货币交易所的原则都是“宽进严出”,在充值过程中,交易平台通常不直接使用数字货币钱包检测用户是否充值到账,而是使用“扫块”(block_scan) 这一方法检测用户的充值。


## 数字货币钱包服务
### 数字货币钱包的分类
目前市面上的数字货币钱包有很多种,看起来似乎有些眼花缭乱,不过,我们可以将它们进行分类后,再快速了解。

下图展示了按照不同属性区分的区块链钱包。

![](https://github.com/yjjnls/blockchain-tutorial-cn/blob/master/img/31.1.jpg)

左一是按照用户端平台划分的钱包,这种钱包实际是在服务端运行的,用户端的钱包实际上只是一个代理,所以用户不需要关心钱包的细节,使用起来十分方便,典型的例子是各种在线钱包。

左二是按照货币类型分类的钱包,主要是指这款钱包到底是否支持多币种,这里的多币种可以是基于以太坊 ERC20 Token 的同一个区块链上的多币种,也可以是集成了比特币和以太坊等不同区块链的多币种。

右二是按照私钥存储的方式来区分的钱包,实际上这里主要涉及了用户私钥是否被平台托管,如果不托管直接存储在用户端,也就是硬件、终端设备、纸质记录,这些都可以被称为 On-chain 的钱包;如果用户无法接触到私钥,私钥被托管在平台,那么这种钱包也被称为 Off-chain 的钱包。

右一是按照访问方式进行分类的钱包,例如可以多个人共同管理,同时它也是需要多重签名支持的钱包,否则就变成了个人私有的钱包。

以上的分类并不是绝对的,一个钱包可以兼具以上不同的属性,例如某个 Mobile 钱包是提供 On-chain 的,也提供多重签名、提供多币种的钱包,这种钱包通常就是业界比较流行的钱包类型。

但是,对于平台方来说,上述钱包类型可能不足以支持自己平台的需求,并发挥出最佳的功效。毕竟作为平台来说,对高可用、分叉检测、区块确认的要求是远远高于普通钱包的,这样的问题又是如何解决的呢?这就引入了一项新的技术。

### 扫描区块技术 Block scan
构成区块链的四个核心技术是:**P2P 网络协议、分布式一致性算法、加密签名算法、账户与交易模型**。这四个技术对应到数字货币钱包中就是,**P2P 网络、持久化存储、账户以及私钥管理、共识与交易验证**四大模块。

其中,持久化存储模块是由全节点钱包自带的嵌入式数据库提供的,这里有 LevelDB、BerkerlyDB、SQLite3 等多种选择。

但是无论选择哪种嵌入式数据库,都面临了一个严峻问题,**精细化的交易查询验证与性能不可兼具**。换句话来说,任何全节点的嵌入式数据库都无法和服务器级别的数据库相媲美。

对于平台开发来说,显然选择服务器级别的数据库是更为合适的选择。那么这里就涉及了一个问题,**如何把全节点钱包中的数据转换成为数据库服务器中的数据,这就需要用到一种扫描区块技术,简称扫块**。

扫块,顾名思义,就是指 **扫描全节点钱包中的所有区块,然后将其解析后存储到数据库服务器的过程**,这些数据库可以是 MongoDB,也可以是 MySQL,取决于你的业务需要。

以下Python 代码展示了基于 MySQL 的关系型表结构,目的是从嵌入式数据库中扫描区块,然后存储到 MySQL 中。

```python
def init_table(conn):
    tables = []
    tb_block = '''
    create table if not EXISTS block (
    number bigint primary KEY ,
    hash char(64) not null,
    bits bigint,
    transaction_count INTEGER ,
    mixhash VARCHAR (128),
    version char(8) ,
    merkle_tree_hash char(64),
    previous_block_hash CHAR (64),
    nonce varchar(128) ,
    time_stamp bigint
    ) DEFAULT charset=utf8;
    '''
    tb_tx = '''
    create table if not EXISTS tx (
    id bigint PRIMARY KEY ,
    block_height bigint REFERENCES block(id),
    hash char(64) not null
    )DEFAULT charset=utf8 ;'''
    tb_address = '''
    create table if not EXISTS address(
    id int PRIMARY KEY ,
    address VARCHAR (64) UNIQUE
    )DEFAULT charset=utf8;
    '''
    tb_output = '''
    create table if not EXISTS tx_output(
    id bigint PRIMARY key,
    hash char(64) NOT NULL ,
    tx_id bigint REFERENCES tx(id),
    output_index bigint not null,
    output_value bigint,
    address_id bigint REFERENCES address(id),
    script varchar(1024),
    asset varchar(64),
    decimal_number varchar(8)
    )DEFAULT charset=ascii;
    '''
    tb_output_fork = '''
    create table if not EXISTS tx_output_fork(
    id bigint PRIMARY key,
    hash char(64) NOT NULL ,
    tx_id bigint,
    output_index bigint not null,
    output_value bigint,
    address_id bigint,
    script varchar(1024),
    asset varchar(64),
    decimal_number varchar(8)
    )DEFAULT charset=ascii;
    '''
    tb_tx_fork = '''
    create table if not EXISTS tx_fork (
    id bigint PRIMARY KEY ,
    block_height bigint,
    hash char(64) not null
    )DEFAULT charset=ascii ;'''
    tb_input_fork = '''
    create table if not EXISTS tx_input_fork(
    id bigint PRIMARY key,
    tx_id bigint,
    belong_tx_id bigint,
    tx_index bigint,
    tx_value bigint not null,
    script varchar(1024),
    address_id bigint,
    asset varchar(64),
    decimal_number varchar(8)
    )DEFAULT charset=ascii;
    '''
    tb_block_fork = '''
    create table if not EXISTS block_fork (
    number bigint primary KEY ,
    hash char(64) not null,
    bits bigint,
    transaction_count INTEGER ,
    mixhash VARCHAR (128),
    version char(8) ,
    merkle_tree_hash char(64),
    previous_block_hash CHAR (64),
    nonce varchar(128) ,
    time_stamp bigint
    ) DEFAULT charset=ascii;
    '''
    tb_output_asset = '''
    create table if not EXISTS tx_output_asset(
    id bigint PRIMARY key,
    hash char(64) NOT NULL ,
    tx_id bigint REFERENCES tx(id),
    output_index bigint not null,
    output_value bigint,
    address_id bigint REFERENCES address(id),
    asset_name varchar(64),
    issuer varchar(64),
    asset_type varchar(8),
    description varchar(64)
    )DEFAULT charset=utf8;
    '''
    tb_input = '''
    create table if not EXISTS tx_input(
    id bigint PRIMARY key,
    tx_id bigint REFERENCES tx(id),
    belong_tx_id bigint REFERENCES tx(id),
    tx_index bigint REFERENCES tx_output(output_index),
    tx_value bigint not null,
    script varchar(1024),
    address_id bigint REFERENCES address(id),
    asset varchar(64),
    decimal_number varchar(8)
    )DEFAULT charset=ascii;
    '''
```
我们按照区块链的结构,可以把表分为四大类:

第一类是区块 block; 第二类是交易 Tx; 第三类是交易输入输出:tb_input,tb_output; 第四类是分叉处理。

下面我贴一个普通的以 JSON 格式展示的区块和交易,你可以对比一下和上述表的关系:

下面是一个区块,里面包含了一笔交易。
```json
{
    "header": {
        "result": {
            "bits": "7097242144892",
            "hash": "cb36f2a1cbbf6a6300f4bf4915a5f54476ab603f2703a99e5d8d2db7ae2b37ed",
            "merkle_tree_hash": "3457b988bc6b61a7ad803f0742a68064c622ec618b833d99d153b92cba264d53",
            "mixhash": "47266114351983928450891657703600980449927404535067001902399906817438963939929",
            "nonce": "1864926684099479906",
            "number": 1000000,
            "previous_block_hash": "049257f31f4412bf115ed44a9305012ccea888cf842c2f0b66a528f258016e50",
            "time_stamp": 1520339120,
            "transaction_count": 1,
            "version": 1
        }
    },
    "txs": {
        "transactions": [
            {
                "hash": "3457b988bc6b61a7ad803f0742a68064c622ec618b833d99d153b92cba264d53",
                "inputs": [
                    {
                        "previous_output": {
                            "hash": "0000000000000000000000000000000000000000000000000000000000000000",
                            "index": 4294967295
                        },
                        "script": "[ 0340420f ]",
                        "sequence": 0
                    }
                ],
                "lock_time": "0",
                "outputs": [
                    {
                        "address": "MUiW2CViWLQBg2TQDsRt1Pcj7KyrdqFPj7",
                        "attachment": {
                            "type": "etp"
                        },
                        "index": 0,
                        "locked_height_range": 0,
                        "script": "dup hash160 [ e45695c2c390625376a7225a7ebea90dbb4147cf ] equalverify checksig",
                        "value": 270750000
                    }
                ],
                "version": "1"
            }
        ]
    }
}
```
我们可以发现区块头部分的数据被存储到 tb_block 表中,然后交易哈希被存储的 tb_tx 表中,接着交易的输入输出被存储到 tb_input 和 tb_output 中,这三者是通过区块高度、交易哈希被链接起来的。

    tb_block <-- 区块高度 --> tb_tx <-- 交易哈希 --> tb_input/tb_output

完整的 Python 脚本可以通过这个链接查看:
https://github.com/mvs-org/mvsd-mysql-sync/blob/master/tools/sync.py

整体的思路是使用 getblock 的 JSON-RPC,从第 0 个高度的区块一直扫描到最新区块,并且存储到 MySQL 中。

这里最难以处理的问题是保持 MySQL 中的区块数据与全节点数据的一致性,也就是 **当区块链分叉时,MySQL 需要感知到发生了分叉,接着移除被分叉的区块,并且接着同步到正确的区块上**。

这个处理方法有不同的思路,上述脚本使用了移动区块数据的方法,也就是将孤儿块移动到 tb_tx_fork 下,接着同步正确的区块。实际上也可以通过标记法,即在 tb_block 中,将此块标记为孤儿块。

关系型的表结构也可以做成标准化的,区块链本身作为基础设施,历史交易已经不可篡改,如果把这些结构化的区块做成公共基础设施,并提供基于 API 的开放调用,这便就是我们常见的区块浏览器了。

上文我们介绍了扫描区块的思路和实践,实际上我们也可以使用 Presto 技术将钱包中的数据转换成类 SQL 查询,但这里服务的稳定性和性能需要经过测试才可以被平台使用。

**扫描区块技术解决了所有区块链资产可视化、高并发查询的问题**,所以它在一些大规模的数字货币交易所中也有应用。区块浏览器就是基于这种技术产生的一种 Web 服务,下面我们就来看一看区块浏览器与扫描区块的具体关系。

### 区块浏览器
我在前面介绍数字货币和交易所时有提到过区块浏览器,它提供了可视化的交易查询和验证服务。

从技术上看,一个区块浏览器的主要工作就是 **把区块扫描到数据库服务器中**,然后搭建一个 Web 访问服务,**用户只需要输入交易哈希或者区块哈希,即可查询到交易是否已经被打包和确认**。

目前比特币和以太坊的流行区块浏览器比较多,不局限在某一个区块浏览器,因为大家看到的区块数据是一样的,区别就是如何更好地展示,做得更好的话,还可以集成一些咨询和资产托管的功能。

从产品意义上来说,我认为区块浏览器更适合叫做资产浏览器,因为它为人们提供了资产证明的服务,而不必肉眼识别交易或者自行手动解析交易,一般来说,区块浏览器也提供基本的 API 查询服务。

区块浏览器也为人们提供了区块和交易的统计数据,帮助人们直接地了解这个区块链的活跃程度,人们也可以根据统计数据制作区块活跃度等指数帮助投资者了解这个区块链项目。

区块浏览器降低了普通人查询和验证交易的门槛,其实它也是整个区块链行业的配套基础设施,而对于平台来说,从第三方获取交易验证始终是一件不安全的事情,也面临着中心化的风险,那么平台如果想搭建自己的交易查询和验证服务,需要如何操作?

这就需要把数字货币钱包服务,集成到自己的系统里。下面我们就来聊一聊具体是如何集成的。

### 数字货币钱包服务
实际上,大规模的区块链应用都需要搭建一个数字货币钱包服务,数字货币钱包服务为系统中的其他模块提供了可扩展的、统一的、安全的交易查询和验证服务。

下图是从交易平台开发归纳出来的数字货币钱包服务。

![](https://github.com/yjjnls/blockchain-tutorial-cn/blob/master/img/31.2.png)

数字货币钱包服务可以为交易平台其他模块提供接口统一的 API,同时将不同的数据结构化到数据库服务中,最后可以通过传统高可用手段完成交易查询和验证。

当然,这也和交易所的规模有关,如果是一个小型交易所,扫块可能不是必需的,但是统一接口的 API 却是必须的。

我认为数字货币钱包服务应当有一套标准的钱包服务框架,支持主流数字货币,从而降低大家的使用和部署门槛。现在可以说区块链的配套设施和技术还很原始,还有很大的发展和提升的余地。