Coding:生成账户的地址、私钥、keystore、助记词

一、使用web3连接到以太坊网络(测试网、主网)

1. 什么是web3

web3是以太坊官方开提供的一个连接以太坊区块链的模块,允许您使用HTTP或IPC与本地或远程以太坊节点进行交互,它包含以太坊生态系统的几乎所有功能。web3模块主要连接以太坊暴露出来的RPC层。开发者利用web3连接RPC层,可以连接任何暴露了RPC接口的节点,从而与区块链交互。web3是一个集合库,支持多种开发语言使用wbe3,其中的JavaScript API叫做web3.js、另外还有web3.py、web3j,web3.js将是我们钱包开发项目的重点。

  • web3.eth:用于与以太坊区块链和智能合约之间的交互。
  • web3.utils:包含一些辅助方法。
  • web3.shh:用于协议进行通信的P2P和广播。
  • web3.bzz:用于与群网络交互的Bzz模块。

github地址:web3.js

web3.js开发文档::web3.js

2. 实例化web3对象

web3要与以坊节点进行交互,需要创建一个web3对象,下面看看如何创建。

var Web3 = require('web3');

// "Web3.providers.givenProvider" will be set if in an Ethereum supported browser.
var web3 = new Web3(Web3.givenProvider || 'ws://some.local-or-remote.node:8546');

根据API可知需要指定节点地址,我们将ws://some.local-or-remote.node:8546换成其它连接到以太坊网络的节点的地址,以此来确定连接的以太坊的网络。那么连接到以太坊网络的节点的地址是多少呢?这里我们需要使用到infura。

3. 获取连接到以太坊网络的节点地址

infura提供公开的 Ethereum主网和测试网络节点,到infura.io网站注册后即可获取各个网络的地址。请按照如下步骤获取地址。

第一步:打开 infura网站地址:https://infura.io/dashboard,使用邮箱注册后登陆如下所示

第二步:点击上图标记的“create new project”按钮创建一个新项目。然后弹出如下弹框,在输入框输入项目名,如”MyEtherWallet“,然后点击“create project”按钮创建。

第三步:然后会显示如下界面,点击下图中的选择框,可以看到提供主网、Kovan测试网络、Ropsten测试网络、Rinkeby测试网络的节点地址。

第四步:选择Kovan测试网络,然后复制地址,将获取到类似这样的地址:https://kovan.infura.io/v3/d93f……cd67,如下。

  1. 连接到以太坊Kovan测试网络 现在将复制的地址替换掉实例化web对象的地址,如下

var Web3 = require("web3")
var web3 = new Web3(Web3.givenProvider || 'https://kovan.infura.io/v3/bc76cd31e8bf48f9a28b73770ffca805');
console.log("Web3:", Web3)
console.log("web3:", web3)

运行后如下所示

连接到以太坊主网与Kovan测试网络一样的,只需复制主网节点的地址去实例化web3即可。由于在主网上交易需要花费gas,因此我们基于Kovan测试网络进行开发,后续开发完成后可再切换到主网。在我们开发的项目源码中,我将获取web3实例的代码封装到了myUtils.js文件的getweb3()方法中,用于整个项目统一调用。

二、获取地址、私钥、keystore、助记词

1. 使用web3创建账号

创建账号需要使用web3.js的如下API

** API **

web3.eth.accounts.create([entropy]);

* 参数:*

  • entropy - String (可选): 它是一个可选项,是一个随机字符串,将作为解锁账号的密码。如果没有传递字符串,则使用random生成随机字符串。

** 返回值: **

Object:包含以下字段的一个帐户对象:

  • address- string:帐户地址。
  • privateKey- string:帐户私钥。前端永远不应该在localstorage中以未加密的方式共享或存储!
  • signTransaction(tx [, callback])- Function:签名交易的方法。
  • sign(data)- Function:签名二进制交易的方法。

** 例子 **

web3.eth.accounts.create();
> {
    address: "0xb8CE9ab6943e0eCED004cDe8e3bBed6568B2Fa01",
    privateKey: "0x348ce564d427a3311b6536bbcff9390d69395b06ed6c486954e971d960fe8709",
    signTransaction: function(tx){...},
    sign: function(data){...},
    encrypt: function(password){...}
}

web3.eth.accounts.create('2435@#@#@±±±±!!!!678543213456764321§34567543213456785432134567');
> {
    address: "0xF2CD2AA0c7926743B1D4310b2BC984a0a453c3d4",
    privateKey: "0xd7325de5c2c1cf0009fac77d3d04a9c004b038883446b065871bc3e831dcd098",
    signTransaction: function(tx){...},
    sign: function(data){...},
    encrypt: function(password){...}
}

2. 获取地址

使用APIweb3.eth.accounts.create()创建了新账户后生成了一个账户对象,在该对象中拥有address属性,即账户的地址。

let account = web3.eth.accounts.create("123456")
let address = account.address
//address:0xfF0B5A0AA68249cD161b606679DB49CBD9a12cd0

3. 获取私钥

使用APIweb3.eth.accounts.create()创建了新账户后生成了一个账户对象,在该对象中拥有privateKey属性,即账户的私钥。

let account = web3.eth.accounts.create()
let privateKey = account.privateKey
//privateKey:0x348ce564d427a3311b6536bbcff9390d69395b06ed6c486954e971d960fe8709

4. 获取keystore

方式一

在账户对象中,我们可以发现它拥有encrypt对象方法,声明为encrypt: function(password){…},因此可以通过下面的方式去获取keystore。

let account = web3.eth.accounts.create(password)
let keystore = account.encrypt(password)

