极客时间已完结课程限时免费阅读

第31讲 | 数字货币钱包服务

第31讲 | 数字货币钱包服务-极客时间

第31讲 | 数字货币钱包服务

讲述:黄洲君

时长08:01大小3.67M

上一篇,我们谈到了“区块链即服务”的概念。实际上,区块链第一个需要解决的服务就是数字货币支付服务。如何将数字货币钱包集成到系统中,我认为这是区块链行业最为迫切的问题。
今天我们就来了解一下数字货币钱包,它面临了什么样的问题,这样的问题又需要什么技术才能解决,而数字货币平台想要定制自己的数字货币钱包服务,又应当如何集成。

数字货币钱包的分类

目前市面上的数字货币钱包有很多种,看起来似乎有些眼花缭乱,不过,我们可以将它们进行分类后,再快速了解。
下图展示了按照不同属性区分的区块链钱包。
左一是按照用户端平台划分的钱包,这种钱包实际是在服务端运行的,用户端的钱包实际上只是一个代理,所以用户不需要关心钱包的细节,使用起来十分方便,典型的例子是各种在线钱包。
左二是按照货币类型分类的钱包,主要是指这款钱包到底是否支持多币种,这里的多币种可以是基于以太坊 ERC20 Token 的同一个区块链上的多币种,也可以是集成了比特币和以太坊等不同区块链的多币种。
右二是按照私钥存储的方式来区分的钱包,实际上这里主要涉及了用户私钥是否被平台托管,如果不托管直接存储在用户端,也就是硬件、终端设备、纸质记录,这些都可以被称为 On-chain 的钱包;如果用户无法接触到私钥,私钥被托管在平台,那么这种钱包也被称为 Off-chain 的钱包。
右一是按照访问方式进行分类的钱包,例如可以多个人共同管理,同时它也是需要多重签名支持的钱包,否则就变成了个人私有的钱包。
以上的分类并不是绝对的,一个钱包可以兼具以上不同的属性,例如某个 Mobile 钱包是提供 On-chain 的,也提供多重签名、提供多币种的钱包,这种钱包通常就是业界比较流行的钱包类型。
但是,对于平台方来说,上述钱包类型可能不足以支持自己平台的需求,并发挥出最佳的功效。毕竟作为平台来说,对高可用、分叉检测、区块确认的要求是远远高于普通钱包的,这样的问题又是如何解决的呢?这就引入了一项新的技术。

扫描区块技术 Block scan

