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

Optimize gas estimation running time #124

Merged
merged 2 commits into from
Jun 26, 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
16 changes: 13 additions & 3 deletions core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -1306,11 +1306,16 @@ func importCiphertext(accessibleState PrecompileAccessibleState, ct *tfheCiphert

// Used when we want to skip FHE computation, e.g. gas estimation.
func importRandomCiphertext(accessibleState PrecompileAccessibleState, t fheUintType) []byte {
nextCtHash := &accessibleState.Interpreter().evm.nextCiphertextHashOnGasEst
ctHashBytes := crypto.Keccak256(nextCtHash.Bytes())
handle := common.BytesToHash(ctHashBytes)
ct := new(tfheCiphertext)
ct.encrypt(*big.NewInt(0), t)
ct.fheUintType = t
ct.hash = &handle
importCiphertext(accessibleState, ct)
ctHash := ct.getHash()
return ctHash[:]
temp := nextCtHash.Clone()
nextCtHash.Add(temp, uint256.NewInt(1))
return ct.getHash().Bytes()
}

func get2VerifiedOperands(accessibleState PrecompileAccessibleState, input []byte) (lhs *verifiedCiphertext, rhs *verifiedCiphertext, err error) {
Expand Down Expand Up @@ -1482,6 +1487,11 @@ func (e *verifyCiphertext) Run(accessibleState PrecompileAccessibleState, caller
ctBytes := input[:len(input)-1]
ctType := fheUintType(input[len(input)-1])

// If we are doing gas estimation, skip execution and insert a random ciphertext as a result.
if !accessibleState.Interpreter().evm.Commit && !accessibleState.Interpreter().evm.EthCall {
return importRandomCiphertext(accessibleState, ctType), nil
}

ct := new(tfheCiphertext)
err := ct.deserializeCompact(ctBytes, ctType)
if err != nil {
Expand Down
18 changes: 11 additions & 7 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,19 +159,23 @@ type EVM struct {

// The logger allows the EVM to report information during execution.
Logger Logger

// An integer used as a counter for unique ciphertext hashes during gas estimation.
nextCiphertextHashOnGasEst uint256.Int
}

// NewEVM returns a new EVM. The returned EVM is not thread safe and should
// only ever be used *once*.
func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig *params.ChainConfig, config Config) *EVM {
evm := &EVM{
Context: blockCtx,
TxContext: txCtx,
StateDB: statedb,
Config: config,
chainConfig: chainConfig,
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil),
Logger: &defaultLogger{},
Context: blockCtx,
TxContext: txCtx,
StateDB: statedb,
Config: config,
chainConfig: chainConfig,
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil),
Logger: &defaultLogger{},
nextCiphertextHashOnGasEst: *uint256.NewInt(0),
}
evm.interpreter = NewEVMInterpreter(evm, config)
return evm
Expand Down
7 changes: 6 additions & 1 deletion core/vm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,10 @@ func verifyIfCiphertextHandle(val common.Hash, interpreter *EVMInterpreter, cont
ct, ok := interpreter.verifiedCiphertexts[val]
if ok {
// If already existing in memory, skip storage and import the same ciphertext at the current depth.
//
// Also works for gas estimation - we don't persist anything to protected storage during gas estimation.
// However, ciphertexts remain in memory for the duration of the call, allowing for this lookup to find it.
// Note that even if a ciphertext has an empty verification depth set, it still remains in memory.
importCiphertextToEVM(interpreter, ct.ciphertext)
return
}
Expand Down Expand Up @@ -721,7 +725,8 @@ func opSstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
newValHash := common.BytesToHash(newValBytes)
oldValHash := interpreter.evm.StateDB.GetState(scope.Contract.Address(), common.Hash(loc.Bytes32()))
protectedStorage := crypto.CreateProtectedStorageContractAddress(scope.Contract.Address())
if newValHash != oldValHash {
// If the value is the same or if we are not going to commit, don't do anything to protected storage.
if newValHash != oldValHash && interpreter.evm.Commit {
// Since the old value is no longer stored in actual contract storage, run garbage collection on protected storage.
garbageCollectProtectedStorage(oldValHash, protectedStorage, interpreter)
// If a verified ciphertext, persist to protected storage.
Expand Down
12 changes: 7 additions & 5 deletions core/vm/tfhe.go
Original file line number Diff line number Diff line change
Expand Up @@ -848,7 +848,7 @@ const (
type tfheCiphertext struct {
ptr unsafe.Pointer
serialization []byte
hash []byte
hash *common.Hash
value *big.Int
fheUintType fheUintType
}
Expand Down Expand Up @@ -1285,13 +1285,15 @@ func (ct *tfheCiphertext) setPtr(ptr unsafe.Pointer) {
}

func (ct *tfheCiphertext) getHash() common.Hash {
if ct.hash != nil {
return *ct.hash
}
if !ct.initialized() {
panic("cannot get hash of non-initialized ciphertext")
}
if ct.hash == nil {
ct.hash = crypto.Keccak256(ct.serialize())
}
return common.BytesToHash(ct.hash)
hash := common.BytesToHash(crypto.Keccak256(ct.serialize()))
ct.hash = &hash
return *ct.hash
}

func (ct *tfheCiphertext) availableForOps() bool {
Expand Down