'開發以太坊錢包的基本思路與安全'

數據庫 比特幣 區塊鏈 設計 技術 黑客 架構師公社 2019-08-05
"

本篇帶大家聊聊中心化存儲錢包的設計以及研發過程中的坑和業務處理。

以太坊錢包的分類

我們知道其實無論 Geth 節點或 Ethereum Wallet 都可以稱作錢包,因為它們都可以存儲以太幣、都可以發送交易指令等。但這裡講的錢包分類是另外一個維度,基於 Geth 節點上層再次開發出來的移動端 App 錢包。根據私鑰存儲的位置可分為兩類:

  • 中心化私鑰存儲的錢包,比如火幣的錢包;
  • 去中心化錢包,私鑰存儲在用戶端,比如 imtoken 錢包。

去中心化錢包

去中心化錢包不是本節要講的重點,這裡只給大家簡單介紹一下。無論是中心化的錢包或去中心化的錢包,在 App 層面都是很輕量級的,App 內是不會內置一個 Geth 節點,交易的查詢或發送都是通過服務器來進行操作,不同點是交易簽名的私鑰由誰來保管和負責。

去中心化的錢包有個關鍵詞:助記詞。可以用下面的表達式來形容助記詞的作用:

私鑰 = 助記詞 = keystore+密碼

通過上面的公式可以看出助記詞的重要作用,也是去中心錢包功能的一個亮點。當在這類錢包中創建一個賬戶之後,錢包會根據生成的私鑰文件,生成一套助記詞,可為英文可為中文,通過助記詞可以反向計算出 keystore + 密碼。助記詞由用戶手抄存放在安全的地方,當進行交易時,輸入助記詞對交易進行簽名,發送交易。當助記詞丟失,也就意味著失去了私鑰,而錢包一般不會保存用戶的私鑰信息,資產將永久丟失。

去中心化錢包的好處是不用擔心平臺從中作梗,不用擔心平臺被黑客攻擊而導致資產損失,但要求個人有一定的私鑰保存能力。

中心錢包

所謂中心化錢包就是將所有的私鑰文件存儲在錢包服務商的服務器內,由服務商來保管這些私鑰文件,也就是說資產屬於你,但私鑰不由你保管。這樣做的好處是用戶根本不用記住私鑰,只用記住在平臺所開設的賬戶、登錄密碼和支付密碼即可。即使忘記了密碼,還是可以通過平臺提供的忘記密碼進行找回,當然,這樣就不具有去中心化錢包的優點了。

下面就帶大家瞭解一下開發這樣一個去中心化錢包的幕後技術工作和思路,後面所提到的錢包如果未做特殊說明,均指中心化錢包。

錢包的核心功能

錢包對外呈現可能有不同的功能,充值、提現、轉賬等,但從本質上來說只有一個功能,那就是轉賬。區塊鏈本質上就是一個賬本,記錄著一筆筆交易,錢包當然離不開這個本質。

  • 充值是錢包的外部賬戶向錢包的地址轉賬;
  • 提現是錢包的賬戶向錢包之外的地址轉賬;
  • 轉賬功能包括錢包內地址互轉和向錢包外地址轉賬;

在這個過程當中也對應著錢包賬戶資金的增加與減少。

用戶地址如何分配

用戶在使用錢包的時候肯定需要有一個屬於當前賬戶對應在區塊鏈上的地址,這個地址如何生成呢?在技術交流群中,不少開發人員是這樣實現的:每次當用戶註冊時就調用 Geth 節點的personal_newAccount方法生成一個地址,並將地址存放在 Geth 節點默認的位置。這種方式可以實現,但從技術上和安全上來講是不可取的。

  • 性能瓶頸之一,調用 Geth 節點生成地址非常耗時,特別是當節點在處理一些同步或高消耗的工作時。
  • 性能瓶頸之二,當 Geth 節點下的私鑰越來越多,Geth 啟動會變得漫長。
  • 安全問題,Geth 節點對外要廣播交易,又要保存敏感的私鑰信息,安全問題巨大。

優化之後的做法是事先批量生成地址,當用戶註冊時只用把地址分配給用戶即可。這樣做的好處是:

  • 私鑰的存儲與 Geth 節點相隔離,確保私鑰與外網的隔離性,從而確保私鑰的安全;
  • 性能的保障,當用戶註冊時只是將數據庫的數據建立了一個關聯,而不用去執行費時的加密算法來生成私鑰和文件。

此種方法生成 Web3j 提供了相應的創建方法,可在無 Geth 節點的情況下通過代碼直接生成符合私鑰規則的公私鑰。當然,如果有開發能力也可以通過 Geth 的源代碼中的私鑰生成方法抽離出一個單獨的與網絡無關的生產私鑰程序。

充值交易

在比特幣的錢包中,有子賬戶的概念,只需要在一個總賬戶下創建 N 多子賬號,用戶充值到此子賬號的比特幣同樣的會顯示在錢包上,同時又提供了查詢一個地址所有交易的方法。遺憾的是以太幣並沒有提供此類接口,只能通過遍歷區塊交易的方法來判斷是否有對應賬戶的充值交易。

