HD Wallets的全称是Hierachical Deterministic Wallets, 对应中文是 分层确定性钱包。

这种钱包能够使用一组助记词来管理所有的账户的所有币种,在比特币的BIP32提案中提出,通过种子来生成主私钥,然后派生海量的子私钥和地址。种子很长,为了方便记录,转换为一组单词记录,这是BIP39提出的。

生成钱包地址的基本流程:1 生成一组助记词 2 助记词转化成种子(通过PBKDF2) 3 种子生成根私钥(通过HMAC-SHA512) 4 通过根私钥生成子私钥

本文的目的是带着读者用代码实现一个HD钱包开发。

开发过程中要用到两个第三方库,一个是Hooked-Web3-Provider,一个是LightWallet。

Hooked-Web3-Provider使用HTTP与geth通信,可以使用秘钥来签署调用交易sendTransaction的实例,因此不需要创建交易数据部分。直接调用sendTransaction完成生成交易数据,发送交易,广播给全网。

LightWallet是一个实现BIP32、BIP39和BIP44的HD钱包。
LightWallet提供API来创建和签署交易,或者使用LightWallet生成的地址和密钥加密和解密数据。它的主要目的是为Hooked-Web3-Provider提供一个签名提供方。它的命名空间有四个,即keystore、signing、encryption和txutils。

signing、encryption和txutils三个命名空间分别用来签名,非对称加密,生成交易,它们的名字大概能反应出各自的功能。keystore命名空间用来生成种子,keystor,这是一个存储加密种子和秘钥的对象。keystore对于其中发现的地址可以自动签名。

HD 钱包中的密钥是用"路径"命名的,且每个级别之间用斜杠(/)字符来表示。由主私钥衍生出的私钥起始以"m"打头。因此,第一个母密钥生成的子私钥是 m/0。第一个公共钥匙是 M/0。第一个子密钥的子密钥就是 m/0/1,以此类推。

密钥的"祖先"是从右向左读,直到你达到了衍生出的它的主密钥。举个例 子,标识符 m/x/y/z 描述的是子密钥 m/x/y 的第 z 个子密钥。而子密钥 m/x/y 又是 m/x 的第 y 个子密钥。m/x 又是 m 的第 x 个子密钥。

代码实现

1. 启动geth网络

假设已经安装好geth。使用命令启动geth网络:

geth --networkid  --dev --dev.period  --rpc --rpcapi "db,eth,net,web3,miner,personal"   --rpccorsdomain "*" --rpcaddr "0.0.0.0" --rpcport ""   console >>log

这个命令指定了networkid是15,当然这是随便取的。 --dev --dev.period 1  --dev是开发网络,但是geth后面的版本中为了方便开发,如果不加--dev.period 1 不会自动挖矿。 --rpcaddr "0.0.0.0" 这样设置是为了让所有的网络都能连上geth。 指定端口8545. console表明启动玩登录到控制台。

2.构建前端

项目代码结构(参考《区块链项目开发指南》):

app.js内容如下:

var express = require("express");
var app = express(); app.use(express.static("public")); app.get("/", function(req, res){
res.sendFile(__dirname + "/public/html/index.html");
}) app.listen(8080);

构建前端的一个node服务。

index.html  页面:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<link rel="stylesheet" href="/css/bootstrap.min.css">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-6 offset-md-3">
<br>
<div class="alert alert-info" id="info" role="alert">
Create or use your existing wallet.
</div>
<form>
<div class="form-group">
<label for="seed">Enter 12-word seed</label>
<input type="text" class="form-control" id="seed">
</div>
<button type="button" class="btn btn-primary" onclick="generate_addresses()">Generate Details</button>
<button type="button" class="btn btn-primary" onclick="generate_seed()">Generate New Seed</button>
</form>
<hr>
<h2 class="text-xs-center">Address, Keys and Balances of the seed</h2>
<ol id="list">
</ol>
<hr>
<h2 class="text-xs-center">Send ether</h2>
<form>
<div class="form-group">
<label for="address1">From address</label>
<input type="text" class="form-control" id="address1">
</div>
<div class="form-group">
<label for="address2">To address</label>
<input type="text" class="form-control" id="address2">
</div>
<div class="form-group">
<label for="ether">Ether</label>
<input type="text" class="form-control" id="ether">
</div>
<button type="button" class="btn btn-primary" onclick="send_ether()">Send Ether</button>
</form>
</div>
</div>
</div> <script src="/js/web3.min.js"></script>
<script src="/js/hooked-web3-provider.min.js"></script>
<script src="/js/lightwallet.min.js"></script>
<script src="/js/main.js"></script>
</body>
</html>

