专栏首页区块链入门蚂蚁区块链第13课 如何搭建一个DAPP应用(以姓名年龄为例)

蚂蚁区块链第13课 如何搭建一个DAPP应用(以姓名年龄为例)

1,摘要

本文介绍如何使用Cloud IDE完成name-age智能合约的编译,获取其ABI和二进制码信息。 接着,本文介绍基于EXPRESS框架搭建的前端页面,完成已部署智能合约的调用,完成了相关功能的呈现。 本文假设用户已经熟悉蚂蚁BAAS的Cloud IDE和Solidity开发编译,不熟悉的查看辉哥的其他文档完成知识准备。

2,智能合约的开发和部署

2.1 需求和智能合约开发

本需求主要是作为入门级DAPP,主要能读取智能合约中的姓名/年龄信息,同时也能写入更新姓名/年龄。 solidity代码实现如下:

pragma solidity ^0.4.23;

contract InfoContract {
    string name;
    uint age;

    event Instructor(string name, uint age);

    function setInfo(string _name, uint _age) public {
        name = _name;
        age = _age;
        emit Instructor(name, age);
    }

    function getInfo() public view returns(string, uint) {
        return (name, age);
    }
}

2.2 账号切换和编译

Cloud IDE默认的账户为test002,我们并不知道其解密私钥。所以需要在Cloud IDE下切换为用户自己创建的账号,例如辉哥的duncanwang账号,输入其中的解密私钥。 然后点击编译完成智能合约的编译工作。

4. 环境配置-更换账户

不知道怎么产生账号和机密私钥的,可参考文章《蚂蚁区块链第8课 如何创建新的账户,获取私钥和identity标识?》

2.3 部署name-age合约并测试

(1)部署合约

5.部署合约

部署成功后,获得其交易信息:

合约ID: 0xe72056692f46ae2443bbfefaa9ffddc09c8952a488afd345118aa295d85c8aba tx hash: 0x1750e6156b63ada81fb54473fe54a97ef565e3c7fa2123ab9f033dfd7fc01672

(2)测试合约 设置辉哥的姓名为duncanwang,年龄为18岁,先完成setInfo,后调用getInfo,发现结果跟预期一致,智能合约测试完成。

6. 调试合约(duncanwang-18)

记录下HASH信息,便于在浏览器查询:

function setInfo("duncanwang", 18) tx hash: 0x46b2289c4fe175bcd80599e91fa8a55e1822f6280ffc90a4e0bd2af6ee832114

function getInfo tx hash: 0x46b2289c4fe175bcd80599e91fa8a55e1822f6280ffc90a4e0bd2af6ee832114

3,前端页面和代码

前端UI页面如下。

对应的代码(home.ejs)实现:

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<style>
div{
  width: 600px;
    margin: 0 auto;
    font-size: 21px;
}
input{
  width: 300px;
    height: 30px;
    font-size: 20px;
}
.submitBtn{
    width: 100px;
    background: #2196F3;
    color: #fff;
    height: 46px;
    border-radius: 13px;
}
</style>
<body>
<div>
    <h1>This is my homepage</h1>

    <form name="name" method="get">
      <p>姓名: <input type="text" name="fname" value="<%= name[0] %>" /></p>
      <p>年龄: <input type="text" name="age" value="<%= name[1] %>"/></p>
      <input type="submit" class="submitBtn" value="获取" onclick="searchAction()" style="width: 100px;"/>
      <input type="submit" class="submitBtn" value="更新" onclick="updateAction()" style="width: 100px;margin-left: 150px;">
    </form>
    <p><%= name[0] %></p>
    <p><%= name[1] %></p>
    <p><%= info %></p>
</div>

</body>
<script>
  function searchAction(){

    document.name.action="/search";

    document.name.submit();

    }

function updateAction(){

    document.name.action="/update";

    document.name.submit();

    }
</script>
</html>

4, 蚂蚁BAAS SDK调用代码

《JS SDK 快速开始》 讲解了如何通过JS SDK连接BAAS的方法。

let express = require("express");
let app = express();

const Chain = require("@alipay/mychain/index.node") //在 node 环境使用 TLS 协议
const fs = require("fs")
 
const accountKey = fs.readFileSync("./certs/duncanwang-user.pem", { encoding: "utf8" })
const accountPassword = "2018ceshi"  //需要替换为自定义的 user.pem 密码
 
const keyInfo = Chain.utils.getKeyInfo(accountKey, accountPassword)
//可打印私钥和公钥,使用 16 进制
//console.log('private key:', keyInfo.privateKey.toString('hex'))
//console.log('public key:', keyInfo.publicKey.toString('hex'))
 
