7. Bulletproofs 开发手册

本文分为两部分:

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

  2. 用例:介绍了如何使用 ChainMaker Bulletproofs,从数据加密、编写智能合约到使用 SDK(本文使用 Go SDK)发送交易进行介绍。

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

common 提供 Bulletproofs 零知识范围证明的基础能力。

CMC 工具提供生成 opening、commitment、proof 和对同态运算结果生成 proof 的功能。

合约 SDK 提供了 commitment 链上同态运算和验证证明的接口方法,目前支持 Bulletproofs 算法的合约 SDK 有:Go、Rust。

7.1.1. common算法部分介绍

7.1.1.1. Prove 相关函数

ProveRandomOpening:使用随机生成的 opening 生成对 X 的承诺和证明

Arguments:

  • x:原始数值

return:

  • []byte:proof

  • []byte:commitment

  • []byte:opening

  • error:可能出现的错误

ProveRandomOpening(x uint64) ([]byte, []byte, []byte, error)

ProveSpecificOpening:使用指定的 opening 生成对 X 的承诺和证明

Arguments:

  • x:原始数值

  • opening:指定的opening

return:

  • []byte:proof

  • []byte:commitment

  • error:可能出现的错误

ProveSpecificOpening(x uint64, opening []byte) ([]byte, []byte, error)

Verify:验证证明 proof 的有效性

Arguments:

  • proof:对承诺隐藏的数值在范围 [0,2^64) 内的证明

  • commitment:对隐藏数值的承诺

return:

  • bool:返回 true 验证通过,证明 commitment 的原始值在[0,2^64)范围内,否则返回 false。

  • error:可能出现的错误

Verify(proof []byte, commitment []byte) (bool, error)

ProveAfterAddNum:使用相同的 opening 对运算后的结果 commitment 生成 proof

ProveAfterAddNum(x, y uint64, openingX, commitmentX []byte) ([]byte, []byte, error)

ProveAfterCommitment:使用两个 opening 的和对运算后的结果 commitment 生成 proof

ProveAfterAddCommitment(x, y uint64, openingX, openingY, commitmentX, commitmentY []byte) ([]byte, []byte, []byte, error)

ProveAfterSubNum:使用相同的 opening 对运算后的结果 commitment 生成 proof

ProveAfterSubNum(x, y uint64, openingX, commitmentX []byte) ([]byte, []byte, error)

ProveAfterSubCommitment:使用两个 opening 的和对运算后的结果 commitment 生成 proof

ProveAfterSubCommitment(x, y uint64, openingX, openingY, commitmentX, commitmentY []byte) ([]byte, []byte, []byte, error)

ProveAfterMulNum:使用 opening 与 y 的乘积对 commitment 与 y 的乘积生成 proof

ProveAfterMulNum(x, y uint64, openingX, commitmentX []byte) ([]byte, []byte, []byte, error)

7.1.1.2. Commitment 相关函数

CommitmentOps 的大部分开发是需要用到的接口都是集成在链上,通过合约 SDK 提供能力,这里仅介绍一个获取 opening 的接口方法:PedersenRNG。

PedersenRNG:生成一个真正的随机标量,作为生成承诺的 opening

return:

  • []byte:opening

  • error:可能出现的错误

PedersenRNG() ([]byte, error)

7.1.2. CMC工具子命令 bulletproofs 介绍

CMC 默认是没有 bulletproofs 相关功能的,需要自己安装好前置依赖库之后,在chainmaker-go/tools/cmc目录下进行编译: go build -tags=bulletproofs -mod=mod

简介

CMC 中的 paillier 命令是在不写客户端代码的情况下快速体验 bulletproofs 功能的辅助命令,用于数据的初始化、链下同态运算和对数据生成proof。

使用./cmc paillier -h查看使用帮助:

./cmc bulletproofs -h
ChainMaker bulletproofs command

Usage:
  cmc bulletproofs [command]

Available Commands:
  genOpening     Bulletproofs generate opening command
  pedersenVerify Bulletproofs pedersenVerify command
  prove          Bulletproofs prove command
  proveMethod    Bulletproofs proveMethod command

Flags:
  -h, --help   help for bulletproofs

Use "cmc bulletproofs [command] --help" for more information about a command.

genOpening 简介

genOpening 生成 opening 并输出到终端,查看命令使用帮助:

./cmc bulletproofs genOpening -h
Bulletproofs generate opening command

Usage:
  cmc bulletproofs genOpening [flags]

Flags:
  -h, --help   help for genOpening

prove 简介

prove 对一个数(在[0, 2^64)内的数)生成 commitment、proof 和 opening,opening 可指定,查看命令使用帮助:

./cmc bulletproofs prove -h
Bulletproofs prove command

Usage:
  cmc bulletproofs prove [flags]

Flags:
  -h, --help             help for prove
      --opening string   opening
      --value int        value (default -1)

参数介绍:

--opening:指定生成 commitment 和 proof 的 opening,若干不提供该参数,则会使用随机生成的 opening。
--value:要证明的值,证明的范围是 [0,2^64),这里支持 [0,2^63)

proveMethod 简介

proveMethod 在需要对链上一个结果提供证明时,可以在链下使用该命令,进行相同的同态计算,并对结果生成 proof,以便发送到链上同链上结果进行验证。查看命令使用帮助:

./cmc bulletproofs proveMethod -h
Bulletproofs proveMethod command

Usage:
  cmc bulletproofs proveMethod [flags]