相關操作:

  • 查詢區塊高度,比對是否是新生成的區塊,eth_blockNumber;
  • 查看區塊內容及詳細交易,eth_gethBlockByNumber;
  • 比對交易的 toAddress 是否為本錢包的地址,如果是則記錄此筆交易到數據庫,並記錄交易狀態(pending、確認次數等);
  • 保證入庫和記賬的冪等性,因為會多次查詢到同一筆交易。

確認次數

並不是我們查詢區塊鏈中的交易就說明這邊交易已經成功了,比特幣是默認確認12此之後,此交易幾乎不會被篡改,以太坊默認6次,那麼怎麼計算確認次數呢?

確認次數 = 當前區塊高度 - 交易所在區塊高度 + 1

此處注意事項:交易有可能會被孤立,在執行此公式時需要驗證一下區塊中此交易是否還是在那個區塊上,是否已經被回滾。同樣的,要做好冪等性保障。

提現交易

提現交易同樣涉及到上提到的知識點,同時它又有需要額外注意的事項。

  • 提現地址的合法性檢查,可參考源代碼中的校驗,此合法性檢查可以避免後續很多問題的出現,比如 nonce 值的維護。
  • 交易的金額檢查,nonce 值檢查(nonce 值會遇到的問題前面已經提到過),特別是私鑰與 Geth 節點分離之後自己來維護私鑰時 nonce 值會是一個很大的問題,比如前一筆交易失敗,nonce 值需要回退,此時後一筆交易已經發出,因為前一筆 nonce 沒有被補齊,後一筆遲遲不會被交易。這些都需要業務進行特殊判斷和處理。
  • 查詢一個地址 nonce,eth_getTransactionCount。


提現與轉賬

提現與轉賬都是發起一筆交易,在以太坊的 json-rpc 中已經提到可以通

過 eth_sendTransaction 和 personal_sendTransaction 直接進行轉賬,這是 Geth 節點所支持的。轉賬前可以通過 unlock 方法先將賬戶解鎖,這些之前章節都有提到過。

但針對私鑰單獨存儲的情況,上面的方式並不適用,可通過將交易先簽名再廣播的模式:

  • 簽名交易(可自主開發,可利用節點本身),eth_sign。
  • 廣播交易,可通過 eth_sendRawTransaction 進行廣播。
  • 錢包的內部轉賬只不過是 from 和 to 地址都是錢包的地址而已,業務層進行適當的處理。


通過這種模式,節點與外界打交道,僅有的功能就是廣播交易,在此之前的所有操作都可以通過內網進行操作,極大的確保的私鑰和交易的安全性。

轉賬手續費

轉賬的手續費算是常識性的內容,可給用戶一些參考值,讓用戶選擇願意支付的手續費,也可通過節點提供的eth_estimateGas來進行預估。

最笨拙而又有效的方法是定期觀察一下區塊鏈上交易成功的交易的 gasPrice 的大概範圍,動態的調整一下價格,而 gasLimit 在不影響交易(太大賬戶餘額檢查時不足)的情況下,儘可能稍微多一些,因為此部分如果未使用還會退換到交易發起賬戶中。

節點孤立

在發起或檢查以太坊交易是否成功不僅僅要檢查確認次數,還需要檢查交易是否被孤立。孤立的情況用下面的圖來展示分析一下:

"

本篇帶大家聊聊中心化存儲錢包的設計以及研發過程中的坑和業務處理。

以太坊錢包的分類

我們知道其實無論 Geth 節點或 Ethereum Wallet 都可以稱作錢包,因為它們都可以存儲以太幣、都可以發送交易指令等。但這裡講的錢包分類是另外一個維度,基於 Geth 節點上層再次開發出來的移動端 App 錢包。根據私鑰存儲的位置可分為兩類:

  • 中心化私鑰存儲的錢包,比如火幣的錢包;
  • 去中心化錢包,私鑰存儲在用戶端,比如 imtoken 錢包。

去中心化錢包

去中心化錢包不是本節要講的重點,這裡只給大家簡單介紹一下。無論是中心化的錢包或去中心化的錢包,在 App 層面都是很輕量級的,App 內是不會內置一個 Geth 節點,交易的查詢或發送都是通過服務器來進行操作,不同點是交易簽名的私鑰由誰來保管和負責。

去中心化的錢包有個關鍵詞:助記詞。可以用下面的表達式來形容助記詞的作用:

私鑰 = 助記詞 = keystore+密碼

通過上面的公式可以看出助記詞的重要作用,也是去中心錢包功能的一個亮點。當在這類錢包中創建一個賬戶之後,錢包會根據生成的私鑰文件,生成一套助記詞,可為英文可為中文,通過助記詞可以反向計算出 keystore + 密碼。助記詞由用戶手抄存放在安全的地方,當進行交易時,輸入助記詞對交易進行簽名,發送交易。當助記詞丟失,也就意味著失去了私鑰,而錢包一般不會保存用戶的私鑰信息,資產將永久丟失。

去中心化錢包的好處是不用擔心平臺從中作梗,不用擔心平臺被黑客攻擊而導致資產損失,但要求個人有一定的私鑰保存能力。

