Skip to content

Commit

Permalink
Remove most geth dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
david-zk committed Nov 9, 2023
1 parent 41f7a61 commit 60b8b9e
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 130 deletions.
12 changes: 0 additions & 12 deletions crypto/crypto.go

This file was deleted.

4 changes: 1 addition & 3 deletions fhevm/contract.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package fhevm

import "github.com/ethereum/go-ethereum/common"

type Contract interface {
Address() common.Address
Address() Address
}
37 changes: 17 additions & 20 deletions fhevm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ import (
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/holiman/uint256"
fhevm_crypto "github.com/zama-ai/fhevm-go/crypto"
)

// A Logger interface for the EVM.
Expand Down Expand Up @@ -42,7 +39,7 @@ func (*DefaultLogger) Error(msg string, keyvals ...interface{}) {
}

func makeKeccakSignature(input string) uint32 {
return binary.BigEndian.Uint32(crypto.Keccak256([]byte(input))[0:4])
return binary.BigEndian.Uint32(Keccak256([]byte(input))[0:4])
}

func isScalarOp(input []byte) (bool, error) {
Expand All @@ -53,19 +50,19 @@ func isScalarOp(input []byte) (bool, error) {
return isScalar, nil
}

func getVerifiedCiphertext(environment EVMEnvironment, ciphertextHash common.Hash) *verifiedCiphertext {
func getVerifiedCiphertext(environment EVMEnvironment, ciphertextHash Hash) *verifiedCiphertext {
return getVerifiedCiphertextFromEVM(environment, ciphertextHash)
}

func get2VerifiedOperands(environment EVMEnvironment, input []byte) (lhs *verifiedCiphertext, rhs *verifiedCiphertext, err error) {
if len(input) != 65 {
return nil, nil, errors.New("input needs to contain two 256-bit sized values and 1 8-bit value")
}
lhs = getVerifiedCiphertext(environment, common.BytesToHash(input[0:32]))
lhs = getVerifiedCiphertext(environment, BytesToHash(input[0:32]))
if lhs == nil {
return nil, nil, errors.New("unverified ciphertext handle")
}
rhs = getVerifiedCiphertext(environment, common.BytesToHash(input[32:64]))
rhs = getVerifiedCiphertext(environment, BytesToHash(input[32:64]))
if rhs == nil {
return nil, nil, errors.New("unverified ciphertext handle")
}
Expand All @@ -77,7 +74,7 @@ func getScalarOperands(environment EVMEnvironment, input []byte) (lhs *verifiedC
if len(input) != 65 {
return nil, nil, errors.New("input needs to contain two 256-bit sized values and 1 8-bit value")
}
lhs = getVerifiedCiphertext(environment, common.BytesToHash(input[0:32]))
lhs = getVerifiedCiphertext(environment, BytesToHash(input[0:32]))
if lhs == nil {
return nil, nil, errors.New("unverified ciphertext handle")
}
Expand Down Expand Up @@ -113,8 +110,8 @@ func importCiphertext(environment EVMEnvironment, ct *tfheCiphertext) *verifiedC

func importRandomCiphertext(environment EVMEnvironment, t FheUintType) []byte {
nextCtHash := &environment.FhevmData().nextCiphertextHashOnGasEst
ctHashBytes := crypto.Keccak256(nextCtHash.Bytes())
handle := common.BytesToHash(ctHashBytes)
ctHashBytes := Keccak256(nextCtHash.Bytes())
handle := BytesToHash(ctHashBytes)
ct := new(tfheCiphertext)
ct.fheUintType = t
ct.hash = &handle
Expand Down Expand Up @@ -168,27 +165,27 @@ func padArrayTo32Multiple(input []byte) []byte {
return input
}

func Create(evm EVMEnvironment, caller common.Address, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
contractAddr = crypto.CreateAddress(caller, evm.GetNonce(caller))
protectedStorageAddr := fhevm_crypto.CreateProtectedStorageContractAddress(contractAddr)
func Create(evm EVMEnvironment, caller Address, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr Address, leftOverGas uint64, err error) {
contractAddr = CreateAddress(caller, evm.GetNonce(caller))
protectedStorageAddr := CreateProtectedStorageContractAddress(contractAddr)
_, _, leftOverGas, err = evm.CreateContract(caller, nil, gas, big.NewInt(0), protectedStorageAddr)
if err != nil {
ret = nil
contractAddr = common.Address{}
contractAddr = Address{}
return
}
// TODO: consider reverting changes to `protectedStorageAddr` if actual contract creation fails.
return evm.CreateContract(caller, code, leftOverGas, value, contractAddr)
}

func Create2(evm EVMEnvironment, caller common.Address, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
codeHash := crypto.Keccak256Hash(code)
contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), codeHash.Bytes())
protectedStorageAddr := fhevm_crypto.CreateProtectedStorageContractAddress(contractAddr)
_, _, leftOverGas, err = evm.CreateContract2(caller, nil, common.Hash{}, gas, big.NewInt(0), protectedStorageAddr)
func Create2(evm EVMEnvironment, caller Address, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr Address, leftOverGas uint64, err error) {
codeHash := Keccak256Hash(code)
contractAddr = CreateAddress2(caller, salt.Bytes32(), codeHash.Bytes())
protectedStorageAddr := CreateProtectedStorageContractAddress(contractAddr)
_, _, leftOverGas, err = evm.CreateContract2(caller, nil, Hash{}, gas, big.NewInt(0), protectedStorageAddr)
if err != nil {
ret = nil
contractAddr = common.Address{}
contractAddr = Address{}
return
}
// TODO: consider reverting changes to `protectedStorageAddr` if actual contract creation fails.
Expand Down
45 changes: 21 additions & 24 deletions fhevm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ import (
"math/big"
"strings"

"github.com/ethereum/go-ethereum/common"
crypto "github.com/ethereum/go-ethereum/crypto"
"github.com/holiman/uint256"
fhevm_crypto "github.com/zama-ai/fhevm-go/crypto"
)

var zero = uint256.NewInt(0).Bytes32()
Expand Down Expand Up @@ -62,11 +59,11 @@ func minUint64(a, b uint64) uint64 {
}

// If references are still left, reduce refCount by 1. Otherwise, zero out the metadata and the ciphertext slots.
func garbageCollectProtectedStorage(flagHandleLocation common.Hash, handle common.Hash, protectedStorage common.Address, env EVMEnvironment) {
func garbageCollectProtectedStorage(flagHandleLocation Hash, handle Hash, protectedStorage Address, env EVMEnvironment) {
// The location of ciphertext metadata is at Keccak256(handle). Doing so avoids attacks from users trying to garbage
// collect arbitrary locations in protected storage. Hashing the handle makes it hard to find a preimage such that
// it ends up in arbitrary non-zero places in protected stroage.
metadataKey := crypto.Keccak256Hash(handle.Bytes())
metadataKey := Keccak256Hash(handle.Bytes())

existingMetadataHash := env.GetState(protectedStorage, metadataKey)
existingMetadataInt := newInt(existingMetadataHash.Bytes())
Expand Down Expand Up @@ -132,15 +129,15 @@ func isVerifiedAtCurrentDepth(environment EVMEnvironment, ct *verifiedCiphertext

// Returns a pointer to the ciphertext if the given hash points to a verified ciphertext.
// Else, it returns nil.
func getVerifiedCiphertextFromEVM(environment EVMEnvironment, ciphertextHash common.Hash) *verifiedCiphertext {
func getVerifiedCiphertextFromEVM(environment EVMEnvironment, ciphertextHash Hash) *verifiedCiphertext {
ct, ok := environment.FhevmData().verifiedCiphertexts[ciphertextHash]
if ok && isVerifiedAtCurrentDepth(environment, ct) {
return ct
}
return nil
}

func verifyIfCiphertextHandle(handle common.Hash, env EVMEnvironment, contractAddress common.Address) error {
func verifyIfCiphertextHandle(handle Hash, env EVMEnvironment, contractAddress Address) error {
ct, ok := env.FhevmData().verifiedCiphertexts[handle]
if ok {
// If already existing in memory, skip storage and import the same ciphertext at the current depth.
Expand All @@ -152,8 +149,8 @@ func verifyIfCiphertextHandle(handle common.Hash, env EVMEnvironment, contractAd
return nil
}

metadataKey := crypto.Keccak256Hash(handle.Bytes())
protectedStorage := fhevm_crypto.CreateProtectedStorageContractAddress(contractAddress)
metadataKey := Keccak256Hash(handle.Bytes())
protectedStorage := CreateProtectedStorageContractAddress(contractAddress)
metadataInt := newInt(env.GetState(protectedStorage, metadataKey).Bytes())
if !metadataInt.IsZero() {
metadata := newCiphertextMetadata(metadataInt.Bytes32())
Expand Down Expand Up @@ -186,7 +183,7 @@ func verifyIfCiphertextHandle(handle common.Hash, env EVMEnvironment, contractAd

func OpSload(pc *uint64, env EVMEnvironment, scope ScopeContext) ([]byte, error) {
loc := scope.GetStack().Peek()
hash := common.Hash(loc.Bytes32())
hash := Hash(loc.Bytes32())
val := env.GetState(scope.GetContract().Address(), hash)
if err := verifyIfCiphertextHandle(val, env, scope.GetContract().Address()); err != nil {
return nil, err
Expand All @@ -196,20 +193,20 @@ func OpSload(pc *uint64, env EVMEnvironment, scope ScopeContext) ([]byte, error)
}

// An arbitrary constant value to flag locations in protected storage.
var flag = common.HexToHash("0xa145ffde0100a145ffde0100a145ffde0100a145ffde0100a145ffde0100fab3")
var flag = HexToHash("0xa145ffde0100a145ffde0100a145ffde0100a145ffde0100a145ffde0100fab3")

// If a verified ciphertext:
// * if the ciphertext does not exist in protected storage, persist it with a refCount = 1
// * if the ciphertexts exists in protected, bump its refCount by 1
func persistIfVerifiedCiphertext(flagHandleLocation common.Hash, handle common.Hash, protectedStorage common.Address, env EVMEnvironment) {
func persistIfVerifiedCiphertext(flagHandleLocation Hash, handle Hash, protectedStorage Address, env EVMEnvironment) {
verifiedCiphertext := getVerifiedCiphertextFromEVM(env, handle)
if verifiedCiphertext == nil {
return
}
logger := env.GetLogger()

// Try to read ciphertext metadata from protected storage.
metadataKey := crypto.Keccak256Hash(handle.Bytes())
metadataKey := Keccak256Hash(handle.Bytes())
metadataInt := newInt(env.GetState(protectedStorage, metadataKey).Bytes())
metadata := ciphertextMetadata{}

Expand All @@ -236,7 +233,7 @@ func persistIfVerifiedCiphertext(flagHandleLocation common.Hash, handle common.H
ctBytes := verifiedCiphertext.ciphertext.serialize()
for i, b := range ctBytes {
if i%32 == 0 && i != 0 {
env.SetState(protectedStorage, ciphertextSlot.Bytes32(), common.BytesToHash(ctPart32))
env.SetState(protectedStorage, ciphertextSlot.Bytes32(), BytesToHash(ctPart32))
ciphertextSlot.AddUint64(ciphertextSlot, 1)
ctPart32 = make([]byte, 32)
partIdx = 0
Expand All @@ -245,7 +242,7 @@ func persistIfVerifiedCiphertext(flagHandleLocation common.Hash, handle common.H
partIdx++
}
if len(ctPart32) != 0 {
env.SetState(protectedStorage, ciphertextSlot.Bytes32(), common.BytesToHash(ctPart32))
env.SetState(protectedStorage, ciphertextSlot.Bytes32(), BytesToHash(ctPart32))
}
} else {
// If metadata exists, bump the refcount by 1.
Expand All @@ -269,20 +266,20 @@ func OpSstore(pc *uint64, env EVMEnvironment, scope ScopeContext) ([]byte, error
return nil, ErrWriteProtection
}
loc := scope.GetStack().Pop()
locHash := common.BytesToHash(loc.Bytes())
locHash := BytesToHash(loc.Bytes())
newVal := scope.GetStack().Pop()
newValHash := common.BytesToHash(newVal.Bytes())
oldValHash := env.GetState(scope.GetContract().Address(), common.Hash(loc.Bytes32()))
newValHash := BytesToHash(newVal.Bytes())
oldValHash := env.GetState(scope.GetContract().Address(), Hash(loc.Bytes32()))
// If the value is the same or if we are not going to commit, don't do anything to protected storage.
if newValHash != oldValHash && env.IsCommitting() {
protectedStorage := fhevm_crypto.CreateProtectedStorageContractAddress(scope.GetContract().Address())
protectedStorage := CreateProtectedStorageContractAddress(scope.GetContract().Address())

// Define flag location as keccak256(keccak256(loc)) in protected storage. Used to mark the location as containing a handle.
// Note: We apply the hash function twice to make sure a flag location in protected storage cannot clash with a ciphertext
// metadata location that is keccak256(keccak256(ciphertext)). Since a location is 32 bytes, it cannot clash with a well-formed
// ciphertext. Therefore, there needs to be a hash collistion for a clash to happen. If hash is applied only once, there could
// be a collision, since malicous users could store at loc = keccak256(ciphertext), making the flag clash with metadata.
flagHandleLocation := crypto.Keccak256Hash(crypto.Keccak256Hash(locHash[:]).Bytes())
flagHandleLocation := Keccak256Hash(Keccak256Hash(locHash[:]).Bytes())

// Since the old value is no longer stored in actual contract storage, run garbage collection on protected storage.
garbageCollectProtectedStorage(flagHandleLocation, oldValHash, protectedStorage, env)
Expand All @@ -297,8 +294,8 @@ func OpSstore(pc *uint64, env EVMEnvironment, scope ScopeContext) ([]byte, error

// If there are ciphertext handles in the arguments to a call, delegate them to the callee.
// Return a map from ciphertext hash -> depthSet before delegation.
func DelegateCiphertextHandlesInArgs(env EVMEnvironment, args []byte) (verified map[common.Hash]*depthSet) {
verified = make(map[common.Hash]*depthSet)
func DelegateCiphertextHandlesInArgs(env EVMEnvironment, args []byte) (verified map[Hash]*depthSet) {
verified = make(map[Hash]*depthSet)
for key, verifiedCiphertext := range env.FhevmData().verifiedCiphertexts {
if contains(args, key.Bytes()) && isVerifiedAtCurrentDepth(env, verifiedCiphertext) {
if env.IsCommitting() {
Expand All @@ -314,7 +311,7 @@ func DelegateCiphertextHandlesInArgs(env EVMEnvironment, args []byte) (verified
return
}

func RestoreVerifiedDepths(env EVMEnvironment, verified map[common.Hash]*depthSet) {
func RestoreVerifiedDepths(env EVMEnvironment, verified map[Hash]*depthSet) {
for k, v := range verified {
env.FhevmData().verifiedCiphertexts[k].verifiedDepths = v
}
Expand Down Expand Up @@ -356,7 +353,7 @@ func OpReturn(pc *uint64, env EVMEnvironment, scope ScopeContext) []byte {

func OpSelfdestruct(pc *uint64, env EVMEnvironment, scope ScopeContext) (beneficiary uint256.Int, balance *big.Int) {
beneficiary = scope.GetStack().Pop()
protectedStorage := fhevm_crypto.CreateProtectedStorageContractAddress(scope.GetContract().Address())
protectedStorage := CreateProtectedStorageContractAddress(scope.GetContract().Address())
balance = env.GetBalance(scope.GetContract().Address())
balance.Add(balance, env.GetBalance(protectedStorage))
env.AddBalance(beneficiary.Bytes20(), balance)
Expand Down
21 changes: 10 additions & 11 deletions fhevm/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,18 @@ package fhevm
import (
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/holiman/uint256"
)

type EVMEnvironment interface {
// StateDB related functions
GetState(common.Address, common.Hash) common.Hash
SetState(common.Address, common.Hash, common.Hash)
GetNonce(common.Address) uint64
AddBalance(common.Address, *big.Int)
GetBalance(common.Address) *big.Int
GetState(Address, Hash) Hash
SetState(Address, Hash, Hash)
GetNonce(Address) uint64
AddBalance(Address, *big.Int)
GetBalance(Address) *big.Int

Suicide(common.Address) bool
Suicide(Address) bool

// EVM call stack depth
GetDepth() int
Expand All @@ -28,16 +27,16 @@ type EVMEnvironment interface {
IsEthCall() bool
IsReadOnly() bool

CreateContract(caller common.Address, code []byte, gas uint64, value *big.Int, address common.Address) ([]byte, common.Address, uint64, error)
CreateContract2(caller common.Address, code []byte, codeHash common.Hash, gas uint64, value *big.Int, address common.Address) ([]byte, common.Address, uint64, error)
CreateContract(caller Address, code []byte, gas uint64, value *big.Int, address Address) ([]byte, Address, uint64, error)
CreateContract2(caller Address, code []byte, codeHash Hash, gas uint64, value *big.Int, address Address) ([]byte, Address, uint64, error)

FhevmData() *FhevmData
FhevmParams() *FhevmParams
}

type FhevmData struct {
// A map from a ciphertext hash to itself and stack depth at which it is verified
verifiedCiphertexts map[common.Hash]*verifiedCiphertext
verifiedCiphertexts map[Hash]*verifiedCiphertext

// All optimistic requires encountered up to that point in the txn execution
optimisticRequires []*tfheCiphertext
Expand All @@ -47,7 +46,7 @@ type FhevmData struct {

func NewFhevmData() FhevmData {
return FhevmData{
verifiedCiphertexts: make(map[common.Hash]*verifiedCiphertext),
verifiedCiphertexts: make(map[Hash]*verifiedCiphertext),
optimisticRequires: make([]*tfheCiphertext, 0),
}
}
6 changes: 2 additions & 4 deletions fhevm/interpreter.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package fhevm

import "github.com/ethereum/go-ethereum/common"

type ScopeContext interface {
GetMemory() Memory
GetStack() Stack
Expand Down Expand Up @@ -50,13 +48,13 @@ type verifiedCiphertext struct {

type PrivilegedMemory struct {
// A map from a ciphertext hash to itself and stack depths at which it is verified
VerifiedCiphertexts map[common.Hash]*verifiedCiphertext
VerifiedCiphertexts map[Hash]*verifiedCiphertext

// All optimistic requires encountered up to that point in the txn execution
OptimisticRequires []*tfheCiphertext
}

var PrivilegedMempory *PrivilegedMemory = &PrivilegedMemory{
make(map[common.Hash]*verifiedCiphertext),
make(map[Hash]*verifiedCiphertext),
make([]*tfheCiphertext, 0),
}
Loading

0 comments on commit 60b8b9e

Please sign in to comment.