After initializing evm.interpreter make sure to point fhevmEnvironment to it evm.fhevmEnvironment.interpreter = evm.interpreter then initialize it fhevm.InitFhevm(&evm.fhevmEnvironment)
Update RunPrecompiledContract
After changing precompiled contract interface in 2, we have to change usages of:
// Create creates a new contract using code as deployment code.func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))// Create the ciphertext storage if not already created.if evm.StateDB.GetNonce(fhevm.CiphertextStorageAddress) ==0 { evm.StateDB.CreateAccount(fhevm.CiphertextStorageAddress) evm.StateDB.SetNonce(fhevm.CiphertextStorageAddress, 1) }return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, CREATE)}// Create2 creates a new contract using code as deployment code.//// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
codeAndHash :=&codeAndHash{code: code} contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes())// Create the ciphertext storage if not already created.if evm.StateDB.GetNonce(fhevm.CiphertextStorageAddress) ==0 { evm.StateDB.CreateAccount(fhevm.CiphertextStorageAddress) evm.StateDB.SetNonce(fhevm.CiphertextStorageAddress, 1) }return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2)}
Implement EVMEnvironment interface
Now implement the fhevm.EVMEnvironment interface for FhevmImplementation:
func (evm *EVM) FhevmEnvironment() fhevm.EVMEnvironment { return&evm.fhevmEnvironment }// If you are using OpenTelemetry, you can return a context that the precompiled fhelib will use// to trace its internal functions. Otherwise, just return nilfunc (evm *FhevmImplementation) OtelContext() context.Context {returnnil}func (evm *FhevmImplementation) GetState(addr common.Address, hash common.Hash) common.Hash {return evm.interpreter.evm.StateDB.GetState(addr, hash)}func (evm *FhevmImplementation) SetState(addr common.Address, hash common.Hash, input common.Hash) { evm.interpreter.evm.StateDB.SetState(addr, hash, input)}func (evm *FhevmImplementation) GetNonce(addr common.Address) uint64 {return evm.interpreter.evm.StateDB.GetNonce(addr)}func (evm *FhevmImplementation) AddBalance(addr common.Address, value *big.Int) { evm.interpreter.evm.StateDB.AddBalance(addr, value)}func (evm *FhevmImplementation) GetBalance(addr common.Address) *big.Int {return evm.interpreter.evm.StateDB.GetBalance(addr)}func (evm *FhevmImplementation) Suicide(addr common.Address) bool { evm.interpreter.evm.StateDB.SelfDestruct(addr)return evm.interpreter.evm.StateDB.HasSelfDestructed(addr)}func (evm *FhevmImplementation) GetDepth() int {return evm.interpreter.evm.depth}func (evm *FhevmImplementation) IsCommitting() bool {return!evm.interpreter.evm.isGasEstimation}func (evm *FhevmImplementation) IsEthCall() bool {return evm.interpreter.evm.isEthCall}func (evm *FhevmImplementation) IsReadOnly() bool {return evm.interpreter.readOnly}func (evm *FhevmImplementation) GetLogger() fhevm.Logger {return evm.logger}func (evm *FhevmImplementation) FhevmData() *fhevm.FhevmData {return&evm.data}func (evm *FhevmImplementation) FhevmParams() *fhevm.FhevmParams {return&evm.params}func (evm *FhevmImplementation) CreateContract(caller common.Address, code []byte, gas uint64, value *big.Int, address common.Address) ([]byte, common.Address, uint64, error) {
return evm.interpreter.evm.create(AccountRef(caller), &codeAndHash{code: code}, gas, value, address, CREATE)}func (evm *FhevmImplementation) CreateContract2(caller common.Address, code []byte, codeHash common.Hash, gas uint64, value *big.Int, address common.Address) ([]byte, common.Address, uint64, error) {
return evm.interpreter.evm.create(AccountRef(caller), &codeAndHash{code: code, hash: codeHash}, gas, value, address, CREATE2)
}
Step 5: update core/vm/instructions.go
Update opSstore
Rewrite opSstore by a call to their fhevm implementation:
In func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
Just before original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32()), add this block to increase SSTORE gas cost for storing a ciphertext:
Add isGasEstimation, isEthCall bool arguments to func DoCall and forward them in the call to doCall
Update usages of doCall and DoCall by simply setting IsEthCall to true when itβs a call, and IsGasEstimation to true when itβs estimating gas
Step 10: update graphql/graphql.go
Update usages of doCall and DoCall by simply setting IsEthCall to true when itβs a call, and IsGasEstimation to true when itβs estimating gas
Initial Setup
In order to use the library, you need to clone the repository and build it. This is required because the library depends on the tfhe-rs library that needs to be built from source (for now), and Go doesn't support such a build.
You can now use it in your project by adding it to go.mod, and adding a replace to point to your local build. An example using fhevm-go v1.0.0:
The replace is necessary for now as Go build system can't build the tfhe-rs library that fhevm-go needs. It's therefore necessary that we build it manually as mentioned above, then point to our ready-to-use directory in go.mod
FheLib Library
FheLib is a library implemented inside fhevm-go. It offers FHE-related functionalities such as homomorphic operations, input verification and so on. FheLib is exposed as a single precompiled contract (or a precompile for short) that is integrated into the underlying blockchain.
FheLib functions can be called by calling the FheLib precompile with a respective EVM function selector.
This page describes the required inputs, behaviours and outputs of some of these functions.
GetCiphertext Function (selector: ff627e77)
The GetCiphertext function returns a serialized TFHE ciphertext given:
the ebool/e(u)int value (also called a handle) for which the ciphertext is requested
GetCiphertext only works via the eth_call RPC.
To call GetCiphertext via eth_call, the following Python can serve as an example:
import http.clientimport json# This is the address of the FheLib precompile. This value is hardcoded per blockchain.fhe_lib_precompile_address ="0x000000000000000000000000000000000000005d"# The ebool/e(u)int value for which the ciphertext is requested.handle ="f038cdc8bf630e239f143abeb039b91ec82ec17a8460582e7a409fa551030c06"# The function selector of GetCiphertext.get_ciphertext_selector ="ff627e77"# Call the FheLib precompile with `data` being the handle to the ciphertext.payload ={"jsonrpc":"2.0","method":"eth_call","params": [{"to": fhe_lib_precompile_address,"data":"0x"+ handle},"latest" ],"id":1,}con = http.client.HTTPConnection("localhost", 8545)con.request("POST", "/", body=json.dumps(payload), headers={"Content-Type": "application/json"})resp = json.loads(con.getresponse().read())# Remove leading "0x" and decode hex to get a byte buffer with the ciphertext.ciphertext =bytes.fromhex(resp["result"][2:])
fhevm-go is an open-source library used to easily integrate the fhEVM into an EVM-compatible blockchain.
Main features
fhevm-go gives your EVM the ability to compute on encrypted data using fully homomorphic encryption by:
a collection of operations on encrypted data via precompiled contracts
various additional EVM components that support encrypted computation
Target users
The library helps EVM maintainers to extend their EVM with the power of FHE. If you are looking for a library to deploy and use smart contracts on an fhEVM, you should better look at fhevm
License
This software is distributed under the BSD-3-Clause-Clear license. If you have any questions, please contact us at hello@zama.ai.