中心錢包

所謂中心化錢包就是將所有的私鑰文件存儲在錢包服務商的服務器內,由服務商來保管這些私鑰文件,也就是說資產屬於你,但私鑰不由你保管。這樣做的好處是用戶根本不用記住私鑰,只用記住在平臺所開設的賬戶、登錄密碼和支付密碼即可。即使忘記了密碼,還是可以通過平臺提供的忘記密碼進行找回,當然,這樣就不具有去中心化錢包的優點了。

下面就帶大家瞭解一下開發這樣一個去中心化錢包的幕後技術工作和思路,後面所提到的錢包如果未做特殊說明,均指中心化錢包。

錢包的核心功能

錢包對外呈現可能有不同的功能,充值、提現、轉賬等,但從本質上來說只有一個功能,那就是轉賬。區塊鏈本質上就是一個賬本,記錄著一筆筆交易,錢包當然離不開這個本質。

  • 充值是錢包的外部賬戶向錢包的地址轉賬;
  • 提現是錢包的賬戶向錢包之外的地址轉賬;
  • 轉賬功能包括錢包內地址互轉和向錢包外地址轉賬;

在這個過程當中也對應著錢包賬戶資金的增加與減少。

用戶地址如何分配

用戶在使用錢包的時候肯定需要有一個屬於當前賬戶對應在區塊鏈上的地址,這個地址如何生成呢?在技術交流群中,不少開發人員是這樣實現的:每次當用戶註冊時就調用 Geth 節點的personal_newAccount方法生成一個地址,並將地址存放在 Geth 節點默認的位置。這種方式可以實現,但從技術上和安全上來講是不可取的。

  • 性能瓶頸之一,調用 Geth 節點生成地址非常耗時,特別是當節點在處理一些同步或高消耗的工作時。
  • 性能瓶頸之二,當 Geth 節點下的私鑰越來越多,Geth 啟動會變得漫長。
  • 安全問題,Geth 節點對外要廣播交易,又要保存敏感的私鑰信息,安全問題巨大。

優化之後的做法是事先批量生成地址,當用戶註冊時只用把地址分配給用戶即可。這樣做的好處是:

  • 私鑰的存儲與 Geth 節點相隔離,確保私鑰與外網的隔離性,從而確保私鑰的安全;
  • 性能的保障,當用戶註冊時只是將數據庫的數據建立了一個關聯,而不用去執行費時的加密算法來生成私鑰和文件。

此種方法生成 Web3j 提供了相應的創建方法,可在無 Geth 節點的情況下通過代碼直接生成符合私鑰規則的公私鑰。當然,如果有開發能力也可以通過 Geth 的源代碼中的私鑰生成方法抽離出一個單獨的與網絡無關的生產私鑰程序。

充值交易

在比特幣的錢包中,有子賬戶的概念,只需要在一個總賬戶下創建 N 多子賬號,用戶充值到此子賬號的比特幣同樣的會顯示在錢包上,同時又提供了查詢一個地址所有交易的方法。遺憾的是以太幣並沒有提供此類接口,只能通過遍歷區塊交易的方法來判斷是否有對應賬戶的充值交易。

相關操作:

  • 查詢區塊高度,比對是否是新生成的區塊,eth_blockNumber;
  • 查看區塊內容及詳細交易,eth_gethBlockByNumber;
  • 比對交易的 toAddress 是否為本錢包的地址,如果是則記錄此筆交易到數據庫,並記錄交易狀態(pending、確認次數等);
  • 保證入庫和記賬的冪等性,因為會多次查詢到同一筆交易。

確認次數

並不是我們查詢區塊鏈中的交易就說明這邊交易已經成功了,比特幣是默認確認12此之後,此交易幾乎不會被篡改,以太坊默認6次,那麼怎麼計算確認次數呢?

確認次數 = 當前區塊高度 - 交易所在區塊高度 + 1

此處注意事項:交易有可能會被孤立,在執行此公式時需要驗證一下區塊中此交易是否還是在那個區塊上,是否已經被回滾。同樣的,要做好冪等性保障。

提現交易

提現交易同樣涉及到上提到的知識點,同時它又有需要額外注意的事項。

  • 提現地址的合法性檢查,可參考源代碼中的校驗,此合法性檢查可以避免後續很多問題的出現,比如 nonce 值的維護。
  • 交易的金額檢查,nonce 值檢查(nonce 值會遇到的問題前面已經提到過),特別是私鑰與 Geth 節點分離之後自己來維護私鑰時 nonce 值會是一個很大的問題,比如前一筆交易失敗,nonce 值需要回退,此時後一筆交易已經發出,因為前一筆 nonce 沒有被補齊,後一筆遲遲不會被交易。這些都需要業務進行特殊判斷和處理。
  • 查詢一個地址 nonce,eth_getTransactionCount。


提現與轉賬

提現與轉賬都是發起一筆交易,在以太坊的 json-rpc 中已經提到可以通