Flags:
      --commitmentX string
      --commitmentY string
  -h, --help                 help for proveMethod
      --method string        prove method: ProveAfterAddNum ProveAfterAddCommitment ProveAfterSubNum ProveAfterSubCommitment ProveAfterMulNum
      --openingX string
      --openingY string
      --valueX int           valueY (default -1)
      --valueY int           valueY (default -1)

参数说明:

--method:要调用的方法名,支持使用帮助中列出的方法
--valueX:原始值X
--valueY:原始值Y
--commitmentX:对X的承诺(必填,当同一个数进行运算时,默认会使用 commitmentX 的值,而不会去尝试使用 commitmentY 的值)
--commitmentY:对Y的承诺(可选,当两个commitment进行同态运算时,需要提供)
--openingX:值X使用的 opening(必填,当同一个数进行运算时,默认会使用 openingX 的值)
--openingY:值Y使用的 opening (可选,当两个 commitment 进行同态运算是,需要提供)

pedersenVerify 简介

pedersenVerify 在链下用于校验 opening、commitment 和原始值之间是否有绑定关系。查看命令使用帮助:

./cmc bulletproofs pedersenVerify -h
Bulletproofs pedersenVerify command

Usage:
  cmc bulletproofs pedersenVerify [flags]

Flags:
      --commitment string   commitment
  -h, --help                help for pedersenVerify
      --opening string      opening
      --value int           value (default -1)

参数介绍:

--value:原始值X
--commitment:对 X 的承诺 commitment
--opening:生成 commitment 时使用的 opening

7.1.3. 智能合约SDK

7.1.3.1. golang

参考bulletproofs sdk

7.1.3.2. tiny-go

BulletproofsContext 接口提供了 bulletproofs 同态运算和验证的能力,接口方法如下:

PedersenAddNum:无须知道 commitment 的原始值 X,计算 X+Y 的 commitment

Arguments:

  • commitment:用于隐藏实际数值 X 的 commitment

  • num:Y,uint64 的字符串表示,超出 uint64 范围在计算时会返回错误

return:

  • []byte:结果 commitment

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

PedersenAddNum(commitment []byte, num string) ([]byte, ResultCode)

PedersenAddCommitment:无须知道 commitment1、commitment2 的原始值 X、Y,计算 X+Y 的 commitment

Arguments:

  • commitment1:用于隐藏实际数值 X 的 commitment

  • commitment2:用于隐藏实际数值 Y 的 commitment

return:

  • []byte:结果 commitment

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

PedersenAddCommitment(commitment1, commitment2 []byte) ([]byte, ResultCode)

PedersenSubNum:无须知道 commitment 的原始值 X,计算 X-Y 的 commitment

Arguments:

  • commitment:用于隐藏实际数值 X 的 commitment

  • num:Y,uint64 的字符串表示,超出 uint64 范围在计算时会返回错误

return:

  • []byte:结果 commitment

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

PedersenSubNum(commitment []byte, num string) ([]byte, ResultCode)

PedersenSubCommitment:无需知道 commitment1、commitment2 的原始值 X、Y,计算 X-Y 的 commitment

Arguments:

  • commitment1:用于隐藏实际数值 X 的 commitment

  • commitment2:用于隐藏实际数值 Y 的 commitment

return:

  • []byte:结果 commitment

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

PedersenSubCommitment(commitment1, commitment2 []byte) ([]byte, ResultCode)

PedersenMulNum:无须知道 commitment 的原始值 X,计算 X*Y 的 commitment

Arguments:

  • commitment:用于隐藏实际数值 X 的 commitment

  • num:Y,uint64 的字符串表示,超出 uint64 范围在计算时会返回错误

return:

  • []byte:结果 commitment

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

PedersenMulNum(commitment []byte, num string) ([]byte, ResultCode)

Verify:验证证明 proof 的有效性

Arguments:

  • proof:对承诺隐藏的数值在范围 [0,2^64) 内的证明

  • commitment:对隐藏数值的承诺

result:

  • []byte:验证正确时返回字符串”1”的字节数组,失败时返回字符串”0”的字节数组

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

Verify(proof, commitment []byte) ([]byte, ResultCode)

7.1.3.3. rust

trait方法 pedersen_add_num:无须知道 commitment 的原始值 X,计算 X+Y 的commitment

Arguments:

  • commitment:用于隐藏实际数值X的 commitment

  • num:Y,u64 的字符串表示,超出 u64 范围在计算时会返回错误

return:

  • return1:结果commitment

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

fn pedersen_add_num(&self, commitment: Vec<u8>, num: &str) -> Result<Vec<u8>, result_code>;

pedersen_add_commitment:无须知道 commitment1、commitment2 的原始值 X、Y,计算 X+Y 的 commitment

Arguments:

  • commitment1:用于隐藏实际数值 X 的 commitment

  • commitment2:用于隐藏实际数值 Y 的 commitment

return:

  • return1:结果commitment

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

fn pedersen_add_commitment(
    &self,
    commitment1: Vec<u8>,
    commitment2: Vec<u8>,
) -> Result<Vec<u8>, result_code>;

pedersen_sub_num:无须知道 commitment 的原始值 X,计算 X-Y 的 commitment

Arguments:

  • commitment:用于隐藏实际数值 X 的 commitment

  • num:Y,u64 的字符串表示,超出 u64 范围在计算时会返回错误

return:

  • return1:结果commitment

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

