9. Paillier 开发手册

本文分为两部分:

  1. 方案接口、工具、合约SDK介绍:对 ChainMaker 的 cmc 工具、合约 SDK 和 common 库提供的算法的介绍。

  2. 用例:介绍了如何使用 ChainMaker Paillier 功能,从 Paillier 公私钥生成、编写智能合约到使用 SDK(本文使用 go 的 SDK) 进行数据的加密上链、链上同态运算和获取运算结果解密。

9.1. 接口、工具、合约SDK介绍

common提供 paillier 半同态加密的基础能力。

cmc工具提供了生成公私钥、加解密的能力,方便用户快速体验同态加密功能。

合约SDK提供了链上同态运算的能力,目前支持同态运算的合约 SDK 有:Go、Rust。

9.1.1. CMC 工具子命令 paillier 介绍

简介

CMC 中的 paillier 命令是用于辅助paillier算法的使用,生成公钥和私钥,并且根据参数保存到指定位置。

使用./cmc paillier -h获取使用帮助:

ChainMaker paillier command

Usage:
  cmc paillier [command]

Available Commands:
  decrypt     use paillier private key decrypt user data, the input encrypted data must in base64 format
  encrypt     use paillier public key encrypt user data, the output encrypted data is in base64 format
  genKey      generates paillier private public key

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:存储路径

encrypt简介

encrypt用于进行 paillier 加密,生成base64格式密文,查看命令详情:

./cmc paillier encrypt -h
use paillier public key encrypt user data, the output encrypted data is in base64 format

Usage:
cmc paillier encrypt [flags]

Flags:
--data string               specify input data for encrypt. e.g. --data="123"
-h, --help                      help for encrypt
--pubkey-file-path string   specify paillier public key file path

encrypt参数详解

--data: 待加密数据
--pubkey-file-path:公钥路径

decrypt简介

encrypt用于进行 paillier 解密,输入数据为base64格式密文,查看命令详情:

./cmc paillier decrypt -h
use paillier private key decrypt user data, the input encrypted data must in base64 format

Usage:
  cmc paillier decrypt [flags]

Flags:
      --data string                specify input data for decrypt. e.g. --data="some base64 string"
  -h, --help                       help for decrypt
      --privkey-file-path string   specify paillier private key file path

decrypt参数详解

--data: 待解密数据
--privkey-file-path:私钥路径

9.1.2. common 算法部分介绍

9.1.2.1. 公钥方法

PubKey 提供了序列化、反序列化、加密和同态运算方法。

Encrypt:加密一个big.Int类型的数得到对应的密文

Arguments:

  • plaintext:用于加密的明文

return:

  • Ct:加密得到的密文

  • error:可能出现的错误

Encrypt(plainText *big.Int) (*Ciphertext, error)

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

// 两个密文相加,返回结果密文
AddCiphertext(cipher1, cipher2 *Ciphertext) (*Ciphertext, error))

// 密文加明文,返回结果密文
AddPlaintext(cipher *Ciphertext, constant *big.Int) (*Ciphertext, error)

// 密文减密文,返回结果密文
SubCiphertext(cipher1, cipher2 *Ciphertext) (*Ciphertext, error)

// 密文减明文,返回结果密文
SubPlaintext(cipher *Ciphertext, constant *big.Int) (*Ciphertext, error)

// 密文乘以明文,返回结果密文
NumMul(cipher *Ciphertext, constant *big.Int)

9.1.2.2. 私钥方法

PrvKey 提供了包含 PubKey 所有的方法以及获取公钥、解密方法。

Decrypt:解密密文得到对应的明文

Arguments:

  • ciphertext:用于解密的密文

return:

  • *big.Int:解密得到的明文

  • error:可能出现的错误

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

GetPubKey:根据私钥获取公钥

return:

  • *PubKey:公钥

  • error:可能出现的错误

GetPubKey() (*PubKey, error)

9.1.2.3. GenKey

GenKey 用于初始化公私钥对

return:

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

  • error:可能出现的错误

GenKey() (*PrvKey, error)

9.1.3. 智能合约SDK

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

9.1.3.1. golang

参考paillier sdk

9.1.3.2. tiny-go

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

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

Arguments:

  • pubKey:公钥的字节数组

  • ct1:密文字节数组

  • ct1:密文字节数组

return:

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

  • ResultCode:函数执行状态码

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

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

Arguments:

  • 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)

9.1.3.3. 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>;

9.2. 用例

9.2.1. 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

9.2.2. 2. 编写智能合约

golang:

参考paillier demo

通过build脚本进行编译得到目标7z文件即可部署。

tiny-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 contract-paillier.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

9.2.3. 3. 使用SDK编写测试用例

SDK并未提供 paillier 相关接口,开发者需要直接调用common库中的 chainmaker.org/sdk-go/common/crypto/paillier包。 该SDK仅支持tiny-go和rust合约文件,如果使用golang合约,请修改合约部署代码,并修改invoke参数handletype为method。

cmc工具同样支持进行密钥生成、加解密、与demo合约进行交互。

总测试函数:

const (
	sdkConfigOrg1Client1Path = "../sdk_configs/sdk_config_org1_client1.yml"

	createContractTimeout = 5
)