過 eth_sendTransaction 和 personal_sendTransaction 直接進行轉賬,這是 Geth 節點所支持的。轉賬前可以通過 unlock 方法先將賬戶解鎖,這些之前章節都有提到過。

但針對私鑰單獨存儲的情況,上面的方式並不適用,可通過將交易先簽名再廣播的模式:

  • 簽名交易(可自主開發,可利用節點本身),eth_sign。
  • 廣播交易,可通過 eth_sendRawTransaction 進行廣播。
  • 錢包的內部轉賬只不過是 from 和 to 地址都是錢包的地址而已,業務層進行適當的處理。


通過這種模式,節點與外界打交道,僅有的功能就是廣播交易,在此之前的所有操作都可以通過內網進行操作,極大的確保的私鑰和交易的安全性。

轉賬手續費

轉賬的手續費算是常識性的內容,可給用戶一些參考值,讓用戶選擇願意支付的手續費,也可通過節點提供的eth_estimateGas來進行預估。

最笨拙而又有效的方法是定期觀察一下區塊鏈上交易成功的交易的 gasPrice 的大概範圍,動態的調整一下價格,而 gasLimit 在不影響交易(太大賬戶餘額檢查時不足)的情況下,儘可能稍微多一些,因為此部分如果未使用還會退換到交易發起賬戶中。

節點孤立

在發起或檢查以太坊交易是否成功不僅僅要檢查確認次數,還需要檢查交易是否被孤立。孤立的情況用下面的圖來展示分析一下:

開發以太坊錢包的基本思路與安全


節點孤立是什麼情況?在上面圖中,第1、2區塊成功打包記入區塊鏈,當到第3塊時,有兩個節點同時挖出第3塊,整個區塊鏈中有一部分認同了上面的鏈,有一部分認同了下面的鏈,此時因為都只有3塊,沒辦法確認哪個是主鏈。但此時區塊已經出現了分叉情況。當更多的塊被挖出,在某一時間節點,上面的鏈的長度比下面的長了,此時所有的節點都會認為最長的為正確的鏈,下面的第3、4塊打包的交易將會被回滾,等待重新打包。此時,第3、4塊的狀況叫做被孤立。

套入簽名的業務邏輯,我們的節點處於被孤立的鏈上時,我們之前掃描到的交易所在區塊高度是可變的,在那個區塊上已經沒有我們的交易了,如果此時對賬戶進行增減記賬,會發生資產的不一致。

那麼如何檢測孤立區塊呢?

  • 監控區塊的高度變化,並記錄在本地數據庫,同時,每收到一筆新的交易,都對比一下此交易記錄的前一塊交易 hash 是否一致,如果不一致說明區別已經被孤立,遞歸判斷找到被孤立的前一塊,然後從那塊繼續掃描,重新整理判斷交易情況。
  • 此處特別注意確保冪等性。
  • 區塊被孤立同時需要更新本地數據中此筆交易狀態為被孤立狀態。


測試環境模擬孤立

  • 備份數據,發送簽名(eth_sign)之後的交易;
  • 挖礦3個區塊;
  • 恢復備份數據;
  • 再挖5個塊;
  • 查詢業務是否正常處理孤立情況。


節點熱備

由於每個節點同步到的數據進度差距太大,沒辦法像中心化的業務一樣做負載均衡,只能通過熱備的形式來保證當一個節點出現故障時能夠快速切換到另外一個節點。因為區塊打包本身就比較耗時,因此此處的時效性要求還是可以容忍的。

"

本篇帶大家聊聊中心化存儲錢包的設計以及研發過程中的坑和業務處理。

以太坊錢包的分類

我們知道其實無論 Geth 節點或 Ethereum Wallet 都可以稱作錢包,因為它們都可以存儲以太幣、都可以發送交易指令等。但這裡講的錢包分類是另外一個維度,基於 Geth 節點上層再次開發出來的移動端 App 錢包。根據私鑰存儲的位置可分為兩類:

  • 中心化私鑰存儲的錢包,比如火幣的錢包;
  • 去中心化錢包,私鑰存儲在用戶端,比如 imtoken 錢包。

去中心化錢包

去中心化錢包不是本節要講的重點,這裡只給大家簡單介紹一下。無論是中心化的錢包或去中心化的錢包,在 App 層面都是很輕量級的,App 內是不會內置一個 Geth 節點,交易的查詢或發送都是通過服務器來進行操作,不同點是交易簽名的私鑰由誰來保管和負責。

去中心化的錢包有個關鍵詞:助記詞。可以用下面的表達式來形容助記詞的作用:

私鑰 = 助記詞 = keystore+密碼

通過上面的公式可以看出助記詞的重要作用,也是去中心錢包功能的一個亮點。當在這類錢包中創建一個賬戶之後,錢包會根據生成的私鑰文件,生成一套助記詞,可為英文可為中文,通過助記詞可以反向計算出 keystore + 密碼。助記詞由用戶手抄存放在安全的地方,當進行交易時,輸入助記詞對交易進行簽名,發送交易。當助記詞丟失,也就意味著失去了私鑰,而錢包一般不會保存用戶的私鑰信息,資產將永久丟失。

