本期精读的文章是: [coinhive 官方文档](https://coinhive.com)及[Monero 官方文档](https://getmonero.org/)

懒得看文章？没关系。

咦，怎么是官方文档？

本期精读有所不同，注重实操，先操作获取感性认识，然后再介绍相关的概念，由浅入深，力求不纠缠细节，但不遮盖环节。阅读本文需要对加密货币有一些基本常识 (如果你是一个开发者但是完全没了解过加密货币，可以参考[这里](http://piotrpasich.com/introduction-bitcoin-for-developers/))。希望同学们看完本文能对加密货币领域有一个更深更切实的感受。如果要了解更多细节，文末总结的延伸阅读链接列表是最好的开始。

要注意一点，文中很多说明是默认基于 XMR 和 BTC 的，他们两个又同源，机制非常相似。**所以很多命题判断并不适用于所有的成千上万的加密货币.** 正相反，新的币种层出不穷，几乎所有的惯例都被打破，所有可能性都被尝试。这一点以下不再做说明。

# 1 引言
首先，干货。10 行代码，5 分钟，不需部署不需构建直接浏览器开挖。

- 本地创建文件 test.html，粘贴如下代码:
  ```plain
  <!doctype html>
  <html>
  <head>
      <title>Mining</title>
  </head>
  <body>
      <div class="coinhive-miner" 
          style="width: 256px; height: 310px"
          data-key="MUtCJzIDhrs01ERrf3qlqdawo35N0CYD">
          <em>Loading...</em>
      </div>
      <script src="https://authedmine.com/lib/simple-ui.min.js" async></script>
  </body>
  </html>
  ```

- 本地双击打开。等待 JS 加载，点击 widget "Start Mining"。开始挖矿! 如下图

  ![mining](https://img.alicdn.com/tfs/TB13KIljiqAXuNjy1XdXXaYcVXa-880-686.png)

  不错，数字已经在跳动，风扇开始工作，永无尽头的挖矿已经开始了。那么重要的是，挖出来的加密货币在哪呢？原来上面的代码里用的还是我的 API key，所以还没挖到你自己那里，所以请继续下面的步骤：

- 在 coinhive 注册账号并登陆。它是做什么的？别急，后面会详细讲。在 `coinhive/settings` 找到自己的 `API keypair`，把 `public key` 复制出来，形如 `MUtCJzIDhrs01ERrf3qlqdawo35N0CYD`

- 替换上面代码中的 data-key 部分，重新开始挖矿。好了，现在挖出的 Monero (这是啥？详见下一节) 已经会到你自己的 coinhive 账户中。用下图来说明，你名下总计算过的 Hash 个数为 264K，当前难度换算为 0.00002 个 Monero(Symbol:XMR)。当前难度为 66G 一个 block，一个 block reward 5.87 XMR，得到一个 XMR 是 11.268G。264K/11.268G = 0.0000234。这就是你目前的收益。

  ![coinhive dashbaord](https://img.alicdn.com/tfs/TB1LnMljiqAXuNjy1XdXXaYcVXa-2424-1118.png)

- 查 bitfinex 可得现在 XMR 价格在 375 美元 (当你看到本文的时候，价格可能早就又波动到不知哪里去了)，所以你 (以及你忠实勤劳的电脑) 获得的实际收益为 0.000234 * 375 USD = 0.008775 USD，快到一分钱了 :)

怎么样，有没有一种浏览器点开即玩一刀 999 级的感觉。以上操作的便捷直接，是建立在无数前人大量的开发和基础设施建设之上的。越是领域早期的工作，越步履维艰，收货也越丰厚。如今加密货币已经走到了一个成年期，逐渐稳定成熟起来。

接下来我们聚焦到上面过程的每个环节，了解下拼图的每一块是怎样被构成全图的。

# 2 聚焦
让我们从最终端最接近用户的环节开始，逐一聚焦，最后走完整条链路。

## 2.1 从浏览器说起
本文标题叫浏览器挖矿，也是和贴合前端的部分。那么为什么可以在浏览器里挖矿？为什么可以很多用户在多个终端浏览器同时为同一个人 (你) 挖矿？

我们知道，挖矿是对加密货币产生机制的俗称。主流大多采取 Proof of Work (PoW) 机制。最常见的 PoW 方式就是由网络中所有节点作为矿工，每个节点都基于 blockchain 前面 block 已有信息计算一个新信息。这个新信息的计算方式往往是某种 hash function，并且人为地被设置为需要巨大计算力和时间才能完成 (其具体难度一般也会实时调整)。当一个节点幸运地 (也依靠强大的算力) 第一个计算出结果后，会把这个结果广播到网络中。其他所有节点会验证这个结果 (我们知道非对称加密算法，验证便宜而计算昂贵)，一旦证实就会停下手里的计算，承认这个计算结果。新的计算结果创造出新的块，区块链的高度增加一层，然后计算继续下去。每一个块的生成一般在 2-10 分钟。这个过程就被叫做挖矿.

既然是通用计算，既然是算一个 hash 值，那么民用级 CPU 和 GPU，浏览器或任何沙盒，虚拟机，移动设备当然就都可以。在我们的例子中，计算过程被做成分布式，每个用户可以各自计算，结果按 chunk 发回 master 汇总。这样就实现了终端用户 - 浏览器 - 共同贡献计算资源 - 换为 XMR 的过程。

这就是对整个链路的一个描述。从中我们会生出一些疑问，比如:

> 给我看看具体算什么 hash？为什么要算 XMR 而不是比特币或者其他？既然第一个算出的赢家通吃所有，为什么我的收益却是线性的？这种描述来看岂不是算力最大的一方**永远**都能算出结果而其他人颗粒无收吗?

要看具体算法，没有问题。bitcoin 在[这里](http://www.righto.com/2014/02/bitcoin-mining-hard-way-algorithms.html)，XMR 则看[CryptoNote Standard 008](https://cryptonote.org/cns/cns008.txt)

读完两个算法我们就有了以上疑问的答案:

### 2.1.1 为什么要算 XMR 而不是比特币或者其他
XMR 不是唯一选择，但是 BTC 是一个不可选的选择。因为 double SHA-265 在专业级 GPU 上会比 CPU 上快 10^4 倍 ([更多信息](https://en.bitcoin.it/wiki/Why_a_GPU_mines_faster_than_a_CPU))。这样一百万用户合力浏览器挖矿还不如一架子双路 Titan，就失去了分布到终端用户的意义。而 CryptoNight 在 GPU 上只比同价值 CPU 快 2 倍。另外 CryptoNight 算法也被设计为不适用[ASIC](https://en.wikipedia.org/wiki/Application-specific_integrated_circuit)。

怎么实现的？CryptoNight 算法开宗明义地写明，运算主体是 Memory-Hard Loop，而不是 Computation-Hard Loop。每个循环都要在内存中检索。实际运行 CryptoNight 时，CPU 都会用最快，最接近 ALU 也是容量最小的 L3 Cache。换到 GPU，显存虽然很大，却没有 L3 Cache 一样的极致读写速度优化，而且由于内存读写成了瓶颈，GPU 中的大量 ALU 也没了用武之地。下图简略地描述了 CryptoNight 循环体的结构:

![CryptoNight](https://img.alicdn.com/tfs/TB1KrQzjiqAXuNjy1XdXXaYcVXa-682-509.png)

### 2.1.2 算力最大的一方永远都能算出结果
看了比特币具体算法，你应该明白了 hash 是靠不停改变 nonce 来生成的。随机取一个值，算了不对，再随机取另一个 nonce 值..。既然是随机取，就不会存在赢家恒赢.

### 2.1.3 为什么我的收益却是线性的
这是一个隐蔽但是却非常重要的问题。答案是，本来确实是赢家通吃。如果你的算力足够大，挖矿时间足够长之后总会轮到你，但是收益会有大幅波动.

就是因为如此，矿工们逐渐建立了矿池组织。大家把算力都投入到一起，合力算，然后不管这次实际是谁算出来，都按照贡献的算力比例分配收益。矿池是一个加密货币建立之初，完全推崇去中心化时没有预料到的结构，也产生了深远的影响。现在来自中国矿池的算力早已超过网络 50%，他们会在挖出的块中打上矿池标记，而这些矿池在加密货币的分叉，路线图中也扮演举足轻重的角色.

所以你的算力并不是直接投入 XMR 网络中，而是投入一个矿池。在我们的例子里矿池就是 coinhive，只不过是一个比较特殊的矿池，特殊在矿池成员都运作在浏览器中。这就是为什么你会得到线性收益而不是 all or none.

自古以来各行业都会自发产生行业工会，建立类似保险和行业守则 / 规范这些人人为我我为人人的机制。在 crypto 行业也不例外。这是意料之外而情理之中.

## 2.2 在浏览器里发生了什么，或 coinhive 干了什么
好，我们搞清了一些基本的 Monero 挖矿机制，下面来看看 coinhive。已经知道 coinhive 帮我们接入它的矿池，让再小的算力也能按比例得到产出。但是还有什么呢？最关键的一点，coinhive 是怎样把一个完整的 mining 过程拆分成小块，让一个或许并不强大的设备上的浏览器，也能快速接收 task，快速完成并且即时上传的呢?

废话不多说，打开源码。本项目没有开源，构建完成后的在https://coinhive.com/lib/coinhive.min.js。先做初步 format 处理，发现有些工作完成在后端，worker shard 一侧。以下用松散的伪码总结一下 client side 的[main success scenario](https://wiki.ihris.org/wiki/Understanding_Use_Cases)流程 (注意很多地方简化了):

```plain
  - start
  - loadWorkerResource
  - load worker-asmjs.min.js
  - CRYPTONIGHT_WORKER_BLOB = createObjectURL(Blob(response_of_worker-asmjs.min.js))
  - _startNow -> _connectAfterSelfTest
  - selfTest -> verify(testJob),
    testJob = {
      verify_id: "1",
      nonce: "204f150c",
      result: "6a9c7dea83b079ce0e012907dd6929bcb0aeec3c1f06c032ca7c3386432bca00",
      blob: "0606c6d8cfd005cad45b0306350a730b0354d52f1b6d671063824287ce4a82c971d109d56d1f1b00000000ee2d1d4fd7c18bdc1b24abb902ac8ecc3d201ffb5904de9e476a7bbb0f9ec1ab04"
    };
    verify = if (!this._isReady) {
        this.verifyJob = job
    } else {
        this.worker.postMessage(job)
    }
    // 实例化若干个JobThread，每个对应一个worker，worker实际执行asmjs.min.js
  - _connect // verify成功，终于建立连接。根据public key固定hash到一个shard池然后随机选一个shard，建立websocket
  - websocket.onmessage: if (type==job) work()
  - work:
    do { hash(input，output) } while !(meetTarget(output));
    websocket.postMessage({nonce，output}) // hash done successfully。submit
```

看一下这个过程，结合[cns003 XMR blockchain specs](https://cryptonote.org/cns/cns003.txt)。XMR 的整体 hash input 很小，是:
```plain
  - size of [block_header，Merkle root hash，and the number of 
    transactions] in bytes (varint)
  - block_header,
  - Merkle root hash,
  - number of transactions (varint).
```

这样用 websocket 发过来毫无问题。之后就是完全独立的计算，调整 nonce 来算不同的 hash 结果。target 就是当前难度的一个指示.

这样整条链路就比较清晰了。再思考一下以下问题:

### 2.2.1 为什么 XMR 适宜分布式客户端计算
因为能够利用每个用户的 CPU 和其中的**高速 L3 Cache**。这是中心化执行难以具备的条件.

任何时候当考虑要不要把某项操作推到客户端进行时，都要想明白可以利用客户端的哪个资源，这个资源在客户端是否有明显优势，是否比后端中心化执行更有利。**很多时候答案是，优势并不明显。那么引入的网络通信成本，法规成本，额外开销可能就并不值得.**

## 2.3 最后的步骤
到了这里，最后剩余步骤就很标准模式化了。coinhive 作为矿场，代管着用户生产的加密货币。用户发请求提出 XMR，就需要提到一个自己的钱包地址保管，比如 [MyMonero](https://mymonero.com)。也可以直接提到 Exchange 交易所，在其中交易成其他币种，包括法币，然后电汇等等形式提现.

如果对钱包或者交易所感兴趣 (这两个也是很大的话题，比如钱包分硬件钱包和软件钱包，离线冷钱包和线上热钱包，private key 和 recover seeds。交易所有多种多样的交易对，有杠杆，期货，空和多，多种挂单类型等等)，可以看[这里](https://www.blockchain-council.org/cryptocurrency/hot-wallet-vs-cold-wallet/)和[这里](https://github.com/txbits/txbits).

# 3 更多讨论

> 讨论地址是：[精读《全链路体验浏览器挖矿》 · Issue #55 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/55)

**如果你想参与讨论，请[点击这里](https://github.com/dt-fe/weekly)，每周都有新的主题，每周五发布。**

# 4 延伸阅读
http://piotrpasich.com/introduction-bitcoin-for-developers

https://en.wikipedia.org/wiki/Monero_(cryptocurrency)

http://www.righto.com/2014/02/bitcoin-mining-hard-way-algorithms.html

https://cryptonote.org/cns/cns001.txt
cns001-008 是 Specs 集合

https://en.bitcoin.it/wiki/Why_a_GPU_mines_faster_than_a_CPU

https://en.wikipedia.org/wiki/Application-specific_integrated_circuit

https://github.com/txbits/txbits