const passphrase = "2018ceshi" //需要替换为自定义的 client.key 密码
//配置选项
let opt = {
  host: '139.196.136.94',    //目标区块链网络节点的 IP
  port: 18130,          //端口号
  timeout: 30000,       //连接超时时间配置
  cert: fs.readFileSync("./certs/client.crt", { encoding: "utf8" }),
  ca: fs.readFileSync("./certs/ca.crt", { encoding: "utf8" }),
  key: fs.readFileSync("./certs/client.key", { encoding: "utf8" }),
  userPublicKey: keyInfo.publicKey,
  userPrivateKey: keyInfo.privateKey,
  userRecoverPublicKey: keyInfo.publicKey,
  userRecoverPrivateKey: keyInfo.privateKey,
  passphrase: passphrase
}
 
//初始化一个连接实例
const chain = Chain(opt)
 
//调用 API 查询最新的一个区块数据
chain.ctr.QueryLastBlock({}, (err, data) => {
  // console.log('raw data:', data)                                     //区块结构数据
  // console.log('block hash:', data.block.block_header.hash)             //区块哈希
  // console.log('block number:', data.block.block_header.block_number) //区块高度
})

//设置ABI信息
const abi = 
[{"constant":true,"inputs":[],"name":"getInfo","outputs":[{"name":"","type":"string"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"string"},{"name":"_age","type":"uint256"}],"name":"setInfo","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"name","type":"string"},{"indexed":false,"name":"age","type":"uint256"}],"name":"Instructor","type":"event"}]
const contractName = "name-age";
const bytecode = "0x608060405234801561001057600080fd5b506103be806100206000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680635a9b0b89146100515780638262963b146100e8575b600080fd5b34801561005d57600080fd5b5061006661015b565b6040518080602001838152602001828103825284818151815260200191508051906020019080838360005b838110156100ac578082015181840152602081019050610091565b50505050905090810190601f1680156100d95780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b3480156100f457600080fd5b50610159600480360381019080803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919291929080359060200190929190505050610207565b005b6060600080600154818054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156101f85780601f106101cd576101008083540402835291602001916101f8565b820191906000526020600020905b8154815290600101906020018083116101db57829003601f168201915b50505050509150915091509091565b816000908051906020019061021d9291906102ed565b50806001819055507f010becc10ca1475887c4ec429def1ccc2e9ea1713fe8b0d4e9a1d009042f6b8e600060015460405180806020018381526020018281038252848181546001816001161561010002031660029004815260200191508054600181600116156101000203166002900480156102da5780601f106102af576101008083540402835291602001916102da565b820191906000526020600020905b8154815290600101906020018083116102bd57829003601f168201915b5050935050505060405180910390a15050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061032e57805160ff191683800117855561035c565b8280016001018555821561035c579182015b8281111561035b578251825591602001919060010190610340565b5b509050610369919061036d565b5090565b61038f91905b8082111561038b576000816000905550600101610373565b5090565b905600a165627a7a723058208ecb5a3721f4deee06f2f384b8ba7b90a47f803f1acbe2ad128b2eebc045bb240029";

// 初始化一个合约实例
let myContract = chain.ctr.contract(contractName, abi) 

// 部署合约,可传递初始化函数需要的参数
/*
myContract.new(bytecode, {
  from: 'duncanwang',
  // parameters: [param1, param2]
}, (err, contract, data) => {
  console.log(data)
})*/

//调用合约getInfo函数,查看当前用户的姓名和年龄
// myContract.getInfo( { from: 'duncanwang' }, (err, name, age) => {
//     console.log('output is:', name, age)
//   })

//调用合约getInfo函数,查看当前用户的姓名和年龄
// myContract.setInfo( "duncanwang", 20,{ from: 'duncanwang' }, (err,data1,data2) => {
//     console.log('output1 is------>:', data1)
//     console.log('output2 is------>:', data2)
//   })

// 调用合约getInfo函数,查看当前用户的姓名和年龄
// myContract.getInfo( { from: 'duncanwang' }, (err, name, age) => {
//     console.log('output is:', name, age)
//   })  



//初始返回一个home页面
app.get("/", function(req ,res) {
  res.render("home.ejs",{
    name: "",
    info: ''
  });
});

//更新接口
app.get("/update", function(req, res){
  myContract.setInfo( req.query.fname, req.query.age >> 0,{ from: 'duncanwang' }, (err,data) => {
    myContract.getInfo( { from: "duncanwang" }, (err, name, age) => {
      console.log('output is:', name)
      res.render("home.ejs",{
        name: name,
        info: '更新成功'
      });
    })
  })
});
//获取接口
app.get("/search", function(req ,res) {
  myContract.getInfo( { from: "duncanwang" }, (err, name, age) => {
    res.render("home.ejs",{
      name: name,
      info: '获取成功'
    });
  })
});

let server = require('http').createServer(app);
      server.listen(5000);{
        console.log("Sever Ready! open on http://localhost:5000");
      }

说明: (1)accountPassword,passphrase为用户自己的私钥密码。还需要在工程对应的地方存放各种证书文件。 (2)abi,contractName, bytecode都是在Cloud IDE编译部署后产生的。用户也可以通过solcjs编译产生。 (3)节点IP和端口从区块链浏览器处获得。

let opt = {
  host: '139.196.136.94',    //目标区块链网络节点的 IP
  port: 18130,          //端口号
...

4个节点IP以及对一个的端口:

2.节点IP

5,工程

辉哥建立了一个name-age的文件夹,里面的目标结构如下所示。

|   alipay-mychain-0.2.27.tgz
|   app.js
|   
+---certs
|       ca.crt
|       client.crt
|       client.key
|       duncanwang-user.key
|       duncanwang-user.pem
|       package-lock.json
|       tee_rsa_public_key.pem
|       
+---contracts
|       InfoContract.sol
|       
+---node_modules
|               
\---views
        home.ejs

说明下: (1)alipay-mychain-0.2.27.tgz 为蚂蚁的JS-SDK包,解压文件会到node_modules。 (2)app.js 调用JS-SDK的代码。 (3)certs为duncanwang账号对应的各种证书和公私钥文件。 (4)contracts/InfoContract.sol 为name-age智能合约文件。 (5)node_modules的内容很多,为NPM安装的各种依赖包。 (6)views/home.ejs 为采用采用node.js实现的前端页面。

6,运行测试

6.1 安装JS SDK

npm i alipay-mychain-0.2.27.tgz --save

安装成功结果:

1. 安装alipay-mychain

6.2 安装EXPRESS框架

npm install express npm install express-generator

6.3 运行NODE服务器

node app

输出结果:

9.node APP运行

6.4 运行界面

在chrome浏览器上打开网址:http://localhost:5000 点击获取”按钮,可以获得智能合约的姓名/年龄数据; 输入姓名:王登辉,年龄:25,可以更新智能合约的姓名/年龄数据。

就这样,依赖于蚂蚁BAAS的第一个DAPP应用已经产生了。

7, 参考

(1)JS SDK 快速开始 https://tech.antfin.com/docs/2/107128 (2)windows下node.js的安装及express使用命令配置 https://jingyan.baidu.com/article/7f41ecec0e3a25593d095c26.html

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 第二十六课 如何从零开始搭建一个Truffle框架的DAPP应用1,摘要2, 需求描述3,操作步骤4,总结

    第六课 技术小白如何开发一个DAPP区块链应用(以宠物商店为例)介绍了如何获取宠物商店的TRUFLLE框架代码,并完成部署的过程。 但是这个是已经成熟的代码框...

    辉哥
  • 第三课 以太坊术语说明及开发者资源列表

    也称钱包,提供账户管理、挖矿、转账、智能合约的部署和执行等等功能,以太坊节点利用以太坊客户端接入到以太坊网络。 现在以太坊客户端主要有:Wallent/ist ...

    辉哥
  • 蚂蚁区块链第14课 如何在TEE硬件隐私加密链上搭建一个DAPP应用(以姓名年龄为例)

    本文介绍通过调用蚂蚁BAAS的TEE硬件隐私链的JS SDK,完成智能合约读取,编译和加密部署功能。然后通过基于EXPRESS框架搭建的前端页面完成该姓名/年龄...

    辉哥
  • 深入理解ES6--块级作用域、字符串、正则、数组

    在for-in和for-of循环中,因为每次迭代不会(像for循环的例子一样)修改已有绑定,而是会创建一个新绑定。

    奋飛
  • Graphql入门

    GraphQL是一个查询语言,由Facebook开发,用于替换RESTful API。服务端可以用任何的语言实现。具体的你可以查看Facebook关于Graph...

    查理大叔
  • Springmvc响应Ajax请求(@ResponseBody)

    爱撒谎的男孩
  • Swift3.0 - 对象判等

    酷走天涯
  • python 多线程(并行编程 3)

    def function(i): print("function called by thread %i" % i) print(threading.cur...

    用户5760343
  • Spring中的18个注解,你会几个

    标识一个该类是Spring MVC controller处理器,用来创建处理http请求的对象.

    java架构师
  • Swift3.0 - 探究Self的用途

    酷走天涯

扫码关注云+社区

领取腾讯云代金券