当前位置:   article > 正文

以太坊Dapp项目-网页钱包开发手册

dapp 钱包开发

以太坊Dapp项目-网页钱包开发手册

修订日期 姓名 邮箱
2018-10-10 brucefeng brucefeng@brucefeng.com

前言

在之前的一篇文章以太坊智能合约项目-Token合约开发与部署中,我们提到了钱包了钱包的概念以及主流的几种钱包,如Mist,MyEtherWallet,MetaMask等,之前我们主要将钱包作为一个开发工具使用,用于智能合约的开发与调试工作,使用较多的是浏览器插件钱包MetaMask

在本文中,我们主要介绍MyEtherWallet以及如何实现一个简易版的MyEtherWallet网页版应用,在本文中,我们能够学习到如下内容

  • node.js开发基础
  • web3.js API使用
  • 以太币转账实现
  • Token转账实现

目前主流的钱包平台已经有太多了,而且有很多已经做得比较完善了,所以我们本文的开发工作只是为了学习以太坊开发技术,并非去设计一个新的钱包软件,重复造轮子几乎没有任何价值。

一. MyEtherWallet介绍

MyEtherWallet 是一个轻钱包,无需下载,所有操作在直接在网页上就可以完成

以太坊Dapp项目-网页钱包开发手册

主要功能如下

  • Net Wallet : 新建钱包
  • Send Ether && Tokens :以太币或者Token转账
  • Contract: 部署智能合约
  • ENS:以太坊域名平台
  • Check TX Status: 查看交易状态
  • View Wallet Info: 查看钱包信息

由于操作比较简单,这里不做详细讲解,在下文中我们对其主要功能,如新建钱包,以太币或者Token转账,查看交易状态进行参照开发。

二.node.js与web.js

1.Node.js

Node.js是一个JS运行时环境,可以解析,执行JavaScript代码,在这个执行环境中,为JS提供了一些服务器级别的操作API,如文件读写,网络通信,Http服务器等,其使用事件驱动,非阻塞IO模型(异步),轻量高效,大多数与JS相关的包都放在npm上,通过命令就可以下载不同的库跟框架,无需在去各个模块的官网上面单独下载,如安装koa直接通过npm install koa即可完成。

2. Web3.js

web3.js是一个库集合,允许使用HTTP或者RPC连接与本地或者远程以太坊节点进行交互,包含以太坊生态系统的特定功能,开发者利用web3模块主要连接以太坊的RPC层,从而与区块链进行交互

  • web3-eth : 与以太坊区块链和智能合约之间的交互
  • web3-ssh: 用于进行通信的p2p和广播
  • web-bzz: 用于群协议,分散的文件存储
  • web3-utils: 主要用于Dapp开发的辅助函数

官方文档: https://web3js.readthedocs.io/en/1.0/

web3.eth.accounts

  • 创建钱包账户

web3.eth.accounts.create();

  • 生成钱包配置

web3.eth.accounts.encrypt(privateKey, password);

  • 通过私钥生成账户对象

web3.eth.accounts.privateKeyToAccount(privateKey);

  • 查询余额

web3.eth.getBalance(address [, defaultBlock] [, callback])

  • 通过私钥跟密码生成配置文件

web3.eth.accounts.encrypt(privateKey, password);

  • 通过配置文件和密码解锁账户

web3.eth.accounts.decrypt(keystoreJsonV3, password);

  • 发送签名交易

web3.eth.sendSignedTransaction(signedTransactionData [, callback])

