蚂蚁区块链第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="获取" onclick="searchAction()" style="width: 100px;"/>-->
      <input type="submit" class="submitBtn" value="更新" onclick="updateAction()" style="width: 100px;margin-left: 80px;">
      <p>aes密码: <input type="text" name="password"/ style="width: 500px"></p>      
      <input type="submit" class="submitBtn" value="解密查询" onclick="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 传入。

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

data 字段内容

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容