我们之前在深入区块链技术部分介绍过,构成区块链的四个核心技术是:P2P 网络协议、分布式一致性算法、加密签名算法、账户与交易模型。这四个技术对应到数字货币钱包中就是,P2P 网络、持久化存储、账户以及私钥管理、共识与交易验证四大模块。
其中,持久化存储模块是由全节点钱包自带的嵌入式数据库提供的,这里有 LevelDB、BerkerlyDB、SQLite3 等多种选择。
但是无论选择哪种嵌入式数据库,都面临了一个严峻问题,精细化的交易查询验证与性能不可兼具。换句话来说,任何全节点的嵌入式数据库都无法和服务器级别的数据库相媲美。
对于平台开发来说,显然选择服务器级别的数据库是更为合适的选择。那么这里就涉及了一个问题,如何把全节点钱包中的数据转换成为数据库服务器中的数据,这就需要用到一种扫描区块技术,简称扫块。
扫块,顾名思义,就是指扫描全节点钱包中的所有区块,然后将其解析后存储到数据库服务器的过程,这些数据库可以是 MongoDB,也可以是 MySQL,取决于你的业务需要。
我们可以举元界区块链扫块的例子,元界上的区块结构与比特币接近,你可以将其类比成比特币区块链。
以下是 Python 代码,展示了基于 MySQL 的关系型表结构,目的是从元界的嵌入式数据库中扫描区块,然后存储到 MySQL 中。
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 格式展示的区块和交易,你可以对比一下和上述表的关系:
下面是一个区块,里面包含了一笔交易。
{
"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 脚本可以通过这个链接查看:
整体的思路是使用 getblock 的 JSON-RPC,从第 0 个高度的区块一直扫描到最新区块,并且存储到 MySQL 中。
这里最难以处理的问题是保持 MySQL 中的区块数据与全节点数据的一致性,也就是当区块链分叉时,MySQL 需要感知到发生了分叉,接着移除被分叉的区块,并且接着同步到正确的区块上。
这个处理方法有不同的思路,上述脚本使用了移动区块数据的方法,也就是将孤儿块移动到 tb_tx_fork 下,接着同步正确的区块。实际上也可以通过标记法,即在 tb_block 中,将此块标记为孤儿块。
关系型的表结构也可以做成标准化的,区块链本身作为基础设施,历史交易已经不可篡改,如果把这些结构化的区块做成公共基础设施,并提供基于 API 的开放调用,这便就是我们常见的区块浏览器了。
上文我们介绍了扫描区块的思路和实践,实际上我们也可以使用 Presto 技术将钱包中的数据转换成类 SQL 查询,但这里服务的稳定性和性能需要经过测试才可以被平台使用。
扫描区块技术解决了所有区块链资产可视化、高并发查询的问题,所以它在一些大规模的数字货币交易所中也有应用。区块浏览器就是基于这种技术产生的一种 Web 服务,下面我们就来看一看区块浏览器与扫描区块的具体关系。

区块浏览器

我在前面介绍数字货币和交易所时有提到过区块浏览器,它提供了可视化的交易查询和验证服务。
从技术上看,一个区块浏览器的主要工作就是把区块扫描到数据库服务器中,然后搭建一个 Web 访问服务,用户只需要输入交易哈希或者区块哈希,即可查询到交易是否已经被打包和确认。
目前比特币和以太坊的流行区块浏览器比较多,不局限在某一个区块浏览器,因为大家看到的区块数据是一样的,区别就是如何更好地展示,做得更好的话,还可以集成一些咨询和资产托管的功能。
从产品意义上来说,我认为区块浏览器更适合叫做资产浏览器,因为它为人们提供了资产证明的服务,而不必肉眼识别交易或者自行手动解析交易,一般来说,区块浏览器也提供基本的 API 查询服务。
区块浏览器也为人们提供了区块和交易的统计数据,帮助人们直接地了解这个区块链的活跃程度,人们也可以根据统计数据制作区块活跃度等指数帮助投资者了解这个区块链项目。
区块浏览器降低了普通人查询和验证交易的门槛,其实它也是整个区块链行业的配套基础设施,而对于平台来说,从第三方获取交易验证始终是一件不安全的事情,也面临着中心化的风险,那么平台如果想搭建自己的交易查询和验证服务,需要如何操作?
这就需要把数字货币钱包服务,集成到自己的系统里。下面我们就来聊一聊具体是如何集成的。

数字货币钱包服务

实际上,大规模的区块链应用都需要搭建一个数字货币钱包服务,数字货币钱包服务为系统中的其他模块提供了可扩展的、统一的、安全的交易查询和验证服务。
下图是我从交易平台开发归纳出来的数字货币钱包服务。
数字货币钱包服务可以为交易平台其他模块提供接口统一的 API,同时将不同的数据结构化到数据库服务中,最后可以通过传统高可用手段完成交易查询和验证。
当然,这也和交易所的规模有关,如果是一个小型交易所,扫块可能不是必需的,但是统一接口的 API 却是必须的。
我认为数字货币钱包服务应当有一套标准的钱包服务框架,支持主流数字货币,从而降低大家的使用和部署门槛,这也和我们上一篇聊到的“区块链即服务”的概念不谋而合。
可以说区块链的配套设施和技术还很原始,还有很大的发展和提升的余地。

总结

好了,今天我们先了解了一下数字货币钱包的分类,接着详细讲解扫块技术,然后又谈到了区块浏览器,最后分享了一下数字货币钱包服务的集成思路,希望可以让你对区块服务的实践有一个初步了解,你也可以根据已有的技术知识重新拆解和分析区块链技术。
那么今天的问题是,数字货币钱包服务可以应用到微服务架构中吗?
分享给需要的人,Ta购买本课程,你将得18
生成海报并分享

赞 5

提建议

上一篇
第30讲 | 区块链即服务BaaS
下一篇
第32讲 | 区块链与供应链(一)
unpreview
 写留言

精选留言(9)

  • 朱显杰
    2018-06-05
    对区块链有写操作的应用,很难用微服务,因为是有状态的。而区块链的读服务或基于中心库查询的web服务,完全可以用微服务。
    6
  • 陈靖
    2018-06-05
    能不能直接撸代码,边撸边讲
    3
  • 麋鹿在泛舟
    2018-06-04
    这里最难以处理的问题是保持 MySQL 中的区块数据与全节点数据的一致性 --- 这个是定期扫描么 还是时时刻刻有变更就扫

    作者回复: 取决于业务敏感性吧。一般是做分叉检测和控制入库高度,不过应该不是最优解决方案

    2
  • 绝露
    2018-06-26
    区块浏览器的查询功能好理解,但是验证是指什么,能解释下吗?
    共 1 条评论
    1
  • 沃野阡陌
    2018-06-05
    请问老师,从交易所提币出来到钱包自己保存,是不是就是您说的可以用区块链browser来看或验证是否已被打包成区块的过程?

    作者回复: 是的,完全正确。

    1
  • MarkTang
    2020-11-12
    您好老师,我对BIP44有几个个疑问 1. 就是我看很多文章 介绍说 address_index 官方建议,每个 account 下的 address_index 不要超过 20?这点我不是很明白,望老师解答。 如这篇:https://learnblockchain.cn/2018/09/28/hdwallet。但有的文字又说这个20是gap,这个20gap是指我遍历一个account下的address_index 20次如果都没有交易记录则不再进行后续地址和账户的遍历的意思吗? 2. 用户使用助记词导入钱包的时候,为了获得他的总余额数量,我是需要遍历他的所有收款地址账户和找零地址账户,再把所有金额加在一起得到总余额吗?这样如果地址数量很大的话,是不是加载就会很慢? 3. 交易的时候,如果用户A需要转给用户B 5个BTC,但是用户A没有一个地址上有5个BTC,都是分散开放的,那么这个时候是需要把他多个子地址的余额凑足5个之后分别都转到用户B的那一个地址吗?如果是的话这样是否每笔交易都会收取矿工费呢? 4. HDWallet 新创建钱包时,是否需要先创建 比如20个收款地址 20个找零地址呢? 他不够时又再新建20个, 这样设计是否合理?
    展开
  • 杨家荣
    2020-02-03
    极客时间第二期 21天打卡行动 29/21 <<深入浅出区块链31>>数字货币钱包服务 回答老师问题: 数字货币钱包服务可以应用到微服务架构中吗? 微服务治理,链路追踪,与这个功能相似; 今日所学: 1,数字货币钱包的分类; 2,扫描区块技术 Block scan; 3,构成区块链的四个核心技术是:P2P 网络协议、分布式一致性算法、加密签名算法、账户与交易模型。 4,扫描区块技术解决了所有区块链资产可视化、高并发查询的问题,所以它在一些大规模的数字货币交易所中也有应用。 5,区块浏览器:从技术上看,一个区块浏览器的主要工作就是把区块扫描到数据库服务器中,然后搭建一个 Web 访问服务,用户只需要输入交易哈希或者区块哈希,即可查询到交易是否已经被打包和确认。 6,数字货币钱包服务:实际上,大规模的区块链应用都需要搭建一个数字货币钱包服务,数字货币钱包服务为系统中的其他模块提供了可扩展的、统一的、安全的交易查询和验证服务。
    展开
  • Geek_f26a61
    2018-06-18
    初次读关于区块链的文章,对于区块链要解决的问题一直困惑。数字货币在公链上发行如何匹配各国的货币发行,发行规模应于经济体总量相关。就象美元与黄金挂钩,支付宝钱包里的钱与实际货币挂钩。问题若不对或对区块链误解请见谅

    作者回复: 你好,同意的。但也未必是货币,也可以是其他有价凭证。

  • 乔良qiaoliang
    2018-06-09
    有机会介绍一下infura.io就太好了