Example

  1. var Tx = require('ethereumjs-tx');
  2. var privateKey = new Buffer('e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', 'hex')
  3. var rawTx = {
  4. nonce: '0x00',
  5. gasPrice: '0x09184e72a000',
  6. gasLimit: '0x2710',
  7. to: '0x0000000000000000000000000000000000000000',
  8. value: '0x00',
  9. data: '0x7f7465737432000000000000000000000000000000000000000000000000000000600057'
  10. }
  11. var tx = new Tx(rawTx);
  12. tx.sign(privateKey);
  13. var serializedTx = tx.serialize();
  14. // console.log(serializedTx.toString('hex'));
  15. // 0xf889808609184e72a00082271094000000000000000000000000000000000000000080a47f74657374320000000000000000000000000000000000000000000000000000006000571ca08a8bbf888cfa37bbf0bb965423625641fc956967b81d12e23709cead01446075a01ce999b56a8a88504be365442ea61239198e23d1fce7d00fcfc5cd3b44b7215f
  16. web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex'))
  17. .on('receipt', console.log);
  18. > // see eth.getTransactionReceipt() for details

(1) nonce

web3.eth.getTransactionCount(address [, defaultBlock] [, callback])

(2) gasPrice

web3.eth.getGasPrice([callback])

(3) gasLimit

  • 预估gas值

3. Koa中间件

Koa号称是基于Node.js平台的下一代web开发框架,Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async 函数,Koa 帮你丢弃回调函数,并有力地增强错误处理。 Koa 并没有捆绑任何中间件, 而是提供了一套优雅的方法,帮助您快速而愉快地编写服务端应用程序。

其使用方法可以参考https://koa.bootcss.com/

Koa的最大特色就是中间件,Koa中间件是简单的函数,调用app.user()传入,MiddlewareFunction函数带有两个参数(ctx,next),中间件作为HTTP Request和HTTP Reponse的桥梁,用来实现连接功能,如

  1. app.use(async (ctx, next) =>{
  2. console.log(`Process ${ctx.request.method} ${ctx.request.url} ...`);
  3. await next();
  4. });

ctx是一个请求的上下文,封装了传入的http消息,并对该消息进行响应,koa提供了一个Request/Response对象提供了Context的request/response属性与用于处理Http请求的方法。

next是被调用来执行上下游中间件的函数,必须手动调用next()运行下游中间件,否则下游中间件不会被正常执行。可以采用两种不同的方法来实现中间件

  • async function
  • common function
(1) 安装启动Koa服务
  • 安装koa
  1. $ mkdir koaTest ; cd koaTest
  2. $ npm init -y
  3. $ npm install koa
  • 创建启动文件
$ vim index.js
  1. var Koa = require("koa") //导入Koa库
  2. var app = new Koa(); //创建Koa应用对象,带有node http服务的koa接口
  3. app.listen(3003); //启动服务的快捷方法
  • 启动服务
$ node index.js

此时会监听3003端口,通过浏览器访问

(2) Context

context是一个请求的上下文,该对象封装了一个传入的http消息,context有request和response属性,我们可以通过涉案值两个属性来处理和相应不同的请求。

$ vim index.js
  1. var Koa = require("koa");
  2. var app = new Koa();
  3. //
  4. app.use(function(ctx,next){
  5. console.log(ctx.request.path);
  6. //通过设置ctx的response的body值可以返回数据到客户端
  7. ctx.response.body = "my koa app";
  8. console.log(ctx.response.type);
  9. });
  10. app.listen("3003");
  11. console.log("koa server is listening on port 3003");
  • app.use(function):将给定的function作为中间件加载到应用中。
  • ctx: 每一个请求都会创建一段上下文,在控制业务逻辑的中间件中,上下文被寄存在ctx对象中,许多上线文属性和方法都被存在ctx.request和ctx.response中,比如访问ctx.type和ctx.length都被代理到reponse对象,ctx.path和ctx.method都被代理到request对象中。
(3) 网页模板

在实际开发中,返回给用户的网页都是通过模板文件进行返回的,可以让Koa先读取模板文件,然后将这个模板返回给用户,需要指定response的type为text/html类型

  1. var Koa = require("koa");
  2. var app = new Koa();
  3. var fs = require("fs");
  4. app.use(ctx=>{
  5. console.log(ctx.path);
  6. //必须指定type
  7. ctx.type = "text/html";
  8. ctx.body = fs.createReadStream("./views/teest.html");
  9. console.log(ctx.type);
  10. });
  11. app.listen(3003);
  12. console.log("koa server is listening on port 3003");
