Skip to content
This repository has been archived by the owner on Dec 23, 2024. It is now read-only.

Readme and License #64

Merged
merged 3 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
284 changes: 284 additions & 0 deletions Integration.md
Original file line number Diff line number Diff line change
@@ -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 follow 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.
youben11 marked this conversation as resolved.
Show resolved Hide resolved

- 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
28 changes: 28 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -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.
81 changes: 81 additions & 0 deletions README.md
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

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 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
$ 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 `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

Full, comprehensive documentation is available at [https://docs.zama.ai/fhevm](https://docs.zama.ai/fhevm) (still nothing about fhevm-go however).
youben11 marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we remove the note about fhevm-go missing from fhevm doc?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but not until we know what to do about the docs


## 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].