Zhuang's Diary

言之有物,持之以恒

hyperledger fabric 2.2 kubernetes

一、本地开发环境依赖

1
2
3
4
5
6
7
8
# Minikube 安装 v1.20.0
curl -Lo minikube https://kubernetes.oss-cn-hangzhou.aliyuncs.com/minikube/releases/v1.20.0/minikube-darwin-amd64
chmod +x minikube
sudo mv minikube /usr/local/bin/
# Minikube start
minikube start --image-mirror-country cn \
--iso-url=https://kubernetes.oss-cn-hangzhou.aliyuncs.com/minikube/iso/minikube-v1.20.0.iso \
--registry-mirror=https://xxxxx.mirror.aliyuncs.com
  • 安装 kubectl 工具
  • 安装 git
  • 安装 docker
  • 安装 hyperledger 相关命令列工具
1
2
3
4
5
6
# 执行以下指令下载 Hyperledger 命令列工具
curl -sSL http://raw.githubusercontent.com/hyperledger/fabric/master/scripts/bootstrap.sh | bash -s -- 2.2.0 1.5.0
# 或者
curl -sSL https://bit.ly/2ysbOFE | bash -s
# 设定环境变数至下载下来的 bin 目录下
export PATH=<path to download location>/bin:$PATH

以上脚本会在本地下载k8s部署需要的容器和可执行文件,云上的话需要准备的镜像如下:

1
2
3
4
centos:latest
hyperledger/fabric-orderer:2.2
hyperledger/fabric-peer:2.2.0
hyperledger/fabric-tools:2.2.0

用于测试的chaincode镜像,marbles是hyperledger官方给的一个sample,但是源码没有打镜像,这里其他人打的一个镜像,我们只是用来测试部署是否成功,之后会用我们开发的存证chaincode镜像替换

1
paragones/chaincode-marbles:1.0
1
2
3
4
# 创建一个名为 hyperledger 的 namespace
kubectl create namespace hyperledger
# 切换至 hyperledger namespace
kubectl config set-context --current --namespace=hyperledger

二、系统架构规划

2.1 节点规划

下图为本范例的部署架构,所有hyperledger 所需节点皆部署于 k8s 中的 hyperledger namespace 当中。架构中包含的节点如下

  • orderer0 : 排序节点0,用于排序区块
  • orderer1 : 排序节点1,用于排序区块
  • orderer2 : 排序节点2,用于排序区块
  • peer0-org1 : 组织1的Peer节点,用于区块的实际运算、背书以及记帐。
  • cli-org1-peer : 用于操纵组织1的Peer节点
  • peer0-org2 : 组织2的Peer节点,用于区块的实际运算、背书以及记帐。
  • cli-org2-peer : 用于操纵组织2的Peer节点

2.2 存储存放规划

节点 挂载路径 路径说明 PVC PV hostPath
orderer0 /var/hyperledger/orderer/ 存放凭证 orderer0-pvc ./fabric/orderer0
orderer0 /var/hyperledger/production 持久化资料 orderer0-persist-pvc ./fabric/orderer0persist
orderer1 /var/hyperledger/orderer/ 存放凭证 orderer1-pvc ./fabric/orderer1
orderer1 /var/hyperledger/production 持久化资料 orderer1-persist-pvc ./fabric/orderer1persist
orderer2 /var/hyperledger/orderer/ 存放凭证 orderer2-pvc ./fabric/orderer2
orderer2 /var/hyperledger/production 持久化资料 orderer2-persist-pvc ./fabric/orderer2persist
Org1-Peer /etc/hyperledger/fabric/ 存放凭证 peer0-org1-pvc ./fabric/peer0org1
Org1-Peer /var/hyperledger/production 持久化资料 peer0-org1-persist-pvc ./fabric/peer0org1persist
Org1-Peer-CLI /opt/gopath/src/github.com/ hyperledger/fabric/peer/crypto/ 存放凭证 peer0-org1-pvc ./fabric/peer0org1
Org2-Peer /etc/hyperledger/fabric/ 存放凭证 peer0-org2-pvc ./fabric/peer0org2
Org2-Peer /var/hyperledger/production 持久化资料 peer0-org2-persist-pvc ./fabric/peer0org2persist
Org2-Peer-CLI /opt/gopath/src/github.com/ hyperledger/fabric/peer/crypto/ 存放凭证 peer0-org1-pvc ./fabric/peer0org2

三、准备凭证

Hyperledger Fabric 于节点沟通时必须依赖凭证进行沟通,因此必须先签发凭证。在凭证的签发过程中可以使用两种方式签发凭证

  • cryptogen 命令
    cryptogen 为 Hyperledger Fabric 生成凭证的命令列工具,于 crypto-config.yaml 定义 Orderer 以及各组织的Peer的数量。
  • fabric-ca 服务
    Fabric CA 是一个为 Hyperledger Fabric 签发凭证的工具,通常每个组织会有自己的 Fabric CA,通過fabric-ca client 获得凭证后,就可以用这些凭证访问Peer。

在此范例中将以 cryptogen 命令列生成各节点所需凭证,以下为签发凭证的指令。可以查看 git repo 中的 crypto-config.yaml 为签发凭证设定文件,crypto-config则为签发结果

1
cryptogen generate --config=crypto-config.yaml --output ./crypto-config

(notice:练习中,需要再次生成新的凭证设定文件时,需要将./crypto-config文件夹删除后,再行运行上述命令。否则凭证设定文件不会被更新覆盖!)

四、准备创世区块

1
configtxgen -profile TwoOrgsOrdererGenesis -channelID devchan -outputBlock ./channel-artifacts/genesis.block

五、产生Channel 所需档案

1
2
3
4
5
6
# 产生Channel 所需档案
configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID "mychannel"
# 产生 Org1 使用的 channel 设定文件
configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/org1Anchors.tx -channelID "mychannel" -asOrg org1MSP
# 产生 Org2 使用的 channel 设定文件
configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/org2Anchors.tx -channelID "mychannel" -asOrg org2MSP

六、同步档案至PV中