(4) 中间件

Koa所有的功能都是通过中间件实现的,中间件处于HTTP Request和HTTP Response之间。

Koa的中间件之间按照编码书序在栈内以此执行,允许执行操作并向下传递请求,之后过滤并必须返回响应,响应的测试代码与步骤如下

  1. var Koa = require("koa");
  2. var app = new Koa();
  3. //es6新语法:
  4. //函数名 =>(参数) =>{}
  5. var one = (ctx,next) =>{
  6. console.log("1.1");
  7. next();
  8. console.log("1.2");
  9. };
  10. var two = (ctx,next) =>{
  11. console.log("2.1");
  12. next();
  13. console.log("2.2");
  14. };
  15. var three = (ctx, next) =>{
  16. console.log("3.1");
  17. next();
  18. console.log("3.2");
  19. };
  20. app.use(one);
  21. app.use(two);
  22. app.use(three);
  23. app.listen(3003);
  24. console.log("服务启动完毕");

返回结果

  1. 2.1
  2. 3.1
  3. 3.2
  4. 2.2
  5. 1.2
(5) 异步中间件

由async标记的函数被称为异步函数,在异步函数中,可以通过await调用另外一个异步函数,使用await时,其所在的方法必须使用关键字async

  1. var Koa = require("koa");
  2. var app = new Koa();
  3. app.use(async(ctx,next) =>{
  4. var start = Date.now();
  5. await next();
  6. console.log(`${ctx.url} ${Date.now - start}`);
  7. });
  8. app.use(async (ctx,next)=>{
  9. ctx.response.body = "async test"
  10. });
  11. app.listen(3003);
  12. console.log("服务启动完毕");
(6) 原生路由
  1. var Koa = require("koa");
  2. var app = new Koa();
  3. //es6新语法:
  4. //函数名 =>(参数) =>{}
  5. app.use((ctx,next)=> {
  6. console.log("%s %s", ctx.method, ctx.url);
  7. next();
  8. });
  9. app.use((ctx,next) =>{
  10. if (ctx.request.path == '/'){
  11. ctx.response.body = 'index page';
  12. }else {
  13. next();
  14. }
  15. });
  16. app.use((ctx,next) =>{
  17. if (ctx.request.path == '/error'){
  18. ctx.response.body = 'error page';
  19. }
  20. });
  21. app.listen(3003);
  22. console.log("服务启动完毕");
(7) koa-router路由

由于原生路由使用比较繁琐,所以可以通过封装好的koa-router模块,使用router.routers()绑定到中间件

安装koa-router

$ npm install koa-router
  1. var Koa = require("koa");
  2. var app = new Koa();
  3. //导入koa-router,注意要加上()才能生效
  4. var router = require("koa-router")()
  5. router.get("/hello",function(ctx,next){
  6. ctx.response.body = "hello,brucefeng";
  7. });
  8. router.get("/bye",function (ctx,next){
  9. ctx.response.body = "good bye brucefeng";
  10. });
  11. //将router路由注册到中间件
  12. app.use(router.routes());
  13. app.listen(3003);
  14. console.log("服务启动完毕");
(8) 请求重定向

一般在如下情况下需要使用到重定向

  • 后台系统升级,对之前的页面不在支持,此时需要使用重定向到新的API上满足用户的访问准确性
  • 完成某个操作后自动跳转至其他页面,如注册成功,登录成功等等
  1. var Koa = require("koa");
  2. var app = new Koa();
  3. //导入koa-router,注意要加上()才能生效
  4. var router = require("koa-router")()
  5. router.get("/hello",function(ctx,next){
  6. ctx.response.body = "hello,brucefeng";
  7. });
  8. router.get("/hi",function (ctx,next){
  9. ctx.response.redirect("/hello")
  10. });
  11. //将router路由注册到中间件
  12. app.use(router.routes());
  13. app.listen(3003);
  14. console.log("服务启动完毕");