重点在main.js

generate_seed函数:
function generate_seed()
{
var new_seed = lightwallet.keystore.generateRandomSeed(); //生成一个随机的种子 document.getElementById("seed").value = new_seed; //放到页面 generate_addresses(new_seed);
} var totalAddresses = 0; function generate_addresses(seed)
{
if(seed == undefined)
{
seed = document.getElementById("seed").value;
} if(!lightwallet.keystore.isSeedValid(seed)) //判断种子是否是有效的种子
{
document.getElementById("info").innerHTML = "Please enter a valid seed";
return;
} totalAddresses = prompt("How many addresses do you want to generate"); //用户输入想生成的账户的个数 if(!Number.isInteger(parseInt(totalAddresses))) //确保输入是一个数字
{
document.getElementById("info").innerHTML = "Please enter valid number of addresses";
return;
} var password = Math.random().toString(); //随机生成一个密码 这个密码可以由用户输入,也可以自动生成。这里为了方便,提升体验,自动生成一个。 lightwallet.keystore.createVault({
//     使用createVault方法创建keystore实例。createVault用一个对象和
// 一个回调函数作为参数。对象可以有4种属性:password、seedPharse、
// salt和hdPathString。password是必选项,其他的都是可选项。如果不提
// 供seedPharse,它会生成和使用一个随机seed。拼接salt与password,以
// 提高对称密钥加密技术的安全性,因为攻击者不仅要找到password还得
// 找到salt。如果不提供salt,它就会随机生成。keystore命名空间存储未加
// 密的salt。hdPathString用于为keystore命名空间提供默认衍生路径,即生
// 成地址、签署交易等。如果不提供衍生路径,则使用该衍生路径。如果
// 不提供hdPathString,则默认值为m/0'/0'/0'。这个衍生路径的默认目的是
// 签名。可以创建新的衍生路径或者使用keystore实例的
// addHdDerivationPath()方法重写当前衍生路径的purpose。还可以使用
// keystore实例的setDefaultHdDerivationPath()方法改变默认衍生路径。
// 最后,一旦keystore命名空间被创建,就通过回调函数返回实例。所
// 以,这里仅用keyword和seed创建了一个keystore。
        password: password,
seedPhrase: seed
}, function (err, ks) {
ks.keyFromPassword(password, function (err, pwDerivedKey) { //使用这个方法生成对应数量的地址和秘钥
if(err)
{
document.getElementById("info").innerHTML = err;
}
else
{
ks.generateNewAddress(pwDerivedKey, totalAddresses);
var addresses = ks.getAddresses(); var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); //创建web3和本地的连接,如果是远程把localhost改成对应的ip即可。
var html = ""; for(var count = 0; count < addresses.length; count++)
{
var address = addresses[count];
var private_key = ks.exportPrivateKey(address, pwDerivedKey); //使用该方法解码和检索地址私钥。
var balance = web3.eth.getBalance("0x" + address); html = html + "<li>";
html = html + "<p><b>Address: </b>0x" + address + "</p>";
html = html + "<p><b>Private Key: </b>0x" + private_key + "</p>";
html = html + "<p><b>Balance: </b>" + web3.fromWei(balance, "ether") + " ether</p>";
html = html + "</li>";
} document.getElementById("list").innerHTML = html;
}
});
});
}

