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

# 下载安装包
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

# 下载安装包
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. 参考【快速入门】进行到源码下载步骤,下载完成后需要做出一些调整,再进行快速入门后续步骤。

  2. 进入到 clone 的 chainmaker-go 项目中,修改项目根目录下的 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. 参考【快速入门】进行到源码下载步骤,下载完成后需要做出一些调整,再进行快速入门后续步骤。

  2. 与上述脚本部署的步骤二进行相同的修改。

  3. 修改 chainmaker-go/DOCKER 目录下的 Dockerfile 文件,使用下面的 dockerfile 替换掉chainmaker-go/DOCKER/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获取使用帮助:

./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 算法的公私钥,并根据参数保存到指定位置,查看命令详情:

./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:可能出现的错误

Encrypt(plaintext *big.Int) (Ct, error)

同态运算方法,同态运算一般在链上进行,所以一般不会使用到这几个方法,而是使用合约SDK提供的同态运算方法,这里不再详细描述。

// 两个密文相加,返回结果密文
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:可能出现的错误

Decrypt(ciphertext Ct) (*big.Int, error)

GetPubKey:根据私钥获取公钥

return:

  • Pub:公钥

  • error:可能出现的错误

GetPubKey() (Pub, error)

KeyGenerator

KeyGenerator 接口包含了初始化公私钥的方法。

return:

  • Prv:生成的私钥(公钥可以从私钥中获取)

  • error:可能出现的错误

GenKey() (Prv, error)

Provider接口

Provider 接口由 provider 实现,用于初始化 paillier 各接口的实现。

NewPubKey:返回实现了 Pub 接口的结构体实例

NewPubKey() Pub

NewPrvKey:返回实现了 Prv 接口的结构体实例

NewPrvKey() Prv

NewCiphertext:返回实现了 Pub 接口的结构体实例

NewCiphertext() Ct

NewKeyGenerator:返回实现了 Pub 接口的结构体实例

NewKeyGenerator() KeyGenerator

智能合约SDK

Go 和 Rust 合约SDK提供了进行同态运算的方法。

go

PaillierContext 接口提供了同态运算方法

AddCiphertext:使用公钥进行密文加密文的同态运算

Arguments:

  • pubKey:公钥的字节数组

  • ct1:密文字节数组

  • ct1:密文字节数组

return:

  • []byte结果密文字节数组

  • ResultCode:函数执行状态码

AddCiphertext(pubKey []byte, ct1 []byte, ct2 []byte) ([]byte, ResultCode)

AddPlaintext:使用公钥进行密文加明文的同态运算

Argumengs:

  • pubKey:公钥字节数组

  • ct:密文字节数组

  • pt:明文,int64的字符串表示,超出int64链上执行将会报错

return:

  • []byte:结果密文字节数组

  • ResultCode:函数执行状态码

AddPlaintext(pubKey, ct []byte, pt string) ([]byte, ResultCode)

SubCiphertext:使用公钥进行密文减密文的同态运算

Arguments:

  • pubKey:公钥的字节数组

  • ct1:密文字节数组

  • ct1:密文字节数组

return:

  • []byte:结果密文字节数组

  • ResultCode:函数执行状态码

SubCiphertext(pubKey, ct1, ct2 []byte) ([]byte, ResultCode)

SubPlaintext:使用公钥进行密文减明文的同态运算

Argumengs:

  • pubKey:公钥字节数组

  • ct:密文字节数组

  • pt:明文,int64的字符串表示,超出int64链上执行将会报错

return:

  • []byte:结果密文字节数组

  • ResultCode:函数执行状态码

SubPlaintext(pubKey, ct []byte, pt string) ([]byte, ResultCode)

NumMul:使用公钥进行密文乘明文的同态运算

Argumengs:

  • pubKey:公钥字节数组

  • ct:密文字节数组

  • pt:明文,int64的字符串表示,超出int64链上执行将会报错

return:

  • []byte:结果密文字节数组

  • ResultCode:函数执行状态码

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

fn add_ciphertext(
    &self,
    pubkey: Vec<u8>,
    ciphertext1: Vec<u8>,
    ciphertext2: Vec<u8>,
) -> Result<Vec<u8>, result_code>;

add_plaintext:使用公钥计算密文与明文的和

Argument:

  • pubKey:两个密文的公钥

  • ciphertext:密文

  • plaintext:明文,i64的字符串表示,超出i64链上执行将会报错

return:

  • return1: 计算结果

  • return2: 函数执行状态码,0:success, 1: failed

fn add_plaintext(
    &self,
    pubkey: Vec<u8>,
    ciphertext: Vec<u8>,
    plaintext: &str,
) -> Result<Vec<u8>, result_code>;

sub_ciphertext:使用公钥计算密文减去密文

Argument:

  • pubKey:两个密文的公钥

  • ciphertext1:密文

  • ciphertext2:密文

return:

  • return1: 计算结果

  • return2: 函数执行状态码,0:success, 1: failed

fn sub_ciphertext(
    &self,
    pubkey: Vec<u8>,
    ciphertext1: Vec<u8>,
    ciphertext2: Vec<u8>,
) -> Result<Vec<u8>, result_code>;

sub_plaintext:使用公钥计算密文减明文

Argument:

  • pubKey:两个密文的公钥

  • ciphertext:密文

  • plaintext:明文,i64的字符串表示,超出i64链上执行将会报错

return:

  • return1: 计算结果

  • return2: 函数执行状态码,0:success, 1: failed

fn sub_plaintext(
     &self,
     pubkey: Vec<u8>,
     ciphertext: Vec<u8>,
     plaintext: &str,
) -> Result<Vec<u8>, result_code>;

num_mul:使用公钥计算密文乘明文

Argument:

  • pubKey:两个密文的公钥

  • ciphertext:密文

  • plaintext:明文,i64的字符串表示,超出i64链上执行将会报错

return:

  • return1: 计算结果

  • return2: 函数执行状态码,0:success, 1: failed

fn num_mul(
    &self,
    pubkey: Vec<u8>,
    ciphertext: Vec<u8>,
    plaintext: &str,
) -> Result<Vec<u8>, result_code>;

用例

1. 使用cmc paillier生成并保存自己的公私钥

./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文件夹,来保存生成的公私钥文件,如下:

tree ./paillier-key
./paillier-key
├── test1.prvKey
└── test1.pubKey

2. 编写智能合约

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文件:

tinygo build -no-debug -opt=s -o counter-go.wasm -target wasm

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<Vec<u8>, 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, &para2);
    } 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, &para2);
    } else if handletype == "NumMul" {
        r = test.num_mul(pubkey.into_bytes(), decode_para1, &para2);
    } 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字节码文件

make build

3. 使用SDK编写测试用例

SDK并未提供 paillier 相关接口,开发者需要直接调用common库中的 chainmaker.org/chainmaker-sdk-go/common/crypto/paillier包。

总测试函数:

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")
}

创建合约:

// 创建合约
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
}

调用合约方法进行链上同态运算:

// 调用合约进行同态运算
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
}

查询运算结果并解密:

// 查询同态执行结果并解密
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
}

执行测试,结果如下:

======================================= 调用合约运算(异步)=======================================
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:<result:"qAtwQdggsF27XARMwufhauCGNROKZWJWHmusS6f/1Vn7Xat5l7mb8dcwY5+hiOHwLDAMhY6TGPezuoSDCQR2cIcVmcIh" gas_used:13150876 > 
QUERY paillier-rust-10001 contract resp -> encrypt(cipher 10): -999999