通过 ctx.response.redirect("/hello")将"/hi"请求重定向到/hello对应的页面

在node.js中访问的url中有中文时,需要通过全局encodeURIComponent(string)进行编码

(9) 获取get请求参数

客户端在请求获取服务的数据时,获取的URL中通常会携带各种参数,服务端如何获取到get请求的参数呢?

  • 格式1:http://127.0.0.1:3003/hello/brucefeng

获取方式: ctx.params.name

  • 格式2:http://127.0.0.1:3003/bye?name=brucefeng

获取方式: ctx.query.name

调用params获取参数的时候,params不是request的属性,需要通过ctx直接调用获取。

(10) 获取post请求参数

Get请求的参数附带在了url上,Post请求的参数在请求体body里面,所以要获取body的数据,需要使用到插件koa-body,通过ctx.request.body.name获取参数.

$ npm install koa-router
  1. var Koa = require("koa");
  2. var app = new Koa();
  3. //导入koa-router,注意要加上()才能生效
  4. var router = require("koa-router")()
  5. //引入koa-body
  6. var koaBody = require("koa-body")
  7. router.post("/hello",function(ctx,next){
  8. var body = ctx.request.body;
  9. ctx.response.body = "hello,bruce";
  10. console.log(body);
  11. console.log(body.username);
  12. });
  13. //设置multipart : true,支持多个参数
  14. app.use(koaBody({
  15. multipart:true
  16. }))
  17. //将router路由注册到中间件
  18. app.use(router.routes());
  19. app.listen(3003);
  20. console.log("服务启动完毕");

//通过命令使用curl插件模拟调用一个Post请求
//curl -H "Content-Type:application/json" -X POST --data '{"username":"brucefeng"}' http://localhost:3003/hello

  1. brucefengdeMBP:ETHWalletDemo brucefeng$ node index.js
  2. 服务启动完毕
  3. { username: 'brucefeng' }
  4. brucefeng
(11) 加载静态资源

加载静态资源,如图片,字体,样式表,脚本等,编码指定静态资源的路径是相对于./static的路径。

$ npm install koa-static
  1. var Koa = require("koa");
  2. var app = new Koa();
  3. //导入koa-router,注意要加上()才能生效
  4. var router = require("koa-router")();
  5. var static = require("koa-static");
  6. var path = require("path")
  7. router.get("/hello",function (ctx,next){
  8. ctx.response.body = "<html> <a href='/0.png'>看我</html>"
  9. })
  10. //静态资源的路径是相对于./static的路径
  11. app.use(static(path.join(__dirname,"./static")))
  12. //将router路由注册到中间件
  13. app.use(router.routes());
  14. app.listen(3003);
  15. console.log("服务启动完毕");

启动服务,通过浏览器访问测试

(12) 模板引擎

模板引擎ejs需要配上模板渲染中间件koa-views使用,如果需要支持其他后缀的文件,需要将文件扩展名映射到引擎中。

$ npm install ejs koa-views

index.js

  1. var Koa = require("koa");
  2. var app = new Koa();
  3. //导入koa-router,注意要加上()才能生效
  4. var router = require("koa-router")();
  5. var static = require("koa-static");
  6. var path = require("path")
  7. var views = require("koa-views")
  8. router.get("/hello",async (ctx,next) =>{
  9. //将json里面的值替换为文件里面的变量
  10. var name = "brucefeng";
  11. await ctx.render("test.ejs",{
  12. name,
  13. "sex":"帅哥"
  14. })
  15. })
  16. router.get("/bye",async (ctx,next)=>{
  17. await ctx.render("home.html",{
  18. "name": "fengyingcong"
  19. })
  20. })
  21. app.use(views(
  22. //默认是views下面获取ejs后缀的文件,如果是其他类型的文件需要指定文件类型
  23. path.join(__dirname,"./static/views"),
  24. {extension:"ejs", map:{html: "ejs"}}
  25. ))
  26. //静态资源的路径是相对于./static的路径
  27. app.use(static(path.join(__dirname,"./static")))
  28. //将router路由注册到中间件
  29. app.use(router.routes());
  30. app.listen(3003);
  31. console.log("服务启动完毕");

