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

1,摘要

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

2,需求和代码介绍

2.1 需求

本需求主要是作为入门级DAPP,主要能读取智能合约中的姓名/年龄信息,同时也能写入更新姓名/年龄。该智能合约需要部署在TEE硬件隐私链上。
部署在标准合约链的参考文章《蚂蚁区块链第13课 如何搭建一个DAPP应用(以姓名年龄为例)》

2.2 智能合约

InfoContract.sol智能合约:

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.3 前端UI和代码

功能说明
(1)输入“姓名”,“年龄”,点击更新,完成加密更新智能合约的数据;
(2)输入AESS密钥,点击“解密查询”,查询结果数据。

对应的“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="获取" "searchAction()" style="width: 100px;"/>-->
      <input type="submit" class="submitBtn" value="更新" "updateAction()" style="width: 100px;margin-left: 80px;">
      <p>aes密码: <input type="text" name="password"/ style="width: 500px"></p>      
      <input type="submit" class="submitBtn" value="解密查询" "encryptAction()" style="width: 100px;margin-left: 80px;">
    </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();

    }
function encryptAction(){

document.name.action="/encrypt";

document.name.submit();

}
</script>
</html>


2.4 JS SDK接口调用文件

JS SDK接口调用文件app.js的代码如下:

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

const Chain = require("@alipay/mychain/index.node") //在 node 环境使用 TLS 协议
const fs = require("fs")
const solc = require('@alipay/solc')
 
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) //区块高度
})*/

const contract = fs.readFileSync('./contracts/InfoContract.sol', {encoding: 'ascii'})

// 第二个参数设定为 1 ,会开启编译优化 optimiser
const output = solc.compile(contract, 1)
const abi = JSON.parse(output.contracts[':InfoContract'].interface)
const bytecode = output.contracts[':InfoContract'].bytecode

// 读取 TEE 合约链节点的公钥文件 tee_rsa_public_key.pem
let rsa2048 = {
  public: fs.readFileSync('./certs/tee_rsa_public_key.pem')
}
// 自定义的 aes 密码,此密码与加密交易的 hash 联合计算生成最终的 aes 密钥
let aes_key = '0x1c4f2919963e8dc040cfddf7d27227de'
 
contractName = 'contract'+Date.now()

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

// 基础数据
let basicInfo = {
  from: 'duncanwang',
  encrypt: true,
  rsaPublicKey: rsa2048.public,
  aesKey: aes_key
}

// 加密部署合约,保护隐私
let autoDeploy = (info) => {
  return new Promise((resolve, reject)=>{
    myContract.new(bytecode,info,(err, contract, data) => {
      resolve(data)
    })
  })
}
// 加密 (查询\获取) 信息
let setInfo = (func_parml, info) => {
  return new Promise((resolve, reject)=>{
    func_parml.type === 'set' ? myContract.setInfo(func_parml.name, func_parml.age, info, (err, contract, data) =>{resolve(data)}) : myContract.getInfo(info,(err, contract, data) =>{resolve({contract,data})})
  })
}
//初始化方法
let initialize = async () => {
  let initial = await autoDeploy(basicInfo)
  let setData = await setInfo({type: 'set', name :'duncanwang' , age : 35}, basicInfo)
  //初始化成功,开启服务
  let server = require('http').createServer(app);
      server.listen(5000);{
        console.log("Sever Ready! open on http://localhost:5000");
      }
}
//获取方法
let getData = async (msg) => {
  let userInfo = await setInfo({type: 'get'}, msg)
  let info = myContract.getOutput('getInfo', Chain.utils.decryptAESWithPassword(userInfo.contract, aes_key, userInfo.data.txhash))
  return info
}
//设置方法
let setData = async (msg) => {
  let setData = await setInfo({type: 'set', name : msg.from , age : msg.age >> 0}, msg)
  let userInfo = await setInfo({type: 'get'}, msg)
  let info = myContract.getOutput('getInfo', Chain.utils.decryptAESWithPassword(userInfo.contract, aes_key, userInfo.data.txhash))
  return info
}
//初始化
console.log('服务开启中...')
initialize()

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

