精讀《全鏈路體驗瀏覽器挖礦》

本期精讀的文章是: coinhive官方文檔及Monero官方文檔

懶得看文章? 沒關係.

咦, 怎麼是官方文檔?

本期精讀有所不同, 注重實操, 先操作獲取感性認識, 然後再介紹相關的概念, 由淺入深, 力求不糾纏細節, 但不遮蓋環節. 閱讀本文需要對加密貨幣有一些基本常識 (如果你是一個開發者但是完全沒了解過加密貨幣, 可以參考這裡). 希望同學們看完本文能對加密貨幣領域有一個更深更切實的體感. 如果要了解更多細節, 文末總結的延伸閱讀鏈接列表是最好的開始.

要注意一點, 文中很多說明是默認基於XMR和BTC的, 他們兩個又同源, 機制非常相似. 所以很多命題判斷並不適用於所有的成千上萬的加密貨幣. 正相反, 新的幣種層出不窮, 幾乎所有的慣例都被打破, 所有可能性都被嘗試. 這一點以下不再做說明.

1 引言

首先, tl;dr乾貨. 10行代碼, 5分鐘, 不需部署不需構建直接瀏覽器開挖.

  • 本地創建文件test.html, 粘貼如下代碼:

<!doctype html>n<html>n<head>n <title>Mining</title>n</head>n<body>n <div class="coinhive-miner" n stylex="width: 256px; height: 310px"n data-key="MUtCJzIDhrs01ERrf3qlqdawo35N0CYD">n <em>Loading...</em>n </div>n <script src="https://authedmine.com/lib/simple-ui.min.js" async></script>n</body>n</html>n

  • 本地雙擊打開. 等待JS載入, 點擊widget "Start Mining". 開始挖礦! 如下圖

不錯, 數字已經在跳動, 風扇開始工作, 永無盡頭的挖礦已經開始了. 那麼重要的是, 挖出來的加密貨幣在哪呢? 原來上面的代碼里用的還是我的API key, 所以還沒挖到你自己那裡 :P 繼續下面的步驟

  • 在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. 這就是你目前的收益.

  • 查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在這裡, XMR則看CryptoNote Standard 008

讀完兩個演算法我們就有了以上疑問的答案:

2.1.1 為什麼要算XMR而不是比特幣或者其他

XMR不是唯一選擇, 但是BTC是一個不可選的選擇. 因為double SHA-265在專業級GPU上會比CPU上快10^4倍 (更多信息). 這樣一百萬用戶合力瀏覽器挖礦還不如一架子雙路Titan, 就失去了分布到終端用戶的意義. 而CryptoNight在GPU上只比同價值CPU快2倍. 另外CryptoNight演算法也被設計為不適用ASIC.

怎麼實現的? CryptoNight演算法開宗明義地寫明, 運算主體是Memory-Hard Loop, 而不是Computation-Hard Loop. 每個循環都要在內存中檢索. 實際運行CryptoNight時, CPU都會用最快, 最接近ALU也是容量最小的L3 Cache. 換到GPU, 顯存雖然很大, 卻沒有L3 Cache一樣的極致讀寫速度優化, 而且由於內存讀寫成了瓶頸, GPU中的大量ALU也沒了用武之地. 下圖簡略地描述了CryptoNight循環體的結構:

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, 快速完成並且即時上傳的呢?

廢話不多說, 打開源碼. 本項目沒有開源, 構建完成後的在coinhive.com/lib/coinhi. 先做初步format處理, 發現有些工作完成在後端, worker shard一側. 以下用鬆散的偽碼總結一下client side的main success scenario流程 (注意很多地方簡化了):

- startn - loadWorkerResourcen - load worker-asmjs.min.jsn - CRYPTONIGHT_WORKER_BLOB = createObjectURL(Blob(response_of_worker-asmjs.min.js))n - _startNow -> _connectAfterSelfTestn - selfTest -> verify(testJob),n testJob = {n verify_id: "1",n nonce: "204f150c",n result: "6a9c7dea83b079ce0e012907dd6929bcb0aeec3c1f06c032ca7c3386432bca00",n blob: "0606c6d8cfd005cad45b0306350a730b0354d52f1b6d671063824287ce4a82c971d109d56d1f1b00000000ee2d1d4fd7c18bdc1b24abb902ac8ecc3d201ffb5904de9e476a7bbb0f9ec1ab04"n };n verify = if (!this._isReady) {n this.verifyJob = jobn } else {n this.worker.postMessage(job)n }n // 實例化若干個JobThread, 每個對應一個worker, worker實際執行asmjs.min.jsn - _connect // verify成功, 終於建立連接. 根據public key固定hash到一個shard池然後隨機選一個shard, 建立websocketn - websocket.onmessage: if (type==job) work()n - work:n do { hash(input, output) } while !(meetTarget(output));n websocket.postMessage({nonce, output}) // hash done successfully. submitn n

看一下這個過程, 結合cns003 XMR blockchain specs. XMR的整體hash input很小, 是:

- size of [block_header, Merkle root hash, and the number of n transactions] in bytes (varint)n - block_header,n - Merkle root hash,n - number of transactions (varint).n

這樣用websocket發過來毫無問題. 之後就是完全獨立的計算, 調整nonce來算不同的hash結果. target就是當前難度的一個指示.

這樣整條鏈路就比較清晰了. 再思考一下以下問題:

2.2.1 為什麼XMR適宜分散式客戶端計算

因為能夠利用每個用戶的CPU和其中的高速L3 Cache. 這是中心化執行難以具備的條件.

任何時候當考慮要不要把某項操作推到客戶端進行時, 都要想明白可以利用客戶端的哪個資源, 這個資源在客戶端是否有明顯優勢, 是否比後端中心化執行更有利. 很多時候答案是, 優勢並不明顯. 那麼引入的網路通信成本, 法規成本, 額外開銷可能就並不值得.

2.3 最後的步驟

到了這裡, 最後剩餘步驟就很標準模式化了. coinhive作為礦場, 代管著用戶生產的加密貨幣. 用戶發請求提出XMR, 就需要提到一個自己的錢包地址保管, 比如MyMonero. 也可以直接提到Exchange交易所, 在其中交易成其他幣種, 包括法幣, 然後電匯等等形式提現.

如果對錢包或者交易所感興趣 (這兩個也是很大的話題, 比如錢包分硬體錢包和軟體錢包, 離線冷錢包和線上熱錢包, private key和recover seeds. 交易所有多種多樣的交易對, 有槓桿, 期貨, 空和多, 多種掛單類型等等), 可以看這裡和這裡.

3 更多討論

討論地址是:精讀《全鏈路體驗瀏覽器挖礦》 · Issue #55 · dt-fe/weekly

如果你想參與討論,請點擊這裡,每周都有新的主題,每周五發布。

4 延伸閱讀

piotrpasich.com/introdu

en.wikipedia.org/wiki/M

righto.com/2014/02/bitc

cryptonote.org/cns/cns0 cns001-008是Specs集合

en.bitcoin.it/wiki/Why_

en.wikipedia.org/wiki/A

github.com/txbits/txbit

推薦閱讀:

ruff開發板初探
React填坑記(一):組件通信
手把手教你為 React 添加雙向數據綁定(一)
一年前端開發,學習永遠趕不上潮流,有一定的PHP基礎,現在動搖了,不知道該繼續前端,還是轉PHP?
前端開發,開發人員怎麼方便的自測IE各個版本?

TAG:区块链Blockchain | 前端开发 | 数字加密货币 |