去中心化錢包的好處是不用擔心平臺從中作梗,不用擔心平臺被黑客攻擊而導致資產損失,但要求個人有一定的私鑰保存能力。

中心錢包

所謂中心化錢包就是將所有的私鑰文件存儲在錢包服務商的服務器內,由服務商來保管這些私鑰文件,也就是說資產屬於你,但私鑰不由你保管。這樣做的好處是用戶根本不用記住私鑰,只用記住在平臺所開設的賬戶、登錄密碼和支付密碼即可。即使忘記了密碼,還是可以通過平臺提供的忘記密碼進行找回,當然,這樣就不具有去中心化錢包的優點了。

下面就帶大家瞭解一下開發這樣一個去中心化錢包的幕後技術工作和思路,後面所提到的錢包如果未做特殊說明,均指中心化錢包。

錢包的核心功能

錢包對外呈現可能有不同的功能,充值、提現、轉賬等,但從本質上來說只有一個功能,那就是轉賬。區塊鏈本質上就是一個賬本,記錄著一筆筆交易,錢包當然離不開這個本質。

  • 充值是錢包的外部賬戶向錢包的地址轉賬;
  • 提現是錢包的賬戶向錢包之外的地址轉賬;
  • 轉賬功能包括錢包內地址互轉和向錢包外地址轉賬;

在這個過程當中也對應著錢包賬戶資金的增加與減少。

用戶地址如何分配

用戶在使用錢包的時候肯定需要有一個屬於當前賬戶對應在區塊鏈上的地址,這個地址如何生成呢?在技術交流群中,不少開發人員是這樣實現的:每次當用戶註冊時就調用 Geth 節點的personal_newAccount方法生成一個地址,並將地址存放在 Geth 節點默認的位置。這種方式可以實現,但從技術上和安全上來講是不可取的。

  • 性能瓶頸之一,調用 Geth 節點生成地址非常耗時,特別是當節點在處理一些同步或高消耗的工作時。
  • 性能瓶頸之二,當 Geth 節點下的私鑰越來越多,Geth 啟動會變得漫長。
  • 安全問題,Geth 節點對外要廣播交易,又要保存敏感的私鑰信息,安全問題巨大。

優化之後的做法是事先批量生成地址,當用戶註冊時只用把地址分配給用戶即可。這樣做的好處是:

  • 私鑰的存儲與 Geth 節點相隔離,確保私鑰與外網的隔離性,從而確保私鑰的安全;
  • 性能的保障,當用戶註冊時只是將數據庫的數據建立了一個關聯,而不用去執行費時的加密算法來生成私鑰和文件。

此種方法生成 Web3j 提供了相應的創建方法,可在無 Geth 節點的情況下通過代碼直接生成符合私鑰規則的公私鑰。當然,如果有開發能力也可以通過 Geth 的源代碼中的私鑰生成方法抽離出一個單獨的與網絡無關的生產私鑰程序。

充值交易

在比特幣的錢包中,有子賬戶的概念,只需要在一個總賬戶下創建 N 多子賬號,用戶充值到此子賬號的比特幣同樣的會顯示在錢包上,同時又提供了查詢一個地址所有交易的方法。遺憾的是以太幣並沒有提供此類接口,只能通過遍歷區塊交易的方法來判斷是否有對應賬戶的充值交易。

相關操作:

  • 查詢區塊高度,比對是否是新生成的區塊,eth_blockNumber;
  • 查看區塊內容及詳細交易,eth_gethBlockByNumber;
  • 比對交易的 toAddress 是否為本錢包的地址,如果是則記錄此筆交易到數據庫,並記錄交易狀態(pending、確認次數等);
  • 保證入庫和記賬的冪等性,因為會多次查詢到同一筆交易。

確認次數

並不是我們查詢區塊鏈中的交易就說明這邊交易已經成功了,比特幣是默認確認12此之後,此交易幾乎不會被篡改,以太坊默認6次,那麼怎麼計算確認次數呢?

確認次數 = 當前區塊高度 - 交易所在區塊高度 + 1

此處注意事項:交易有可能會被孤立,在執行此公式時需要驗證一下區塊中此交易是否還是在那個區塊上,是否已經被回滾。同樣的,要做好冪等性保障。

提現交易

提現交易同樣涉及到上提到的知識點,同時它又有需要額外注意的事項。

  • 提現地址的合法性檢查,可參考源代碼中的校驗,此合法性檢查可以避免後續很多問題的出現,比如 nonce 值的維護。
  • 交易的金額檢查,nonce 值檢查(nonce 值會遇到的問題前面已經提到過),特別是私鑰與 Geth 節點分離之後自己來維護私鑰時 nonce 值會是一個很大的問題,比如前一筆交易失敗,nonce 值需要回退,此時後一筆交易已經發出,因為前一筆 nonce 沒有被補齊,後一筆遲遲不會被交易。這些都需要業務進行特殊判斷和處理。
  • 查詢一個地址 nonce,eth_getTransactionCount。


提現與轉賬

提現與轉賬都是發起一筆交易,在以太坊的 json-rpc 中已經提到可以通

