Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Main internal #117

Merged
merged 4 commits into from
Jun 11, 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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build: build-tfhe-rs-capi

.PHONY: test
test: build-tfhe-rs-capi
cd fhevm && go test -v ./...
cd fhevm && go clean -cache && TFHE_EXECUTOR_CONTRACT_ADDRESS=0x05fD9B5EFE0a996095f42Ed7e77c390810CF660c go test -v ./...

.PHONY: build-tfhe-rs-capi
build-tfhe-rs-capi:
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,5 @@ The library helps EVM maintainers to extend their EVM with the power of FHE. If

## License


This software is distributed under the BSD-3-Clause-Clear license. If you have any questions, please contact us at hello@zama.ai.
155 changes: 155 additions & 0 deletions fhevm/ciphertext_storage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package fhevm

import (
"encoding/hex"

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

var ciphertextStorage = common.BytesToAddress([]byte{94})

func newInt(buf []byte) *uint256.Int {
i := uint256.NewInt(0)
return i.SetBytes(buf)
}

func minUint64(a, b uint64) uint64 {
if a < b {
return a
}
return b
}

// Ciphertext metadata is stored in a single 32-byte slot.
// Currently, we only utilize 9 bytes from the slot.
type ciphertextMetadata struct {
length uint64
fheUintType tfhe.FheUintType
}

func (m ciphertextMetadata) serialize() [32]byte {
u := uint256.NewInt(0)
u[0] = m.length
u[1] = uint64(m.fheUintType)
return u.Bytes32()
}

func (m *ciphertextMetadata) deserialize(buf [32]byte) *ciphertextMetadata {
u := uint256.NewInt(0)
u.SetBytes(buf[:])
m.length = u[0]
m.fheUintType = tfhe.FheUintType(u[1])
return m
}

func newCiphertextMetadata(buf [32]byte) *ciphertextMetadata {
m := ciphertextMetadata{}
return m.deserialize(buf)
}

func isCiphertextPersisted(env EVMEnvironment, handle common.Hash) bool {
metadataInt := newInt(env.GetState(ciphertextStorage, handle).Bytes())
return !metadataInt.IsZero()
}

// Returns the ciphertext metadata for the given handle or nil if it doesn't point to a ciphertext.
func loadCiphertextMetadata(env EVMEnvironment, handle common.Hash) *ciphertextMetadata {
metadataInt := newInt(env.GetState(ciphertextStorage, handle).Bytes())
if metadataInt.IsZero() {
return nil
}
return newCiphertextMetadata(metadataInt.Bytes32())
}

// Returns the ciphertext for the given `handle“ and the gas needed to laod the ciphertext.
// Returned gas would be zero if already loaded to memory.
// If `handle` doesn't point to a ciphertext or an error occurs, (nil, 0) is returned.
func loadCiphertext(env EVMEnvironment, handle common.Hash) (ct *tfhe.TfheCiphertext, gas uint64) {
logger := env.GetLogger()
ct, loaded := env.FhevmData().loadedCiphertexts[handle]
if loaded {
return ct, 0
}

metadataInt := newInt(env.GetState(ciphertextStorage, handle).Bytes())
if metadataInt.IsZero() {
return nil, 0
}
metadata := newCiphertextMetadata(metadataInt.Bytes32())
ctBytes := make([]byte, 0)
left := metadata.length
idx := newInt(handle.Bytes())
idx.AddUint64(idx, 1)
for left > 0 {
bytes := env.GetState(ciphertextStorage, idx.Bytes32())
toAppend := minUint64(uint64(len(bytes)), left)
left -= toAppend
ctBytes = append(ctBytes, bytes[0:toAppend]...)
idx.AddUint64(idx, 1)
}
ct = new(tfhe.TfheCiphertext)
err := ct.Deserialize(ctBytes, metadata.fheUintType)
if err != nil {
logger.Error("failed to deserialize ciphertext from storage", "err", err)
return nil, 0
}
env.FhevmData().loadedCiphertexts[handle] = ct
return ct, env.FhevmParams().GasCosts.FheStorageSloadGas[ct.Type()]
}

func insertCiphertextToMemory(env EVMEnvironment, ct *tfhe.TfheCiphertext) {
env.FhevmData().loadedCiphertexts[ct.GetHash()] = ct
}

// Persist the given ciphertext.
func persistCiphertext(env EVMEnvironment, ct *tfhe.TfheCiphertext) {
logger := env.GetLogger()
if isCiphertextPersisted(env, ct.GetHash()) {
// Assuming a handle is a hash of the ciphertext, if metadata is already existing in storage it means the ciphertext is too.
logger.Info("ciphertext already persisted to storage", "handle", ct.GetHash().Hex())
return
}

metadata := ciphertextMetadata{}
metadata.length = uint64(tfhe.ExpandedFheCiphertextSize[ct.FheUintType])
metadata.fheUintType = ct.FheUintType

// Persist the metadata in storage.
env.SetState(ciphertextStorage, ct.GetHash(), metadata.serialize())

ciphertextSlot := newInt(ct.GetHash().Bytes())
ciphertextSlot.AddUint64(ciphertextSlot, 1)
if env.IsCommitting() {
logger.Info("persisting new ciphertext",
"handle", hex.EncodeToString(ct.GetHash().Bytes()),
"type", metadata.fheUintType,
"len", metadata.length,
"ciphertextSlot", hex.EncodeToString(ciphertextSlot.Bytes()))
}
ctPart32 := make([]byte, 32)
partIdx := 0
ctBytes := ct.Serialize()
for i, b := range ctBytes {
if i%32 == 0 && i != 0 {
env.SetState(ciphertextStorage, ciphertextSlot.Bytes32(), common.BytesToHash(ctPart32))
ciphertextSlot.AddUint64(ciphertextSlot, 1)
ctPart32 = make([]byte, 32)
partIdx = 0
}
ctPart32[partIdx] = b
partIdx++
}
if len(ctPart32) != 0 {
env.SetState(ciphertextStorage, ciphertextSlot.Bytes32(), common.BytesToHash(ctPart32))
}
}

func GetCiphertextFromMemory(env EVMEnvironment, handle common.Hash) *tfhe.TfheCiphertext {
ct, found := env.FhevmData().loadedCiphertexts[handle]
if found {
return ct
}
return nil
}
Loading
Loading