# Paillier 开发手册 本文分为四部分: 1. 前置依赖:paillier 算法依赖库 GMP 和 M4的安装。 2. 部署指南:介绍了如何部署一条具有 paillier 半同态运算能力链。 3. 方案接口、工具、合约SDK介绍:对 ChainMaker 的 cmc 工具、合约 SDK 和 common 库提供的算法的介绍。 4. 用例:介绍了如何使用 ChainMaker Paillier 功能,从 Paillier 公私钥生成、编写智能合约到使用 SDK(本文使用 go 的 SDK) 进行数据的加密上链、链上同态运算和获取运算结果解密。 ## 前置依赖 paillier 加密算法依赖 GMP 库,GMP 依赖 M4 库,使用 paillier 相关功能必须提前安装好 M4 和 GMP 库。 | 依赖库名称 | 版本 | 描述 | 是否必须 | | --- | --- | --- | --- | | M4 | v1.4.18 | 广泛应用与unix上的宏处理器,GMP依赖M4库 | 是 | | GMP | v6.2.1 | 多精度计算库,paillier算法依赖GMP库 | 是 | > 适用于 Linux。 1、安装 M4 ```shell # 下载安装包 wget https://mirrors.kernel.org/gnu/m4/m4-1.4.18.tar.gz # 解压并安装 tar -xzvf m4-1.4.18.tar.gz cd m4-1.4.18 ./configure --prefix=/usr/local make && make install # 检查安装是否成功 check: m4 --version ``` 2、安装 GMP ```shell # 下载安装包 wget https://gmplib.org/download/gmp/gmp-6.2.1.tar.bz2 # 解压并安装 # .bz2 压缩包需要使用 bzip2,可通过包管理器安装例如 yum install bzip2、apt-get install bzip2、pacman -S bzip2 tar -jxvf gmp-6.2.1.tar.bz2 cd gmp-6.2.1 ./configure --prefix=/usr/local make && make check && make install ``` ## 部署指南 > 注意:当前版本(v1.2.0)按照快速入门进行部署之后,节点是不具备 paillier 能力的。使用 chainmaker 的 paillier 半同态加密能力需要按照如下步骤做相应调整。 链的部署启动有两种方式: 1、 脚本部署 1. 参考[【快速入门】](../tutorial/快速入门.md)进行到源码下载步骤,下载完成后需要做出一些调整,再进行快速入门后续步骤。 2. 进入到 clone 的 chainmaker-go 项目中,修改项目根目录下的 Makefile 文件内部分内容: ```makefile chainmaker: @cd main && go build -tags=paillier -mod=mod -o ../bin/chainmaker package: @cd main && GOPATH=${GOPATH} go build -tags=paillier -mod=mod -o ../bin/chainmaker ... compile: @cd main && go build -tags=paillier -mod=mod -o ../bin/chainmaker cmc: @cd tools/cmc && GOPATH=${GOPATH} go build -tags=paillier -mod=mod -o ../../bin/cmc ``` 3. 进行快速入门后续步骤,完成部署。 2、Docker 部署 1. 参考[【快速入门】](../tutorial/快速入门.md)进行到源码下载步骤,下载完成后需要做出一些调整,再进行快速入门后续步骤。 2. 与上述脚本部署的步骤二进行相同的修改。 3. 修改 chainmaker-go/DOCKER 目录下的 Dockerfile 文件,使用下面的 dockerfile 替换掉*chainmaker-go/DOCKER/Dockerfile*文件的内容: ```dockerfile FROM golang:1.16.2 as builder RUN sed -i 's#http://deb.debian.org#https://mirrors.163.com#g' \ /etc/apt/sources.list \ && apt-get update \ && apt-get install --assume-yes apt-utils bzip2 ENV GOPROXY=https://goproxy.cn,direct COPY . /chainmaker-go RUN mkdir /chainmaker-go/libs \ && cd /chainmaker-go/libs \ && wget https://mirrors.kernel.org/gnu/m4/m4-1.4.18.tar.gz \ && wget https://gmplib.org/download/gmp/gmp-6.2.1.tar.bz2 RUN cd /chainmaker-go/libs \ && tar -xzvf m4-1.4.18.tar.gz \ && cd m4-1.4.18 \ && sed -i 's/IO_ftrylockfile/IO_EOF_SEEN/' lib/*.c \ && echo "#define _IO_IN_BACKUP 0x100" >> lib/stdio-impl.h \ && ./configure --prefix=/usr/local \ && make \ && make install RUN cd /chainmaker-go/libs \ && tar -jxvf gmp-6.2.1.tar.bz2 \ && cd gmp-6.2.1 \ && ./configure --prefix=/usr/local \ && make \ && make check \ && make install RUN cd /chainmaker-go && make chainmaker # the second stage FROM ubuntu:20.04 RUN rm /bin/sh && ln -s /bin/bash /bin/sh RUN sed -i 's#http://deb.debian.org#https://mirrors.163.com#g' \ /etc/apt/sources.list \ && apt-get update \ && apt-get install -y vim net-tools tree gcc g++ make ENV TZ "Asia/Shanghai" RUN DEBIAN_FRONTEND=noninteractive apt-get install -y tzdata \ && echo $TZ > /etc/timezone \ && ln -fs /usr/share/zoneinfo/$TZ /etc/localtime \ && dpkg-reconfigure tzdata -f noninteractive COPY --from=builder /chainmaker-go/libs/m4-1.4.18 /chainmaker-go/libs/m4-1.4.18 COPY --from=builder /chainmaker-go/libs/gmp-6.2.1 /chainmaker-go/libs/gmp-6.2.1 RUN cd /chainmaker-go/libs/m4-1.4.18 \ && ./configure --prefix=/usr/local \ && make && make install RUN cd /chainmaker-go/libs/gmp-6.2.1 \ && ./configure --prefix=/usr/local \ && make && make check && make install COPY --from=builder /chainmaker-go/main/libwasmer_runtime_c_api.so /usr/lib/libwasmer.so COPY --from=builder /chainmaker-go/main/prebuilt/linux/wxdec /usr/bin COPY --from=builder /chainmaker-go/bin/chainmaker /chainmaker-go/bin/ COPY --from=builder /chainmaker-go/config /chainmaker-go/config/ RUN mkdir -p /chainmaker-go/log/ RUN chmod 755 /usr/bin/wxdec WORKDIR /chainmaker-go/bin ``` 4. 进行快速入门后续步骤,完成部署。 部署成功后就可以写合约来使用 paillier 了。接下来让我们看一下如何使用 paillier 相关工具及 SDK 进行开发。 ## 接口、工具、合约SDK介绍 `common`提供 paillier 半同态加密的基础能力。 `cmc`工具提供了生成公私钥、加解密的能力,方便用户快速体验同态加密功能。 `合约SDK`提供了链上同态运算的能力,目前支持同态运算的合约 SDK 有:Go、Rust。 ### CMC 工具子命令 paillier 介绍 > CMC 默认是没有 paillier 相关功能的,需要自己安装好前置依赖库之后,在*chainmaker-go/tools/cmc*目录下进行编译: > `go build -tags=paillier -mod=mod` 简介 CMC 中的 paillier 命令是用于辅助paillier算法的使用,生成公钥和私钥,并且根据参数保存到指定位置。 使用`./cmc paillier -h`获取使用帮助: ```shell ./cmc paillier -h ChainMaker paillier command Usage: cmc paillier [command] Available Commands: genKey Generate paillier's private, public Keys, and storage. Flags: -h, --help help for paillier Use "cmc paillier [command] --help" for more information about a command. ``` genKey简介 `genKey`用于生成 paillier 算法的公私钥,并根据参数保存到指定位置,查看命令详情: ```shell ./cmc paillier genKey -h generates paillier private public key Usage: cmc paillier genKey [flags] Flags: -h, --help help for genKey --name string --path string the result storage file path, and the file name is the id ``` genKey参数详解: ``` -h, --help:获取使用帮助 --name:用于保存公私钥的文件名,公钥和私钥文件名相同,后缀分别为`.prv`、`.pub` --path:存储路径 ``` ### common 算法部分介绍 #### Pub接口 Pub 接口由 Pubkey 实现,包含了基本的序列化、反序列化、加密和同态运算方法,这些方法在执行时只需要公钥即可。 **Encrypt**:加密一个big.Int类型的数得到对应的密文。 Arguments: - plaintext:用于加密的明文 return: - Ct:加密得到的密文 - error:可能出现的错误 ```go Encrypt(plaintext *big.Int) (Ct, error) ``` 同态运算方法,同态运算一般在链上进行,所以一般不会使用到这几个方法,而是使用合约SDK提供的同态运算方法,这里不再详细描述。 ```go // 两个密文相加,返回结果密文 AddCiphertext(ciphertext1, ciphertext2 Ct) (Ct, error) // 密文加明文,返回结果密文 AddPlaintext(ciphertext Ct, plaintext *big.Int) (Ct, error) // 密文减密文,返回结果密文 SubCiphertext(ciphertext1, ciphertext2 Ct) (Ct, error) // 密文减明文,返回结果密文 SubPlaintext(ciphertext Ct, plaintext *big.Int) (Ct, error) // 密文乘以明文,返回结果密文 NumMul(ciphertext Ct, plaintext *big.Int) (Ct, error) ``` #### Prv接口 Prv 接口由 Prvkey 实现,包含了 Pub 所有的方法以及获取公钥、解密方法。 **Decrypt**:解密密文得到对应的明文 Arguments: - ciphertext:用于解密的密文 return: - *big.Int:解密得到的明文 - error:可能出现的错误 ```go Decrypt(ciphertext Ct) (*big.Int, error) ``` **GetPubKey**:根据私钥获取公钥 return: - Pub:公钥 - error:可能出现的错误 ```go GetPubKey() (Pub, error) ``` #### KeyGenerator KeyGenerator 接口包含了初始化公私钥的方法。 return: - Prv:生成的私钥(公钥可以从私钥中获取) - error:可能出现的错误 ```go GenKey() (Prv, error) ``` #### Provider接口 Provider 接口由 provider 实现,用于初始化 paillier 各接口的实现。 NewPubKey:返回实现了 Pub 接口的结构体实例 ```go NewPubKey() Pub ``` NewPrvKey:返回实现了 Prv 接口的结构体实例 ```go NewPrvKey() Prv ``` NewCiphertext:返回实现了 Pub 接口的结构体实例 ```go NewCiphertext() Ct ``` NewKeyGenerator:返回实现了 Pub 接口的结构体实例 ```go NewKeyGenerator() KeyGenerator ``` ### 智能合约SDK Go 和 Rust 合约SDK提供了进行同态运算的方法。 #### go PaillierContext 接口提供了同态运算方法 **AddCiphertext**:使用公钥进行密文加密文的同态运算 Arguments: - pubKey:公钥的字节数组 - ct1:密文字节数组 - ct1:密文字节数组 return: - []byte结果密文字节数组 - ResultCode:函数执行状态码 ~~~go AddCiphertext(pubKey []byte, ct1 []byte, ct2 []byte) ([]byte, ResultCode) ~~~ **AddPlaintext**:使用公钥进行密文加明文的同态运算 Argumengs: - pubKey:公钥字节数组 - ct:密文字节数组 - pt:明文,int64的字符串表示,超出int64链上执行将会报错 return: - []byte:结果密文字节数组 - ResultCode:函数执行状态码 ~~~go AddPlaintext(pubKey, ct []byte, pt string) ([]byte, ResultCode) ~~~ **SubCiphertext**:使用公钥进行密文减密文的同态运算 Arguments: - pubKey:公钥的字节数组 - ct1:密文字节数组 - ct1:密文字节数组 return: - []byte:结果密文字节数组 - ResultCode:函数执行状态码 ~~~go SubCiphertext(pubKey, ct1, ct2 []byte) ([]byte, ResultCode) ~~~ **SubPlaintext**:使用公钥进行密文减明文的同态运算 Argumengs: - pubKey:公钥字节数组 - ct:密文字节数组 - pt:明文,int64的字符串表示,超出int64链上执行将会报错 return: - []byte:结果密文字节数组 - ResultCode:函数执行状态码 ~~~go SubPlaintext(pubKey, ct []byte, pt string) ([]byte, ResultCode) ~~~ **NumMul**:使用公钥进行密文乘明文的同态运算 Argumengs: - pubKey:公钥字节数组 - ct:密文字节数组 - pt:明文,int64的字符串表示,超出int64链上执行将会报错 return: - []byte:结果密文字节数组 - ResultCode:函数执行状态码 ~~~go NumMul(pubKey, ct []byte, pt string) ([]byte, ResultCode) ~~~ #### rust rust合约与go合约相同,由trait `PaillierSimContext`提供了同态运算的方法: **add_ciphertext**:使用公钥计算两个密文的和 Argument: - pubKey:两个密文的公钥 - ciphertext1:密文 - ciphertext2:密文 return: - return1: 计算结果 - return2: 函数执行状态码,0:success, 1: failed ```rust fn add_ciphertext( &self, pubkey: Vec, ciphertext1: Vec, ciphertext2: Vec, ) -> Result, result_code>; ``` **add_plaintext**:使用公钥计算密文与明文的和 Argument: - pubKey:两个密文的公钥 - ciphertext:密文 - plaintext:明文,i64的字符串表示,超出i64链上执行将会报错 return: - return1: 计算结果 - return2: 函数执行状态码,0:success, 1: failed ```rust fn add_plaintext( &self, pubkey: Vec, ciphertext: Vec, plaintext: &str, ) -> Result, result_code>; ``` **sub_ciphertext**:使用公钥计算密文减去密文 Argument: - pubKey:两个密文的公钥 - ciphertext1:密文 - ciphertext2:密文 return: - return1: 计算结果 - return2: 函数执行状态码,0:success, 1: failed ```rust fn sub_ciphertext( &self, pubkey: Vec, ciphertext1: Vec, ciphertext2: Vec, ) -> Result, result_code>; ``` **sub_plaintext**:使用公钥计算密文减明文 Argument: - pubKey:两个密文的公钥 - ciphertext:密文 - plaintext:明文,i64的字符串表示,超出i64链上执行将会报错 return: - return1: 计算结果 - return2: 函数执行状态码,0:success, 1: failed ```rust fn sub_plaintext( &self, pubkey: Vec, ciphertext: Vec, plaintext: &str, ) -> Result, result_code>; ``` **num_mul**:使用公钥计算密文乘明文 Argument: - pubKey:两个密文的公钥 - ciphertext:密文 - plaintext:明文,i64的字符串表示,超出i64链上执行将会报错 return: - return1: 计算结果 - return2: 函数执行状态码,0:success, 1: failed ```rust fn num_mul( &self, pubkey: Vec, ciphertext: Vec, plaintext: &str, ) -> Result, result_code>; ``` ## 用例 ### 1. 使用cmc paillier生成并保存自己的公私钥 ```sh ./cmc paillier genKey --name=test1 --path=./paillier-key [paillier Private Key] storage file path: paillier-key/test1.prvKey [paillier Public Key] storage file path: paillier-key/test1.pubKey ``` 会在当前目录生成,paillier-key文件夹,来保存生成的公私钥文件,如下: ```shell tree ./paillier-key ./paillier-key ├── test1.prvKey └── test1.pubKey ``` ### 2. 编写智能合约 go: ```go package main import ( "encoding/base64" "strconv" ) // 安装合约时会执行此方法,必须 //export init_contract func initContract() { // 此处可写安装合约的初始化逻辑 } // 升级合约时会执行此方法,必须 //export upgrade func upgrade() { // 此处可写升级合约的逻辑 } //export paillier_test_set func paillier_test_set() { pubkeyBytes, _ := Arg("pubkey") handletype, _ := Arg("handletype") encodePara1, _ := Arg("para1") encodePara2, _ := Arg("para2") para1Bytes, _ := base64.StdEncoding.DecodeString(encodePara1) var result_code ResultCode var result_data []byte var result_data_str string test := NewPaillierContext() if handletype == "AddCiphertext" { para2Bytes, _ := base64.StdEncoding.DecodeString(encodePara2) result_data, result_code = test.AddCiphertext([]byte(pubkeyBytes), para1Bytes, para2Bytes) } else if handletype == "AddPlaintext" { result_data, result_code = test.AddPlaintext([]byte(pubkeyBytes), para1Bytes, encodePara2) } else if handletype == "SubCiphertext" { para2Bytes, _ := base64.StdEncoding.DecodeString(encodePara2) result_data, result_code = test.SubCiphertext([]byte(pubkeyBytes), para1Bytes, para2Bytes) } else if handletype == "SubPlaintext" { result_data, result_code = test.SubPlaintext([]byte(pubkeyBytes), para1Bytes, encodePara2) } else if handletype == "NumMul" { result_data, result_code = test.NumMul([]byte(pubkeyBytes), para1Bytes, encodePara2) } else { ErrorResult("finish paillier_test_set failure: error para: " + handletype) } if result_code != SUCCESS { ErrorResult("finish paillier_test_set failure: error result code: " + string(result_code)) } result_data_str = base64.StdEncoding.EncodeToString(result_data) result := PutState("paillier_test", handletype, result_data_str) if result_code == 0 { SuccessResult("finish paillier_test_set success") } else { ErrorResult("finish paillier_test_set failure") } } //export paillier_test_get func paillier_test_get() { handletype, _ := Arg("handletype") value, result := GetState("paillier_test", handletype) SuccessResult(value) } //export bulletproofs_test_set func bulletproofs_test_set() { LogMessage("[bulletproofs] ========================================start") LogMessage("[bulletproofs] bulletproofs_test_set") handleType, _ := Arg("handletype") param1, _ := Arg("para1") param2, _ := Arg("para2") param1Bytes, _ := base64.StdEncoding.DecodeString(param1) var result_code ResultCode var result_data []byte var result_data_str string bulletproofsContext := NewBulletproofsContext() switch handleType { case BulletproofsOpTypePedersenAddNum: result_data, result_code = bulletproofsContext.PedersenAddNum(param1Bytes, param2) case BulletproofsOpTypePedersenAddCommitment: param2Bytes, _ := base64.StdEncoding.DecodeString(param2) result_data, result_code = bulletproofsContext.PedersenAddCommitment(param1Bytes, param2Bytes) case BulletproofsOpTypePedersenSubNum: result_data, result_code = bulletproofsContext.PedersenSubNum(param1Bytes, param2) case BulletproofsOpTypePedersenSubCommitment: param2Bytes, _ := base64.StdEncoding.DecodeString(param2) result_data, result_code = bulletproofsContext.PedersenSubCommitment(param1Bytes, param2Bytes) case BulletproofsOpTypePedersenMulNum: result_data, result_code = bulletproofsContext.PedersenMulNum(param1Bytes, param2) case BulletproofsVerify: param2Bytes, _ := base64.StdEncoding.DecodeString(param2) result_data, result_code = bulletproofsContext.Verify(param1Bytes, param2Bytes) default: ErrorResult("bulletproofs_test_set failed, error: " + handleType) result_code = 1 } if result_code != SUCCESS { ErrorResult("bulletproofs_test_set failed, error: " + string(rune(result_code))) } result_data_str = base64.StdEncoding.EncodeToString(result_data) result := PutState("bulletproofs_test", handleType, result_data_str) if result_code == 0 { SuccessResult("bulletproofs_test_set success") } else { ErrorResult("bulletproofs_test_set failure") } } //export bulletproofs_test_get func bulletproofs_test_get() { handletype, _ := Arg("handletype") value, result := GetState("bulletproofs_test", handletype) if handletype == "BulletproofsVerify" { decodeValue, err := base64.StdEncoding.DecodeString(value) if err != nil { ErrorResult("base64.StdEncoding.DecodeString(value) failed") } LogMessage(handletype) SuccessResult(string(decodeValue)) } else { LogMessage(handletype) SuccessResult(value) } } func main() { } ``` 编译生成wasm文件: ```shell tinygo build -no-debug -opt=s -o counter-go.wasm -target wasm ``` rust: ```rust // 安装合约时会执行此方法,必须 #[no_mangle] pub extern "C" fn init_contract() { // 安装时的业务逻辑,可为空 sim_context::log("init_contract"); } // 升级合约时会执行此方法,必须 #[no_mangle] pub extern "C" fn upgrade() { // 升级时的业务逻辑,可为空 sim_context::log("upgrade success"); } #[no_mangle] pub extern "C" fn paillier_test_set() { sim_context::log("[paillier] ========================================start"); sim_context::log("[paillier] input func: paillier_test_set"); let ctx = sim_context::get_sim_context(); let pubkey = ctx.arg_default_blank("pubkey"); let handletype = ctx.arg_default_blank("handletype"); let para1 = ctx.arg_default_blank("para1"); let decode_para1 = decode(para1.as_bytes()).unwrap(); let para2 = ctx.arg_default_blank("para2"); let test = ctx.get_paillier_sim_context(); let r: Result, i32>; if handletype == "AddCiphertext" { let decode_para2 = decode(para2.as_bytes()).unwrap(); r = test.add_ciphertext(pubkey.into_bytes(), decode_para1, decode_para2); } else if handletype == "AddPlaintext" { r = test.add_plaintext(pubkey.into_bytes(), decode_para1, ¶2); } else if handletype == "SubCiphertext" { let decode_para2 = decode(para2.as_bytes()).unwrap(); r = test.sub_ciphertext(pubkey.into_bytes(), decode_para1, decode_para2); } else if handletype == "SubPlaintext" { r = test.sub_plaintext(pubkey.into_bytes(), decode_para1, ¶2); } else if handletype == "NumMul" { r = test.num_mul(pubkey.into_bytes(), decode_para1, ¶2); } else { ctx.error(&format!( "finish paillier_test_set failure: error para: {}", handletype )); return; } if r.is_err() { ctx.error("finish paillier_test_set failure"); return; } let data = r.unwrap(); let data_u8 = data.as_slice(); let data_str = encode(data_u8); let put_code = ctx.put_state("paillier_test", &handletype, data_str.as_bytes()); ctx.ok("finish paillier_test_set success".as_bytes()); } #[no_mangle] pub extern "C" fn paillier_test_get() { let ctx = sim_context::get_sim_context(); let handletype = ctx.arg_default_blank("handletype"); let r = ctx.get_state("paillier_test", &handletype); if r.is_err() { sim_context::log("[zitao] paillier_test_get error"); ctx.error("finish paillier_test_get failure"); return; } let data = r.unwrap(); let result = String::from_utf8(data); let result_str = result.unwrap(); ctx.ok(result_str.as_bytes()); } ``` 编译生成wasm字节码文件 ```shell make build ``` ### 3. 使用SDK编写测试用例 > SDK并未提供 paillier 相关接口,开发者需要直接调用common库中的 *chainmaker.org/chainmaker-sdk-go/common/crypto/paillier*包。 总测试函数: ```go const ( // go 合约 //paillierContractName = "paillier-rust-10001" //paillierByteCodePath = "./testdata/counter-go-demo/counter-go.wasm" //runtime = common.RuntimeType_GASM // rust 合约 paillierContractName = "paillier-rust-10001" paillierByteCodePath = "./testdata/counter-go-demo/chainmaker_contract.wasm" runtime = common.RuntimeType_WASMER paillierPubKeyFilePath = "./testdata/paillier-key/test1.pubKey" paillierPrvKeyFilePath = "./testdata/paillier-key/test1.prvKey" ) func TestPaillierContractCounterGo(t *testing.T) { client, err := createClientWithConfig() require.Nil(t, err) admin1, err := createAdmin(orgId1) require.Nil(t, err) admin2, err := createAdmin(orgId2) require.Nil(t, err) admin3, err := createAdmin(orgId3) require.Nil(t, err) admin4, err := createAdmin(orgId4) require.Nil(t, err) fmt.Println("======================================= 创建合约(异步)=======================================") testPaillierCreate(t, client, admin1, admin2, admin3, admin4, false) time.Sleep(5 * time.Second) fmt.Println("======================================= 调用合约运算(异步)=======================================") testPaillierOperation(t, client, "paillier_test_set", false) time.Sleep(5 * time.Second) fmt.Println("======================================= 查询结果并解密(异步)=======================================") testPaillierQueryResult(t, client, "paillier_test_get") } ``` 创建合约: ```go // 创建合约 func testPaillierCreate(t *testing.T, client *ChainClient, admin1, admin2, admin3, admin4 *ChainClient, withSyncResult bool) { resp, err := createUserPaillierContract(client, admin1, admin2, admin3, admin4, paillierContractName, version, paillierByteCodePath, runtime, []*common.KeyValuePair{}, withSyncResult) require.Nil(t, err) fmt.Printf("CREATE contract-paillier-1 contract resp: %+v\n", resp) } func createUserPaillierContract(client *ChainClient, admin1, admin2, admin3, admin4 *ChainClient, contractName, version, byteCodePath string, runtime common.RuntimeType, kvs []*common.KeyValuePair, withSyncResult bool) (*common.TxResponse, error) { payloadBytes, err := client.CreateContractCreatePayload(contractName, version, byteCodePath, runtime, kvs) if err != nil { return nil, err } // 各组织Admin权限用户签名 signedPayloadBytes1, err := admin1.SignContractManagePayload(payloadBytes) if err != nil { return nil, err } signedPayloadBytes2, err := admin2.SignContractManagePayload(payloadBytes) if err != nil { return nil, err } signedPayloadBytes3, err := admin3.SignContractManagePayload(payloadBytes) if err != nil { return nil, err } signedPayloadBytes4, err := admin4.SignContractManagePayload(payloadBytes) if err != nil { return nil, err } // 收集并合并签名 mergeSignedPayloadBytes, err := client.MergeContractManageSignedPayload([][]byte{signedPayloadBytes1, signedPayloadBytes2, signedPayloadBytes3, signedPayloadBytes4}) if err != nil { return nil, err } // 发送创建合约请求 resp, err := client.SendContractManageRequest(mergeSignedPayloadBytes, createContractTimeout, withSyncResult) if err != nil { return nil, err } err = checkProposalRequestResp(resp, true) if err != nil { return nil, err } return resp, nil } ``` 调用合约方法进行链上同态运算: ```go // 调用合约进行同态运算 func testPaillierOperation(t *testing.T, client *ChainClient, s string, b bool) { pubKeyBytes, err := ioutil.ReadFile(paillierPubKeyFilePath) require.Nil(t, err) payloadParams, err := CreatePaillierTransactionPayloadParams(pubKeyBytes, 1, 1000000) resp, err := client.InvokeContract(paillierContractName, s, "", payloadParams, -1, b) require.Nil(t, err) if resp.Code != common.TxStatusCode_SUCCESS { fmt.Printf("invoke contract failed, [code:%d]/[msg:%s]\n", resp.Code, resp.Message) } if !b { fmt.Printf("invoke contract success, resp: [code:%d]/[msg:%s]/[txId:%s]\n", resp.Code, resp.Message, resp.ContractResult.Result) } else { fmt.Printf("invoke contract success, resp: [code:%d]/[msg:%s]/[contractResult:%s]\n", resp.Code, resp.Message, resp.ContractResult) } } func CreatePaillierTransactionPayloadParams(pubKeyBytes []byte, plaintext1, plaintext2 int64) (map[string]string, error) { pubKey := paillier.Helper().NewPubKey() err := pubKey.Unmarshal(pubKeyBytes) if err != nil { } pt1 := new(big.Int).SetInt64(plaintext1) ciphertext1, err := pubKey.Encrypt(pt1) if err != nil { return nil, err } ct1Bytes, err := ciphertext1.Marshal() if err != nil { return nil, err } keyGenerator := paillier.Helper().NewKeyGenerator() prv2, _ := keyGenerator.GenKey() pub2, _ := prv2.GetPubKey() _, _ = pub2.Marshal() pt2 := new(big.Int).SetInt64(plaintext2) ciphertext2, err := pubKey.Encrypt(pt2) if err != nil { return nil, err } ct2Bytes, err := ciphertext2.Marshal() if err != nil { return nil, err } ct1Str := base64.StdEncoding.EncodeToString(ct1Bytes) ct2Str := base64.StdEncoding.EncodeToString(ct2Bytes) payloadParams := make(map[string]string) //payloadParams["handletype"] = "AddCiphertext" //payloadParams["handletype"] = "AddPlaintext" payloadParams["handletype"] = "SubCiphertext" //payloadParams["handletype"] = "SubCiphertextStr" //payloadParams["handletype"] = "SubPlaintext" //payloadParams["handletype"] = "NumMul" payloadParams["para1"] = ct1Str payloadParams["para2"] = ct2Str payloadParams["pubkey"] = string(pubKeyBytes) return payloadParams, nil } ``` 查询运算结果并解密: ```go // 查询同态执行结果并解密 func testPaillierQueryResult(t *testing.T, c *ChainClient, s string) { //paillierMethod := "AddCiphertext" //paillierMethod := "AddPlaintext" paillierMethod := "SubCiphertext" //paillierMethod := "SubCiphertextStr" //paillierMethod := "SubPlaintext" params1, err := QueryPaillierResult(c, paillierContractName, s, paillierMethod, -1, paillierPrvKeyFilePath) require.Nil(t, err) fmt.Printf("QUERY %s contract resp -> encrypt(cipher 10): %d\n", paillierContractName, params1) } func QueryPaillierResult(c *ChainClient, contractName, method, paillierDataItemId string, timeout int64, paillierPrvKeyPath string) (int64, error) { resultStr, err := QueryPaillierResultById(c, contractName, method, paillierDataItemId, timeout) if err != nil { return 0, err } ct := paillier.Helper().NewCiphertext() resultBytes, err := base64.StdEncoding.DecodeString(string(resultStr)) if err != nil { return 0, err } err = ct.Unmarshal(resultBytes) if err != nil { return 0, err } prvKey := paillier.Helper().NewPrvKey() prvKeyBytes, err := ioutil.ReadFile(paillierPrvKeyPath) if err != nil { return 0, fmt.Errorf("open paillierKey file failed, [err:%s]", err) } err = prvKey.Unmarshal(prvKeyBytes) if err != nil { return 0, err } decrypt, err := prvKey.Decrypt(ct) if err != nil { return 0, err } return decrypt.Int64(), nil } func QueryPaillierResultById(c *ChainClient, contractName, method, paillierMethod string, timeout int64) ([]byte, error) { pairsMap := make(map[string]string) pairsMap["handletype"] = paillierMethod resp, err := c.QueryContract(contractName, method, pairsMap, timeout) if err != nil { return nil, err } result := resp.ContractResult.Result return result, nil } ``` 执行测试,结果如下: ```go ======================================= 调用合约运算(异步)======================================= 2021-06-23 10:35:03.278 [DEBUG] [SDK] sdk/sdk_user_contract.go:197 [SDK] begin to INVOKE contract, [contractName:paillier-rust-10001]/[method:paillier_test_set]/[txId:54e59c8b80fc44f2a593e6a32c491ba92e4b6ef9751f4d1da5a740f74acb427a]/[params:map[handletype:SubCiphertext para1:pQlgF+ZErLS+IKlNnqtlKsXlV6RdSqxZLLbpN8e6AT7pSAO2omXW8RZouuGZCEwVyLpQouhH8gEfILG7IVR8sX2tSKMQ para2:qS56HCwNbSWSvg6LfdWCKiS00LeDTNs5ZdfJMJ+F986G19paLnbKl5MjqzBagz0fyurwDCg9gL80jFuHzrM+EyHATJud pubkey:8b8df031cb89dadfc9af90cbcbc77678763be567b676b7457f3c2bd4984c4099]] 2021-06-23 10:35:03.278 [DEBUG] [SDK] sdk/sdk_client.go:368 [SDK] proposalRequest resp: message:"OK" invoke contract success, resp: [code:0]/[msg:OK]/[txId:54e59c8b80fc44f2a593e6a32c491ba92e4b6ef9751f4d1da5a740f74acb427a] ======================================= 查询结果并解密(异步)======================================= 2021-06-23 10:35:08.279 [DEBUG] [SDK] sdk/sdk_user_contract.go:240 [SDK] begin to QUERY contract, [contractName:paillier-rust-10001]/[method:paillier_test_get]/[txId:6c56521a218f4d13a95ad710df314b0851c595b118d04ce7a90d6756e95ad2ba]/[params:map[handletype:SubCiphertext]] 2021-06-23 10:35:08.282 [DEBUG] [SDK] sdk/sdk_client.go:368 [SDK] proposalRequest resp: message:"SUCCESS" contract_result: QUERY paillier-rust-10001 contract resp -> encrypt(cipher 10): -999999 ```