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账号,输入其中的解密私钥。
然后点击编译完成智能合约的编译工作。
不知道怎么产生账号和机密私钥的,可参考文章《蚂蚁区块链第8课 如何创建新的账户,获取私钥和identity标识?》。
2.3 部署name-age合约并测试
(1)部署合约
部署成功后,获得其交易信息:
合约ID:
0xe72056692f46ae2443bbfefaa9ffddc09c8952a488afd345118aa295d85c8aba
tx hash:
0x1750e6156b63ada81fb54473fe54a97ef565e3c7fa2123ab9f033dfd7fc01672
(2)测试合约
设置辉哥的姓名为duncanwang,年龄为18岁,先完成setInfo,后调用getInfo,发现结果跟预期一致,智能合约测试完成。
记录下HASH信息,便于在浏览器查询:
function setInfo("duncanwang", 18)
tx hash:
0x46b2289c4fe175bcd80599e91fa8a55e1822f6280ffc90a4e0bd2af6ee832114
function getInfo
tx hash:
0x46b2289c4fe175bcd80599e91fa8a55e1822f6280ffc90a4e0bd2af6ee832114
3,前端页面和代码
前端UI页面如下。
对应的代码(home.ejs)实现:
Document
This is my homepage
<%= name[0] %>
<%= name[1] %>
<%= info %>
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以及对一个的端口:
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
安装成功结果:
6.2 安装EXPRESS框架
npm install express
npm install express-generator
6.3 运行NODE服务器
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