//更新接口
app.get("/update", async function(req, res){
  let msg = basicInfo
  msg.age = req.query.age
  let info = await setData(msg)
  res.render("home.ejs",{
    name: info,
    info: '更新成功'
  }); 
});

//获取接口
app.get("/search", async (req ,res) => {
  let info = await getData(basicInfo)
  console.log('-----info-----', info)
  res.render("home.ejs",{
    name: info,
    info: '获取成功'
  });
});

//加密查询接口

app.get("/encrypt", async (req ,res) => {
  let msg = basicInfo
  msg.aesKey = req.query.password
  if(req.query.password !== '0x1c4f2919963e8dc040cfddf7d27227de') {
    res.render("home.ejs",{
      name: ['', ''],
      info: '密码错误,请重新输入!'
    });
    return
  }
  let info = await getData(msg)
  res.render("home.ejs",{
    name: info,
    info: '加密查询成功'
  });
});

JS SDK 增加了特别的交易接口来支持 TEE 合约链的交易隐私保护,具体参考以下接口说明介绍。

合约相关的加密交易

同样,考虑到对合约操作相关接口使用最为广泛,JS SDK 让合约操作相关接口直接支持加密交易,具体使用方式如下。

new

new 用来加密部署合约,保护合约隐私。

请求参数

将以下参数整体封装为 object 传入。

参数必填类型说明
bytecodetruestring目标合约的字节码,为 16 进制表示
datatrueobject包含 from、parameters 等配置

data 字段内容

字段必填类型说明
encrypttruebool说明此交易是否要加密,true:加密;false/不指定:不加密。
rsaPublicKeytruestring目标 TEE 合约链环境的节点 RSA 公钥, 从 BaaS 平台 TEE 合约链下载。
aesKeytruestring 或 Buffer此参数将作为一个 password 形式与目标加密的交易 hash 一起计算生成最终的 AES 对称密钥,如果使用 string 类型,会区分前缀是否包含“0x”来解释内容,包含“0x”则使用 16 进制读取,否则按照 ASCII 编码读取。
fromtruestring需要配置的当前账户名。
parameterstrueArray如果合约包含初始化函数,并且此函数需要参数列表,可以通过 parameters 传递。

说明

  • 相比于普通的合约方法 new 增加了加密需要的 3 个参数:encrypt、rsaPublicKey、aesKey。类似的,合约方法调用、合约升级也是增加 3 个参数配置而已,其它参数配置与非加密使用方式一致。
  • 其中 aesKey 参数将作为一个password形式与目标加密的交易hash一起计算,生成最终的aes对称密钥,因此每个加密交易由于hash不同,即使用相同的aesKey,最终生成的aes对称密钥也不同,这样生成方式便于交易发送者未来对部分交易的最终aes密钥进行分享,而不需要分享aesKey。

2.5 工程文件

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

| 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实现的前端页面。

3,部署测试

3.1 安装solc

npm i alipay-solc-0.1.12.tgz --save

成功结果:

【结果】
D:\jusanban\doc\26-培训分享\01-研发运营销售\01-区块链\05-蚂蚁区块链\8. Solidity-JS SDK-错误码\DAPP\name-age-tee>npm i alipay-solc-0.1.12.tgz --save
npm WARN saveError ENOENT: no such file or directory, open 'D:\jusanban\doc\26-培训分享\01-研发运营销售\01-区块链\05-蚂蚁区块链\8. Solidity-JS SDK-错误码\DAPP\name-age-tee\package.json'
npm WARN enoent ENOENT: no such file or directory, open 'D:\jusanban\doc\26-培训分享\01-研发运营销售\01-区块链\05-蚂蚁区块链\8. Solidity-JS SDK-错误码\DAPP\name-age-tee\package.json'
npm WARN name-age-tee No description
npm WARN name-age-tee No repository field.
npm WARN name-age-tee No README data
npm WARN name-age-tee No license field.

