From 17300e2bd22582180cd5ab5da83a810fa77c7a84 Mon Sep 17 00:00:00 2001 From: youben11 Date: Tue, 16 Jan 2024 15:33:19 +0100 Subject: [PATCH 1/3] chore: add license --- LICENSE | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..86d4f2c --- /dev/null +++ b/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause Clear License + +Copyright © 2024 ZAMA. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or other +materials provided with the distribution. + +3. Neither the name of ZAMA nor the names of its contributors may be used to endorse +or promote products derived from this software without specific prior written permission. + +NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. +THIS SOFTWARE IS PROVIDED BY THE ZAMA AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +ZAMA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file From 17df073c2f56a7fb059940620283fbd4b8905df8 Mon Sep 17 00:00:00 2001 From: youben11 Date: Tue, 16 Jan 2024 15:33:33 +0100 Subject: [PATCH 2/3] chore: add README --- Integration.md | 284 +++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 81 ++++++++++++++ 2 files changed, 365 insertions(+) create mode 100644 Integration.md create mode 100644 README.md diff --git a/Integration.md b/Integration.md new file mode 100644 index 0000000..f88abd3 --- /dev/null +++ b/Integration.md @@ -0,0 +1,284 @@ +# Integration + +This document is a guide listing detailed steps to integrate [fhevm-go](https://github.com/zama-ai/fhevm-go) into [go-ethereum](https://github.com/ethereum/go-ethereum) or any other implementations that follows the same architecture. + +> [!NOTE] +> This document is based on go-ethereum v1.13.5 + +### Steps + +1. In `core/state_transition.go: func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation bool, isHomestead, isEIP2028 bool, isEIP3860 bool) (uint64, error)` replace the last return with `return fhevm.TxDataFractionalGas(gas), nil` which will impact tests as the returned gas won’t be the same. +2. In `core/vm/contracts.go` + - Update the `PrecompiledContract` interface to: + + ```go + type PrecompileAccessibleState interface { + Interpreter() *EVMInterpreter + } + + type PrecompiledContract interface { + RequiredGas(accessibleState PrecompileAccessibleState, input []byte) uint64 + Run(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, readOnly bool) ([]byte, error) + } + ``` + + You will have to update all previous uses of this interface. + + - Add `common.BytesToAddress([]byte{93}): &fheLib{}` to all precompiled contract maps (e.g. `PrecompiledContractsHomestead` ) + + **Note:** We used 93 as the address of the precompile here, but you can choose any other address as far as client libraries know where to find it. + + - Implement the `fheLib` precompile + + ```go + // fheLib calls into the different precompile functions available in the fhevm + type fheLib struct{} + + func (c *fheLib) RequiredGas(accessibleState PrecompileAccessibleState, input []byte) uint64 { + return fhevm.FheLibRequiredGas(accessibleState.Interpreter().evm.FhevmEnvironment(), input) + } + + func (c *fheLib) Run(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, readOnly bool) ([]byte, error) { + return fhevm.FheLibRun(accessibleState.Interpreter().evm.FhevmEnvironment(), caller, addr, input, readOnly) + } + ``` + + - Rewrite `RunPrecompiledContract` as: + + ```go + func RunPrecompiledContract(p PrecompiledContract, accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) { + gasCost := p.RequiredGas(accessibleState, input) + if suppliedGas < gasCost { + return nil, 0, ErrOutOfGas + } + + suppliedGas -= gasCost + output, err := p.Run(accessibleState, caller, addr, input, accessibleState.Interpreter().readOnly) + + return output, suppliedGas, err + } + ``` + +3. In `core/vm/errors.go` + - Register errors at initialization in `fhevm-go` to be recognized at runtime + + ```go + func init() { + fhevm.RegisterErrors(ErrOutOfGas, ErrCodeStoreOutOfGas, ErrDepth, ErrInsufficientBalance, + ErrContractAddressCollision, ErrExecutionReverted, ErrMaxInitCodeSizeExceeded, ErrMaxCodeSizeExceeded, + ErrInvalidJump, ErrWriteProtection, ErrReturnDataOutOfBounds, ErrGasUintOverflow, ErrInvalidCode, + ErrNonceUintOverflow, nil, nil, nil) + } + ``` + +4. In `core/vm/evm.go` + - Update `EVM` struct with new fields + + ```go + fhevmEnvironment FhevmImplementation + isGasEstimation bool + isEthCall bool + ``` + + While implementing `fhevmEnvironment` as + + ```go + type FhevmImplementation struct { + interpreter *EVMInterpreter + data fhevm.FhevmData + logger fhevm.Logger + params fhevm.FhevmParams + } + ``` + + - In `func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig *params.ChainConfig, config Config) *EVM` + - Initialize `isGasEstimation` using `config.IsGasEstimation` + - Initialize `isEthCall` using `config.IsEthCall` + - Initialize `fhevmEnvironment` with `FhevmImplementation{interpreter: nil, logger: &fhevm.DefaultLogger{}, data: fhevm.NewFhevmData(), params: fhevm.DefaultFhevmParams()}` + - After initializing `evm.interpreter` make sure to point `fhevmEnvironment` to it `evm.fhevmEnvironment.interpreter = evm.interpreter` then initialize it `fhevm.InitFhevm(&evm.fhevmEnvironment)` + - After changing precompiled contract interface in 2, we have to change usages of `RunPrecompiledContract` from `RunPrecompiledContract(p, input, gas)` to `RunPrecompiledContract(p, evm, caller.Address(), addr, input, gas)` + - Rewrite `Create` and `Create2` by a call to their fhevm implementation + + ```go + func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { + return fhevm.Create(evm.FhevmEnvironment(), caller.Address(), code, gas, value) + } + + 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) { + return fhevm.Create2(evm.FhevmEnvironment(), caller.Address(), code, gas, endowment, salt) + } + ``` + + - Now implement the `fhevm.EVMEnvironment` interface for `FhevmImplementation` + + ```go + func (evm *EVM) FhevmEnvironment() fhevm.EVMEnvironment { return &evm.fhevmEnvironment } + + 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) + } + ``` + +5. In `core/vm/instructions.go` + - Rewrite `opSload` , `opSstore` , and `opReturn` by a call to their fhevm implementation + + ```go + func opSload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + return fhevm.OpSload(pc, interpreter.evm.FhevmEnvironment(), scope) + } + + func opSstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + return fhevm.OpSstore(pc, interpreter.evm.FhevmEnvironment(), scope) + } + + func opReturn(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + return fhevm.OpReturn(pc, interpreter.evm.FhevmEnvironment(), scope), errStopToken + } + ``` + + - In `opCall` , `opCallCode` , and `opStaticCall` : Add lines to delegate ciphertexts before the call and to restore at the end of function (using `defer`) + + **Note:** There might be other functions that calls other contracts (e.g. `opDelegateCall`), in which case you will also need to do the same modifications as below. Basically, anything that will execute code after incrementing the depth would need this. + + ```go + // Add the 2 following lines + verifiedBefore := fhevm.DelegateCiphertextHandlesInArgs(interpreter.evm.FhevmEnvironment(), args) + defer fhevm.RestoreVerifiedDepths(interpreter.evm.FhevmEnvironment(), verifiedBefore) + // The call function is named differently in the 3 functions to update + ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, bigVal) + ``` + + - In `func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error)` : + + ```go + // Replace the following lines + beneficiary := scope.Stack.pop() + balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address()) + interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance) + interpreter.evm.StateDB.SelfDestruct(scope.Contract.Address()) + // with this call to the fhevm + beneficiary, balance := fhevm.OpSelfdestruct(pc, interpreter.evm.FhevmEnvironment(), scope) + ``` + +6. In `core/vm/interpreter.go` + - Update `Config` struct with new fields + + ```go + IsEthCall bool + IsGasEstimation bool + ``` + + - Implements the following methods + + ```go + func (s *ScopeContext) GetMemory() fhevm.Memory { + return s.Memory + } + + func (s *ScopeContext) GetStack() fhevm.Stack { + return s.Stack + } + + func (s *ScopeContext) GetContract() fhevm.Contract { + return s.Contract + } + ``` + + - In `func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error)` + - Add the deletion of verified ciphertexts at current depth in the `defer` at the top + + ```go + defer func() { + fhevm.RemoveVerifiedCipherextsAtCurrentDepth(in.evm.FhevmEnvironment()) + in.evm.depth-- + }() + ``` + + - Replace the clearing of the stop token error at the end with the evaluation of the remaining optimistic requires + + ```go + if err == errStopToken { + err = fhevm.EvalRemOptReqWhenStopToken(in.evm.FhevmEnvironment()) + } + ``` + +7. In `core/vm/stack.go` Implement the following methods + + ```go + func (st *Stack) Pop() uint256.Int { + return st.pop() + } + + func (st *Stack) Peek() *uint256.Int { + return st.peek() + } + ``` + +8. In `internal/ethapi/api.go` + - Add `isGasEstimation, isEthCall bool` arguments to `func doCall` and pass them in `vm.Config` during EVM creation + + ```go + evm, vmError := b.GetEVM(ctx, msg, state, header, &vm.Config{NoBaseFee: true, IsGasEstimation: isGasEstimation, IsEthCall: isEthCall}, &blockCtx) + ``` + + - 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 +9. In `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 \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..93cc16e --- /dev/null +++ b/README.md @@ -0,0 +1,81 @@ +

