做一个区块链钱包并不难(2)

两年前,比特币蓄势待发,逐步飙升跨越了2万美元。一时间区块链、比特币、以太坊成为了大家茶余饭后的谈资,没几个月之后,许多人进场被割了韭菜,到现在整个币圈不温不火。在这来去匆匆的浪潮中,加入了家和区块链有点相关的公司,了解一些关于区块链的技术,做了个 App,里面有个区块链钱包。在这虚有不虚,浮浮沉沉的环境里过了一年多,有一些关于区块链比较特殊的内容可以沉淀的,比如说 怎么 ICO 发币,怎么做钱包,想要在这个市场平淡阶段,做点总结。

比特币背后的区块链,其实有很多有意思的算法和计算机网络相关的内容。区块链钱包无非也是软件工程的实现,架构的设计,功能的实现只是普遍的分布式软件开发,不过是需要多懂一些区块链和平台相关的知识。

这篇文章主要从功能需求、应用场景、架构、安全性这四个方面,介绍什么是区块链钱包,为什么要做一个区块链钱包,怎么做区块链钱包。

备注:做一个区块链钱包并不难(1)会是关于如何在以太坊上发布一个自己的货币,其实也就是 ICO,但是因为过程没啥技术型,多是繁琐的操作,等有时间再写吧。。。

什么是区块链钱包?

说是区块链钱包,范围其实有点大,更应该说是一个以太坊钱包,以太坊是现在最大的公共区块链平台,个人或者公司可以在上面发布属于自己的货币,这里钱包的作用是用来储存以太坊上的各种币。

从应用的角度,可以将以太坊的钱包粗略的分为两类:

  1. 以太坊上数不清各种币的储存和管理。典型的代表就是 imToken,任何的币都能转入到这个钱包里,交易所有被黑的风险,将币从交易所转出到这些个人钱包里,会更加安全一些。
  2. App 里管理单种币的钱包。比如说简书在2018年发布的 简书贝 (FTN),用户发文、阅读、点赞、评论等,系统会奖励简书贝,用户可以在软件里讲将简书贝换成人民币,或者在简书里兑换成其他奖励。

这篇文章讲的主要是第二种,嵌入在 App 里的以太坊钱包,钱包主要用来储存在以太坊上发的一个币,为了简单称呼,我们叫它 庄币(ZBB),后面还会涉及到以太坊上的主流货币以太币(ETH)。每发一个币,都会有一个地址,我们称之为 庄币地址,所有跟庄币相关的交易,都会记录到这个地址。

大概了解了什么是区块链钱包后,随之而来的问题是 为什么要做区块链钱包?

为什么要做区块链钱包?

可以从两个角度考虑这个问题。

第一,对于已经发币的公司,币除了在交易所以流通之外,应该要有实际的落地场景,而币要落地,要和别的应用结合,肯定需要有钱包来管理。

第二,对于已有应用的公司,在原有应用上加入区块链成分,已到达一些别的作用,比如说简书,在应用里加上了简书贝,做为刺激用户活跃度、增加用户留存的手段,这种情况下,也是一定会需要有这么个钱包的。

那要怎么做和 App 相结合的区块链钱包呢?

怎么做区块链钱包?

功能需求

区块链的钱包和管理传统软件里的虚拟货币的钱包有什么不同呢?比如 Q币钱包,Q币是 QQ 里的一个货币,我们只能充值、在QQ里把它花掉,或者换成现金,除了 QQ 之外,没有办法把Q币转到另一个地方去。但是以太坊的币不一样,本来就存在于以太坊上,可以放到任何一个「钱包」里,可以转入到我们开发的 App 的钱包里,也可以转出到别的以太坊钱包,所以以太坊钱包需要的两个功能是 转入和转出。除了这个之外,最基本的是要能查看钱包的余额。根据这几个需求,可以对应到了下面的一些基础功能。

  1. 账户管理:用户能管理自己的钱包,查看余额。
  2. 转出:用户将币从 App 里的钱包转到以太坊的另一个钱包里。
  3. 内部转账:用户要给 App 里的另一个用户转账,因为币并没有离开这个应用,所以称为内部转账。
  4. 转入:用户可以将以太坊的币转入到 App 的以太坊钱包里。

功能实现

我们一步步来实现这些需求。

账户管理

在传统软件里,我们是通过用户名或者邮箱来识别用户,转账用的也是用户名,比如说支付宝转账,可以通过用户名,直接把钱打给另一个人。在以太坊的世界里,也需要类似用户名的东西:公钥,或者叫公钥地址,这个公钥是随机生成的,每个公钥对应一个私钥,要转账给别人,必须要有私钥,就好像要开这个锁住的钱包,要有对应的钥匙。所以一个以太坊钱包,最基本的是要储存给每个用户随机生成的公钥和私钥,还有用户的余额。

内部转账

内部转账和传统软件转账是一样的。传统软件可以通过用户名转账,只是在数据库里去调整两个用户的余额。在我们这个以太坊钱包里,不仅可以通过用户名转账,还可以通过公钥转账,收款人是对方公钥地址。如果这个公钥地址在我们数据库里,那就是内部用户,调整一下数据库就可以。如果不是内部用户,说明这个地址是在以太坊上的,就需要转出到以太坊。