過 eth_sendTransaction 和 personal_sendTransaction 直接進行轉賬,這是 Geth 節點所支持的。轉賬前可以通過 unlock 方法先將賬戶解鎖,這些之前章節都有提到過。

但針對私鑰單獨存儲的情況,上面的方式並不適用,可通過將交易先簽名再廣播的模式:

  • 簽名交易(可自主開發,可利用節點本身),eth_sign。
  • 廣播交易,可通過 eth_sendRawTransaction 進行廣播。
  • 錢包的內部轉賬只不過是 from 和 to 地址都是錢包的地址而已,業務層進行適當的處理。


通過這種模式,節點與外界打交道,僅有的功能就是廣播交易,在此之前的所有操作都可以通過內網進行操作,極大的確保的私鑰和交易的安全性。

轉賬手續費

轉賬的手續費算是常識性的內容,可給用戶一些參考值,讓用戶選擇願意支付的手續費,也可通過節點提供的eth_estimateGas來進行預估。

最笨拙而又有效的方法是定期觀察一下區塊鏈上交易成功的交易的 gasPrice 的大概範圍,動態的調整一下價格,而 gasLimit 在不影響交易(太大賬戶餘額檢查時不足)的情況下,儘可能稍微多一些,因為此部分如果未使用還會退換到交易發起賬戶中。

節點孤立

在發起或檢查以太坊交易是否成功不僅僅要檢查確認次數,還需要檢查交易是否被孤立。孤立的情況用下面的圖來展示分析一下:

開發以太坊錢包的基本思路與安全


節點孤立是什麼情況?在上面圖中,第1、2區塊成功打包記入區塊鏈,當到第3塊時,有兩個節點同時挖出第3塊,整個區塊鏈中有一部分認同了上面的鏈,有一部分認同了下面的鏈,此時因為都只有3塊,沒辦法確認哪個是主鏈。但此時區塊已經出現了分叉情況。當更多的塊被挖出,在某一時間節點,上面的鏈的長度比下面的長了,此時所有的節點都會認為最長的為正確的鏈,下面的第3、4塊打包的交易將會被回滾,等待重新打包。此時,第3、4塊的狀況叫做被孤立。

套入簽名的業務邏輯,我們的節點處於被孤立的鏈上時,我們之前掃描到的交易所在區塊高度是可變的,在那個區塊上已經沒有我們的交易了,如果此時對賬戶進行增減記賬,會發生資產的不一致。

那麼如何檢測孤立區塊呢?

  • 監控區塊的高度變化,並記錄在本地數據庫,同時,每收到一筆新的交易,都對比一下此交易記錄的前一塊交易 hash 是否一致,如果不一致說明區別已經被孤立,遞歸判斷找到被孤立的前一塊,然後從那塊繼續掃描,重新整理判斷交易情況。
  • 此處特別注意確保冪等性。
  • 區塊被孤立同時需要更新本地數據中此筆交易狀態為被孤立狀態。


測試環境模擬孤立

  • 備份數據,發送簽名(eth_sign)之後的交易;
  • 挖礦3個區塊;
  • 恢復備份數據;
  • 再挖5個塊;
  • 查詢業務是否正常處理孤立情況。


節點熱備

由於每個節點同步到的數據進度差距太大,沒辦法像中心化的業務一樣做負載均衡,只能通過熱備的形式來保證當一個節點出現故障時能夠快速切換到另外一個節點。因為區塊打包本身就比較耗時,因此此處的時效性要求還是可以容忍的。

開發以太坊錢包的基本思路與安全


節點安全

之前很多朋友因為將 Geth 節點公網開放而導致資產損失,正是因為沒有正確的認識到 json-rpc 不同權限的接口的問題。在前面的章節中我們已經介紹了不同的節點的權限。這裡再次聲明一下,以下節點慎重對外往開放:

  • personal
  • net
  • txpool
  • miner
  • admin
  • 等我們不需要的

其實最安全的模式就是 geth 節點只對外進行廣播交易。

私鑰管理

上面已經穿插著講了此塊內容,將私鑰單獨存放甚至進行二次加密。

錢包的整體架構

錢包可以通過此種架構來達到高可用和安全性兼顧。

"

本篇帶大家聊聊中心化存儲錢包的設計以及研發過程中的坑和業務處理。

以太坊錢包的分類

我們知道其實無論 Geth 節點或 Ethereum Wallet 都可以稱作錢包,因為它們都可以存儲以太幣、都可以發送交易指令等。但這裡講的錢包分類是另外一個維度,基於 Geth 節點上層再次開發出來的移動端 App 錢包。根據私鑰存儲的位置可分為兩類:

  • 中心化私鑰存儲的錢包,比如火幣的錢包;
  • 去中心化錢包,私鑰存儲在用戶端,比如 imtoken 錢包。

去中心化錢包

去中心化錢包不是本節要講的重點,這裡只給大家簡單介紹一下。無論是中心化的錢包或去中心化的錢包,在 App 層面都是很輕量級的,App 內是不會內置一個 Geth 節點,交易的查詢或發送都是通過服務器來進行操作,不同點是交易簽名的私鑰由誰來保管和負責。