const (

	// go 合约
	paillierContractName = "pailliergo100001"
	paillierByteCodePath = "../../testdata/paillier-wasm-demo/contract-paillier.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 main() {
	TestPaillierContractCounterGo()
}

func TestPaillierContractCounterGo() {
	t := new(testing.T)
	client, err := examples.CreateChainClientWithSDKConf(sdkConfigOrg1Client1Path)
	require.Nil(t, err)

	fmt.Println("======================================= 创建合约(异步)=======================================")
	testPaillierCreate(client, examples.UserNameOrg1Admin1, examples.UserNameOrg2Admin1, examples.UserNameOrg3Admin1, examples.UserNameOrg4Admin1, false)
	time.Sleep(5 * time.Second)

	fmt.Println("======================================= 调用合约运算(异步)=======================================")
	testPaillierOperation(client, "paillier_test_set", false)
	time.Sleep(5 * time.Second)

	fmt.Println("======================================= 查询结果并解密(异步)=======================================")
	testPaillierQueryResult(t, client, "paillier_test_get")
}

创建合约:

// 创建合约
func testPaillierCreate(client *sdk.ChainClient, admin1, admin2, admin3,
	admin4 string, withSyncResult bool) {
	resp, err := createUserContract(client, admin1, admin2, admin3, admin4,
		paillierContractName, examples.Version, paillierByteCodePath, runtime, []*common.KeyValuePair{}, withSyncResult)
	if err != nil {
		log.Fatalln(err)
	}

	fmt.Printf("CREATE contract-hibe-1 contract resp: %+v\n", resp)
}

func createUserContract(client *sdk.ChainClient, admin1, admin2, admin3, admin4 string,
	contractName, version, byteCodePath string, runtime common.RuntimeType, kvs []*common.KeyValuePair, withSyncResult bool) (*common.TxResponse, error) {

	payload, err := client.CreateContractCreatePayload(contractName, version, byteCodePath, runtime, kvs)
	if err != nil {
		return nil, err
	}

	endorsers, err := examples.GetEndorsers(payload, admin1, admin2, admin3, admin4)
	if err != nil {
		return nil, err
	}

	resp, err := client.SendContractManageRequest(payload, endorsers, createContractTimeout, withSyncResult)
	if err != nil {
		return nil, err
	}

	return resp, nil
}

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

// 调用合约进行同态运算
func testPaillierOperation(client *sdk.ChainClient, s string, b bool) {
	pubKeyBytes, err := ioutil.ReadFile(paillierPubKeyFilePath)
	//require.Nil(t, err)
	if err != nil {
		log.Fatalln(err)
	}
	payloadParams, err := CreatePaillierTransactionPayloadParams(pubKeyBytes, 1, 1000000)
	resp, err := client.InvokeContract(paillierContractName, s, "", payloadParams, -1, b)
	//require.Nil(t, err)
	if err != nil {
		log.Fatalln(err)
	}

	if resp.Code != common.TxStatusCode_SUCCESS {
		fmt.Printf("invoke contract failed, [code:%d]/[msg:%s]\n", resp.Code, resp.Message)
	}

}

func CreatePaillierTransactionPayloadParams(pubKeyBytes []byte, plaintext1, plaintext2 int64) ([]*common.KeyValuePair, error) {
	pubKey := new(paillier.PubKey)
	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
	}

	prv2, _ := paillier.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 := []*common.KeyValuePair{
		{
			Key:   "handletype",
			Value: []byte("SubCiphertext"),
		},
		{
			Key:   "para1",
			Value: []byte(ct1Str),
		},
		{
			Key:   "para2",
			Value: []byte(ct2Str),
		},
		{
			Key:   "pubkey",
			Value: pubKeyBytes,
		},
	}
	/*
		old
		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 *sdk.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 *sdk.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 := new(paillier.Ciphertext)
	resultBytes, err := base64.StdEncoding.DecodeString(string(resultStr))
	if err != nil {
		return 0, err
	}
	err = ct.Unmarshal(resultBytes)
	if err != nil {
		return 0, err
	}

	prvKey := new(paillier.PrvKey)

	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 *sdk.ChainClient, contractName, method, paillierMethod string, timeout int64) ([]byte, error) {
	pairs := []*common.KeyValuePair{
		{Key: "handletype", Value: []byte(paillierMethod)},
	}

	/*
		old
		pairsMap := make(map[string]string)
		pairsMap["handletype"] = paillierMethod
	*/

	resp, err := c.QueryContract(contractName, method, pairs, timeout)
	if err != nil {
		return nil, err
	}

	result := resp.ContractResult.Result

	return result, nil
}

执行测试,结果如下:

=== RUN   TestPaillierContractCounterGo
======================================= 创建合约(异步)=======================================
CREATE contract-paillier-1 contract resp: message:"OK" contract_result:<result:"5aa609367c8342e08459ec1ec1321b954d88569399284277bcc5887b72d8d2c5" message:"OK" > 
======================================= 调用合约运算(异步)=======================================
invoke contract success, resp: [code:0]/[msg:OK]/[txId:81ac75ccc8914ce694a0df6ddebe2c5fddea319d784a461cb52ec9b5fc373125]
======================================= 查询结果并解密(异步)=======================================
QUERY paillier-rust-10001 contract resp -> encrypt(cipher 10): -999999
--- PASS: TestPaillierContractCounterGo (19.23s)
PASS