004 以太坊钱包开发-创建用户

安装模板引擎

1
2
3
4
5
6
7
$ cd myWallet

# 安装koa模板使用中间件
$ npm install --save koa-views

# 安装ejs模板引擎
$ npm install --save ejs

引入模版引擎

修改 myWallet/index.js,加载模版引擎

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const Koa = require('koa');
const routers = require('./routers/index')
const koaBody = require('koa-body');
const views = require('koa-views')
const path = require('path')

const app = new Koa();

// 引入 koa-body 中间件
app.use(koaBody({
multipart: true
}));

// 加载模板引擎
app.use(views(path.join(__dirname, './view'), {
map : {html:'ejs'}
}))

// 初始化路由中间件
app.use(routers.routes()).use(routers.allowedMethods())
app.listen(3000);
console.log('server is starting at port 3000')

新建 view 页面

进入项目目录,新建 view 文件夹,新建 account.html

1
2
3
$ cd myWallet
$ mkdir view
$ vi account.html

account.html 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>fujinliang.top</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
account
</body>
</html>

修改 controllers/account.js ,调用 ctx.render("account") 实现页面的跳转

1
2
3
4
5
module.exports = {
async getAccountList (ctx) {
await ctx.render("account")
}
}

通过 node index.js 启动项目, 通过访问 http://localhost:3000 查看结果,页面显示如下:

1
account

引入 Bootstrap css框架

进入 https://getbootstrap.com/ 下载 bootstrap

如何存放 bootstrap 的样式文件?

1
需要引入静态文件目录

引入静态文件目录

1
2
$ cd myWallet
$ npm install koa-static

修改 index.js 引入koa-static

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...

const static = require('koa-static')

const app = new Koa();

// 静态资源目录对于相对入口文件index.js的路径
const staticPath = './static'

app.use(static(
path.join( __dirname, staticPath)
))

...

通过 web3 实现用户创建

用户创建的主要流程:通过 web3.eth.accounts.create() 创建用户,会得到 account 的 地址及私钥。通过 account.encrypt(pwd) 生成keystore 的内容,同时通过 fs.writeFile 方法将内容写入到文件之中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
async createAccount (ctx) {
let returnResult = {
code: 0,
msg: '成功!',
data: {}
}

let data = ctx.request.body
const pwd = data.pwd

// 调用 web3 创建账户
let account = await web3.eth.accounts.create();

// encrypt 返回一个JSON 对象
const keystoreJson = await account.encrypt(pwd)

// 将 JSON 对象转为字符串
const keystoreStr = JSON.stringify(keystoreJson)

// 生成随机文件存储 keystore 的字符串
const randFilename = "UTC--"+new Date().toISOString()+"--"+account.address

// 设置存储文件的目录
const filePath =path.join(__dirname,"../static/keystore/"+randFilename)

// 将 keystore 的内容写入文件中
await fileUtil.writeFile(filePath,keystoreStr)

// 将 用户地址、私钥、keystore 数据返回
const result = {"account":account.address,"privateKey":account.privateKey,"keystore": config.keystoreUrl+randFilename}
returnResult.data = result
ctx.body = returnResult
},

将 keystore 的内容写到文件之中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const fs = require("fs")

module.exports = {

readFile(fPath) {
return new Promise(function (resolve, reject) {
fs.readFile(fPath, function(err, data) {
if (err) reject(err);
else resolve(data);
});
});
},

writeFile(fPath, content) {
return new Promise(function(resolve, reject) {
fs.writeFile(fPath, content, function(err, data) {
if (err) reject(err);
else resolve("Successed");
});
});
}
}

添加路由的配置

修改 routers/index.js ,添加如下代码:

1
router.post('/account/create',accountController.createAccount)

创建账户的前端页面

创建钱包的页面主要包含以下功能:

  1. 输入密码生成 keystore
  2. 下载 keystore 文件
  3. 下载私钥
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>fujinliang.top</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" media="screen" href="/css/bootstrap.min.css" />
<style>
.account {
margin-top: 100px;
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<ul class="nav nav-pills">
<li class="nav-item">
<a class="nav-link active" href="#">添加账户</a>
</li>
</ul>
<div id="create" class="account">
<p style="font-size: 30px">新建一个账户</p>
<p>输入密码</p>
<div style="padding: 50px 100px">
<input type="text" class="form-control" id="pwd" placeholder="输入密码" />
</div>
<button type="button" class="btn btn-primary" onclick="createAccount()">创建账户</button>
</div>

<div id="download" class="account" style="display: none" >
<p style="font-size: 30px">保存你的<span style="color: red">Keystore</span> 文件</p>
<a id="downloadUrl" class="btn btn-success" href="#" role="button">下载 Keystore 文件 (UTC / JSON)</a>
<div style="margin-top:100px">
<button onclick="showKey()" type="button" class="btn btn-primary">下一步</button>
</div>
</div>

<div id="privateKey" class="account" style="display: none">
<p style="font-size: 30px">保存你的<span style="color: red">私钥</span></p>
<textarea class="form-control" id="prikey" rows="3" disabled></textarea>
</div>
</div>
</body>
<script src="/js/jquery-3.3.1.min.js"></script>
<script>
function createAccount(){

var pwd = $("#pwd").val()
if (pwd == "") {
alert("密码不能为空!");
return
}

$.post("/account/create","pwd="+pwd, function(result){
alert(result.data)
if (result.code == 0) {
$("#create").hide()
$("#download").show()
$("#downloadUrl").attr("href", result.data.keystore)
alert(result.data.privateKey)
$("#prikey").html(result.data.privateKey)
}
})
}

function showKey(){
$("#download").hide()
$("#privateKey").show()
}
</script>
</html>

演示效果

启动私链网络

使用 geth 启动私有网络

1
2
3
4
$ geth --datadir ~/privatechain/data0 --networkid 110  --rpc console
```

### 启动项目

$ cd myWallet
$ node index.js
`

访问 http://localhost:3000 查看项目:

源码下载

https://github.com/didianV5/web3EthWallet/tree/master/004_myWallet

谢谢你请我吃糖果