操作场景
用户可以利用 TBaaS 提供的同态加密的能力,便捷的在智能合约中使用同态加密。使用同态加密可以分为以下三个步骤:
1. 使用 TBaaS 提供的 paitool 生成同态公私钥,同时使用生成的公钥对用户数据线下加密。
2. 使用 TBaaS 提供的 Go 语言智能合约包 paillier,编写同态相关的业务层操作,例如同态加、同态减、部分同态乘等。
3. 当业务逻辑完成后,用户可以从智能合约中获取到对应的同态加密数据,线下使用对应的同态私钥进行数据解密,从而可以获取链上业务操作完成后的真实数据。
操作步骤
Paitool 同态公私钥生成以及数据加密
1. 依次执行以下命令,使用 paitool 生成两组同态加密的公私钥对。
./paitool genkey -length=2048 -pkout=pk1.pai -skout=sk1.pai./paitool genkey -length=2048 -pkout=pk2.pai -skout=sk2.pai
其中,pk1.pai, pk2.pai 是同态加密公钥文件,sk1.pai, sk2.pai 是同态加密私钥文件。
2. 依次执行以下命令,使用 paitool 对数字100和200分别用不同的公钥进行同态加密。
./paitool encrypt -pkin=pk1.pai -plaintext=100 -cipherout=cipher1.pai./paitool encrypt -pkin=pk2.pai -plaintext=200 -cipherout=cipher2.pai
其中,cipher1.pai 是数字100同态加密后的密文数据,cipher2.pai 是数字200加密后的密文数据。
智能合约同态加密示例
Hyperledger Fabric 提供了很多官方的智能合约样例,具体请参考 fabric 官方示例。本示例以 Hyperledger Fabric 官方提供的 example02 样例为例进行同态修改。该示例的 Init 函数用于初始化两个 key/value 键值对,其中 value 是用同态加密后的数据,Invoke 函数用于根据不同业务逻辑进行细分调用,最终调用以下业务逻辑接口:
invoke:用于 key 之间的 value 转移。
query:用于查询 key 所对应的值。
Init 函数示例
Init 函数主要用于在智能合约实例化和升级的时候默认调用。在实现 Init 函数的过程中,可以使用 Go 语言版本的合约 API 来对参数和账本进行操作。在这个示例中,通过调用 API GetFunctionAndParameters 获取到用户输入参数。在获取用户输入参数后,通过调用 API PutState 将数据写到账本中。
// Init函数用于初始化两个键值对,用户输入的参数为KEY1_NAME, VALUE1, KEY2_NAME, VALUE2,其中VALUE1和VALUE2都是同态加密后的数据// 在例子中,VALUE1是cipher1.pai的内容, VALUE2是ciphe2.pai的内容func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {fmt.Println("ex02 Init")// 调用API GetFunctionAndParameters 获取用户输入参数_, args := stub.GetFunctionAndParameters()var A, B string // Entitiesvar Aval, Bval string // Asset holdingsvar err errorif len(args) != 4 {return shim.Error("Incorrect number of arguments. Expecting 4")}// Initialize the chaincodeA = args[0]Aval = args[1]B = args[2]Bval = args[3]// 调用API PutState把数据写入账本// Write the state to the ledgererr = stub.PutState(A, []byte(Aval))if err != nil {return shim.Error(err.Error())}err = stub.PutState(B, []byte(Bval))if err != nil {return shim.Error(err.Error())}return shim.Success(nil)}
Invoke 函数示例
Invoke 函数可以对用户的不同的智能合约业务逻辑进行拆分。本示例通过调用 API GetFunctionAndParameters 获取到用户的具体业务类型和参数,再分别调用不同的函数,如 invoke 和 query 函数。
// Invoke把用户调用的function细分到几个子function, 包含invoke和queryfunc (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {fmt.Println("ex02 Invoke")// 调用API GetFunctionAndParameters获取用户输入的业务类型和参数function, args := stub.GetFunctionAndParameters()if function == "invoke" {// Make payment of X units from A to Breturn t.invoke(stub, args)} else if function == "query" {// the old "Query" is now implemtned in invokereturn t.query(stub, args)}return shim.Error("Invalid invoke function name")}
业务逻辑 invoke 函数示例
业务逻辑 invoke 函数主要用于实现业务逻辑中的资产转移。本示例中通过调用 API GetState 获取到 KEY 对应的同态加密资产总值,通过调用用户业务逻辑实现资产转移,通过调用 API PutState 将用户最终资产写入账本。
在此过程中,调用了同态加密的接口 GetPublicKeyFromHex 用于获取同态公钥,GetCiphertextFromHex 用于获取同态加密数据,Sub 用于同态密文和明文相减,Add 用于同态密文和明文相加以及 GetCiphertextHex 用于获取同态加密后的16进制密文数据。
// invoke实现两个键之间的value转移,输入为KEY1_NAME, KEY1_PUBKEYINHEX, KEY2_NAME,KEY2_PUBKEYINHEX,VALUE// 在例子中,KEY1_PUBKEYINHEX是pk1.pai内容的base64, KEY2_PUBKEYINHEX是pk2.pai的内容base64func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response {var A, B string // Entitiesvar Apkpem, Bpkpem []byte //public key in hexvar Aval, Bval string // Asset encrypted holdingsvar X *big.Int // Transaction valuevar err errorif len(args) != 5 {return shim.Error("Incorrect number of arguments. Expecting 5")}A = args[0]Apkpem, err = base64.StdEncoding.DecodeString(args[1])if err != nil {return shim.Error("Failed to get Apkpem in hex")}B = args[2]Bpkpem, err = base64.StdEncoding.DecodeString(args[3])if err != nil{return shim.Error("Failed to get Bpkpem in hex")}X, isOK := new(big.Int).SetString(args[4], 10)if !isOK {return shim.Error("Fail to get X")}// Get the state from the ledger// API GetState获取对应账户的资产,这里的资产是同态加密后的数据Avalbytes, err := stub.GetState(A)if err != nil {return shim.Error("Failed to get state")}if Avalbytes == nil {return shim.Error("Entity not found")}Aval = string(Avalbytes)Bvalbytes, err := stub.GetState(B)if err != nil {return shim.Error("Failed to get state")}if Bvalbytes == nil {return shim.Error("Entity not found")}Bval = string(Bvalbytes)// 执行具体业务逻辑,这里是对应资产进行转移// Perform the execution// 调用同态接口GetPublicKeyFromHex获取公钥信息Apk, err := paillier.GetPublicKeyFromHex(string(Apkpem))if err != nil {return shim.Error("Fail to get A public key in PublicKey")}// 调用同态接口GetCiphertextFromHex获取同态密文信息Acipher, err := paillier.GetCiphertextFromHex(Aval)if err != nil {return shim.Error("Fail to get Ciphertext for A")}// 调用同态接口Sub执行密文和明文相减Aciphernew, err := paillier.Sub(Apk, Acipher, X.Text(10))if err != nil {return shim.Error("Fail to compute Aciphernew")}// 调用同态接口GetCiphertextHex获取同态密文16进制stringAvalnew, err := paillier.GetCiphertextHex(Aciphernew)if err != nil {return shim.Error("Fail to get Avalnew")}Bpk, err := paillier.GetPublicKeyFromHex(string(Bpkpem))if err != nil {return shim.Error("Fail to get B public key in PublicKey")}Bcipher, err := paillier.GetCiphertextFromHex(Bval)if err != nil {return shim.Error("Fail to get Ciphertext for B")}// 调用同态接口Add执行密文和明文相加Bciphernew, err := paillier.Add(Bpk, Bcipher, X.Text(10))if err != nil {return shim.Error("Fail to compute Bciphernew")}Bvalnew, err := paillier.GetCiphertextHex(Bciphernew)if err != nil {return shim.Error("Fail to get Bvalnew")}// API PutState将对应资产写入账本// Write the state back to the ledgererr = stub.PutState(A, []byte(Avalnew))if err != nil {return shim.Error(err.Error())}err = stub.PutState(B, []byte(Bvalnew))if err != nil {return shim.Error(err.Error())}return shim.Success(nil)}
业务逻辑 query 函数示例
业务逻辑 query 函数主要用于实现业务逻辑中的账户查询功能,本示例通过调用 API GetState 查询对应账户的资产。
// query主要是查询键对应的值,输入为KEY_NAME// query callback representing the query of a chaincodefunc (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {var A string // Entitiesvar err errorif len(args) != 1 {return shim.Error("Incorrect number of arguments. Expecting name of the person to query")}A = args[0]// Get the state from the ledgerAvalbytes, err := stub.GetState(A)if err != nil {jsonResp := "{\\"Error\\":\\"Failed to get state for " + A + "\\"}"return shim.Error(jsonResp)}if Avalbytes == nil {jsonResp := "{\\"Error\\":\\"Nil amount for " + A + "\\"}"return shim.Error(jsonResp)}return shim.Success(Avalbytes)}
Paitool 同态数据解密
依次执行以下命令,通过智能合约从链上获取到最新的业务逻辑操作过的同态密文数据。拥有对应同态私钥的用户,可以解密出相应的明文。
./paitool decrypt -skin=sk1.pai -cipherin=cipher1new.pai./paitool decrypt -skin=sk2.pai -cipherin=cipher2new.pai
其中,cipher1new.pai 对应的是智能合约中 KEY1_NAME 存储的值,cipher2new.pai 对应的是智能合约中 KEY2_NAME 存储的值。