发送eth:

function send_ether()
{
var seed = document.getElementById("seed").value; if(!lightwallet.keystore.isSeedValid(seed))
{
document.getElementById("info").innerHTML = "Please enter a valid seed";
return;
} var password = Math.random().toString(); lightwallet.keystore.createVault({
password: password,
seedPhrase: seed
}, function (err, ks) {
ks.keyFromPassword(password, function (err, pwDerivedKey) {
if(err)
{
document.getElementById("info").innerHTML = err;
}
else
{
ks.generateNewAddress(pwDerivedKey, totalAddresses); ks.passwordProvider = function (callback) {
callback(null, password);
}; var provider = new HookedWeb3Provider({
host: "http://localhost:8545",
transaction_signer: ks
}); //利用ks做交易的签署者。它调用ks的hasAddress方法和signTransactions方法
var web3 = new Web3(provider);

                var from = document.getElementById("address1").value;
var to = document.getElementById("address2").value;
var value = web3.toWei(document.getElementById("ether").value, "ether"); web3.eth.sendTransaction({
from: from,
to: to,
value: value,
gas: 21000
}, function(error, result){
if(error)
{
document.getElementById("info").innerHTML = error;
}
else
{
document.getElementById("info").innerHTML = "Txn hash: " + result;
}
})
}
});
});
}

测试:

到项目根目录,执行 npm install 加载相关库。

执行  node app.js 启动前端服务。端口8080.  访问 localhost:8080

点击 generate new seed  ,输入2,生成两个账户。

然后进入到一开始打开的geth客户端,通过 eth.accounts 查看geth下的账号。复制地址,通过eth.getBalance("XX") 来获取地址余额。

转入一部分余额到上面生成的地址中,例如0xb4c7cf322956f0345b613f246d5d2f4ba03028f6.

eth.sendTransaction({from: "0x8d1c1dd6f48c33c97924e5f310905e1822a6cbd0", to: "0xb4c7cf322956f0345b613f246d5d2f4ba03028f6", value: web3.toWei(100, "ether")})

点击页面的 generate details 刷新,同样的seed会生成同样的账户。

这个时候会发现账户多出100eth。然后可以测试钱包内地址,钱包对钱包外地址转账。

项目giithub地址:https://github.com/figo050518/wallet

分层确定性钱包开发的代码实现(HD钱包服务)的更多相关文章

  1. [转]HD钱包的助记词与密钥生成原理

    本文转自:https://blog.csdn.net/opassf/article/details/79978047 区块链相关的话题持续发酵之时,应该不少人知道加密货币钱包,钱包是普通用户与加密货币 ...

  2. 理解开发HD 钱包涉及的 BIP32、BIP44、BIP39

    如果你还在被HD钱包.BIP32.BIP44.BIP39搞的一头雾水,来看看这边文章吧. 数字钱包概念 钱包用来存钱的,在区块链中,我们的数字资产都会对应到一个账户地址上, 只有拥有账户的钥匙(私钥) ...

  3. 说一说Web开发中两种常用的分层架构及其对应的代码模型

    昨天妹子让我帮她解决个问题,本以为可以轻松搞定,但是打开他们项目的一瞬间,我头皮发麻.本身功能不多的一个小项目,解决方案里竟然有几十个类库.仅仅搞明白各个类库的作用,代码层次之间的引用关系就花了一个多 ...

  4. 以太坊钱包开发系列2 - 账号Keystore文件导入导出

    以太坊去中心化网页钱包开发系列,将从零开始开发出一个可以实际使用的钱包,本系列文章是理论与实战相结合,一共有四篇:创建钱包账号.账号Keystore文件导入导出.展示钱包信息及发起签名交易.发送Tok ...

  5. 分分钟搞懂 HD 钱包

    转自:http://blog.sina.com.cn/s/blog_12ce70a430102v8c7.html 第一次看到 HD 这个词被用在比特币钱包中时,很容易就把它理解成硬件(Hardware ...

  6. 以太坊钱包开发系列4 - 发送Token(代币)

    以太坊去中心化网页钱包开发系列,将从零开始开发出一个可以实际使用的钱包,本系列文章是理论与实战相结合,一共有四篇:创建钱包账号.账号Keystore文件导入导出.展示钱包信息及发起签名交易.发送Tok ...

  7. iOS开发通过代码方式使用AutoLayout (NSLayoutConstraint + Masonry)

    iOS开发通过代码方式使用AutoLayout (NSLayoutConstraint + Masonry) 随着iPhone6/6+设备的上市,如何让手头上的APP适配多种机型多种屏幕尺寸变得尤为迫 ...

  8. [Unity]Unity开发NGUI代码实现ScrollView(放大视图)

    Unity开发NGUI代码实现ScrollView(放大视图) 下载NGUI包 导入NGUI3.9.1版本package 创建MainCameraScript.cs脚本 MainCameraScrip ...

  9. [Unity]Unity开发NGUI代码实现ScrollView(滚动视图)

    Unity开发NGUI代码实现ScrollView(滚动视图) 下载NGUI包 导入NGUI3.9.1版本package 链接: http://pan.baidu.com/s/1mgksPBU 密码: ...

