Paillier 开发手册
本文分为四部分:
前置依赖:paillier 算法依赖库 GMP 和 M4的安装。
部署指南:介绍了如何部署一条具有 paillier 半同态运算能力链。
方案接口、工具、合约SDK介绍:对 ChainMaker 的 cmc 工具、合约 SDK 和 common 库提供的算法的介绍。
用例:介绍了如何使用 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、 脚本部署
参考【快速入门】进行到源码下载步骤,下载完成后需要做出一些调整,再进行快速入门后续步骤。
进入到 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
进行快速入门后续步骤,完成部署。
2、Docker 部署
参考【快速入门】进行到源码下载步骤,下载完成后需要做出一些调整,再进行快速入门后续步骤。
与上述脚本部署的步骤二进行相同的修改。
修改 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
进行快速入门后续步骤,完成部署。
部署成功后就可以写合约来使用 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, ¶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字节码文件
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