螞蟻區(qū)塊鏈第14課 如何在TEE硬件隱私加密鏈上搭建一個(gè)DAPP應(yīng)用(以姓名年齡為例)

1,摘要

本文介紹通過(guò)調(diào)用螞蟻BAAS的TEE硬件隱私鏈的JS SDK,完成智能合約讀取,編譯和加密部署功能。然后通過(guò)基于EXPRESS框架搭建的前端頁(yè)面完成該姓名/年齡前端系統(tǒng)的寫(xiě)入/查詢功能,演示隱私鏈的接口基本功能。

2,需求和代碼介紹

2.1 需求

本需求主要是作為入門級(jí)DAPP,主要能讀取智能合約中的姓名/年齡信息,同時(shí)也能寫(xiě)入更新姓名/年齡。該智能合約需要部署在TEE硬件隱私鏈上。
部署在標(biāo)準(zhǔn)合約鏈的參考文章《螞蟻區(qū)塊鏈第13課 如何搭建一個(gè)DAPP應(yīng)用(以姓名年齡為例)》。

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和代碼

功能說(shuō)明
(1)輸入“姓名”,“年齡”,點(diǎn)擊更新,完成加密更新智能合約的數(shù)據(jù);
(2)輸入AESS密鑰,點(diǎn)擊“解密查詢”,查詢結(jié)果數(shù)據(jù)。

對(duì)應(yīng)的“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接口調(diào)用文件

JS SDK接口調(diào)用文件app.js的代碼如下:

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

const Chain = require("@alipay/mychain/index.node") //在 node 環(huán)境使用 TLS 協(xié)議
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 進(jìn)制
console.log('private key:', keyInfo.privateKey.toString('hex'))
console.log('public key:', keyInfo.publicKey.toString('hex'))
 
const passphrase = "2018ceshi" //需要替換為自定義的 client.key 密碼
//配置選項(xiàng)
let opt = {
  host: '139.196.136.94',    //目標(biāo)區(qū)塊鏈網(wǎng)絡(luò)節(jié)點(diǎn)的 IP
  port: 18130,          //端口號(hào)
  timeout: 30000,       //連接超時(shí)時(shí)間配置
  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
}
 
//初始化一個(gè)連接實(shí)例
const chain = Chain(opt)
 
//調(diào)用 API 查詢最新的一個(gè)區(qū)塊數(shù)據(jù)
/*
chain.ctr.QueryLastBlock({}, (err, data) => {
  console.log('raw data:', data)                                     //區(qū)塊結(jié)構(gòu)數(shù)據(jù)
  console.log('block hash:', data.block.block_header.hash)             //區(qū)塊哈希
  console.log('block number:', data.block.block_header.block_number) //區(qū)塊高度
})*/

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

// 第二個(gè)參數(shù)設(shè)定為 1 ,會(huì)開(kāi)啟編譯優(yōu)化 optimiser
const output = solc.compile(contract, 1)
const abi = JSON.parse(output.contracts[':InfoContract'].interface)
const bytecode = output.contracts[':InfoContract'].bytecode

// 讀取 TEE 合約鏈節(jié)點(diǎn)的公鑰文件 tee_rsa_public_key.pem
let rsa2048 = {
  public: fs.readFileSync('./certs/tee_rsa_public_key.pem')
}
// 自定義的 aes 密碼,此密碼與加密交易的 hash 聯(lián)合計(jì)算生成最終的 aes 密鑰
let aes_key = '0x1c4f2919963e8dc040cfddf7d27227de'
 
contractName = 'contract'+Date.now()

// 初始化一個(gè)合約實(shí)例
let myContract = chain.ctr.contract(contractName, abi) 

// 基礎(chǔ)數(shù)據(jù)
let basicInfo = {
  from: 'duncanwang',
  encrypt: true,
  rsaPublicKey: rsa2048.public,
  aesKey: aes_key
}

// 加密部署合約,保護(hù)隱私
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)
  //初始化成功,開(kāi)啟服務(wù)
  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
}
//設(shè)置方法
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('服務(wù)開(kāi)啟中...')
initialize()