fn pedersen_sub_num(&self, commitment: Vec<u8>, num: &str) -> Result<Vec<u8>, result_code>;

PedersenSubCommitment:无须知道 commitment1、commitment2 的原始值 X、Y,计算 X-Y 的 commitment

Arguments:

  • commitment1:用于隐藏实际数值 X 的 commitment

  • commitment2:用于隐藏实际数值 Y 的 commitment

return:

  • return1:结果commitment

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

fn pedersen_sub_commitment(
    &self,
    commitment1: Vec<u8>,
    commitment2: Vec<u8>,
) -> Result<Vec<u8>, result_code>;

pedersen_mul_num:无须知道 ommitment 的原始值 X,计算 X-Y 的 commitment

Arguments:

  • commitment:用于隐藏实际数值 X 的 commitment

  • num:Y,u64 的字符串表示,超出 u64 范围在计算时会返回错误

return:

  • return1:结果commitment

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

fn pedersen_mul_num(&self, commitment: Vec<u8>, num: &str) -> Result<Vec<u8>, result_code>;

verify:验证证明 proof 的有效性

Arguments:

  • proof:对 commitment 在范围 [0,2^64) 内的证明

  • commitment:对隐藏数值的承诺

result:

  • return1:验证正确时返回字符串”1”的字节数组,失败时返回字符串”0”的字节数组

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

fn verify(&self, proof: Vec<u8>, commitment: Vec<u8>) -> Result<Vec<u8>, result_code>;

7.2. 用例

7.2.1. 编写智能合约

7.2.1.1. golang

参考bulletproofs demo

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

7.2.1.2. tiny-go

package main

import (
	"encoding/base64"
	"strconv"
)

// 安装合约时会执行此方法,必须
//export init_contract
func initContract() {
	// 此处可写安装合约的初始化逻辑

}

// 升级合约时会执行此方法,必须
//export upgrade
func upgrade() {
	// 此处可写升级合约的逻辑

}

//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)
	LogMessage("[bulletproofs] PutState: key=bulletproofs_test" + ",name=" + handleType + ",value=" + result_data_str + ",result:" + strconv.FormatInt(int64(result), 10))
	LogMessage("[bulletproofs] ========================================end")
	if result_code == 0 {
		SuccessResult("bulletproofs_test_set success")
	} else {
		ErrorResult("bulletproofs_test_set failure")
	}
}