去中心化的錢包有個關鍵詞:助記詞。可以用下面的表達式來形容助記詞的作用:

私鑰 = 助記詞 = keystore+密碼

通過上面的公式可以看出助記詞的重要作用,也是去中心錢包功能的一個亮點。當在這類錢包中創建一個賬戶之後,錢包會根據生成的私鑰文件,生成一套助記詞,可為英文可為中文,通過助記詞可以反向計算出 keystore + 密碼。助記詞由用戶手抄存放在安全的地方,當進行交易時,輸入助記詞對交易進行簽名,發送交易。當助記詞丟失,也就意味著失去了私鑰,而錢包一般不會保存用戶的私鑰信息,資產將永久丟失。

去中心化錢包的好處是不用擔心平臺從中作梗,不用擔心平臺被黑客攻擊而導致資產損失,但要求個人有一定的私鑰保存能力。

中心錢包

所謂中心化錢包就是將所有的私鑰文件存儲在錢包服務商的服務器內,由服務商來保管這些私鑰文件,也就是說資產屬於你,但私鑰不由你保管。這樣做的好處是用戶根本不用記住私鑰,只用記住在平臺所開設的賬戶、登錄密碼和支付密碼即可。即使忘記了密碼,還是可以通過平臺提供的忘記密碼進行找回,當然,這樣就不具有去中心化錢包的優點了。

下面就帶大家瞭解一下開發這樣一個去中心化錢包的幕後技術工作和思路,後面所提到的錢包如果未做特殊說明,均指中心化錢包。

錢包的核心功能

錢包對外呈現可能有不同的功能,充值、提現、轉賬等,但從本質上來說只有一個功能,那就是轉賬。區塊鏈本質上就是一個賬本,記錄著一筆筆交易,錢包當然離不開這個本質。

  • 充值是錢包的外部賬戶向錢包的地址轉賬;
  • 提現是錢包的賬戶向錢包之外的地址轉賬;
  • 轉賬功能包括錢包內地址互轉和向錢包外地址轉賬;

在這個過程當中也對應著錢包賬戶資金的增加與減少。

用戶地址如何分配

用戶在使用錢包的時候肯定需要有一個屬於當前賬戶對應在區塊鏈上的地址,這個地址如何生成呢?在技術交流群中,不少開發人員是這樣實現的:每次當用戶註冊時就調用 Geth 節點的personal_newAccount方法生成一個地址,並將地址存放在 Geth 節點默認的位置。這種方式可以實現,但從技術上和安全上來講是不可取的。

  • 性能瓶頸之一,調用 Geth 節點生成地址非常耗時,特別是當節點在處理一些同步或高消耗的工作時。
  • 性能瓶頸之二,當 Geth 節點下的私鑰越來越多,Geth 啟動會變得漫長。
  • 安全問題,Geth 節點對外要廣播交易,又要保存敏感的私鑰信息,安全問題巨大。

優化之後的做法是事先批量生成地址,當用戶註冊時只用把地址分配給用戶即可。這樣做的好處是:

  • 私鑰的存儲與 Geth 節點相隔離,確保私鑰與外網的隔離性,從而確保私鑰的安全;
  • 性能的保障,當用戶註冊時只是將數據庫的數據建立了一個關聯,而不用去執行費時的加密算法來生成私鑰和文件。

此種方法生成 Web3j 提供了相應的創建方法,可在無 Geth 節點的情況下通過代碼直接生成符合私鑰規則的公私鑰。當然,如果有開發能力也可以通過 Geth 的源代碼中的私鑰生成方法抽離出一個單獨的與網絡無關的生產私鑰程序。

充值交易

在比特幣的錢包中,有子賬戶的概念,只需要在一個總賬戶下創建 N 多子賬號,用戶充值到此子賬號的比特幣同樣的會顯示在錢包上,同時又提供了查詢一個地址所有交易的方法。遺憾的是以太幣並沒有提供此類接口,只能通過遍歷區塊交易的方法來判斷是否有對應賬戶的充值交易。

相關操作:

  • 查詢區塊高度,比對是否是新生成的區塊,eth_blockNumber;
  • 查看區塊內容及詳細交易,eth_gethBlockByNumber;
  • 比對交易的 toAddress 是否為本錢包的地址,如果是則記錄此筆交易到數據庫,並記錄交易狀態(pending、確認次數等);
  • 保證入庫和記賬的冪等性,因為會多次查詢到同一筆交易。

確認次數

並不是我們查詢區塊鏈中的交易就說明這邊交易已經成功了,比特幣是默認確認12此之後,此交易幾乎不會被篡改,以太坊默認6次,那麼怎麼計算確認次數呢?

確認次數 = 當前區塊高度 - 交易所在區塊高度 + 1

此處注意事項:交易有可能會被孤立,在執行此公式時需要驗證一下區塊中此交易是否還是在那個區塊上,是否已經被回滾。同樣的,要做好冪等性保障。

提現交易