随机推荐

  1. [容易]Fizz Buzz 问题

    题目来源:http://www.lintcode.com/zh-cn/problem/fizz-buzz/

  2. Numpy数组的保存与读取方法

    1. 数组以二进制格式保存 np.save和np.load是读写磁盘数组数据的两个主要函数.默认情况下,数组以未压缩的原始二进制格式保存在扩展名为npy的文件中,以数组a为例 np.save(&quo ...

  3. Vue:实践学习笔记(5)——Vue-Cli脚手架的使用

    Vue:实践学习笔记(5)——Vue-Cli脚手架的使用 快速开始 项目配置 可视化配置 vue ui 命令配置 vue init webpack vue-demo(项目名) 运行测试 进入vue-d ...

  4. BZOJ 1614 [Usaco2007 Jan]Telephone Lines架设电话线:spfa + 二分【路径中最大边长最小】

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1614 题意: 给你一个无向图,n个点,m条边. 你需要找出一条从1到n的路径,使得这条路径 ...

  5. laravel基础课程---12、lavarel的ajax操作2(lavarel的ajax使用总结)

    laravel基础课程---12.lavarel的ajax操作2(lavarel的ajax使用总结) 一.总结 一句话总结: 比较简单:就是js请求ajax,然后控制器获取请求参数,返回数据即可 1. ...

  6. 查 101.201.62.30 IP信誉方法

    查 101.201.62.30 IP信誉方法https://www.virustotal.com/#/ip-address/101.201.62.30https://talosintelligence ...

  7. BZOJ-1568: Blue Mary开公司 (李超线段树)

    Description Input 第一行 :一个整数N ,表示方案和询问的总数. 接下来N行,每行开头一个单词“Query”或“Project”. 若单词为Query,则后接一个整数T,表示Blue ...

  8. 「UVA557」 Burger(概率

    本题征求翻译.如果你能提供翻译或者题意简述,请 提交翻译 ,感谢你的贡献. 题目描述 PDF 输入输出格式 输入格式: 输出格式: 输入输出样例 输入样例#1: 复制 3 6 10 256 输出样例# ...

  9. 如何解决outlook2013邮件规则for other machine的失效问题

    如何解决outlook2013邮件规则for other machine的失效问题 问题描述:因为重装系统,outlook2013进去后->Rules and Alerts->发现所有原来 ...

  10. Mycat概述

    Mycat是什么?从定义和分类来看,它是一个开源的分布式数据库系统,是一个实现了MySQL协议的的Server,前端用户可以把它看作是一个数据库代理,用MySQL客户端工具和命令行访问,而其后端可以用 ...