转出

转出需要根据另一个人的公钥,把币转到以太坊里另一个「钱包」里。

要将一笔交易,发到以太坊上,需要用以太币 (ETH) 付一定的交易费 (gas),那这笔交易费应该由谁来付?如果由用户来付,需要用户先把以太币转到这个这个 App 的钱包里,不过这就需要钱包还能管理以太币。如果由公司来付的话,可以不用这个功能和操作,但是公司会需要去承担这部分的费用。具体怎么选择,还看公司的商业决策。这里是一个很典型的商业决策会影响技术决策的一个例子,而且会导致完全不同的设计。

这里我们假设公司来付这比费用。每个用户的账户里都没有以太币,要付这个费用,最简单是设置一个独立的账户,我们称它为 Hot Wallet,Hot Wallet 里要转入以太币, 所有的交易费用都从会这个账户里扣除。

通常情况下,Hot Wallet 里也需要有我们的庄币。举个例子,在像简书这种应用中,简书贝是主动奖励给用户的,用户有多少余额,其实只是数据库里的数据,并不是他以太坊钱包里真的有那些简书贝,这种情况下要转出,就需要从公司的统一账户 Hot Wallet 里转出。

总结一下,转出其实是从 Hot Wallet 把币转给另一个账户

整个转出的流程大概是这样的:

  1. 把这笔交易发送到以太坊上。
  2. 以太坊会去判断这比交易能不能成功,比如说交易费用不够会导致交易失败。
  3. 交易成功,等待几个认证(confirmation)。因为区块链的特性,显示交易成功,只代表一个“矿工”验证了你的交易没有问题,可能别的会觉得你的交易有问题,所以需要多等几个认证,通常有个 5、6个认证基本就没问题了。每笔交易都是在以太坊的一个 block 里,每个 block 有一个号码,是递增的,以太坊种每多一个新的 block,就是多了一个认证。

转入

用户只需要知道他们在这个 App 里的公钥,就能在以太坊上操作,把币转到 App 里。交易费由用户支付,多数操作钱包也不用去处理。

但是我们的钱包需要不断的去查看以太坊,看有没有发送给我们用户的交易,有的话要给用户加上余额。

Hot Wallet 管理

Hot Wallet 是公司的统一以太坊账户,里面有以太币和庄币。转出时,以太币和庄币会减少,如果都用完了,就没办法转出了,所以在快用完时,需要有人去管理充值。

相对应的,转入之后,币是在每个用户各自的账户里,这是一个比较麻烦管理的地方,因为都是从 Hot Wallet 转出,那这些币在每个用户的公钥里,就闲置了,我们要有办法转出到到 Hot Wallet 中用。

架构

有了具体功能和每个功能的实现方式,需要考虑要怎么架构比较好。

在用户越来越多后,需要考虑高并发。假设转出的费用由公司承担,通常公司在业务上会加一些限制,比如一个月只能转多少比等。转入因为需要用户自己去承担费用,一个用户不可能不断的转入小额,而是一次性转入大额,所以整体来说转出和转入的流量不会非常大,系统对这方面的并发性要求不高。

另外要考虑系统的高可用。用户在做了转入或者转出后,会希望在最短的时间里看到操作是不是成功了。不能转出一笔钱,一个小时后才告诉用户是不是转成功了。所以,会对高可用要求比较高。

整体的设计重点在于高可用,所以采用分布式微服务的方式来做这个架构:

Ethereum Wallet Architecture

组成

主要分成三个服务:

  1. 钱包服务 Wallet Service
    • 有状态。连数据库,储存用户的公钥、私钥、余额、交易等数据。
  2. 以太坊服务 Ethereum Service
    • 无状态。负责所有和以太坊相关的查询、发送。
  3. 以太坊监听服务 Ethereum Listener
    • 无状态。负责监听以太坊上最新的 Block 和 相关交易。

各个服务之间是怎么串起来实现功能,等会再细说,在这之前有另外一个需要考虑的点:要怎么和以太坊同步,怎么把一个交易发送到以太坊,怎么知道以太坊上最新的交易信息?

有两种解决方案:

  1. 自己在 AWS 上部署一个以太坊节点,AWS 有区块链相关的部署支持,然后再开放出 HTTP 和 Web Socket 的连接方式。
  2. 用第三方服务,比如 Infura,提供了 HTTP 和 Web Socket 的连接方式,我们就把以太坊这一块当成一个黑盒,用就是了。

两个方案各有优劣。自己部署成本高,维护成本也很高。用第三方服务担心的是不够稳定,Infura 据说时不时会挂,间接影响到我们的可用性。Infura 发布了一个新的 Infura+,据说可以达到高可用。对于前期的开发、测试,选择 Infura 免费而且省时省事,可以等到真正有需要再自己去部署。

功能实现

Wallet Service 是核心,储存了用户的公钥、私钥、账户余额、交易等数据。所有的功能都是从这里触发,其中账户管理内部转账在这个服务里就能够完成。