提現交易同樣涉及到上提到的知識點,同時它又有需要額外注意的事項。

  • 提現地址的合法性檢查,可參考源代碼中的校驗,此合法性檢查可以避免後續很多問題的出現,比如 nonce 值的維護。
  • 交易的金額檢查,nonce 值檢查(nonce 值會遇到的問題前面已經提到過),特別是私鑰與 Geth 節點分離之後自己來維護私鑰時 nonce 值會是一個很大的問題,比如前一筆交易失敗,nonce 值需要回退,此時後一筆交易已經發出,因為前一筆 nonce 沒有被補齊,後一筆遲遲不會被交易。這些都需要業務進行特殊判斷和處理。
  • 查詢一個地址 nonce,eth_getTransactionCount。


提現與轉賬

提現與轉賬都是發起一筆交易,在以太坊的 json-rpc 中已經提到可以通

過 eth_sendTransaction 和 personal_sendTransaction 直接進行轉賬,這是 Geth 節點所支持的。轉賬前可以通過 unlock 方法先將賬戶解鎖,這些之前章節都有提到過。

但針對私鑰單獨存儲的情況,上面的方式並不適用,可通過將交易先簽名再廣播的模式:

  • 簽名交易(可自主開發,可利用節點本身),eth_sign。
  • 廣播交易,可通過 eth_sendRawTransaction 進行廣播。
  • 錢包的內部轉賬只不過是 from 和 to 地址都是錢包的地址而已,業務層進行適當的處理。


通過這種模式,節點與外界打交道,僅有的功能就是廣播交易,在此之前的所有操作都可以通過內網進行操作,極大的確保的私鑰和交易的安全性。

轉賬手續費

轉賬的手續費算是常識性的內容,可給用戶一些參考值,讓用戶選擇願意支付的手續費,也可通過節點提供的eth_estimateGas來進行預估。

最笨拙而又有效的方法是定期觀察一下區塊鏈上交易成功的交易的 gasPrice 的大概範圍,動態的調整一下價格,而 gasLimit 在不影響交易(太大賬戶餘額檢查時不足)的情況下,儘可能稍微多一些,因為此部分如果未使用還會退換到交易發起賬戶中。

節點孤立

在發起或檢查以太坊交易是否成功不僅僅要檢查確認次數,還需要檢查交易是否被孤立。孤立的情況用下面的圖來展示分析一下:

開發以太坊錢包的基本思路與安全


節點孤立是什麼情況?在上面圖中,第1、2區塊成功打包記入區塊鏈,當到第3塊時,有兩個節點同時挖出第3塊,整個區塊鏈中有一部分認同了上面的鏈,有一部分認同了下面的鏈,此時因為都只有3塊,沒辦法確認哪個是主鏈。但此時區塊已經出現了分叉情況。當更多的塊被挖出,在某一時間節點,上面的鏈的長度比下面的長了,此時所有的節點都會認為最長的為正確的鏈,下面的第3、4塊打包的交易將會被回滾,等待重新打包。此時,第3、4塊的狀況叫做被孤立。

套入簽名的業務邏輯,我們的節點處於被孤立的鏈上時,我們之前掃描到的交易所在區塊高度是可變的,在那個區塊上已經沒有我們的交易了,如果此時對賬戶進行增減記賬,會發生資產的不一致。

那麼如何檢測孤立區塊呢?

  • 監控區塊的高度變化,並記錄在本地數據庫,同時,每收到一筆新的交易,都對比一下此交易記錄的前一塊交易 hash 是否一致,如果不一致說明區別已經被孤立,遞歸判斷找到被孤立的前一塊,然後從那塊繼續掃描,重新整理判斷交易情況。
  • 此處特別注意確保冪等性。
  • 區塊被孤立同時需要更新本地數據中此筆交易狀態為被孤立狀態。


測試環境模擬孤立

  • 備份數據,發送簽名(eth_sign)之後的交易;
  • 挖礦3個區塊;
  • 恢復備份數據;
  • 再挖5個塊;
  • 查詢業務是否正常處理孤立情況。


節點熱備

由於每個節點同步到的數據進度差距太大,沒辦法像中心化的業務一樣做負載均衡,只能通過熱備的形式來保證當一個節點出現故障時能夠快速切換到另外一個節點。因為區塊打包本身就比較耗時,因此此處的時效性要求還是可以容忍的。

開發以太坊錢包的基本思路與安全


節點安全

之前很多朋友因為將 Geth 節點公網開放而導致資產損失,正是因為沒有正確的認識到 json-rpc 不同權限的接口的問題。在前面的章節中我們已經介紹了不同的節點的權限。這裡再次聲明一下,以下節點慎重對外往開放:

  • personal
  • net
  • txpool
  • miner
  • admin
  • 等我們不需要的

其實最安全的模式就是 geth 節點只對外進行廣播交易。

私鑰管理

上面已經穿插著講了此塊內容,將私鑰單獨存放甚至進行二次加密。

錢包的整體架構

錢包可以通過此種架構來達到高可用和安全性兼顧。

開發以太坊錢包的基本思路與安全


關注小編,小編會每天為你分享有趣的技術文章哦。偷偷告訴你私信小編“學習”會有意想不到的驚喜喲~~!

"

相關推薦

推薦中...