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

feat: add ciphertext delegation for opReturn #21

Merged
merged 1 commit into from
Oct 27, 2023
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
5 changes: 3 additions & 2 deletions fhevm/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import (
"errors"
)

// List of EVM execution errors needed by the fhEVM
// List of EVM execution errors needed by the fhEVM.
// TODO: initialize errors from erros passed by users. That would make fhevm-go errors match the EVM environment's errors.
var (
ErrWriteProtection = errors.New("write protection")
ErrExecutionReverted = errors.New("execution reverted")
)
71 changes: 66 additions & 5 deletions fhevm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"encoding/hex"
"errors"
"strings"

"github.com/ethereum/go-ethereum/common"
crypto "github.com/ethereum/go-ethereum/crypto"
Expand All @@ -18,6 +19,10 @@ func newInt(buf []byte) *uint256.Int {
return i.SetBytes(buf)
}

func contains(haystack []byte, needle []byte) bool {
return strings.Contains(string(haystack), string(needle))
}

// Ciphertext metadata is stored in protected storage, in a 32-byte slot.
// Currently, we only utilize 17 bytes from the slot.
type ciphertextMetadata struct {
Expand Down Expand Up @@ -258,10 +263,7 @@ func persistIfVerifiedCiphertext(flagHandleLocation common.Hash, handle common.H
env.SetState(protectedStorage, metadataKey, metadata.serialize())
}

func OpSstore(pc *uint64, env EVMEnvironment, scope ScopeContext) ([]byte, error) {
if env.IsReadOnly() {
return nil, ErrWriteProtection
}
func OpSstore(pc *uint64, env EVMEnvironment, scope ScopeContext) []byte {
loc := scope.GetStack().Pop()
locHash := common.BytesToHash(loc.Bytes())
newVal := scope.GetStack().Pop()
Expand All @@ -286,5 +288,64 @@ func OpSstore(pc *uint64, env EVMEnvironment, scope ScopeContext) ([]byte, error
}
// Set the SSTORE's value in the actual contract.
env.SetState(scope.GetContract().Address(), loc.Bytes32(), newValHash)
return nil, nil
return nil
}

// 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)
for key, verifiedCiphertext := range env.GetFhevmData().verifiedCiphertexts {
if contains(args, key.Bytes()) && isVerifiedAtCurrentDepth(env, verifiedCiphertext) {
if env.IsCommitting() {
env.GetLogger().Info("delegateCiphertextHandlesInArgs",
"handle", verifiedCiphertext.ciphertext.getHash().Hex(),
"fromDepth", env.GetDepth(),
"toDepth", env.GetDepth()+1)
}
verified[key] = verifiedCiphertext.verifiedDepths.clone()
verifiedCiphertext.verifiedDepths.add(env.GetDepth() + 1)
}
}
return
}

func RestoreVerifiedDepths(env EVMEnvironment, verified map[common.Hash]*depthSet) {
for k, v := range verified {
env.GetFhevmData().verifiedCiphertexts[k].verifiedDepths = v
}
}

func delegateCiphertextHandlesToCaller(env EVMEnvironment, ret []byte) {
for key, verifiedCiphertext := range env.GetFhevmData().verifiedCiphertexts {
if contains(ret, key.Bytes()) && isVerifiedAtCurrentDepth(env, verifiedCiphertext) {
if env.IsCommitting() {
env.GetLogger().Info("opReturn making ciphertext available to caller",
"handle", verifiedCiphertext.ciphertext.getHash().Hex(),
"fromDepth", env.GetDepth(),
"toDepth", env.GetDepth()-1)
}
// If a handle is returned, automatically make it available to the caller.
verifiedCiphertext.verifiedDepths.add(env.GetDepth() - 1)
}
}
}

func RemoveVerifiedCipherextsAtCurrentDepth(env EVMEnvironment) {
for _, verifiedCiphertext := range env.GetFhevmData().verifiedCiphertexts {
if env.IsCommitting() {
env.GetLogger().Info("Run removing ciphertext from depth",
"handle", verifiedCiphertext.ciphertext.getHash().Hex(),
"depth", env.GetDepth())
}
// Delete the current EVM depth from the set of verified depths.
verifiedCiphertext.verifiedDepths.del(env.GetDepth())
}
}

func OpReturn(pc *uint64, env EVMEnvironment, scope ScopeContext) []byte {
offset, size := scope.GetStack().Pop(), scope.GetStack().Pop()
ret := scope.GetMemory().GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
delegateCiphertextHandlesToCaller(env, ret)
return ret
}
1 change: 1 addition & 0 deletions fhevm/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package fhevm
import "github.com/ethereum/go-ethereum/common"

type ScopeContext interface {
GetMemory() Memory
GetStack() Stack
GetContract() Contract
}
Expand Down
34 changes: 34 additions & 0 deletions fhevm/memory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// BSD 3-Clause Clear License

// Copyright © 2023 ZAMA.
// All rights reserved.

// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package fhevm

import "github.com/holiman/uint256"

type Memory interface {
Set(offset, size uint64, value []byte)
Set32(offset uint64, val *uint256.Int)
Resize(size uint64)
GetCopy(offset, size int64) (cpy []byte)
GetPtr(offset, size int64) []byte
Len() int
Data() []byte
}
2 changes: 0 additions & 2 deletions fhevm/precompiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ type PrecompiledContract interface {
Run(environment *EVMEnvironment, caller common.Address, addr common.Address, input []byte, readOnly bool) (ret []byte, err error)
}

var ErrExecutionReverted = errors.New("execution reverted")

var signatureFheAdd = makeKeccakSignature("fheAdd(uint256,uint256,bytes1)")
var signatureCast = makeKeccakSignature("cast(uint256,bytes1)")
var signatureDecrypt = makeKeccakSignature("decrypt(uint256)")
Expand Down