+ + +

+
+

+ 📒 Read documentation | 💛 Community support +

+

+ + + + + + + + +

+
+ +**fhevm-go** is an open-source library used to easily integrate the [fhEVM](https://docs.zama.ai/fhevm) into an EVM-compatible blockchain. + + +## Main features + +Give your EVM the ability to compute on encrypted data using homomorphic encryption. +- Support of a large collections of encrypted operations via precompiled contracts +- Ready to use EVM components that supports encrypted computation + + +## Getting started + +In order to use the library, you need to clone the repository and build it. This is required because the library depends on another library that needs to be built from source (for now), and Go doesn't support such build. +```bash +$ git clone https://github.com/zama-ai/fhevm-go +$ cd fhevm-go +$ make build +``` + +That's it! 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: +``` +... +require( + ... + github.com/zama-ai/fhevm-go v1.0.0 + ... +) + +replace( + ... + github.com/zama-ai/fhevm-go v1.0.0 => /path/to/your/local/fhevm-go + ... +) +... +``` + +> [!NOTE] +> The replace in necessary for now as Go build system can't build the third party 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` + +## Documentation + +Full, comprehensive documentation is available at [https://docs.zama.ai/fhevm](https://docs.zama.ai/fhevm) (still nothing about fhevm-go however). + +## 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 [https://github.com/zama-ai/fhevm](https://github.com/zama-ai/fhevm-go/releases) + +## Tutorials + +- [Integration guide](Integration.md) +- Idea: video doing the integration + +## Need support? + + + + + +## License + +This software is distributed under the BSD-3-Clause-Clear license. If you have any questions, please contact us at hello@zama.ai. From 99fb39565d4506be00810edb5c34e6e0a70f5794 Mon Sep 17 00:00:00 2001 From: Ayoub Benaissa Date: Thu, 18 Jan 2024 10:19:43 +0100 Subject: [PATCH 3/3] chore: edit readme with suggestions Co-authored-by: Petar Ivanov <29689712+dartdart26@users.noreply.github.com> --- Integration.md | 2 +- README.md | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Integration.md b/Integration.md index f88abd3..dc8414e 100644 --- a/Integration.md +++ b/Integration.md @@ -1,6 +1,6 @@ # Integration -This document is a guide listing detailed steps to integrate [fhevm-go](https://github.com/zama-ai/fhevm-go) into [go-ethereum](https://github.com/ethereum/go-ethereum) or any other implementations that follows the same architecture. +This document is a guide listing detailed steps to integrate [fhevm-go](https://github.com/zama-ai/fhevm-go) into [go-ethereum](https://github.com/ethereum/go-ethereum) or any other implementations that follow the same architecture. > [!NOTE] > This document is based on go-ethereum v1.13.5 diff --git a/README.md b/README.md index 93cc16e..1e7d2cc 100644 --- a/README.md +++ b/README.md @@ -23,14 +23,14 @@ ## Main features -Give your EVM the ability to compute on encrypted data using homomorphic encryption. -- Support of a large collections of encrypted operations via precompiled contracts -- Ready to use EVM components that supports encrypted computation +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 ## Getting started -In order to use the library, you need to clone the repository and build it. This is required because the library depends on another library that needs to be built from source (for now), and Go doesn't support such build. +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. ```bash $ git clone https://github.com/zama-ai/fhevm-go $ cd fhevm-go @@ -55,7 +55,7 @@ replace( ``` > [!NOTE] -> The replace in necessary for now as Go build system can't build the third party 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` +> The replace in 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` ## Documentation