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

钱包 Mar 19, 2020

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

安装模板引擎

$ cd myWallet

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

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

引入模版引擎

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

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

$ cd myWallet
$ mkdir view
$ vi account.html

account.html 代码如下:

<!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") 实现页面的跳转

module.exports = {
    async getAccountList (ctx) {
        await ctx.render("account")
    }
}

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

account

引入 Bootstrap css框架

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

如何存放 bootstrap 的样式文件?

需要引入静态文件目录

引入静态文件目录

$ cd myWallet
$ npm install koa-static

修改 index.js 引入koa-static

...

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 方法将内容写入到文件之中。

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 的内容写到文件之中。

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 ,添加如下代码:

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

创建账户的前端页面

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

  1. 输入密码生成 keystore
  2. 下载 keystore 文件
  3. 下载私钥
<!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 启动私有网络

$ 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

恭喜! 你已经成功订阅了.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.