From 831f55c4133c19090cab66f170d81c07a806e15c Mon Sep 17 00:00:00 2001 From: Petar Ivanov <29689712+dartdart26@users.noreply.github.com> Date: Tue, 3 Jan 2023 14:05:44 +0200 Subject: [PATCH] Add support for ciphertext verification Send a POST request to the `tfhe-zkpoks` service with the input and, if successful, treat the response as a verified ciphertext. Add the `verify` and `verify_rpc_address` configuration options to control whether verification should be done and the RPC address of the `tfhe-zkpoks` service, respectively. --- core/vm/contracts.go | 62 +++++++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 413f7b150a9c..3c6f4e4eddf7 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -1229,6 +1229,11 @@ type tomlConfigOptions struct { Mode string OracleDBAddress string } + + Zk struct { + Verify bool + VerifyRPCAddress string + } } var tomlConfig tomlConfigOptions @@ -1256,7 +1261,8 @@ func generateEd25519Keys() error { return nil } -var httpClient http.Client = http.Client{} +var requireHttpClient http.Client = http.Client{} +var zkHttpClient http.Client = http.Client{} var publicSignatureKey []byte var privateSignatureKey []byte @@ -1474,7 +1480,7 @@ func fheEncryptToNetworkKey(value uint64) ([]byte, error) { return ctBytes, nil } -func fheEncryptToUserKey(value uint64, userAddress common.Address) (ret []byte, err error) { +func fheEncryptToUserKey(value uint64, userAddress common.Address) ([]byte, error) { if value > 15 { return nil, errors.New("input must be less than 15") } @@ -1514,13 +1520,39 @@ func (e *verifyCiphertext) RequiredGas(input []byte) uint64 { return 8 } -func (e *verifyCiphertext) Run(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, readOnly bool) (ret []byte, err error) { - const CiphertextSize = 65544 - - // TODO: Verify proof. - // For testing: If input size <= `CiphertextSize`, treat the whole input as ciphertext. - ciphertext := input[0:minInt(CiphertextSize, len(input))] +func verifyZkProof(input []byte) ([]byte, error) { + req, err := http.NewRequest(http.MethodPost, tomlConfig.Zk.VerifyRPCAddress, bytes.NewReader(input)) + if err != nil { + return nil, err + } + req.Header.Add("Content-Type", "application/msgpack") + resp, err := zkHttpClient.Do(req) + if err != nil { + return nil, err + } + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failure HTTP status code on ZK verify: %d", resp.StatusCode) + } + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.New("failed reading ZK verification response body") + } + return body, nil +} +func (e *verifyCiphertext) Run(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, readOnly bool) ([]byte, error) { + var ciphertext []byte + if !tomlConfig.Zk.Verify { + // For testing: If input size <= `CiphertextSize`, treat the whole input as ciphertext. + const CiphertextSize = 65544 + ciphertext = input[0:minInt(CiphertextSize, len(input))] + } else { + var err error + ciphertext, err = verifyZkProof(input) + if err != nil { + return nil, err + } + } ctHash := crypto.Keccak256Hash(ciphertext) accessibleState.Interpreter().verifiedCiphertexts[ctHash] = &verifiedCiphertext{accessibleState.Interpreter().evm.depth, ciphertext} return ctHash.Bytes(), nil @@ -1529,13 +1561,13 @@ func (e *verifyCiphertext) Run(accessibleState PrecompileAccessibleState, caller // Return a memory with a layout that matches the `bytes` EVM type, namely: // - 32 byte integer in big-endian order as length // - the actual bytes in the `bytes` value -func toEVMBytes(input []byte) (ret []byte) { +func toEVMBytes(input []byte) []byte { len := uint64(len(input)) lenBytes32 := uint256.NewInt(len).Bytes32() - ret = make([]byte, 0, len+32) + ret := make([]byte, 0, len+32) ret = append(ret, lenBytes32[:]...) ret = append(ret, input...) - return + return ret } type reencrypt struct{} @@ -1617,12 +1649,12 @@ func putRequire(ciphertext []byte, value bool) error { if err != nil { return err } - resp, err := httpClient.Do(req) + resp, err := requireHttpClient.Do(req) if err != nil { return err } if resp.StatusCode != 200 { - return fmt.Errorf("failure HTTP status code: %d", resp.StatusCode) + return fmt.Errorf("failure HTTP status code on require PUT: %d", resp.StatusCode) } return nil } @@ -1633,12 +1665,12 @@ func getRequire(ciphertext []byte) (bool, error) { if err != nil { return false, nil } - resp, err := httpClient.Do(req) + resp, err := requireHttpClient.Do(req) if err != nil { return false, err } if resp.StatusCode != 200 { - return false, fmt.Errorf("failure HTTP status code: %d", resp.StatusCode) + return false, fmt.Errorf("require: failure HTTP status code on require GET: %d", resp.StatusCode) } body, err := ioutil.ReadAll(resp.Body) if err != nil {