方式二

获取keystore文件使用web3.js的如下API

API*

web3.eth.accounts.encrypt(privateKey, password);
将私钥加密到keystore文件中。

** 参数: **

  • privateKey- String:要加密的私钥。
  • password- String:用于加密的密码。

** 返回值: **

Object:加密后的keystore文件

** 例子 **

web3.eth.accounts.encrypt('0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318', 'test!')
> {
    version: 3,
    id: '04e9bcbb-96fa-497b-94d1-14df4cd20af6',
    address: '2c7536e3605d9c16a7a3d7b1898e529396a65c23',
    crypto: {
        ciphertext: 'a1c25da3ecde4e6a24f3697251dd15d6208520efc84ad97397e906e6df24d251',
        cipherparams: { iv: '2885df2b63f7ef247d753c82fa20038a' },
        cipher: 'aes-128-ctr',
        kdf: 'scrypt',
        kdfparams: {
            dklen: 32,
            salt: '4531b3c174cc3ff32a6a7a85d6761b410db674807b2d216d022318ceee50be10',
            n: 262144,
            r: 8,
            p: 1
        },
        mac: 'b8b010fff37f9ae5559a352a185e86f9b9c1d7f7a9f1bd4e82a5dd35468fc7f6'
    }
}

5. 使用bip39生成助记词

在以太坊常见钱包中,只有MetaMask钱包支持导出助记词,那么如何实现生成助记词呢?这就需要用到HD 钱包。HD 钱包和BIP协议的相关概念请查看”04-密码、私钥、keystore与助记词之间的爱恨情仇”章节中助记词的内容。

由于项目使用的是web3.js创建账号,所以这里只提供了生成助记词的示例,并没有将它集成到项目中,但是可以集成通过助记词解锁账号的功能。

下面通过bip39生成助记词,需要先安装bip39,cd到项目跟路径运行命令npm i bip39。


let bip39 = require('bip39')
let mnemonic = bip39.generateMnemonic()
console.log(mnemonic)
//输出:rotate boss click maximum exercise dune diagram because only any minute monitor

三、项目源码

后端使用web3.js的web3.eth.accounts.create()方法创建账号,然后通过account.encrypt()方法生成keystore文件,提供对应的keystore文件与私钥给前端。

1. newAccount.js

controllers文件夹下新建newAccount.js文件,后端实现返回创建账号的页面与创建账户。


let web3 = require("../utils/myUtils").getweb3()
let fs = require("fs")
let path = require("path")

module.exports = {
    //获取创建账号的页面
    newAccountHtml: async (ctx) => {
        await ctx.render("newaccount.html")
    },

    //创建账户的表单提交被触发的方法
    newAccount: async (ctx) => {
        console.log("password:", ctx.request.body.password)

        //1.创建钱包账号
        let account = web3.eth.accounts.create(ctx.request.body.password)
        console.log(account)

        //2.根据账号和密码生成keystore文件
        let keystore = account.encrypt(ctx.request.body.password)
        console.log(keystore)

        //3.将keysotr保存到文件
        let keystoreString = JSON.stringify(keystore)
        let time = new Date()
        let fileName = 'UTC--'+time.toISOString()+'--'+account.address.slice(2)
        console.log(fileName)
        let filePath = path.join(__dirname, "../static/keystore", fileName)
        fs.writeFileSync(filePath, keystoreString)

        //4.将账号信息返回给客户端
        await ctx.render("downloadkeystore.html", {
            "downloadurl":"/keystore/"+fileName,
            "privatekey":account.privateKey
        })
    }
}

2. router.js

将创建账户的页面与表单提交的接口绑定到路由。

......

let newAccountController = require("../controllers/newAccount")

//获取创建钱包账户的页面
router.get("/account/new.html", newAccountController.newAccountHtml)
//提交创建钱包账户的表单
router.post("/account/new", newAccountController.newAccount)

3. newaccount.html

在views文件夹下新建newaccount.html文件,实现前端创建账户的页面。


<html>

<head>
    <title>创建钱包</title>
    <script src="/js/lib/jquery-3.3.1.min.js"></script>
    <link rel="stylesheet" href="/css/wallet.css">
</head>

<body>

    <%include block/nav.html%>

    <div id="main">
        <h1>创建一个新的账号</h1>
        <form method="POST" action="/account/new">
            <input type="text" placeholder="请输入密码" name="password">
            <button type="submit">创建账号</button>
        </form>
    </div>
</body>

</html>

4. downloadkeystore.html

在views文件夹下新建downloadkeystore.html文件,实现前端将创建后的账号信息提供给用户保存下来,如keystore文件、私钥。


<html>

<head>
    <title>保存你的keystore</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/wallet.css">
</head>

<body>

    <%include block/nav.html%>

    <div id="main">
        <div id="save-keystore">
            <h1>保存你的keystore</h1>
            <a class="button" href="<%= downloadurl %>">下载keystore文件</a>
            <br><br>
            <button onclick="saveKeystoreNext()">下一步</button>
        </div>
        <div id="save-privatekey" style="display: none">
            <h1>保存你的私钥</h1>
            <div>
                <%= privatekey %>
            </div>
        </div>
    </div>

</body>

</html>

5. wallet.js

编辑static文件夹下的wallet.js文件,实现隐藏keystore下载页面,显示私钥。

function saveKeystoreNext() {
    $("#save-keystore").hide()
    $("#save-privatekey").show()
}

四、项目运行效果

项目源码Github地址

作者:李旭

来源:http://www.chaindesk.cn

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