static/views/test.ejs

  1. <!DOCTYPE <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8" />
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <title>Page Title</title>
  7. <meta name="viewport" content="width=device-width, initial-scale=1">
  8. <link rel="stylesheet" type="text/css" media="screen" href="main.css" />
  9. <script src="main.js"></script>
  10. </head>
  11. <body>
  12. <div>姓名: <%= name %> 性别: <%= sex %></div>
  13. </body>
  14. </html>

static/views/home.html

  1. <!DOCTYPE <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8" />
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <title>Page Title</title>
  7. <meta name="viewport" content="width=device-width, initial-scale=1">
  8. <link rel="stylesheet" type="text/css" media="screen" href="main.css" />
  9. <script src="main.js"></script>
  10. </head>
  11. <body>
  12. <div>姓名: <%= name %> </div>
  13. </body>
  14. </html>

三.初始化项目环境

1.导入项目依赖

  1. $ mkdir ETHWalletDemo ;cd ETHWalletDemo
  2. $ npm init -y
  3. $ npm i web3 koa koa-body koa-static koa-views koa-router ejs

以太坊Dapp项目-网页钱包开发手册

2.创建路由文件

  1. $ mkdir router ; cd router
  2. $ vim router.js
  1. var router = require("koa-router")()
  2. //定义路由newaccount
  3. router.get("/newaccount",(ctx,next)=>{
  4. ctx.response.body = "创建钱包"
  5. })
  6. module.exports = router

3.创建入口文件

ETHWalletDemo/index.js

  1. //导入./router/router包
  2. var router = require("./router/router.js")
  3. //引入Koa库
  4. var Koa = require("koa")
  5. //通过koa创建一个应用程序
  6. var app = new Koa()
  7. app.use(router.routes())
  8. app.listen(3003)
  9. console.log("钱包启动成功,请访问http://127.0.0.1:3003/newaccount进行测试")

4.创建MVC框架

ETHWalletDemo下创建

  1. $ mkdir models views controllers #创建MVC框架目录
  2. $ mkdir utils #创建帮助文件目录
  3. $ mkdir -p static/{css,js,html} #创建静态文件目录

5.完善项目框架

ETHWalletDemo/index.js

  1. //引入Koa库
  2. var Koa = require("koa")
  3. //通过koa创建一个应用程序
  4. var app = new Koa()
  5. //导入./router/router包
  6. var router = require("./router/router.js")
  7. var static = require("koa-static")
  8. var path = require("path")
  9. var views = require("koa-views")
  10. var koaBody = require("koa-body")
  11. //拦截获取网络请求,自定义的function需要使用next()
  12. app.use(async (ctx,next)=>{
  13. console.log(`${ctx.url} ${ctx.method}...`)
  14. await next();
  15. })
  16. //注册中间件
  17. //注册静态文件的库到中间件
  18. app.use(static(path.join(__dirname,"static")))
  19. //注册模板引擎的库到中间件
  20. app.use(views(path.join(__dirname,"views"),{extension:"ejs", map:{html: "ejs"}}))
  21. //针对于文件上传时,可以解析多个字段
  22. app.use(koaBody({multipart:true}))
  23. app.use(router.routes())
  24. app.listen(3003)
  25. console.log("钱包启动成功,请访问http://127.0.0.1:3003/...进行测试")

四.创建钱包账户

1.创建钱包账户

(1) 封装web3库调用

utils/myUtils.js

  1. var web3 = require("../utils/myUtils").getWeb3()
  2. module.exports = {
  3. getWeb3: ()=>{
  4. var Web3 = require("web3");
  5. var web3 = new Web3(Web3.givenProvider || 'http://127.0.0.1:8545');
  6. return web3;
  7. }
  8. }
(2) 创建控制器

controllers/newAccount.js


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/861590
推荐阅读
相关标签
  

闽ICP备14008679号