1. 程式人生 > >[轉]【比特幣錢包開發 五】新建錢包:生成子賬號地址與路徑

[轉]【比特幣錢包開發 五】新建錢包:生成子賬號地址與路徑

本文轉自:https://www.chaindesk.cn/witbook/9/74

課程目標
掌握連線到比特幣正式網路與測試網進行開發
學會建立錢包與備份錢包
生成賬號地址
前言
比特幣的賬號是通過bip協議生成種子,然後擴充套件成多個子地址,這些子地址都由同一個種子可以推匯出來,而知道其中一個子賬號的私鑰不能推導兄弟和父輩節點的私鑰,所以將同一個種子擴充套件生成的子賬號統一為一個賬號,在每次交易時都可以使用它的新的子地址去交易,這樣更安全,也更難追蹤,也常常將這個大賬號稱為錢包。因此對賬號的操作,我們需要先來建立錢包賬號,那麼本章的內容就是學習如何建立錢包,和錢包的一些常用功能。

一、獲取錢包客戶端物件
在上一章的內容中已經將建立錢包客戶端物件的方法封裝到了Models/walletClient.js檔案中的getWalletClient()方法中,如下:

getWalletClient: () => {
var Client = require('bitcore-wallet-client');

var client = new Client({
baseUrl: config.BWS_URL,
verbose: false,
});
return client
},

在例項化的時候需要指定bitcore-wallet服務端地址,我們使用的是bitpay的地址BWS_URL: 'https://bws.bitpay.com/bws/api',你也可以自己搭建一個錢包服務端。

現在我們在業務類wallet中呼叫該方法。

var client = require("../models/walletClient").getWalletClient()
1


現在client物件還沒有生成認證,可以看到在__proto下clien有很多個方法,接下來通過client物件呼叫建立錢包。

二、建立錢包的API文件說明
建立錢包的API文件如下:

API.createWallet(walletName, copayerName, m, n, opts, opts.network, opts.walletPrivKey, opts.id, opts.withMnemonics, cb)
Create a wallet.

Parameters

walletName: String, Create a wallet.

copayerName: String, Create a wallet.

m: Number, Create a wallet.

n: Number, Create a wallet.

opts: object, (optional: advanced options)

opts.network: string, 'livenet' or 'testnet'

opts.walletPrivKey: String, set a walletPrivKey (instead of random)

opts.id: String, set a id for wallet (instead of server given)

opts.withMnemonics: String, generate credentials

cb: , Create a wallet.

Returns: undefined

根據API的定義可知,必傳引數有如下六個:

walletName: String, 建立的錢包名稱。

copayerName: String, 建立錢包的人,即錢包的擁有者。

m: Number, 該錢包擁有者個數。若為1,則是個人錢包;若大於1,則是共享錢包,需要多重簽名。

n: Number, 與m對應,不能大於m,指定需要簽名的個數。若是個人錢包,則為1。

opts: 可選引數,它是一個物件,包含的字斷如下:

opts.network: string, 錢包連線的網路型別,支援正式網路和測試網路,分別表示為:livenet、testnet。

opts.walletPrivKey: 錢包私鑰,可以不用指定,會隨機生成。

opts.id: String, 錢包ID,可以不用指定,會自動生成。

opts.withMnemonics: String, 助記詞,用於生成憑證,對錢包的操作都是用這個憑證進行的操作。

cb: 回撥方法。該方法是沒有返回值的,響應的資料是通過callba回掉進行傳遞。

三、編碼實現建立錢包
1. 小試牛刀
根據上面的分析我們進行如下的呼叫。

var client = require("../models/walletClient").getWalletClient()

client.createWallet("MyWallet", "lixu", 1, 1, {
network: "testnet",
coin: "btc",
}, function (err, ret) {
console.log(ret,ret)
console.log(client)
})

執行後生成的client物件如下:

 

可知建立後的錢包作為client物件的credential屬性,裡面的字斷記錄了錢包的詳細資料。這樣我們便建立了一個名稱為“MyWallet”、擁有者是“lixu”、在測試網路下的比特幣個人錢包,預設使用BIP44協議建立賬號、地址型別是P2PKH支付給公鑰雜湊。另外,我們沒有發現該賬號的助記詞,所以建立錢包的方法還需完善,那麼正確的步驟是這樣的呢?

2. 通過隨機助記詞生成種子
我們需要在建立錢包之間呼叫seedFromRandomWithMnemonic()方法通過隨機助記詞生成種子,目的是為了能夠匯出該錢包的助記詞備份錢包。下面API文件就不作解釋了,大家可以在GitHub檢視,它的用法如下

client.seedFromRandomWithMnemonic({
passphrase: "12345678",
network: "testnet",
coin: "btc",
})

注意:

密碼字斷不能忘記,需要使用密碼與助記詞共同生成種子,密碼不一樣種子就不一樣,導致無法找回賬號。現在執行後的結果如下所示。
network設定為testnet則是連線到測試網,若連線到正式網路則是“livenet”。


3. 匯出錢包
我們建立好錢包後,下次再次使用錢包時需要使用import()方法匯入錢包去啟用,所以我們建立錢包後需要備份錢包資料,與以太坊中keystore類似。備份錢包資料需要用到export()方法,它將該錢包物件匯出存到檔案中,用於下次通過檔案開啟錢包。import()方法的用法接下來會進行介紹。

特別提醒:

本專案為了讓大家快速開發比特幣錢包專案,對於非重點內容沒有花時間去講解。那麼這裡專案中匯出的錢包為了便於講解是放在的伺服器中,這是非常不安全的做法,對於有經驗的開發者自然是瞭解如何處理這類敏感資料。另外,服務端是對同一個使用者”lixu“即程式碼中“copayerName”欄位建立的錢包,在實際開發中需要實現使用者模組建立使用者體系,這裡就簡寫了。建議將此資料放到客戶端儲存,並且需要加密。

......

var path = require('path');
var fs = require('fs');

client.createWallet("MyWallet", "lixu", 1, 1, {
network: "testnet",
coin: "btc",
}, function (err, ret) {
console.log(ret,ret)
let filePath = path.join(path.join(__dirname, "../static/wallet_file"), "MyWallet.dat")
fs.writeFileSync(filePath, client.export());
})

將錢包資料儲存在以.bat結尾的檔案中,以錢包名稱命名,所以錢包名稱重複會覆蓋錢包賬號。執行後開啟static/wallet_file資料夾下的MyWallet.bat檔案,它的資料以json字串儲存,如下圖,使用格式化工具後檢視可知它的資料就是client.credentials字斷中的資料。

 

4. 新建地址
新建立的錢包沒有自動生成地址,為了開啟該錢包的時候有一個地址,所以我們可以在建立成功後呼叫新建地址的方法讓他擁有第一個賬號地址。當然,它的路徑肯定就是“M/0/0”了。接下來我們會獲取錢包的子賬號地址進行檢視。

......

console.log(ret,ret)
let filePath = path.join(path.join(__dirname, "../static/wallet_file"), "MyWallet.dat")
fs.writeFileSync(filePath, client.export());

client.createAddress({}, function (err, addr) {
console.log(err, addr)
});

輸出如下:

 

四、完整原始碼
1. controllers/wallet.js
在controllers資料夾下新建wallet.js檔案,後端實現錢包模組功能,下面先實現建立錢包的功能。

前端需要傳遞walletname, password兩個引數,最後將新建的第一個賬號地址返回給前端,資料結構如下:

{
code: 0,
status: 'success',
data: {
address: 'mrPo6ePcPKFezfRV4Q2pzpmFLTQERvNZKx'
}
}

將常量放在了config配置檔案中,在上一章中已經作了介紹。


var client = require("../models/walletClient").getWalletClient()
var path = require('path');
var fs = require('fs');
var config = require("../config/config")
var { success, fail } = require("../utils/myUtils")

module.exports = {
walletCreate: (req, res) => {
let { walletname, password } = req.body
console.log(req.body)

client.seedFromRandomWithMnemonic({
passphrase: password,
network: config.networkType,
coin: config.coinType,
})
console.log(client.credentials.mnemonic)
client.createWallet(walletname, config.coinType, 1, 1, {
network: config.networkType,
withMnemonics: client.credentials.mnemonic,
coin: config.coinType,

}, function (err, ret) {
console.log(err, ret)
if (err) {
res.send(fail(err.message))
return
}
let filePath = path.join(config.walletFilePath, walletname + ".dat")
fs.writeFileSync(filePath, client.export());

client.createAddress({}, function (err, addr) {
console.log(err, addr)
if (err) {
res.send(fail(err.message))
return
}
res.send(success({ "address": addr.address }))
});
})
},
}

2. router/router.js
將建立錢包的介面繫結到路由。

let router = require('express').Router();

let walletController = require("../controllers/wallet")

//錢包
router.post("/wallet/create", walletController.walletCreate)

router.get("/wallet.html", (req, res) => {
res.render("wallet.html");
})

module.exports = router

3. static/js/wallet.js
在文件載入完成後設定建立錢包表單的驗證與網路請求。

$(document).ready(function () {

//建立錢包
$("#wallet-create-form").validate({
rules: {
walletname: {
required: true,
},
password: {
required: true,
},
},
messages: {
walletname: {
required: "請輸入新建的錢包名稱",
},
password: {
required: "請輸入新建的錢包密碼",
},
},
submitHandler: function (form) {
$(form).ajaxSubmit({
url: "/wallet/create",
type: "post",
dataType: "json",
success: function (res, status) {
console.log(status + JSON.stringify(res))
alert(JSON.stringify(res.data))
if (res.code == 0) {
window.location.reload()
}
},
error: function (res, status) {
console.log(status + JSON.stringify(res))
}
});
}
})

})

4. views/wallet.html
前端:錢包列表的初始頁面。

<html>

<head>
<title>錢包</title>
<script src="/js/lib/jquery-3.3.1.min.js"></script>
<script src="/js/lib/jquery.url.js"></script>
<script src="/js/wallet.js"></script>
<link rel="stylesheet" href="/css/btcwallet.css">
</head>

<body>
<%include block/nav.html%>

<div id="main">
<h1>錢包列表</h1>
<form id="wallet-create-form">
<button type="submit">建立錢包</button>
<input type="text" name="walletname" placeholder="請輸入錢包名稱">
<input type="text" name="password" placeholder="請輸入錢包密碼">
</form>
</div>
</body>

</html>

五、專案執行效果


專案原始碼Github地址

版權宣告:部落格中的文章版權歸博主所有,未經授權禁止轉載,轉載請聯絡作者(微信:lixu1770105)取得同意並註明出處。

未經授權禁止轉載、改編,轉載請註明出處!

本文地址: https://www.chaindesk.cn/witbook/9/74
作者:李旭
連結:https://www.chaindesk.cn/witbook/9/74
來源:http://www.chaindesk.cn
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。

其他章節: https://www.chaindesk.cn/witbook/9