转出

  1. 将交易发到以太坊。从 Wallet Service 触发后,通过 gRPC 交由 Ethereum Service 发送到以太坊,Ethereum Service 会返回发送到以太坊中交易的 Transaction Hash,Wallet Service 需要存起来。这时,交易在系统中还是 pending 的状态。
  2. Ethereum Listener 去监听最新的交易,发给 Wallet Service 处理。现在还不知道这个交易在以太坊中能不能成功,比如说交易费没给够,交易最终会失败。通过 Ethereum Listener 去监听庄币地址上的交易,发现最新的,就通过 Message Queue 发送到 Wallet Service。Wallet Service 会去看这个交易的 Transaction Hash 是不是我们之前发送的,如果是的话,根据交易的状态,成功还是失败,去更改交易的状态,successful 或者 failed
  3. 等5个认证,确认交易。假设交易成功,Ethereum Listerner 同时还会不断监听最新的 block,通过 Message Queue 发给 Wallet Service。Wallet Service 每收到一个 Block,会去对比这个最新的 block 号码和之前发送时的号码,如果想差为5,表明有5个认证,这笔转出交易的状态改为 confirmed,确认交易完成。

转入

  1. 用户从以太坊发起把庄币转到 App 里的公钥地址。(不在系统里操作)
  2. 和转出的第二步类似,但是 Wallet Service 从 Message Queue 收到交易信息后,查看目标地址是不是我们 App 里的某个公钥,如果是,储存这一条交易。
  3. 和转出第三步一模一样,等待 5 个认证。

Hot Wallet 管理

Hot Wallet 的管理主要有两部分:Balance Watcher 和 Consolidator

我们需要去监控 Hot Wallet 里 以太币 和 庄币 还有多少,当低于某个值时,发通知去提醒管理员充值,把这个功能命名为 Balance Watcher ,定时每隔几分钟/小时去查以太坊上 以太币 和 庄币 的余额。

另外,之前提到要有办法把每个用户转入到各自账户里的庄币转出,放到一个统一管理的地方,可以是 Hot Wallet,也可以是别的地址,命名为 Consolidator。这个功能会比较复杂,要把币转出,必须要交易费,需要从 Hot Wallet 先转交易费到用户账户里。因为交易费是以太币,转以太币已需要交易费,所以要把交易费转到账户里,还需要花一次交易费。整个过程要出两次的交易费,成本相当高。所以这个要看具体怎么去转,比如忽略低额账户,只转出高额账户。

高可用

这几个服务可以部署在 Kubernetes 里,起个几个节点,在一个挂了之后,有备份,还能自动重启一个补充。

Wallet Service 是核心服务,可以起三个节点,需要注意的是接收 Message Queue 里的信息时,要设置 Consumer 的负载均衡。

Ethereum Service 主要影响到的是庄币的转出,本身是无状态的,接受的也都是同步消息,挂了再重启就行了。

Ethereum Listener 如果挂了,将没有办法转入,没有办法进行任何认证。等它再重启后,挂掉这段时间的 block 和交易没有被处理,所以需要有挂掉后的重启逻辑。可以这么处理,Etheruem Listener 通过 grpc 告诉 Wallet Service 最新的 block,Wallet Service 去查询数据库里最新的 block,把中间遗漏的 block 数据分批(数据量可能很大)发给 Ethereum Listener,Ethereum Listener 再去查找这些遗漏的 Block 里的数据。

其他

区块链网络里,特别需要注意的一个问题是 双重支付 (Double Spend),举个例子,用户只有10块钱,因为区块链交易不是马上完成的,用户可以同时用这10块钱进行两笔交易。比特币、以太坊都有地址能防止这些,但是在系统和以太坊之间进行交流,转出/转入时,需要去考虑什么时候扣除/增加用户余额。

安全性

每隔一小段时间,就会有新闻爆出xxx交易所交易所被盗了够我们富成一个废物的钱,区块链相关的项目中安全也是重中之重。

防盗

在刚刚的设计中,Hot Wallet 的安全性是最重要的,层层加密都不为过。另外,可以使用多个 Hot Wallet,来降低被一锅端的风险。

另外一个地方和传统软件一样,数据库的加密。不然让入侵的人盗了说有的私钥,所有用户都可能变成提款机。

防玩坏

除了防技术性的黑客外,还要防钻漏洞的用户,或者故意搞破坏的。

比如前面提到的,要限制转出,每个月限制多少笔,每笔最低金额为多少。

简书里对转出还做了分批操作,不是能直接到账,而是在之后的一个月内,每星期到账一部分。

展望

区块链钱包说白了就是一个分布式的系统,只是除了分布式的一些知识外,还需要对区块链、以太坊有所了解。当然,这些只是基本功能的实现,还可以有更复杂的功能,比如在里面加上币的 OTC 交易所,能直接在 App 里和别的用户买卖币,比如做一个币能派上用场的交易商场,可以用币去买对应的东西等。

很多事情,听到一个个又宽又大的名词时,觉得很难,分解开来,其实并不难。