+ @alipay/solc@0.1.12
added 65 packages from 35 contributors and audited 2409 packages in 6.74s
found 0 vulnerabilities

3.2 安装JS SDK

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

【成功结果】

D:\jusanban\doc\26-培训分享\01-研发运营销售\01-区块链\05-蚂蚁区块链\8. Solidity-JS SDK-错误码\DAPP\name-age-tee>npm i alipay-mychain-0.2.27.tgz --save

> secp256k1@3.6.2 install D:\jusanban\doc\26-培训分享\01-研发运营销售\01-区块链\05-蚂蚁区块链\8. Solidity-JS SDK-错误码\DAPP\name-age-tee\node_modules\secp256k1
> npm run rebuild || echo "Secp256k1 bindings compilation fail. Pure JS implementation will be used."


> secp256k1@3.6.2 rebuild D:\jusanban\doc\26-培训分享\01-研发运营销售\01-区块链\05-蚂蚁区块链\8. Solidity-JS SDK-错误码\DAPP\name-age-tee\node_modules\secp256k1
> node-gyp rebuild
...

npm WARN name-age-tee No description
npm WARN name-age-tee No repository field.
npm WARN name-age-tee No README data
npm WARN name-age-tee No license field.

+ @alipay/mychain@0.2.27
added 50 packages from 32 contributors and audited 710 packages in 53.948s
found 0 vulnerabilities

3.3 安装EXPRESS模块

npm install express
npm install express-generator
成功输出结果:

D:\jusanban\doc\26-培训分享\01-研发运营销售\01-区块链\05-蚂蚁区块链\8. Solidity-JS SDK-错误码\DAPP\name-age-tee>npm install express
npm WARN saveError ENOENT: no such file or directory, open 'D:\jusanban\doc\26-培训分享\01-研发运营销售\01-区块链\05-蚂蚁区块链\8. Solidity-JS SDK-错误码\DAPP\name-age-tee\package.json'
npm WARN enoent ENOENT: no such file or directory, open 'D:\jusanban\doc\26-培训分享\01-研发运营销售\01-区块链\05-蚂蚁区块链\8. Solidity-JS SDK-错误码\DAPP\name-age-tee\package.json'
npm WARN name-age-tee No description
npm WARN name-age-tee No repository field.
npm WARN name-age-tee No README data
npm WARN name-age-tee No license field.

+ express@4.16.4
added 46 packages from 35 contributors and audited 2884 packages in 10.871s
found 0 vulnerabilities

D:\jusanban\doc\26-培训分享\01-研发运营销售\01-区块链\05-蚂蚁区块链\8. Solidity-JS SDK-错误码\DAPP\name-age-tee>npm install express-generator
npm WARN saveError ENOENT: no such file or directory, open 'D:\jusanban\doc\26-培训分享\01-研发运营销售\01-区块链\05-蚂蚁区块链\8. Solidity-JS SDK-错误码\DAPP\name-age-tee\package.json'
npm WARN enoent ENOENT: no such file or directory, open 'D:\jusanban\doc\26-培训分享\01-研发运营销售\01-区块链\05-蚂蚁区块链\8. Solidity-JS SDK-错误码\DAPP\name-age-tee\package.json'
npm WARN name-age-tee No description
npm WARN name-age-tee No repository field.
npm WARN name-age-tee No README data
npm WARN name-age-tee No license field.

+ express-generator@4.16.0
added 6 packages from 11 contributors and audited 3044 packages in 4.964s
found 0 vulnerabilities

3.4 运行NODE.JS服务

node app

输出结果:

3.5 界面操作

输入“duncanwang”,18,然后点击“更新”按钮,完成加密更新函数调用。

输入aes密码“0x1c4f2919963e8dc040cfddf7d27227de”,点击“解密查询”,可得结果:

在TEE硬件隐私加密链上搭建一个DAPP应用(以姓名年龄为例)的任务成功完成。

4,参考

(1)TEE 硬件隐私合约链 JS SDK 说明
https://tech.antfin.com/docs/2/107140

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笔名辉哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值