//初始返回一個(gè)home頁(yè)面
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: '密碼錯(cuò)誤,請(qǐng)重新輸入!'
    });
    return
  }
  let info = await getData(msg)
  res.render("home.ejs",{
    name: info,
    info: '加密查詢成功'
  });
});

JS SDK 增加了特別的交易接口來(lái)支持 TEE 合約鏈的交易隱私保護(hù),具體參考以下接口說(shuō)明介紹。

合約相關(guān)的加密交易

同樣,考慮到對(duì)合約操作相關(guān)接口使用最為廣泛,JS SDK 讓合約操作相關(guān)接口直接支持加密交易,具體使用方式如下。

new

new 用來(lái)加密部署合約,保護(hù)合約隱私。

請(qǐng)求參數(shù)

將以下參數(shù)整體封裝為 object 傳入。

參數(shù) 必填 類型 說(shuō)明
bytecode true string 目標(biāo)合約的字節(jié)碼,為 16 進(jìn)制表示
data true object 包含 from、parameters 等配置

data 字段內(nèi)容

字段 必填 類型 說(shuō)明
encrypt true bool 說(shuō)明此交易是否要加密,true:加密;false/不指定:不加密。
rsaPublicKey true string 目標(biāo) TEE 合約鏈環(huán)境的節(jié)點(diǎn) RSA 公鑰, 從 BaaS 平臺(tái) TEE 合約鏈下載。
aesKey true string 或 Buffer 此參數(shù)將作為一個(gè) password 形式與目標(biāo)加密的交易 hash 一起計(jì)算生成最終的 AES 對(duì)稱密鑰,如果使用 string 類型,會(huì)區(qū)分前綴是否包含“0x”來(lái)解釋內(nèi)容,包含“0x”則使用 16 進(jìn)制讀取,否則按照 ASCII 編碼讀取。
from true string 需要配置的當(dāng)前賬戶名。
parameters true Array 如果合約包含初始化函數(shù),并且此函數(shù)需要參數(shù)列表,可以通過(guò) parameters 傳遞。

說(shuō)明

  • 相比于普通的合約方法 new 增加了加密需要的 3 個(gè)參數(shù):encrypt、rsaPublicKey、aesKey。類似的,合約方法調(diào)用、合約升級(jí)也是增加 3 個(gè)參數(shù)配置而已,其它參數(shù)配置與非加密使用方式一致。
  • 其中 aesKey 參數(shù)將作為一個(gè)password形式與目標(biāo)加密的交易hash一起計(jì)算,生成最終的aes對(duì)稱密鑰,因此每個(gè)加密交易由于hash不同,即使用相同的aesKey,最終生成的aes對(duì)稱密鑰也不同,這樣生成方式便于交易發(fā)送者未來(lái)對(duì)部分交易的最終aes密鑰進(jìn)行分享,而不需要分享aesKey。

2.5 工程文件

輝哥建立了一個(gè)name-age-tee的文件夾,里面的目標(biāo)結(jié)構(gòu)如下所示。

| 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
說(shuō)明下:
(1)alipay-mychain-0.2.27.tgz 為螞蟻的JS-SDK包,解壓文件會(huì)到node_modules。
(2)app.js 調(diào)用JS-SDK的代碼。
(3)certs為duncanwang賬號(hào)對(duì)應(yīng)的各種證書(shū)和公私鑰文件。
(4)contracts/InfoContract.sol 為name-age智能合約文件。
(5)node_modules的內(nèi)容很多,為NPM安裝的各種依賴包。
(6)views/home.ejs 為采用采用node.js實(shí)現(xiàn)的前端頁(yè)面。

3,部署測(cè)試

3.1 安裝solc

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

成功結(jié)果:

【結(jié)果】
D:\jusanban\doc\26-培訓(xùn)分享\01-研發(fā)運(yùn)營(yíng)銷售\01-區(qū)塊鏈\05-螞蟻區(qū)塊鏈\8. Solidity-JS SDK-錯(cuò)誤碼\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-培訓(xùn)分享\01-研發(fā)運(yùn)營(yíng)銷售\01-區(qū)塊鏈\05-螞蟻區(qū)塊鏈\8. Solidity-JS SDK-錯(cuò)誤碼\DAPP\name-age-tee\package.json'
npm WARN enoent ENOENT: no such file or directory, open 'D:\jusanban\doc\26-培訓(xùn)分享\01-研發(fā)運(yùn)營(yíng)銷售\01-區(qū)塊鏈\05-螞蟻區(qū)塊鏈\8. Solidity-JS SDK-錯(cuò)誤碼\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

【成功結(jié)果】

D:\jusanban\doc\26-培訓(xùn)分享\01-研發(fā)運(yùn)營(yíng)銷售\01-區(qū)塊鏈\05-螞蟻區(qū)塊鏈\8. Solidity-JS SDK-錯(cuò)誤碼\DAPP\name-age-tee>npm i alipay-mychain-0.2.27.tgz --save

> secp256k1@3.6.2 install D:\jusanban\doc\26-培訓(xùn)分享\01-研發(fā)運(yùn)營(yíng)銷售\01-區(qū)塊鏈\05-螞蟻區(qū)塊鏈\8. Solidity-JS SDK-錯(cuò)誤碼\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-培訓(xùn)分享\01-研發(fā)運(yùn)營(yíng)銷售\01-區(qū)塊鏈\05-螞蟻區(qū)塊鏈\8. Solidity-JS SDK-錯(cuò)誤碼\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
成功輸出結(jié)果:

D:\jusanban\doc\26-培訓(xùn)分享\01-研發(fā)運(yùn)營(yíng)銷售\01-區(qū)塊鏈\05-螞蟻區(qū)塊鏈\8. Solidity-JS SDK-錯(cuò)誤碼\DAPP\name-age-tee>npm install express
npm WARN saveError ENOENT: no such file or directory, open 'D:\jusanban\doc\26-培訓(xùn)分享\01-研發(fā)運(yùn)營(yíng)銷售\01-區(qū)塊鏈\05-螞蟻區(qū)塊鏈\8. Solidity-JS SDK-錯(cuò)誤碼\DAPP\name-age-tee\package.json'
npm WARN enoent ENOENT: no such file or directory, open 'D:\jusanban\doc\26-培訓(xùn)分享\01-研發(fā)運(yùn)營(yíng)銷售\01-區(qū)塊鏈\05-螞蟻區(qū)塊鏈\8. Solidity-JS SDK-錯(cuò)誤碼\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-培訓(xùn)分享\01-研發(fā)運(yùn)營(yíng)銷售\01-區(qū)塊鏈\05-螞蟻區(qū)塊鏈\8. Solidity-JS SDK-錯(cuò)誤碼\DAPP\name-age-tee>npm install express-generator
npm WARN saveError ENOENT: no such file or directory, open 'D:\jusanban\doc\26-培訓(xùn)分享\01-研發(fā)運(yùn)營(yíng)銷售\01-區(qū)塊鏈\05-螞蟻區(qū)塊鏈\8. Solidity-JS SDK-錯(cuò)誤碼\DAPP\name-age-tee\package.json'
npm WARN enoent ENOENT: no such file or directory, open 'D:\jusanban\doc\26-培訓(xùn)分享\01-研發(fā)運(yùn)營(yíng)銷售\01-區(qū)塊鏈\05-螞蟻區(qū)塊鏈\8. Solidity-JS SDK-錯(cuò)誤碼\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 運(yùn)行NODE.JS服務(wù)

node app

輸出結(jié)果:

3.5 界面操作

輸入“duncanwang”,18,然后點(diǎn)擊“更新”按鈕,完成加密更新函數(shù)調(diào)用。

輸入aes密碼“0x1c4f2919963e8dc040cfddf7d27227de”,點(diǎn)擊“解密查詢”,可得結(jié)果:

在TEE硬件隱私加密鏈上搭建一個(gè)DAPP應(yīng)用(以姓名年齡為例)的任務(wù)成功完成。

4,參考

(1)TEE 硬件隱私合約鏈 JS SDK 說(shuō)明
https://tech.antfin.com/docs/2/107140

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容