//export bulletproofs_test_get
func bulletproofs_test_get() {
	LogMessage("[bulletproofs] ========================================start")
	LogMessage("[bulletproofs] input func: paillier_test_get")
	handletype, _ := Arg("handletype")
	value, result := GetState("bulletproofs_test", handletype)
	LogMessage("[bulletproofs] GetState: key=bulletproofs_test" + ",name=" + handletype + ",value=" + value + ",result:" + strconv.FormatInt(int64(result), 10))
	LogMessage("[bulletproofs] change event_test_get[value]: " + value)
	LogMessage("[bulletproofs] ========================================end")
	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() {

}

编译得到contract-bulletproofs.wasm字节码文件:

tinygo build -no-debug -opt=s -o contract-bulletproofs.wasm -target wasm

7.2.1.3. 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 bulletproofs_test_set() {
    sim_context::log("[bulletproofs] ========================================start");
    sim_context::log("[bulletproofs] bulletproofs_test_set");

    let ctx = sim_context::get_sim_context();
    let handle_type = 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 decode_para2 = decode(para2.as_bytes()).unwrap();

    sim_context::log(&format!(
        "[bulletproofs] bulletproofs_test_set [handletype]: {}",
        handle_type
    ));

    sim_context::log(&format!(
        "[bulletproofs] bulletproofs_test_set [para1]: {}",
        para1
    ));

    sim_context::log(&format!(
        "[bulletproofs] bulletproofs_test_set [para2]: {}",
        para2
    ));

    let test = ctx.get_bulletproofs_sim_context();
    let result: Result<Vec<u8>, i32>;

    if handle_type == "PedersenAddNum" {
        // let decode_para2: u64 = para2.parse().unwrap();
        // let decode_para2 = para2.parse::<u64>().unwrap();
        result = test.pedersen_add_num(decode_para1, &para2)
    } else if handle_type == "PedersenAddCommitment" {
        let decode_para2 = decode(para2.as_bytes()).unwrap();
        result = test.pedersen_add_commitment(decode_para1, decode_para2)
    } else if handle_type == "PedersenSubNum" {
        // let decode_para2: u64 = para2.parse().unwrap();
        result = test.pedersen_sub_num(decode_para1, &para2)
    } else if handle_type == "PedersenSubCommitment" {
        let decode_para2 = decode(para2.as_bytes()).unwrap();
        result = test.pedersen_sub_commitment(decode_para1, decode_para2)
    } else if handle_type == "PedersenMulNum" {
        // let decode_para2: u64 = para2.parse().unwrap();
        result = test.pedersen_mul_num(decode_para1, &para2)
    } else if handle_type == "BulletproofsVerify" {
        let decode_para2 = decode(para2.as_bytes()).unwrap();
        result = test.verify(decode_para1, decode_para2)
    } else {
        ctx.error(&format!(
            "finish event_test_set failure: error para: {}",
            handle_type
        ));
        return;
    }

    if result.is_err() {
        ctx.error("finish event_test_set failure");
        return;
    }

    let data = result.unwrap();
    let data_u8 = data.as_slice();
    let data_str = encode(data_u8);
    let put_code = ctx.put_state("bulletproofs_test", &handle_type, data_str.as_bytes());

    ctx.log(&format!(
        "[bulletproofs] PutState: key=bulletproofs_test, name={}, value={}, result={}",
        handle_type, data_str, put_code
    ));
    ctx.log("[bulletproofs] ========================================end");
    // ctx.ok("finish event_test_set success".as_bytes());
    ctx.ok(data_str.as_bytes());
}

#[no_mangle]
pub extern "C" fn bulletproofs_test_get() {
    sim_context::log("[bulletproofs] ========================================end");
    sim_context::log("[bulletproofs] bulletproofs_test_get");

    let ctx = sim_context::get_sim_context();
    let handle_type = ctx.arg_default_blank("handletype");
    let result = ctx.get_state("bulletproofs_test", &handle_type);
    if result.is_err() {
        sim_context::log("[bulletproofs] bulletproofs_test_get error");
        ctx.error("finish bulletproofs_test_get failure");
        return;
    }
    let data = result.unwrap();
    let result = String::from_utf8(data);
    let result_str = result.unwrap();
    ctx.log(&format!(
        "[bulletproofs] GetState: key=bulletproofs_test_get, name={}, value={}, result={}",
        handle_type, result_str, 0
    ));

    ctx.log("[bulletproofs] ========================================end");

    if handle_type == "BulletproofsVerify" {
        ctx.ok(&*decode(result_str.as_bytes()).unwrap());
    } else {
        ctx.ok(result_str.as_bytes());
    }
}

编译得到chainmaker_contract.wasm字节码文件,在目录chainmaker-contract-sdk-rust/target/wasm32-unknown-unknown/release下:

make build

7.2.2. 使用SDK编写测试用例:

注意:

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

把上一步编译好的合约文件放到指定位置。

总测试函数:

const (
	sdkConfigOrg1Client1Path = "../sdk_configs/sdk_config_org1_client1.yml"
	createContractTimeout    = 5
)

const (
	// rust
	//bulletproofsContractName = "bulletproofs-rust-1001"
	//bulletproofsByteCodePath = "../../testdata/counter-go-demo/chainmaker_contract.wasm"
	//bulletproofsRuntime      = common.RuntimeType_WASMER

	// go
	bulletproofsContractName = "bulletproofsgo1001"
	bulletproofsByteCodePath = "../../testdata/bulletproofs-wasm-demo/contract-bulletproofs.wasm"
	bulletproofsRuntime      = common.RuntimeType_GASM

	// 链上合约SDK接口
	BulletProofsOpTypePedersenAddNum        = "PedersenAddNum"
	BulletProofsOpTypePedersenAddCommitment = "PedersenAddCommitment"
	BulletProofsOpTypePedersenSubNum        = "PedersenSubNum"
	BulletProofsOpTypePedersenSubCommitment = "PedersenSubCommitment"
	BulletProofsOpTypePedersenMulNum        = "PedersenMulNum"
	BulletProofsVerify                      = "BulletproofsVerify"

	// 测试数据
)

var (
	// 测试数据
	A            uint64 = 100
	X            uint64 = 20
	commitmentA1 []byte
	commitmentA2 []byte
	proofA1      []byte
	proofA2      []byte
	openingA1    []byte
)

func main() {
	TestBulletproofsContractCounterGo()
}

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

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

	funcName := BulletProofsOpTypePedersenAddNum
	//funcName := BulletProofsOpTypePedersenAddCommitment
	//funcName := BulletProofsOpTypePedersenSubNum
	//funcName := BulletProofsOpTypePedersenSubCommitment
	//funcName := BulletProofsOpTypePedersenMulNum

	fmt.Printf("============================= 调用合约 链上计算并存储 =============================\n")
	testBulletproofsSet(client, "bulletproofs_test_set", funcName, true)
	time.Sleep(5 * time.Second)

	fmt.Printf("============================= 查询计算结果 =============================\n")
	testBulletProofsGetOpResult(t, client, "bulletproofs_test_get", funcName, false)
	time.Sleep(5 * time.Second)

	fmt.Printf("============================= 调用合约验证 proof 和 查询的 commitment =============================\n")
	testBulletproofsVerify(client, "bulletproofs_test_set", BulletProofsVerify, true)
	time.Sleep(5 * time.Second)

	fmt.Printf("============================= 查询验证结果 =============================\n")
	testBulletProofsGetVerifyResult(t, client, "bulletproofs_test_get", BulletProofsVerify, false)
	time.Sleep(5 * time.Second)
}

创建合约:

// 创建合约
func testUserBulletproofsContractCounterGoCreate(client *sdk.ChainClient, admin1, admin2, admin3,
	admin4 string, withSyncResult bool) {

	resp, err := createUserContract(client, admin1, admin2, admin3, admin4,
		bulletproofsContractName, examples.Version, bulletproofsByteCodePath, bulletproofsRuntime, []*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 testBulletproofsSet(client *sdk.ChainClient, method string, opType string, b bool) {
	// 构造payloadParams
	payloadParams, err := constructBulletproofsSetData(opType)
	if err != nil {
		return
	}
	resp, err := client.InvokeContract(bulletproofsContractName, method, "", payloadParams, -1, b)
	if err != nil {
		fmt.Println(err)
	}

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

func constructBulletproofsSetData(opType string) ([]*common.KeyValuePair, error) {
	// 1. 对原始数据生成承诺和证明
	var err error
	proofA1, commitmentA1, openingA1, err = bulletproofs.ProveRandomOpening(A)
	if err != nil {
		return nil, err
	}

	//_, commitmentX, openingX, err := bulletproofs.ProveRandomOpening(X)
	//if err != nil {
	// return nil, err
	//}

	// 2. 计算并生成证明
	proofA2, _, err = bulletproofs.ProveAfterAddNum(A, X, openingA1, commitmentA1)
	//proofA2, _, err = bulletproofs.ProveAfterSubNum(A, X, openingA1, commitmentA1)
	//proofA2, _, _, err = bulletproofs.ProveAfterAddCommitment(A, X, openingA1, openingX, commitmentA1, commitmentX)
	//proofA2, _, _, err = bulletproofs.ProveAfterSubCommitment(A, X, openingA1, openingX, commitmentA1, commitmentX)
	//proofA2, _, _, err = bulletproofs.ProveAfterMulNum(A, 10, openingA1, commitmentA1)
	//proofA2, _, _, err = bulletproofs.ProveAfterMulNum(A, X, openingA1, commitmentA1)
	if err != nil {
		return nil, err
	}

	// 3. 原始 commitment-proof 对儿 和 新生成的 proof 上链
	// 3.1. 构造上链 payloadParams
	base64CommitmentA1Str := base64.StdEncoding.EncodeToString(commitmentA1)
	XStr := strconv.FormatInt(int64(X), 10)
	//base64X := base64.StdEncoding.EncodeToString([]byte(XStr))
	//base64X := base64.StdEncoding.EncodeToString(commitmentX)

	payloadParams := []*common.KeyValuePair{
		{
			Key:   "handletype",
			Value: []byte(opType),
		},
		{
			Key:   "para1",
			Value: []byte(base64CommitmentA1Str),
		},
		{
			Key:   "para2",
			Value: []byte(XStr),
		},
	}

	return payloadParams, nil
}

查询计算结果

// 查询计算结果
func testBulletProofsGetOpResult(t *testing.T, client *sdk.ChainClient, method string, opType string, b bool) {
	var err error
	commitmentA2, err = queryBulletproofsCommitment(client, bulletproofsContractName, method, opType, -1)
	require.Nil(t, err)
	fmt.Printf("QUERY %s contract resp -> : %s\n", bulletproofsContractName, commitmentA2)
}

func queryBulletproofsCommitment(client *sdk.ChainClient, contractName, method, bpMethod string, timeout int64) ([]byte, error) {

	resultBytes, err := queryBulletProofsCommitmentByHandleType(client, contractName, method, bpMethod, timeout)
	if err != nil {
		return nil, err
	}

	if bpMethod != BulletProofsVerify {
		resultBytes, err = base64.StdEncoding.DecodeString(string(resultBytes))
		if err != nil {
			return nil, err
		}
	}

	return resultBytes, nil
}

func queryBulletProofsCommitmentByHandleType(client *sdk.ChainClient, contractName, method, bpMethod string, timeout int64) ([]byte, error) {
	pair := []*common.KeyValuePair{
		{Key: "handletype", Value: []byte(bpMethod)},
	}

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

	result := resp.ContractResult.Result

	return result, nil
}

提供链下生成的 proof 和 查询到的链上计算结果 commitment,调用合约进行验证

// 调用合约验证 proof 和 查询的 commitment
func testBulletproofsVerify(client *sdk.ChainClient, method string, opType string, b bool) {
	// 构造payloadParams
	payloadParams, err := constructBulletproofsVerifyData(opType)
	if err != nil {
		return
	}
	resp, err := client.InvokeContract(bulletproofsContractName, method, "", payloadParams, -1, b)
	if err != nil {
		fmt.Println(err)
	}

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

func constructBulletproofsVerifyData(opType string) ([]*common.KeyValuePair, error) {
	// 1. 对原始数据生成承诺和证明
	//var err error
	//proofA1, commitmentA1, openingA1, err = bulletproofs.ProveRandomOpening(A)
	//if err != nil {
	//	return nil, err
	//}
	//
	//// 2. 计算并生成证明
	//proofA2, _, err := bulletproofs.ProveAfterAddNum(A, X, openingA1, commitmentA1)
	//if err != nil {
	//	return nil, err
	//}

	// 3. 原始 commitment-proof 对儿 和 新生成的 proof 上链
	// 3.1. 构造上链 payloadParams
	base64CommitmentA2Str := base64.StdEncoding.EncodeToString(commitmentA2)
	base64ProofA2Str := base64.StdEncoding.EncodeToString(proofA2)
	//base64ProofA2Str := base64.StdEncoding.EncodeToString(proofA1)

	payloadParams := []*common.KeyValuePair{
		{
			Key:   "handletype",
			Value: []byte(opType),
		},
		{
			Key:   "para1",
			Value: []byte(base64ProofA2Str),
		},
		{
			Key:   "para2",
			Value: []byte(base64CommitmentA2Str),
		},
	}

	return payloadParams, nil
}

查询验证结果

// 查询验证结果
func testBulletProofsGetVerifyResult(t *testing.T, client *sdk.ChainClient, method string, opType string, b bool) {
	result, err := queryBulletproofsCommitment(client, bulletproofsContractName, method, opType, -1)
	require.Nil(t, err)
	fmt.Printf("QUERY %s contract resp -> : %s\n", bulletproofsContractName, result)
}

执行测试:

======================================= 创建合约(异步)=======================================
2021-06-23 15:13:28.369	[DEBUG]	[SDK]	sdk/sdk_user_contract.go:31	[SDK] create [ContractCreate] to be signed payload
2021-06-23 15:13:28.369	[DEBUG]	[SDK]	sdk/sdk_user_contract.go:60	[SDK] create [ContractManage] to be signed payload
2021-06-23 15:13:28.405	[DEBUG]	[SDK]	sdk/sdk_client.go:368	[SDK] proposalRequest resp: message:"OK" 
CREATE bulletproofs-rust-1001 contract resp: message:"OK" contract_result:<result:"0caaf7f3f20641cfbd2ad17c6a440f0446cefa4344334260882ea412764bd8df" message:"OK" > 
============================= 调用合约 链上计算并存储 =============================
2021-06-23 15:13:33.469	[DEBUG]	[SDK]	sdk/sdk_user_contract.go:197	[SDK] begin to INVOKE contract, [contractName:bulletproofs-rust-1001]/[method:bulletproofs_test_set]/[txId:50e11d2c676744f2b14aded5bebb6cd8bd2f5c289c8a4df682995c455186f6a4]/[params:map[handletype:PedersenMulNum para1:XgR9wCSTBGZIegcUb7N171NDzWX/Vs7T3WIbgMSnG0c= para2:20]]
2021-06-23 15:13:33.470	[DEBUG]	[SDK]	sdk/sdk_client.go:368	[SDK] proposalRequest resp: message:"OK" 
2021-06-23 15:13:33.470	[DEBUG]	[SDK]	sdk/sdk_system_contract.go:29	[SDK] begin to QUERY system contract, [method:GET_TX_BY_TX_ID]/[txId:50e11d2c676744f2b14aded5bebb6cd8bd2f5c289c8a4df682995c455186f6a4]
2021-06-23 15:13:33.471	[DEBUG]	[SDK]	sdk/sdk_client.go:368	[SDK] proposalRequest resp: code:CONTRACT_FAIL message:"txStatusCode:4, resultCode:1, contractName[SYSTEM_CONTRACT_QUERY] method[GET_TX_BY_TX_ID] txType[QUERY_SYSTEM_CONTRACT], no such transaction, chainId:chain1" contract_result:<code:FAIL message:"no such transaction, chainId:chain1" > 
2021-06-23 15:13:33.972	[DEBUG]	[SDK]	sdk/sdk_system_contract.go:29	[SDK] begin to QUERY system contract, [method:GET_TX_BY_TX_ID]/[txId:50e11d2c676744f2b14aded5bebb6cd8bd2f5c289c8a4df682995c455186f6a4]
2021-06-23 15:13:33.972	[DEBUG]	[SDK]	sdk/sdk_client.go:368	[SDK] proposalRequest resp: code:CONTRACT_FAIL message:"txStatusCode:4, resultCode:1, contractName[SYSTEM_CONTRACT_QUERY] method[GET_TX_BY_TX_ID] txType[QUERY_SYSTEM_CONTRACT], no such transaction, chainId:chain1" contract_result:<code:FAIL message:"no such transaction, chainId:chain1" > 
2021-06-23 15:13:34.473	[DEBUG]	[SDK]	sdk/sdk_system_contract.go:29	[SDK] begin to QUERY system contract, [method:GET_TX_BY_TX_ID]/[txId:50e11d2c676744f2b14aded5bebb6cd8bd2f5c289c8a4df682995c455186f6a4]
2021-06-23 15:13:34.473	[DEBUG]	[SDK]	sdk/sdk_client.go:368	[SDK] proposalRequest resp: message:"SUCCESS" contract_result:<result:"\n\305\003\n\214\001\n\006chain1\022:\n\026wx-org1.chainmaker.org\022 \347|\2228\305\0364F\331B\371K\330\200<\304\363Q%O\207q\371r\024m{\374n\013\347\364\"@50e11d2c676744f2b14aded5bebb6cd8bd2f5c289c8a4df682995c455186f6a4(\235\276\313\206\006\022\221\001\n\026bulletproofs-rust-1001\022\025bulletproofs_test_set\0325\n\005para1\022,XgR9wCSTBGZIegcUb7N171NDzWX/Vs7T3WIbgMSnG0c=\032\013\n\005para2\022\00220\032\034\n\nhandletype\022\016PedersenMulNum\032G0E\002!\000\356\302\350\275\340~B\000\315\276r4\361]\240\242\330\250/<#V\274\250E\361\031\330/\327\232\356\002 T-\333\344Y\223F\374\004(\303Z\362\273:\341Mb\335<\342M\373\037\220\372F\367\001U\352\233\"W\0223\022,aL+AlqjxZ/dTSDDy7TuPVIaIbGna49l8Me8u+HxeFhw= \320\333\345\010\032 i\007\334\032\215\257Uk\023\004\352]\233\277\213\037\357\210\267\000\332\221\264\234c\307'\3444\273Y\363\020," message:"OK" > 
invoke contract success, resp: [code:0]/[msg:OK]/[contractResult:result:"aL+AlqjxZ/dTSDDy7TuPVIaIbGna49l8Me8u+HxeFhw=" gas_used:18443728 ]
============================= 查询计算结果 =============================
2021-06-23 15:13:39.474	[DEBUG]	[SDK]	sdk/sdk_user_contract.go:240	[SDK] begin to QUERY contract, [contractName:bulletproofs-rust-1001]/[method:bulletproofs_test_get]/[txId:67dcb1ffcad54fa28062fe184c9a27e2258e597779634f17b5c23cd522eb88de]/[params:map[handletype:PedersenMulNum]]
2021-06-23 15:13:39.477	[DEBUG]	[SDK]	sdk/sdk_client.go:368	[SDK] proposalRequest resp: message:"SUCCESS" contract_result:<result:"aL+AlqjxZ/dTSDDy7TuPVIaIbGna49l8Me8u+HxeFhw=" gas_used:13003432 > 
QUERY bulletproofs-rust-1001 contract resp -> : h�����g�SH0��;�T��li���|1�.�|^
============================= 调用合约验证 proof 和 查询的 commitment =============================
2021-06-23 15:13:44.478	[DEBUG]	[SDK]	sdk/sdk_user_contract.go:197	[SDK] begin to INVOKE contract, [contractName:bulletproofs-rust-1001]/[method:bulletproofs_test_set]/[txId:c8d9f0ae6faa413d84662324fe23935374cbebe0a2fa4d21a6b3b5f4b19b3a59]/[params:map[handletype:BulletproofsVerify para1:xtngQVNXzDIMmsXBW5656k/35X4Pa+mukB8wmVYGPxMG0w2mNo/gZ++mSBXocQfAW2q+l5uiQCLkZdroxCIEMAwCgeC98coHu5isUWzHVrObxuw4gTRhSjv3MpEO3dZSFpmEzwDzJr41syyRrVBHdoh24D/5PmyJvVGjdQgf+CZVm2UU50Rb8ODOIr/YHJh4qdXDPMG09FOLeU3pDMFpCYVKGBJcUCidUC+dU74zHXN56Xt4jsgzSsMqsX3hY5AP8invNY4wwp708fqq+kj864fvG7A7SUpkQk5C2izjqgaInTDvdU9h6Velz7P8cjG3bt9CeUMjqoOgMEwmrWIbJo7RECeLidOORw5rrAVyPKY9zKQk3LIgIqIt40dc7IZF6LLZ+EZbr+s/Yxe1wYR5ZUU6LU418E0UUMQ5y2AJMFjMRjBIpXHgCTdOo1+fEG4vyNUjlBhkS7ULZvoalxVfD2DPquye3XdT0yEFf7VTscM2TTx35XDV0vlI64wQsM0neGLRvqL/9nhu3f+44G/mqAavZiG7tmoCa9W6xYYxKSJG50duCmqlcrYVXABpFhAmHpW6p6cIgM9ePuRzyrkbCSAOKlXt1QINHouVsnj0G4cZDbliwvkQppn2RfC/uLBqlFejmqF4bpTgyEj7y206FtK2Fz8oevBQioiKO9mq435e7YRphiZHmFyrYFHvWO5EM6ZQ7ihh27axIGDHPNfnZjxMLM19DTTXK17vjpk3HksjkdWF53erB7WGJa7UDb1vZq1OUb3eqSW1O/Sk1Ck1YtfXquCsqpYsIw/g1asir1TSpouobOFopgEI2vobD7DSq1j5BrEcPipyssVhum54A/KEckOZ0USY0gZs4oAEBwF7OYqG4xoTh2FKgox593kI para2:aL+AlqjxZ/dTSDDy7TuPVIaIbGna49l8Me8u+HxeFhw=]]
2021-06-23 15:13:44.480	[DEBUG]	[SDK]	sdk/sdk_client.go:368	[SDK] proposalRequest resp: message:"OK" 
2021-06-23 15:13:44.480	[DEBUG]	[SDK]	sdk/sdk_system_contract.go:29	[SDK] begin to QUERY system contract, [method:GET_TX_BY_TX_ID]/[txId:c8d9f0ae6faa413d84662324fe23935374cbebe0a2fa4d21a6b3b5f4b19b3a59]
2021-06-23 15:13:44.482	[DEBUG]	[SDK]	sdk/sdk_client.go:368	[SDK] proposalRequest resp: code:CONTRACT_FAIL message:"txStatusCode:4, resultCode:1, contractName[SYSTEM_CONTRACT_QUERY] method[GET_TX_BY_TX_ID] txType[QUERY_SYSTEM_CONTRACT], no such transaction, chainId:chain1" contract_result:<code:FAIL message:"no such transaction, chainId:chain1" > 
2021-06-23 15:13:44.982	[DEBUG]	[SDK]	sdk/sdk_system_contract.go:29	[SDK] begin to QUERY system contract, [method:GET_TX_BY_TX_ID]/[txId:c8d9f0ae6faa413d84662324fe23935374cbebe0a2fa4d21a6b3b5f4b19b3a59]
2021-06-23 15:13:44.984	[DEBUG]	[SDK]	sdk/sdk_client.go:368	[SDK] proposalRequest resp: code:CONTRACT_FAIL message:"txStatusCode:4, resultCode:1, contractName[SYSTEM_CONTRACT_QUERY] method[GET_TX_BY_TX_ID] txType[QUERY_SYSTEM_CONTRACT], no such transaction, chainId:chain1" contract_result:<code:FAIL message:"no such transaction, chainId:chain1" > 
2021-06-23 15:13:45.484	[DEBUG]	[SDK]	sdk/sdk_system_contract.go:29	[SDK] begin to QUERY system contract, [method:GET_TX_BY_TX_ID]/[txId:c8d9f0ae6faa413d84662324fe23935374cbebe0a2fa4d21a6b3b5f4b19b3a59]
2021-06-23 15:13:45.487	[DEBUG]	[SDK]	sdk/sdk_client.go:368	[SDK] proposalRequest resp: code:CONTRACT_FAIL message:"txStatusCode:4, resultCode:1, contractName[SYSTEM_CONTRACT_QUERY] method[GET_TX_BY_TX_ID] txType[QUERY_SYSTEM_CONTRACT], no such transaction, chainId:chain1" contract_result:<code:FAIL message:"no such transaction, chainId:chain1" > 
2021-06-23 15:13:46.487	[DEBUG]	[SDK]	sdk/sdk_system_contract.go:29	[SDK] begin to QUERY system contract, [method:GET_TX_BY_TX_ID]/[txId:c8d9f0ae6faa413d84662324fe23935374cbebe0a2fa4d21a6b3b5f4b19b3a59]
2021-06-23 15:13:46.489	[DEBUG]	[SDK]	sdk/sdk_client.go:368	[SDK] proposalRequest resp: message:"SUCCESS" contract_result:<result:"\n\241\n\n\214\001\n\006chain1\022:\n\026wx-org1.chainmaker.org\022 \347|\2228\305\0364F\331B\371K\330\200<\304\363Q%O\207q\371r\024m{\374n\013\347\364\"@c8d9f0ae6faa413d84662324fe23935374cbebe0a2fa4d21a6b3b5f4b19b3a59(\250\276\313\206\006\022\225\010\n\026bulletproofs-rust-1001\022\025bulletproofs_test_set\032\212\007\n\005para1\022\200\007xtngQVNXzDIMmsXBW5656k/35X4Pa+mukB8wmVYGPxMG0w2mNo/gZ++mSBXocQfAW2q+l5uiQCLkZdroxCIEMAwCgeC98coHu5isUWzHVrObxuw4gTRhSjv3MpEO3dZSFpmEzwDzJr41syyRrVBHdoh24D/5PmyJvVGjdQgf+CZVm2UU50Rb8ODOIr/YHJh4qdXDPMG09FOLeU3pDMFpCYVKGBJcUCidUC+dU74zHXN56Xt4jsgzSsMqsX3hY5AP8invNY4wwp708fqq+kj864fvG7A7SUpkQk5C2izjqgaInTDvdU9h6Velz7P8cjG3bt9CeUMjqoOgMEwmrWIbJo7RECeLidOORw5rrAVyPKY9zKQk3LIgIqIt40dc7IZF6LLZ+EZbr+s/Yxe1wYR5ZUU6LU418E0UUMQ5y2AJMFjMRjBIpXHgCTdOo1+fEG4vyNUjlBhkS7ULZvoalxVfD2DPquye3XdT0yEFf7VTscM2TTx35XDV0vlI64wQsM0neGLRvqL/9nhu3f+44G/mqAavZiG7tmoCa9W6xYYxKSJG50duCmqlcrYVXABpFhAmHpW6p6cIgM9ePuRzyrkbCSAOKlXt1QINHouVsnj0G4cZDbliwvkQppn2RfC/uLBqlFejmqF4bpTgyEj7y206FtK2Fz8oevBQioiKO9mq435e7YRphiZHmFyrYFHvWO5EM6ZQ7ihh27axIGDHPNfnZjxMLM19DTTXK17vjpk3HksjkdWF53erB7WGJa7UDb1vZq1OUb3eqSW1O/Sk1Ck1YtfXquCsqpYsIw/g1asir1TSpouobOFopgEI2vobD7DSq1j5BrEcPipyssVhum54A/KEckOZ0USY0gZs4oAEBwF7OYqG4xoTh2FKgox593kI\0325\n\005para2\022,aL+AlqjxZ/dTSDDy7TuPVIaIbGna49l8Me8u+HxeFhw=\032 \n\nhandletype\022\022BulletproofsVerify\032G0E\002 i\273?\010k\270\352.jX\234$\036\242h\335\034O0\342I^*\250\344\222\035 !p\371\036\002!\000\247a!\320\331\021\274\311n\344\025\352\265\350\"EsOg\335\232\013\006M\034\306\363\201\360\264\366\345\"/\022\013\022\004MQ== \307\355\270\017\032 0i.\272^\367\177)\307/\307\004\304\037\364\357\366M\321\352\364\205f2*\211\201\237\320\271\242G\020-" message:"OK" > 
invoke contract success, resp: [code:0]/[msg:OK]/[contractResult:result:"MQ==" gas_used:32388807 ]
============================= 查询验证结果 =============================
2021-06-23 15:13:51.489	[DEBUG]	[SDK]	sdk/sdk_user_contract.go:240	[SDK] begin to QUERY contract, [contractName:bulletproofs-rust-1001]/[method:bulletproofs_test_get]/[txId:d78d77c847ba4cc697556a68ae39498b83750e8dc0e64382b7738395aa1b9692]/[params:map[handletype:BulletproofsVerify]]
2021-06-23 15:13:51.493	[DEBUG]	[SDK]	sdk/sdk_client.go:368	[SDK] proposalRequest resp: message:"SUCCESS" contract_result:<result:"1" gas_used:13146253 > 
QUERY bulletproofs-rust-1001 contract resp -> : 1
--- PASS: TestBulletproofsContractCounterGo (28.34s)
PASS