在前述三、四、五章节中我们已经准备了以下内容

  • 各节点所需凭证
  • 创世区块
  • Channel 设定档

由于 Hyperledger 之 container 必须先将以上档案放置至正确位置后 container 才能正常启动。但本次安装不使用NFS 预先将档案填入,而是先启临时的 container 同步档案。
在本步骤我们将启动两个用于填充档案的 container

container 名称 说明
orderer-bastion 填充 orderer0-pvc,orderer1-pvc,orderer2-pvc 所需要的文档
peer-bastion 填充 peer0-org1-pvc,peer0-org2-pvc 所需要的文档

以下为同步文档的步骤

6.1 启动临时 container

1
kubectl create -f /deploy-hyperledger-fabric-on-k8s/file-populate-bastion/

这个指令将会产生 pv/pvc/orderer-bastion/peer-bastion 这些资源

6.2 同步 Orderer 所需文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 登录进入 orderer-bastion
kubectl exec -it ${orderer-bastion-pod-name} bash

# 安装 git 并 clone deploy-hyperledger-fabric-on-k8s repo
yum install git -y
git clone https://github.com/willzhuang/deploy-hyperledger-fabric-on-k8s.git

# 同步 orderer0 创世区块
cp deploy-hyperledger-fabric-on-k8s/channel-artifacts/genesis.block /orderer0-pvc/
# 同步 orderer0 凭证
cp -r deploy-hyperledger-fabric-on-k8s/crypto-config/ordererOrganizations/consortium/orderers/orderer0/* /orderer0-pvc/

# 同步 orderer1 创世区块
cp deploy-hyperledger-fabric-on-k8s/channel-artifacts/genesis.block /orderer1-pvc/
# 同步 orderer1 凭证
cp -r deploy-hyperledger-fabric-on-k8s/crypto-config/ordererOrganizations/consortium/orderers/orderer1/* /orderer1-pvc/

# 同步 orderer2 创世区块
cp deploy-hyperledger-fabric-on-k8s/channel-artifacts/genesis.block /orderer2-pvc/
# 同步 orderer2 凭证
cp -r deploy-hyperledger-fabric-on-k8s/crypto-config/ordererOrganizations/consortium/orderers/orderer2/* /orderer2-pvc/

6.3 同步 Peer 所需要的文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 登录进入 peer-bastion
kubectl exec -it ${peer-bastion-pod-name} bash

# 安装 git 並 clone deploy-hyperledger-fabric-on-k8s repo
yum install git -y
git clone https://github.com/willzhuang/deploy-hyperledger-fabric-on-k8s.git

# 同步 org1 peer0 所需之凭证
cp -r /deploy-hyperledger-fabric-on-k8s/crypto-config/peerOrganizations/org1/peers/peer0-org1/* peer0-org1-pvc/
# 同步 org1 peer0 cli 所需之凭证
cp -r /deploy-hyperledger-fabric-on-k8s/crypto-config/* /peer0-org1-pvc/
# 同步 org1 peer0 所需之 channel设定文档
cp /deploy-hyperledger-fabric-on-k8s/channel-artifacts/* peer0-org1-pvc/

# 同步 org2 peer0 所需之凭证
cp -r /deploy-hyperledger-fabric-on-k8s/crypto-config/peerOrganizations/org2/peers/peer0-org2/* peer0-org2-pvc/
# 同步 org2 peer0 cli 所需之凭证
cp -r /deploy-hyperledger-fabric-on-k8s/crypto-config/* /peer0-org2-pvc/
# 同步 org2 peer0 所需之 channel设定文档
cp /deploy-hyperledger-fabric-on-k8s/channel-artifacts/* peer0-org2-pvc/

七、启动 Orderer

1
2
# 启动 orderer cluster
kubectl create -f /deploy-hyperledger-fabric-on-k8s/orderer/

內含 orderer0,orderer1,orderer2 所需的 deployment 与 service 资源

八、启动 Peer

1
2
3
4
5
6
7
# 启动 org1 peer0 
kubectl create -f /deploy-hyperledger-fabric-on-k8s/org1/
# 內含 org1 peer0/cli 所需的 deployment,configmap,service 资源

# 启动 org2 peer0
kubectl create -f /deploy-hyperledger-fabric-on-k8s/org2/
# 內含 org2 peer0/cli 所需的 deployment,configmap,service 资源

九、创建channel

9.1 将 org1 peer0 加入 channel

1
2
# 登录进入 cli pod 
kubectl exec -it ${org1-peer0-cli-pod-name} sh
1
2
# 产生 channel 区块
peer channel create -o orderer0:7050 -c mychannel -f ./scripts/channel-artifacts/channel.tx --tls true --cafile $ORDERER_CA

运行成功后,log信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/opt/gopath/src/github.com/hyperledger/fabric/peer # peer channel create -o orderer0:7050 -c mychannel -f ./scripts/channel-artifacts/channel.tx -
-tls true --cafile $ORDERER_CA
2021-08-10 05:41:03.212 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2021-08-10 05:41:03.242 UTC [cli.common] readBlock -> INFO 002 Expect block, but got status: &{NOT_FOUND}
2021-08-10 05:41:03.246 UTC [channelCmd] InitCmdFactory -> INFO 003 Endorser and orderer connections initialized
2021-08-10 05:41:03.447 UTC [cli.common] readBlock -> INFO 004 Expect block, but got status: &{SERVICE_UNAVAILABLE}
2021-08-10 05:41:03.450 UTC [channelCmd] InitCmdFactory -> INFO 005 Endorser and orderer connections initialized
2021-08-10 05:41:03.651 UTC [cli.common] readBlock -> INFO 006 Expect block, but got status: &{SERVICE_UNAVAILABLE}
2021-08-10 05:41:03.654 UTC [channelCmd] InitCmdFactory -> INFO 007 Endorser and orderer connections initialized
2021-08-10 05:41:03.857 UTC [cli.common] readBlock -> INFO 008 Expect block, but got status: &{SERVICE_UNAVAILABLE}
2021-08-10 05:41:03.866 UTC [channelCmd] InitCmdFactory -> INFO 009 Endorser and orderer connections initialized
2021-08-10 05:41:04.068 UTC [cli.common] readBlock -> INFO 00a Expect block, but got status: &{SERVICE_UNAVAILABLE}
2021-08-10 05:41:04.070 UTC [channelCmd] InitCmdFactory -> INFO 00b Endorser and orderer connections initialized
2021-08-10 05:41:04.273 UTC [cli.common] readBlock -> INFO 00c Expect block, but got status: &{SERVICE_UNAVAILABLE}
2021-08-10 05:41:04.277 UTC [channelCmd] InitCmdFactory -> INFO 00d Endorser and orderer connections initialized
2021-08-10 05:41:04.480 UTC [cli.common] readBlock -> INFO 00e Received block: 0

1
2
# 加入 channel
peer channel join -b mychannel.block

运行成功后,log信息如下:

1
2
3
/opt/gopath/src/github.com/hyperledger/fabric/peer # peer channel join -b mychannel.block
2021-08-10 05:48:59.716 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2021-08-10 05:48:59.736 UTC [channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel

1
2
# 查看是否在 channel 当中
peer channel list

9.2 將 org2 peer0 加入 channel

1
2
3
4
5
6
7
8
9
10
11
# 登录进入 cli pod 
kubectl exec -it pod ${org2-peer0-cli-pod-name} sh

# 取得 channel 区块
peer channel fetch 0 mychannel.block -c mychannel -o orderer0:7050 --tls --cafile $ORDERER_CA

# 加入 channel
peer channel join -b mychannel.block

# 查看是否在 channel 当中
peer channel list

运行成功后,log信息如下:

1
2
3
4
5
6
7
8
9
10
/opt/gopath/src/github.com/hyperledger/fabric/peer # peer channel fetch 0 mychannel.block -c mychannel -o orderer0:7050 --tls --cafile $ORDERER_CA
2021-08-10 06:05:04.550 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2021-08-10 06:05:04.553 UTC [cli.common] readBlock -> INFO 002 Received block: 0
/opt/gopath/src/github.com/hyperledger/fabric/peer # peer channel join -b mychannel.block
2021-08-10 06:05:16.493 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2021-08-10 06:05:16.513 UTC [channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel
/opt/gopath/src/github.com/hyperledger/fabric/peer # peer channel list
2021-08-10 06:05:24.222 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
Channels peers has joined:
mychannel

十、Fabric节点上安装外的Chaincode

10.1 安装chaincode
‘marbles’ 链码作为范例

打包org1

1
2
3
4
5
6
# 进入github的chaincode/packaging
# 将connection.json 打包成为 code.tar.gz
$ tar cfz code.tar.gz connection.json

#再將code.tar.gz metadata.json包成marbles-org1.tgz
$ tar cfz marbles-org1.tgz code.tar.gz metadata.json

将org1 tar文档安装到peer cli pod中

1
2
3
4
5
6
7
8
9
10
11
# 将marbles-org1.tgz放入peer pod
kubectl cp marbles-org1.tgz hyperledger/${cli-org1-name}:/opt/gopath/src/github.com/hyperledger/fabric/peer

# 登录进入${cli-org1-name} pod
kubectl exec -it ${cli-org1-name} -- /bin/bash

# 在peer-cli上安装chaincode
$ peer lifecycle chaincode install marbles-org1.tgz

# 查询链码,识别字符串
$ peer lifecycle chaincode queryinstalled

打包org2,如打包or1一样的步骤,请修改connection.json中的address

1
"address": "chaincode-marbles-org2.hyperledger:7052"

依照下列打包步骤

1
2
3
4
5
6
7
$ rm -f code.tar.gz
$ tar cfz code.tar.gz connection.json
$ tar cfz marbles-org2.tgz code.tar.gz metadata.json
$ peer lifecycle chaincode install marbles-org2.tgz

# 查询链码,识别字符串
$ peer lifecycle chaincode queryinstalled

至此,已完成外部链码设置。


10.2 部署”智能合约”

制作golang的alpine镜像文件

1
2
3
4
5
$ docker build -t chaincode/marbles:1.0 .

# 将文件推送至dockerhub
$ docker login
$ docker push chaincode/marbles:1.0

进入github中 chaincode\k8s ,找到org1-chaincode-deployment.yaml和org2-chaincode-deployment.yaml中的CHAINCODE_CCID

将yaml文件中的CHAINCODE_CCID更换成为对应的 chaincode 识别码

1
2
# 部署 chaincode 到aks上
$ kubectl create -f chaincode/k8s

完成部署。


10.3审核链码的安装

请在peer-cli-org1/peer-cli-org2 这2个pod中允许链码的安装,记得修改CHAINCODE_CCID
※peer-cli-org1 及 peer-cli-org2的CHAINCODE_CCID 需要对应到各自的chaincode识别码

1
2
$ peer lifecycle chaincode approveformyorg --channelID mychannel --name marbles --version 1.0 --init-required --package-id marbles:e001937433673b11673d660d142c722fc372905db87f88d2448eee42c9c63064 --sequence 1 -o orderer0:7050 --tls --cafile $ORDERER_CA --signature-policy "AND ('org1MSP.peer','org2MSP.peer')

检查所有org允许状态

1
2
$peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name marbles --version 1.0 --init-require
d --sequence 1 -o -orderer0:7050 --tls --cafile $ORDERER_CA --signature-policy "AND ('org1MSP.peer','org2MSP.peer')"

至此,2个peer-cli都审核完毕。

之后提交到channel 中

1
2
peer lifecycle chaincode commit -o orderer0:7050 --channelID mychannel --name marbles --version 1.0 --sequence 1 --init-required --tls true --cafile $ORDERER_CA --peerAddresses peer0-org1:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1/peers/peer0-org1/tls/ca.crt --peerAddresses peer0-org2:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2/peers/peer0-org2/tls/ca.crt --signature-policy "AND ('org1MSP.peer','org2MSP.peer')"


10.4测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 查询安装的链码
peer lifecycle chaincode queryinstalled
# 查询审核通过的链码
peer lifecycle chaincode queryapproved --channelID mychannel --name marbles
# 查询提交完成的链码
peer lifecycle chaincode querycommitted --channelID mychannel --name marbles
# 塞弹珠(第一次)
peer chaincode invoke -o orderer0:7050 --isInit --tls true --cafile $ORDERER_CA -C mychannel -n marbles \
--peerAddresses peer0-org1:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1/peers/peer0-org1/tls/ca.crt \
--peerAddresses peer0-org2:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2/peers/peer0-org2/tls/ca.crt \
-c '{"Args":["initMarble","marble1","blue","35","tom"]}' --waitForEvent
# 塞弹珠
peer chaincode invoke -o orderer0:7050 --tls true --cafile $ORDERER_CA -C mychannel -n marbles \
--peerAddresses peer0-org1:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1/peers/peer0-org1/tls/ca.crt \
--peerAddresses peer0-org2:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2/peers/peer0-org2/tls/ca.crt \
-c '{"Args":["initMarble","marble5","red","50","tom"]}' --waitForEvent
# 查询弹珠
peer chaincode query -C mychannel -n marbles -c '{"Args":["readMarble","marble5"]}'


开发Client Application

目前hyperledger不对外,请使用kubectl port-forward的方式连进去。
指令:

1
kubectl port-forward ${pord-id} 7050:7050

参考 fabric-samples 中typescript所撰写的范例 ※另有java, go 语言的范例!!

以下为typerscript 范例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

async function main() {
try {
const ccpPath = path.resolve(__dirname, '..', 'connection.json');

const fileExists = fs.existsSync(ccpPath);
if (!fileExists) {
throw new Error('no such file or directory: ${ccpPath}');
}
const contents = fs.readFileSync(ccpPath, 'utf8');
console.log(contents);

const connectionProfile = JSON.parse(contents);
const gateway = new Gateway();
const wallet = await buildWallet(walletPath);

const identity: X509Identity = {
credentials: {
//crypto-config\peerOrganizations\org1\users\Admin@org1\msp\signcerts\Admin@org1-cert.pem
certificate: '-----BEGIN CERTIFICATE-----\nMIICBjCCAaygAwIBAgIRAKeXh5QGRlhFCf31amakNiowCgYIKoZIzj0EAwIwWzEL
//crypto-config\peerOrganizations\org1\users\Admin@org1\msp\keystore
privateKey: '-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg9tkGBGl8aIWYbN/i\nqKjl5dK3D3qXx6ltn3YrlH4NFPehRANCAATMJIf0mY3FQDysZOevzbsBwqttfVuW\nwoIFV9Z0TwBEXXAQ0HPd9u77zV4my0onty3rJnWKF2kuhIn5PUzz61zk\n-----END PRIVATE KEY-----\n',
},
mspId: org1UserId,
type: 'X.509',
};
await wallet.put(org1UserId, identity);


const gatewayOpts: GatewayOptions = {
wallet,
identity: org1UserId,
discovery: { enabled: false, asLocalhost: false }, // using asLocalhost as this gateway is using a fabric network deployed locally
};

await gateway.connect(connectionProfile, gatewayOpts);

const network = await gateway.getNetwork('mychannel');

const contract = network.getContract('marbles');
let result = await contract.evaluateTransaction('readMarble', 'marble2');
console.log(`*** Result: ${prettyJSONString(result.toString())}`);
} catch (error) {
console.error(`******** FAILED to run the application: ${error}`);
}
}

main();

上述为app.ts,此app.ts会读取connection.json档,此档用于定义peer url以及peer的tls凭证路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
{
"name": "Network",
"version": "1.1",
"channels": {
"mychannel": {
"peers": [
"peer0-org1",
"peer0-org2"
]
}
},
"organizations": {
"Org1": {
"mspid": "org1MSP",
"peers": [
"peer0-org1"
]
},
"Org2": {
"mspid": "org2MSP",
"peers": [
"peer0-org2"
]
}
},
"peers": {
"peer0-org1": {
"url": "grpcs://peer0-org1:7051",
"grpcOptions": {
"ssl-target-name-override": "peer0-org1"
},
"tlsCACerts": {
"path": "crypto-config\peerOrganizations\org1\tlsca\tlsca.org1-cert.pem"
}
},
"peer0-org2": {
"url": "grpcs://peer0-org2:7051",
"grpcOptions": {
"ssl-target-name-override": "peer0-org2"
},
"tlsCACerts": {
"path": "crypto-config\peerOrganizations\org2\tlsca\tlsca.org2-cert.pem"
}
}
}
}

  • application连到peer的时候必须要有tls 凭证去验证peer service是否安全及正确。
  • 当交易的时候需要提供该使用者(user or admin)的交易凭证(x509 cert +private key)

路径对应如下:
tls CA Cert: crypto-config\peerOrganizations\org1\tlsca\tlsca.org1-cert.pem
X509 certificate: crypto-config\peerOrganizations\org1\users\Admin@org1\msp\signcerts\Admin@org1-cert.pem
X509 privateKey: crypto-config\peerOrganizations\org1\users\Admin@org1\msp\keystore\priv_sk
[color=#d85887]

成功收到资料如图


附录、参考连结

  1. Hyperledger 参考链接
  1. Kubenetes 参考链接
  1. NFS 架构参考链接
  1. 部署 fabric 和 CA 与 Kubenetes 之上

    Deploy Hyperledger Fabric network on Kubernetes

EYBlockchain/starlight: solidity –> zApp transpiler (github.com)

​ zApps 是零知识应用程序。 它们就像 dApp(去中心化应用程序),但具有隐私性。 zApp 编写起来很棘手,但 Solidity 合约编写起来很可爱。 那么为什么不尝试用 Solidity 编写一个 zApp 呢?

starlight帮助开发人员做到这一点:

  • 编写 Solidity 合约
  • 在合约中添加一些新的隐私装饰器(以获得“Zolidity”合约)
  • 运行 zappify
  • 作为回报,获得一个完全可用的 zApp
  • Solidity 合约 –> Zolidity 合约 –> zappify –> zApp

此转译器的主要目标是使开发人员能够快速为 zApps 起草框架。

​ 造成网页近重复的可能原因主要包括:镜像网站;内容复制;嵌入广告;计数改变;少量修改
​ 传统比较两个文本相似性的方法,大多是将文本分词之后,转化为特征向量距离的度量,比如常见的欧氏距离、海明距离或者余弦角度等等。两两比较固然能很好地适应,但这种方法的一个最大的缺点就是,无法将其扩展到海量数据。例如,试想像Google那种收录了数以几十亿互联网信息的大型搜索引擎,每天都会通过爬虫的方式为自己的索引库新增的数百万网页,如果待收录每一条数据都去和网页库里面的每条记录算一下余弦角度,其计算量是相当恐怖的。
​ 考虑采用为每一个web文档通过hash的方式生成一个指纹(fingerprint)。但是,传统的加密式hash,比如md5,其设计的目的是为了让整个分布尽可能地均匀,输入内容哪怕只有轻微变化,hash就会发生很大地变化。我们理想当中的哈希函数,需要对几乎相同的输入内容,产生相同或者相近的hashcode,换句话说,hashcode的相似程度要能直接反映输入内容的相似程度。很明显,前面所说的md5等传统hash无法满足我们的需求。
simhash是locality sensitive hash(局部敏感哈希)的一种,它产生的hash签名在一定程度上可以表征原内容的相似度。最早由Moses Charikar在《similarity estimation techniques from rounding algorithms》一文中提出。Google就是基于此算法实现网页文件查重的。假设有以下三段文本:
the cat sat on the mat
the cat sat on a mat
we all scream for ice cream
使用传统hash可能会产生如下的结果:

1
2
3
4
5
6
7
8
9
irb(main):006:0> p1 = ‘the cat sat on the mat’
irb(main):005:0> p2 = ‘the cat sat on a mat’
irb(main):007:0> p3 = ‘we all scream for ice cream’
irb(main):007:0> p1.hash
=> 415542861
irb(main):007:0> p2.hash
=> 668720516
irb(main):007:0> p3.hash
=> 767429688

使用simhash会应该产生类似如下的结果:

1
2
3
4
5
6
7
8
9
irb(main):003:0> p1.simhash
=> 851459198
00110010110000000011110001111110
irb(main):004:0> p2.simhash
=> 847263864
00110010100000000011100001111000
irb(main):002:0> p3.simhash
=> 984968088
00111010101101010110101110011000

​ 海明距离的定义,为两个二进制串中不同位的数量。上述三个文本的simhash结果,其两两之间的海明距离为(p1,p2)=4,(p1,p3)=16以及(p2,p3)=12。事实上,这正好符合文本之间的相似度,p1和p2间的相似度要远大于与p3的。距离越小,则相似度越大。一般大文本去重,大小<=3的即可判断为重复。
​ 如何实现这种hash算法呢?simhash算法分为5个步骤:1、分词、2、hash、3、加权、4、合并、5、降维

  1. 分词:

选择适合自己的分词库进行分词即可。
如“欢迎来到随迹”->(分词后)“欢迎”、“来到”、“随迹”

  1. hash:

对每个词计算其hash值,hash值为二进制数01组成的n-bit签名。
设“欢迎“(100101)、“来到”(101011)、“随迹”(101011)

  1. 加权:

对于给定的文本,权值即为分词后对应词出现的数量。给所有特征向量进行加权,即W = Hash * weight;这里我们假设三个词权值分别为4、5、9;
根据计算规则遇到1则hash值和权值正相乘,遇到0则hash值和权值负相乘
例如给“欢迎”的hash值“100101”加权得 到:W(欢迎) = 1001014 = 4 -4 -4 4 -4 4,给“来到”的hash值“101011”加权得到:W(来到)=1010115 = 5 -5 5 -5 5 5,剩下的按此规则计算

  1. 合并:

将上述各个特征向量的加权结果累加,变成只有一个序列串。拿前两个特征向量举例,例如“欢迎”的“4 -4 -4 4 -4 4”和“来到”的“5 -5 5 -5 5 5”进行累加,得到“4+5 -4+-5 -4+5 4+-5 -4+5 4+5”,得到“9 -9 1 -1 1”。

  1. 降维:

对于n-bit签名的累加结果,如果大于0则置1,否则置0,从而得到该语句的simhash值,最后我们便可以根据不同语句simhash的海 明距离来判断它们的相似度。例如把上面计算出来的“9 -9 1 -1 1 9”降维(某位大于0记为1,小于0记为0),得到的01串为:“1 0 1 0 1 1”,从而形成它们的simhash签名。

参考链接 ==> https://docs.circom.io/

安装

1
2
npm install -g circom
npm install -g snarkjs

构建电路

1.创建circuit.circom文件

1
2
3
4
5
6
7
8
template Multiplier() {
signal private input a;
signal private input b;
signal output c;
c <== a*b;
}

component main = Multiplier();

​ 此电路的目的是让我们向某人证明我们能够因式分解整数c。具体来说,使用此电路,我们将能够证明我们知道两个数字(a和b)相乘得到c,而不会显示a和b。这个电路有2个 private 输入信号,名为 ab ,还有一个输出 c。输入和输出使用<==运算符进行关联。在circom中,<==运算符做两件事。 首先是连接信号。 第二个是施加约束。在本例中,我们使用<==c连接到ab,同时将c约束为a * b的值,即电路做的事情是让强制信号 ca*b 的值。在声明 Multiplier 模板之后, 我们使用名为main的组件实例化它。注意:编译电路时,必须始终有一个名为main的组件。

2.编译电路

1
circom circuit.circom --r1cs --wasm --sym
  • --r1cs: 生成 circuit.r1cs ( r1cs 电路的二进制格式约束系统)
  • --wasm: 生成 circuit.wasm ( wasm 代码用来生成见证 witness 稍后再介绍)
  • --sym: 生成 circuit.sym (以注释方式调试和打印约束系统所需的符号文件)

r1cs是将代数电路转换为zk-snark的第一步。

构建零知识证明

​ 使用snarkjs 来生成和验证 zk-SNARK 证明。我们将证明能够分解数字33。也就是说,将证明我们知道两个整数a和b,以便将它们相乘时得出33。

1.查看电路有关的信息

​ 使用 snarkJS,您可以获得电路的一般统计数据并打印约束。

1
2
3
4
5
6
7
8
➜  circom snarkjs info -r circuit.r1cs 
[INFO] snarkJS: Curve: bn-128
[INFO] snarkJS: # of Wires: 4
[INFO] snarkJS: # of Constraints: 1
[INFO] snarkJS: # of Private Inputs: 2
[INFO] snarkJS: # of Public Inputs: 0
[INFO] snarkJS: # of Labels: 4
[INFO] snarkJS: # of Outputs: 1
1
2
➜  circom snarkjs print -r circuit.r1cs -s circuit.sym
[INFO] snarkJS: [ 21888242871839275222246405745257275088548364400416034343698204186575808495616main.a ] * [ main.b ] - [ 21888242871839275222246405745257275088548364400416034343698204186575808495616main.c ] = 0

2.setup

2.1 设置的生成分为两个阶段:第一步是创建一些称为tau的幂的值。此处,我们将保留一个“ powers of tau”文件。 运行以下命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
➜  circom snarkjs powersoftau new bn128 12 pot12_0000.ptau -v
[DEBUG] snarkJS: Calculating First Challenge Hash
[DEBUG] snarkJS: Calculate Initial Hash: tauG1
[DEBUG] snarkJS: Calculate Initial Hash: tauG2
[DEBUG] snarkJS: Calculate Initial Hash: alphaTauG1
[DEBUG] snarkJS: Calculate Initial Hash: betaTauG1
[DEBUG] snarkJS: Blank Contribution Hash:
786a02f7 42015903 c6c6fd85 2552d272
912f4740 e1584761 8a86e217 f71f5419
d25e1031 afee5853 13896444 934eb04b
903a685b 1448b755 d56f701a fe9be2ce
[INFO] snarkJS: First Contribution Hash:
9e63a5f6 2b96538d aaed2372 481920d1
a40b9195 9ea38ef9 f5f6a303 3b886516
0710d067 c09d0961 5f928ea5 17bcdf49
ad75abd2 c8340b40 0e3b18e9 68b4ffef
➜ circom snarkjs powersoftau contribute pot12_0000.ptau pot12_0001.ptau --name="First contribution" -v
Enter a random text. (Entropy): zhuangweiming
[DEBUG] snarkJS: Calculating First Challenge Hash
[DEBUG] snarkJS: Calculate Initial Hash: tauG1
[DEBUG] snarkJS: Calculate Initial Hash: tauG2
[DEBUG] snarkJS: Calculate Initial Hash: alphaTauG1
[DEBUG] snarkJS: Calculate Initial Hash: betaTauG1
[DEBUG] snarkJS: processing: tauG1: 0/8191
[DEBUG] snarkJS: processing: tauG2: 0/4096
[DEBUG] snarkJS: processing: alphaTauG1: 0/4096
[DEBUG] snarkJS: processing: betaTauG1: 0/4096
[DEBUG] snarkJS: processing: betaTauG2: 0/1
[INFO] snarkJS: Contribution Response Hash imported:
e15fa0f4 126ae14d 0e724ffa ee1adaba
1f9f44fb a963db61 e0eda605 4a172efa
124d177b 3a565656 1f1a3a23 6dea3366
3b21ec32 8a26d7b2 ac98ff89 2b1962ae
[INFO] snarkJS: Next Challenge Hash:
b77a3861 ad4a4cbb abe3e870 69787d8c
85ab1372 c15bff14 352c15fb 3116c3c8
81791ae6 3258fbcb 898cad52 c9863e00
c336b311 f8bfb38e 3cefc2cd 917c86eb

​ 系统将提示您输入一些随机文本,这些文本将用作熵的来源。输入文本后,命令将输出一个名为pot12_001.ptau的脚本。设置的第一部分是通用的,对任何电路都有用,因此您可以将其保存以在以后的项目中重复使用。

​ 要获得zk-SNARK证明的证明和验证密钥,必须生成设置。此步骤需要生成一些需要消除的随机值。 消除过程至关重要:如果暴露了这些价值,则整个方案的安全性将受到损害。想要构建设置,我们使用多方计算(MPC)仪式,该仪式允许多个独立方共同构建参数。使用MPC,一个参与者删除其贡献的秘密副本就足够了,以确保整个方案的安全。
​ 可信设置的构建分为两个阶段:对任何电路均有效的常规MPC仪式(称为powers of tau ceremony),以及为每个特定电路构造的第二阶段(阶段2)。任何人都可以通过其随机性参与MPC仪式,通常,在获取最终参数之前,将应用一个随机信标

2.2 下一步,称为设置的阶段2,运行命令如下:

1
snarkjs powersoftau prepare phase2 pot12_0001.ptau pot12_final.ptau -v

​ 以上命令运行完成后,pot12_final.ptau文件被生成。该计算在笔记本电脑上大概花费了3分钟时间。

​ 第二阶段的产生与我们使用tau的能力相似。将生成一个.zkey文件,其中将包含证明和验证密钥以及所有第二阶段贡献。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Start a new zkey and make a contribution
➜ circom snarkjs zkey new circuit.r1cs pot12_final.ptau circuit_0000.zkey
[INFO] snarkJS: Reading r1cs
[INFO] snarkJS: Reading tauG1
[INFO] snarkJS: Reading tauG2
[INFO] snarkJS: Reading alphatauG1
[INFO] snarkJS: Reading betatauG1
[INFO] snarkJS: Circuit hash:
965f5c51 98906eb7 8fc597d1 d7b31bdf
ad7f0491 d1cc081d 8d236685 489f05be
b621e87a 1ba57a28 25071ac3 a69a4df1
1ec4b2c6 aa36b8f2 94e37d17 270ce6bf

➜ circom snarkjs zkey contribute circuit_0000.zkey circuit_final.zkey --name="1st Contributor Name" -v
Enter a random text. (Entropy): zhuangweiming
[DEBUG] snarkJS: Applying key: L Section: 0/2
[DEBUG] snarkJS: Applying key: H Section: 0/4
[INFO] snarkJS: Circuit Hash:
965f5c51 98906eb7 8fc597d1 d7b31bdf
ad7f0491 d1cc081d 8d236685 489f05be
b621e87a 1ba57a28 25071ac3 a69a4df1
1ec4b2c6 aa36b8f2 94e37d17 270ce6bf
[INFO] snarkJS: Contribution Hash:
7987d679 5c914cb3 4138e57a ceee7ae4
8974b323 585bb48f 056fdd8f ea858cd3
a625d63e d8394b9c 974912e4 801aff06
0064f25a 677e727a a289252d 397b8d04

​ 和上述步骤一样,系统将提示您输入一些随机文本以提供熵的来源。输出将是一个名为circuit_final.zkey的文件,我们将使用该文件导出验证密钥(verification key)。

​ 现在,来自circuit_final.zkey的验证密钥导出到文件Verification_key.json中。verification key:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
{
"protocol": "groth16",
"curve": "bn128",
"nPublic": 1,
"vk_alpha_1": [
"15175462370442750548879662426586324604498073737772476635702014058846911119264",
"11126336670194938919608091368599342163660484801811140235738809534621680589277",
"1"
],
"vk_beta_2": [
[
"5251596196706483766793357074374737595900200707659808196530067991530054260554",
"2264756861473257353315497004278362201201416308719887395373581359048933031269"
],[
"10552939216865106848464515813683167112323139053174380454808956637789377680967",
"2701413932452289802745906093844601151278714195849736706988417564960748034273"
],[
"1",
"0"
]
],
"vk_gamma_2": [
[
"10857046999023057135944570762232829481370756359578518086990519993285655852781",
"11559732032986387107991004021392285783925812861821192530917403151452391805634"
],[
"8495653923123431417604973247489272438418190587263600148770280649306958101930",
"4082367875863433681332203403145435568316851327593401208105741076214120093531"
],[
"1",
"0"
]
],
"vk_delta_2": [
[
"8590425680399084984710617722188581478119753421410764960451677916514215428082",
"15713152249579034550437632242139020796726126371657275384984401462748083625785"
],[
"4862154669589263828958978916321240414342527577427918353789686327852433696883",
"9569117504828540945921139710836305890297695048604628362102771433345477012098"
],[
"1",
"0"
]
],
"vk_alphabeta_12": [
[
[
"1121360319902761693820363426996646168569211552394580075970634177898785008311",
"14249756278025620543130105348741511241747839379662316431830590326729096142975"
],[
"16423439174084021024919681708878615489972548829583206833909869670703901853110",
"9329143387573491748526724433415355164900218750592620992570237269470665264202"
],[
"19623272010929083665872244835381945771016147820964222485760645671773298080071",
"9896773903597386179712166971836923631098313509393860803813715414524549373292"
]
],[
[
"18313799470766842592075662760908626166323669476646669856687182611043590126171",
"7532895603020793142335129648471958187885524608608576812436682807569433790546"
],[
"20059629119556696413695556916087392540474931269939016250934117162067722623426",
"11285185652573117971807135387812020477130047554451136821582438888864777467010"
],[
"18429091017205122590573696082242994893262627891230351210235517202888391625680",
"7734180768772801433386131669021273948517355047659921136453462913759461451288"
]
]
],
"IC": [
[
"17305572679177982597803616153716539127293296363987765110136569516232291230411",
"7425872175786571904106950706682618227429476545503095908552111445555350791878",
"1"
],[
"16125521217907931491019162042616560858833563034453402251972608389495277275869",
"20962607216912168718282504555696865046978428189883507401556888923455626427056",
"1"
]
]
}

​ 可以验证.ptau或.zkey文件的计算是否正确:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
➜  circom snarkjs powersoftau verify pot12_final.ptau
[INFO] snarkJS: Powers Of tau file OK!
[INFO] snarkJS: Next challenge hash:
b77a3861 ad4a4cbb abe3e870 69787d8c
85ab1372 c15bff14 352c15fb 3116c3c8
81791ae6 3258fbcb 898cad52 c9863e00
c336b311 f8bfb38e 3cefc2cd 917c86eb
[INFO] snarkJS: -----------------------------------------------------
[INFO] snarkJS: Contribution #1: First contribution
[INFO] snarkJS: Next Challenge:
b77a3861 ad4a4cbb abe3e870 69787d8c
85ab1372 c15bff14 352c15fb 3116c3c8
81791ae6 3258fbcb 898cad52 c9863e00
c336b311 f8bfb38e 3cefc2cd 917c86eb
[INFO] snarkJS: Response Hash:
e15fa0f4 126ae14d 0e724ffa ee1adaba
1f9f44fb a963db61 e0eda605 4a172efa
124d177b 3a565656 1f1a3a23 6dea3366
3b21ec32 8a26d7b2 ac98ff89 2b1962ae
[INFO] snarkJS: Response Hash:
9e63a5f6 2b96538d aaed2372 481920d1
a40b9195 9ea38ef9 f5f6a303 3b886516
0710d067 c09d0961 5f928ea5 17bcdf49
ad75abd2 c8340b40 0e3b18e9 68b4ffef
[INFO] snarkJS: -----------------------------------------------------
[INFO] snarkJS: Powers of Tau Ok!
➜ circom snarkjs zkey verify circuit.r1cs pot12_final.ptau circuit_final.zkey
[INFO] snarkJS: Reading r1cs
[INFO] snarkJS: Reading tauG1
[INFO] snarkJS: Reading tauG2
[INFO] snarkJS: Reading alphatauG1
[INFO] snarkJS: Reading betatauG1
[INFO] snarkJS: Circuit hash:
965f5c51 98906eb7 8fc597d1 d7b31bdf
ad7f0491 d1cc081d 8d236685 489f05be
b621e87a 1ba57a28 25071ac3 a69a4df1
1ec4b2c6 aa36b8f2 94e37d17 270ce6bf
[INFO] snarkJS: Circuit Hash:
965f5c51 98906eb7 8fc597d1 d7b31bdf
ad7f0491 d1cc081d 8d236685 489f05be
b621e87a 1ba57a28 25071ac3 a69a4df1
1ec4b2c6 aa36b8f2 94e37d17 270ce6bf
[INFO] snarkJS: -------------------------
[INFO] snarkJS: contribution #1 1st Contributor Name:
7987d679 5c914cb3 4138e57a ceee7ae4
8974b323 585bb48f 056fdd8f ea858cd3
a625d63e d8394b9c 974912e4 801aff06
0064f25a 677e727a a289252d 397b8d04
[INFO] snarkJS: -------------------------
[INFO] snarkJS: ZKey Ok!

3.计算witness

​ 在创建任何证明之前,我们需要计算与电路的所有约束匹配的电路的所有信号。为此,我们将使用由circom生成的Wasm模块来帮助完成此工作。我们只需要提供一个包含输入的文件,模块将执行电路并计算所有中间信号和输出。输入,中间信号和输出的集合称为见证人(witness)。

​ 本次的范例中,我们想证明我们能够分解数字33。因此,指定a=3, b=11。创建一个文件,命名为 input.json

1
{"a": 3, "b": 11}

​ 下面开始就算 witness,生成 witness 文件, 命令如下:

1
snarkjs wtns calculate circuit.wasm input.json witness.wtns

4.创建证明

​ 这个命令默认会使用 prooving_key.jsonwitness.json 文件去生成 proof.jsonpublic.json

1
snarkjs groth16 prove circuit_final.zkey witness.wtns proof.json public.json

proof.json 文件包含了实际的证明。而 public.json 文件将仅包含公共的输入(当前的例子没有)和输出(当前的例子是 33)。

5.验证证明

1
2
➜  circom snarkjs groth16 verify verification_key.json public.json proof.json
[INFO] snarkJS: OK!

​ 此命令使用我们之前导出的verify_key.json文件,proof.json和public.json来检查证明是否有效。如果证明有效,则命令输出OK。一个有效的证明不仅证明我们知道满足电路要求的一组信号,而且证明我们使用的公共输入和输出与public.json文件中描述的匹配。

实际上,在此阶段,将把proof.jsonpublic.json文件都交给验证者。但是,出于教程的目的,我们还将扮演验证者的角色。借助证明以及公共输入和输出,我们现在可以向验证者证明我们知道一个或多个满足电路约束的私有信号,而无需透露有关那些私有信号的任何信息。从验证者的角度来看,她可以验证我们是否知道见证中包含的一组私有信号,而无需访问它。这是zk-proof背后魔术的核心!

6.智能合约验证证明

1
snarkjs zkey export solidityverifier circuit_final.zkey verifier.sol

​ 上述命令使用验证密钥circuit_final.zkey并在名为verifier.sol的文件中输出合约代码。可以看到该代码包含两个协定:Pairing和Verifier。只需要部署Verifier合约。

​ 生成智能合约调用的参数,命令如下:

1
2
➜  circom snarkjs zkesc public.json proof.json
["0x29b83d63472e4f47b092fd4ced43a566697f0575186964c7cc3deec2c5c1b0bd", "0x2eb116882d2bacdbeece8a2adf2ab6d0f4420b60e82b4b66ddb18cc26ab2bae2"],[["0x170d208ad3a8bf4358c207706bce9e87cbad11037f642d1ab06f65a5ebf10eb4", "0x0dc07edd498cc16c5174c2a5a2aeed86c2f5689ed25486e8981768d7c7ceae13"],["0x1c52e6144e980ae72e5c5679c979c70e0da4a519fc087dda3b96bbea09179b20", "0x2849fdea744fa924ae19eb15917dac4543c0fa3b888293dfcd365c6251f25223"]],["0x01918c12515eb00b823f0e2a63ef53e15792b4b4fd331b595ffc670e929d6b99", "0x2246e9bb88119c6f8f1c04735beeee038ddc6e66572cb44f1c926575db80be9a"],["0x0000000000000000000000000000000000000000000000000000000000000021"]

​ 验证者具有一个称为verifyProof的函数,当且仅当证明和输入有效时,该函数才返回TRUE

​ 如果仅更改参数中的任何位,则可以检查结果返回 false 。

漏洞修复

​ 我们已经表明可以生成证明我们知道两个因子a和b使得a*b=33的证明。但这不能证明我们知道如何分解数字33,因为我们可以选择a = 1和b = 33,通常对于任何整数n,选择输入a=1,b=n。在此,我们需要修改电路,以便不可能将数字1分配给任何输入。为此,我们将使用0是唯一没有反数的事实。

​ 这里的技巧是使用0不可求倒数的属性,约束不接受 1 作为任何一个输入,即(a-1) 不可求倒数的方式来约束电路。

​ 如果 a 是 1 则 (a-1)*inv = 1 是不可能成立的,通过 1/(a-1) 来计算 inv 。

修正电路:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template Multiplier() {
signal private input a;
signal private input b;
signal output c;
signal inva;
signal invb;

inva <-- 1/(a-1);
(a-1)*inva === 1;
invb <-- 1/(b-1);
(b-1)*invb === 1;

c <== a*b;
}

component main = Multiplier();

​ 您可能已经注意到,我们引入了两个新的运算符 : <--===<----> 操作符运算符只为信号分配一个值,而不创建任何约束。=== 操作符添加约束而不分配值。如前所述,<== 为信号分配一个值并添加一个约束。这意味着它只是 <--=== 的组合。但是,由于并非总是希望在同一步骤中同时完成这两个步骤,因此circom 的灵活性使我们可以将这一步分为两步。

​ 事实证明,电路仍然存在一个细微的问题:由于运算是在有限域(Z_r)上进行的,因此我们需要确保乘法不会溢出。

最后

circomlib 写好了一些基本的电路,如:binaritzations、comparators, eddsa, hashes, merkle trees 等等。