This repository has been archived by the owner on Dec 23, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
367 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,286 @@ | ||
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
<p align="center"> | ||
<!-- product name logo --> | ||
<img width=600 src="https://github.com/zama-ai/fhevm/assets/1384478/265d051c-e177-42b4-b9a2-d2b2e474131b"> | ||
</p> | ||
<hr/> | ||
<p align="center"> | ||
<a href="https://docs.zama.ai/fhevm"> 📒 Read documentation</a> | <a href="https://zama.ai/community"> 💛 Community support</a> | ||
</p> | ||
<p align="center"> | ||
<!-- Version badge using shields.io --> | ||
<a href="https://github.com/zama-ai/fhevm-go/releases"> | ||
<img src="https://img.shields.io/github/v/release/zama-ai/fhevm-go?style=flat-square"> | ||
</a> | ||
<!-- Zama Bounty Program --> | ||
<a href="https://github.com/zama-ai/bounty-program"> | ||
<img src="https://img.shields.io/badge/Contribute-Zama%20Bounty%20Program-yellow?style=flat-square"> | ||
</a> | ||
</p> | ||
<hr/> | ||
|
||
**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? | ||
|
||
<a target="_blank" href="https://community.zama.ai"> | ||
<img src="https://user-images.githubusercontent.com/5758427/231145251-9cb3f03f-3e0e-4750-afb8-2e6cf391fa43.png"> | ||
</a> | ||
|
||
## License | ||
|
||
This software is distributed under the BSD-3-Clause-Clear license. If you have any questions, please contact us at [email protected]. |