diff --git a/docs/getting_started/Integration.md b/docs/getting_started/Integration.md index 2a66c7e..d67f7ab 100644 --- a/docs/getting_started/Integration.md +++ b/docs/getting_started/Integration.md @@ -13,6 +13,7 @@ This document is based on go-ethereum v1.13.5 ```go func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation bool, isHomestead, isEIP2028 bool, isEIP3860 bool) (uint64, error) ``` + replace the last return with: ```go @@ -39,9 +40,11 @@ type PrecompiledContract interface { #### Update all previous uses of this interface Add: + ```go common.BytesToAddress([]byte{93}): &fheLib{} ``` + to all precompiled contract maps (e.g. `PrecompiledContractsHomestead` ) {% hint style="info" %} @@ -129,10 +132,13 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig #### Update RunPrecompiledContract After changing precompiled contract interface in 2, we have to change usages of: + ```go RunPrecompiledContract(p, input, gas) ``` + to: + ```go RunPrecompiledContract(p, evm, caller.Address(), addr, input, gas) ``` @@ -260,6 +266,7 @@ defer fhevm.RestoreVerifiedDepths(interpreter.evm.FhevmEnvironment(), verifiedBe ``` The call function is named differently in the 3 functions to update: + ```go ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, bigVal) ``` @@ -267,11 +274,13 @@ ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, b #### Update `opSelfdestruct` In: + ```go func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) ``` Replace the following lines: + ```go beneficiary := scope.Stack.pop() balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address()) @@ -280,6 +289,7 @@ interpreter.evm.StateDB.SelfDestruct(scope.Contract.Address()) ``` with this call to the fhevm: + ```go beneficiary, balance := fhevm.OpSelfdestruct(pc, interpreter.evm.FhevmEnvironment(), scope) ``` @@ -326,14 +336,6 @@ defer func() { }() ``` -- 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()) -} -``` - ### Step 7: update `core/vm/stack.go` #### Implement the following methods @@ -351,9 +353,11 @@ func (st *Stack) Peek() *uint256.Int { ### Step 8: update `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 diff --git a/fhevm/contracts_test.go b/fhevm/contracts_test.go index 58203df..38f866b 100644 --- a/fhevm/contracts_test.go +++ b/fhevm/contracts_test.go @@ -77,26 +77,6 @@ func toPrecompileInputNoScalar(isScalar bool, hashes ...common.Hash) []byte { return ret } -func evaluateRemainingOptimisticRequiresWithoutKms(environment EVMEnvironment) (bool, error) { - requires := environment.FhevmData().optimisticRequires - len := len(requires) - defer func() { environment.FhevmData().resetOptimisticRequires() }() - if len != 0 { - var cumulative *tfhe.TfheCiphertext = requires[0] - var err error - for i := 1; i < len; i++ { - cumulative, err = cumulative.Bitand(requires[i]) - if err != nil { - environment.GetLogger().Error("evaluateRemainingOptimisticRequires bitand failed", "err", err) - return false, err - } - } - result, err := cumulative.Decrypt() - return result.Uint64() != 0, err - } - return true, nil -} - func decryptRunWithoutKms(environment EVMEnvironment, caller common.Address, addr common.Address, input []byte, readOnly bool) ([]byte, error) { logger := environment.GetLogger() // if not gas estimation and not view function fail if decryptions are disabled in transactions @@ -122,13 +102,6 @@ func decryptRunWithoutKms(environment EVMEnvironment, caller common.Address, add if !environment.IsCommitting() && !environment.IsEthCall() { return bytes.Repeat([]byte{0xFF}, 32), nil } - // Make sure we don't decrypt before any optimistic requires are checked. - optReqResult, optReqErr := evaluateRemainingOptimisticRequiresWithoutKms(environment) - if optReqErr != nil { - return nil, optReqErr - } else if !optReqResult { - return nil, ErrExecutionReverted - } plaintext, err := ct.ciphertext.Decrypt() if err != nil { @@ -4170,27 +4143,11 @@ func TestFheRandBoundedEthCall(t *testing.T) { } } -func EvalRemOptReqWhenStopTokenWithoutKms(env EVMEnvironment) (err error) { - err = nil - // If we are finishing execution (about to go to from depth 1 to depth 0), evaluate - // any remaining optimistic requires. - if env.GetDepth() == 1 { - result, evalErr := evaluateRemainingOptimisticRequiresWithoutKms(env) - if evalErr != nil { - err = evalErr - } else if !result { - err = ErrExecutionReverted - } - } - return err -} - func interpreterRunWithStopContract(environment *MockEVMEnvironment, interpreter *vm.EVMInterpreter, contract *vm.Contract, input []byte, readOnly bool) (ret []byte, err error) { ret, _ = interpreter.Run(contract, input, readOnly) // the following functions are meant to be ran from within interpreter.run so we increment depth to emulate that environment.depth++ RemoveVerifiedCipherextsAtCurrentDepth(environment) - err = EvalRemOptReqWhenStopTokenWithoutKms(environment) environment.depth-- return ret, err } @@ -4215,205 +4172,6 @@ func newStopOpcodeContract() *vm.Contract { return c } -func TestLibOneTrueOptimisticRequire(t *testing.T) { - var value uint64 = 1 - signature := "optimisticRequire(uint256)" - hashRes := crypto.Keccak256([]byte(signature)) - signatureBytes := hashRes[0:4] - depth := 1 - environment := newTestEVMEnvironment() - environment.depth = depth - addr := common.Address{} - readOnly := false - input := make([]byte, 0) - hash := verifyCiphertextInTestMemory(environment, value, depth, tfhe.FheUint8).GetHash() - input = append(input, signatureBytes...) - input = append(input, hash.Bytes()...) - out, err := FheLibRun(environment, addr, addr, input, readOnly) - if err != nil { - t.Fatalf(err.Error()) - } else if len(out) != 0 { - t.Fatalf("require expected output len of 0, got %v", len(out)) - } - - interpreter := newInterpreterFromEnvironment(environment) - // Call the interpreter with a single STOP opcode and expect that the optimistic require doesn't revert. - out, err = interpreterRunWithStopContract(environment, interpreter, newStopOpcodeContract(), make([]byte, 0), readOnly) - if err != nil { - t.Fatalf(err.Error()) - } else if out != nil { - t.Fatalf("expected empty response") - } -} - -func TestOneFalseOptimisticRequire(t *testing.T) { - var value uint64 = 0 - depth := 0 - environment := newTestEVMEnvironment() - environment.depth = depth - addr := common.Address{} - readOnly := false - hash := verifyCiphertextInTestMemory(environment, value, depth, tfhe.FheUint8).GetHash() - out, err := optimisticRequireRun(environment, addr, addr, hash.Bytes(), readOnly, nil) - if err != nil { - t.Fatalf(err.Error()) - } else if len(out) != 0 { - t.Fatalf("require expected output len of 0, got %v", len(out)) - } - interpreter := newInterpreterFromEnvironment(environment) - // Call the interpreter with a single STOP opcode and expect that the optimistic require reverts. - out, err = interpreterRunWithStopContract(environment, interpreter, newStopOpcodeContract(), make([]byte, 0), readOnly) - if err == nil || err != ErrExecutionReverted { - t.Fatalf("require expected reversal on value 0") - } else if out != nil { - t.Fatalf("expected empty response") - } -} - -func TestTwoTrueOptimisticRequires(t *testing.T) { - var value uint64 = 1 - depth := 1 - environment := newTestEVMEnvironment() - environment.depth = depth - addr := common.Address{} - readOnly := false - hash := verifyCiphertextInTestMemory(environment, value, depth, tfhe.FheUint8).GetHash() - out, err := optimisticRequireRun(environment, addr, addr, hash.Bytes(), readOnly, nil) - if err != nil { - t.Fatalf(err.Error()) - } else if len(out) != 0 { - t.Fatalf("require expected output len of 0, got %v", len(out)) - } - hash = verifyCiphertextInTestMemory(environment, value, depth, tfhe.FheUint8).GetHash() - out, err = optimisticRequireRun(environment, addr, addr, hash.Bytes(), readOnly, nil) - if err != nil { - t.Fatalf(err.Error()) - } else if len(out) != 0 { - t.Fatalf("require expected output len of 0, got %v", len(out)) - } - interpreter := newInterpreterFromEnvironment(environment) - // Call the interpreter with a single STOP opcode and expect that the optimistic require doesn't revert. - out, err = interpreterRunWithStopContract(environment, interpreter, newStopOpcodeContract(), make([]byte, 0), readOnly) - if err != nil { - t.Fatalf(err.Error()) - } else if out != nil { - t.Fatalf("expected empty response") - } -} - -func TestOptimisticRequireTwiceOnSameCiphertext(t *testing.T) { - var value uint64 = 1 - depth := 1 - environment := newTestEVMEnvironment() - environment.depth = depth - addr := common.Address{} - readOnly := false - ct := verifyCiphertextInTestMemory(environment, value, depth, tfhe.FheUint8) - hash := ct.GetHash() - out, err := optimisticRequireRun(environment, addr, addr, hash.Bytes(), readOnly, nil) - if err != nil { - t.Fatalf(err.Error()) - } else if len(out) != 0 { - t.Fatalf("require expected output len of 0, got %v", len(out)) - } - out, err = optimisticRequireRun(environment, addr, addr, hash.Bytes(), readOnly, nil) - if err != nil { - t.Fatalf(err.Error()) - } else if len(out) != 0 { - t.Fatalf("require expected output len of 0, got %v", len(out)) - } - interpreter := newInterpreterFromEnvironment(environment) - // Call the interpreter with a single STOP opcode and expect that the optimistic require doesn't revert. - out, err = interpreterRunWithStopContract(environment, interpreter, newStopOpcodeContract(), make([]byte, 0), readOnly) - if err != nil { - t.Fatalf(err.Error()) - } else if out != nil { - t.Fatalf("expected empty response") - } -} - -func TestOneFalseAndOneTrueOptimisticRequire(t *testing.T) { - depth := 0 - environment := newTestEVMEnvironment() - environment.depth = depth - addr := common.Address{} - readOnly := false - hash := verifyCiphertextInTestMemory(environment, 0, depth, tfhe.FheUint8).GetHash() - out, err := optimisticRequireRun(environment, addr, addr, hash.Bytes(), readOnly, nil) - if err != nil { - t.Fatalf(err.Error()) - } else if len(out) != 0 { - t.Fatalf("require expected output len of 0, got %v", len(out)) - } - hash = verifyCiphertextInTestMemory(environment, 1, depth, tfhe.FheUint8).GetHash() - out, err = optimisticRequireRun(environment, addr, addr, hash.Bytes(), readOnly, nil) - if err != nil { - t.Fatalf(err.Error()) - } else if len(out) != 0 { - t.Fatalf("require expected output len of 0, got %v", len(out)) - } - interpreter := newInterpreterFromEnvironment(environment) - // Call the interpreter with a single STOP opcode and expect that the optimistic require reverts. - out, err = interpreterRunWithStopContract(environment, interpreter, newStopOpcodeContract(), make([]byte, 0), readOnly) - if err == nil || err != ErrExecutionReverted { - t.Fatalf("require expected reversal on value 0") - } else if out != nil { - t.Fatalf("expected empty response") - } -} - -func TestDecryptWithFalseOptimisticRequire(t *testing.T) { - depth := 0 - environment := newTestEVMEnvironment() - environment.depth = depth - addr := common.Address{} - readOnly := false - // Call optimistic require with a false value and expect it succeeds. - hash := verifyCiphertextInTestMemory(environment, 0, depth, tfhe.FheUint8).GetHash() - out, err := optimisticRequireRun(environment, addr, addr, hash.Bytes(), readOnly, nil) - if err != nil { - t.Fatalf(err.Error()) - } else if len(out) != 0 { - t.Fatalf("require expected output len of 0, got %v", len(out)) - } - // Call decrypt and expect it to fail due to the optimistic require being false. - _, err = decryptRunWithoutKms(environment, addr, addr, hash.Bytes(), readOnly) - if err == nil { - t.Fatalf("expected decrypt fails due to false optimistic require") - } - // Make sure there are no more optimistic requires after the decrypt call. - if len(environment.FhevmData().optimisticRequires) != 0 { - t.Fatalf("expected that there are no optimistic requires after decrypt") - } -} - -func TestDecryptWithTrueOptimisticRequire(t *testing.T) { - depth := 0 - environment := newTestEVMEnvironment() - environment.depth = depth - addr := common.Address{} - readOnly := false - // Call optimistic require with a false value and expect it succeeds. - hash := verifyCiphertextInTestMemory(environment, 1, depth, tfhe.FheUint8).GetHash() - out, err := optimisticRequireRun(environment, addr, addr, hash.Bytes(), readOnly, nil) - if err != nil { - t.Fatalf(err.Error()) - } else if len(out) != 0 { - t.Fatalf("require expected output len of 0, got %v", len(out)) - } - // Call decrypt and expect it to succeed due to the optimistic require being true. - out, err = decryptRunWithoutKms(environment, addr, addr, hash.Bytes(), readOnly) - if err != nil { - t.Fatalf(err.Error()) - } else if len(out) != 32 { - t.Fatalf("decrypt expected output len of 32, got %v", len(out)) - } - // Make sure there are no more optimistic requires after the decrypt call. - if len(environment.FhevmData().optimisticRequires) != 0 { - t.Fatalf("expected that there are no optimistic requires after decrypt") - } -} - func TestDecryptInTransactionDisabled(t *testing.T) { depth := 0 environment := newTestEVMEnvironment() diff --git a/fhevm/fhelib.go b/fhevm/fhelib.go index cab695c..44f7c6b 100644 --- a/fhevm/fhelib.go +++ b/fhevm/fhelib.go @@ -226,12 +226,6 @@ var fhelibMethods = []*FheLibMethod{ requiredGasFunction: verifyCiphertextRequiredGas, runFunction: verifyCiphertextRun, }, - { - name: "optimisticRequire", - argTypes: "(uint256)", - requiredGasFunction: optimisticRequireRequiredGas, - runFunction: optimisticRequireRun, - }, { name: "getCiphertext", argTypes: "(address,uint256)", diff --git a/fhevm/interface.go b/fhevm/interface.go index bc8941c..79c4ca6 100644 --- a/fhevm/interface.go +++ b/fhevm/interface.go @@ -6,7 +6,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/holiman/uint256" - "github.com/zama-ai/fhevm-go/fhevm/tfhe" ) type EVMEnvironment interface { @@ -47,25 +46,11 @@ type FhevmData struct { // A map from a ciphertext hash to itself and stack depth at which it is verified verifiedCiphertexts map[common.Hash]*verifiedCiphertext - // All optimistic requires encountered up to that point in the txn execution - optimisticRequires []*tfhe.TfheCiphertext - nextCiphertextHashOnGasEst uint256.Int } -// Set the optimisticRequires array to an empty array -func (data *FhevmData) resetOptimisticRequires() { - data.optimisticRequires = make([]*tfhe.TfheCiphertext, 0) -} - -// Append one ciphertext to the optimisticRequires array -func (data *FhevmData) appendOptimisticRequires(ct *tfhe.TfheCiphertext) { - data.optimisticRequires = append(data.optimisticRequires, ct) -} - func NewFhevmData() FhevmData { return FhevmData{ verifiedCiphertexts: make(map[common.Hash]*verifiedCiphertext), - optimisticRequires: make([]*tfhe.TfheCiphertext, 0), } } diff --git a/fhevm/interpreter.go b/fhevm/interpreter.go index d3a1155..5d62cfb 100644 --- a/fhevm/interpreter.go +++ b/fhevm/interpreter.go @@ -69,31 +69,9 @@ func (vc *verifiedCiphertext) hash() common.Hash { type PrivilegedMemory struct { // A map from a ciphertext hash to itself and stack depths at which it is verified VerifiedCiphertexts map[common.Hash]*verifiedCiphertext - - // All optimistic requires encountered up to that point in the txn execution - optimisticRequires []*tfhe.TfheCiphertext } var PrivilegedMempory *PrivilegedMemory = &PrivilegedMemory{ make(map[common.Hash]*verifiedCiphertext), - make([]*tfhe.TfheCiphertext, 0), } -// Evaluate remaining optimistic requires when Interpreter.Run get an errStopToken -// -// This function is meant to be integrated as part of vm.EVMInterpreter.Run in the case -// there was an errStopToken -func EvalRemOptReqWhenStopToken(env EVMEnvironment) (err error) { - err = nil - // If we are finishing execution (about to go to from depth 1 to depth 0), evaluate - // any remaining optimistic requires. - if env.GetDepth() == 1 { - result, evalErr := evaluateRemainingOptimisticRequires(env) - if evalErr != nil { - err = evalErr - } else if !result { - err = ErrExecutionReverted - } - } - return err -} diff --git a/fhevm/operators_crypto.go b/fhevm/operators_crypto.go index 1f14a03..e3db9b7 100644 --- a/fhevm/operators_crypto.go +++ b/fhevm/operators_crypto.go @@ -100,13 +100,6 @@ func reencryptRun(environment EVMEnvironment, caller common.Address, addr common ct := getVerifiedCiphertext(environment, common.BytesToHash(input[0:32])) if ct != nil { otelDescribeOperandsFheTypes(runSpan, ct.fheUintType()) - // Make sure we don't decrypt before any optimistic requires are checked. - // optReqResult, optReqErr := evaluateRemainingOptimisticRequires(environment) - // if optReqErr != nil { - // return nil, optReqErr - // } else if !optReqResult { - // return nil, ErrExecutionReverted - // } var fheType kms.FheType switch ct.fheUintType() { @@ -172,34 +165,6 @@ func reencryptRun(environment EVMEnvironment, caller common.Address, addr common return nil, errors.New(msg) } -func optimisticRequireRun(environment EVMEnvironment, caller common.Address, addr common.Address, input []byte, readOnly bool, runSpan trace.Span) ([]byte, error) { - input = input[:minInt(32, len(input))] - - logger := environment.GetLogger() - if len(input) != 32 { - msg := "optimisticRequire input len must be 32 bytes" - logger.Error(msg, "input", hex.EncodeToString(input), "len", len(input)) - return nil, errors.New(msg) - } - ct := getVerifiedCiphertext(environment, common.BytesToHash(input)) - if ct == nil { - msg := "optimisticRequire unverified handle" - logger.Error(msg, "input", hex.EncodeToString(input)) - return nil, errors.New(msg) - } - otelDescribeOperandsFheTypes(runSpan, ct.fheUintType()) - // If we are doing gas estimation, don't do anything as we would assume all requires are true. - if !environment.IsCommitting() && !environment.IsEthCall() { - return nil, nil - } - if ct.fheUintType() != tfhe.FheUint8 { - msg := "optimisticRequire ciphertext type is not FheUint8" - logger.Error(msg, "type", ct.fheUintType()) - return nil, errors.New(msg) - } - environment.FhevmData().appendOptimisticRequires(ct.ciphertext) - return nil, nil -} func decryptRun(environment EVMEnvironment, caller common.Address, addr common.Address, input []byte, readOnly bool, runSpan trace.Span) ([]byte, error) { input = input[:minInt(32, len(input))] @@ -229,13 +194,6 @@ func decryptRun(environment EVMEnvironment, caller common.Address, addr common.A if !environment.IsCommitting() && !environment.IsEthCall() { return bytes.Repeat([]byte{0xFF}, 32), nil } - // Make sure we don't decrypt before any optimistic requires are checked. - optReqResult, optReqErr := evaluateRemainingOptimisticRequires(environment) - if optReqErr != nil { - return nil, optReqErr - } else if !optReqResult { - return nil, ErrExecutionReverted - } plaintext, err := decryptValue(environment, ct.ciphertext) if err != nil { @@ -331,29 +289,6 @@ func decryptValue(environment EVMEnvironment, ct *tfhe.TfheCiphertext) (uint64, return uint64(res.Plaintext), err } -// If there are optimistic requires, check them by doing bitwise AND on all of them. -// That works, because we assume their values are either 0 or 1. If there is at least -// one 0, the result will be 0 (false). -func evaluateRemainingOptimisticRequires(environment EVMEnvironment) (bool, error) { - requires := environment.FhevmData().optimisticRequires - len := len(requires) - defer func() { environment.FhevmData().resetOptimisticRequires() }() - if len != 0 { - var cumulative *tfhe.TfheCiphertext = requires[0] - var err error - for i := 1; i < len; i++ { - cumulative, err = cumulative.Bitand(requires[i]) - if err != nil { - environment.GetLogger().Error("evaluateRemainingOptimisticRequires bitand failed", "err", err) - return false, err - } - } - result, err := decryptValue(environment, cumulative) - return result != 0, err - } - return true, nil -} - func castRun(environment EVMEnvironment, caller common.Address, addr common.Address, input []byte, readOnly bool, runSpan trace.Span) ([]byte, error) { input = input[:minInt(33, len(input))] diff --git a/fhevm/operators_crypto_gas.go b/fhevm/operators_crypto_gas.go index 676a549..71bab04 100644 --- a/fhevm/operators_crypto_gas.go +++ b/fhevm/operators_crypto_gas.go @@ -34,32 +34,6 @@ func reencryptRequiredGas(environment EVMEnvironment, input []byte) uint64 { return environment.FhevmParams().GasCosts.FheReencrypt[ct.fheUintType()] } -func optimisticRequireRequiredGas(environment EVMEnvironment, input []byte) uint64 { - input = input[:minInt(32, len(input))] - - logger := environment.GetLogger() - if len(input) != 32 { - logger.Error("optimisticRequire RequiredGas() input len must be 32 bytes", - "input", hex.EncodeToString(input), "len", len(input)) - return 0 - } - ct := getVerifiedCiphertext(environment, common.BytesToHash(input)) - if ct == nil { - logger.Error("optimisticRequire RequiredGas() input doesn't point to verified ciphertext", - "input", hex.EncodeToString(input)) - return 0 - } - if ct.fheUintType() != tfhe.FheUint8 { - logger.Error("optimisticRequire RequiredGas() ciphertext type is not FheUint8", - "type", ct.fheUintType()) - return 0 - } - if len(environment.FhevmData().optimisticRequires) == 0 { - return environment.FhevmParams().GasCosts.FheOptRequire[tfhe.FheUint8] - } - return environment.FhevmParams().GasCosts.FheOptRequireBitAnd[tfhe.FheUint8] -} - func getCiphertextRequiredGas(environment EVMEnvironment, input []byte) uint64 { input = input[:minInt(64, len(input))] diff --git a/fhevm/params.go b/fhevm/params.go index 0386797..e98d0ab 100644 --- a/fhevm/params.go +++ b/fhevm/params.go @@ -62,8 +62,6 @@ type GasCosts struct { FheRand map[tfhe.FheUintType]uint64 FheIfThenElse map[tfhe.FheUintType]uint64 FheVerify map[tfhe.FheUintType]uint64 - FheOptRequire map[tfhe.FheUintType]uint64 - FheOptRequireBitAnd map[tfhe.FheUintType]uint64 FheGetCiphertext map[tfhe.FheUintType]uint64 } @@ -215,23 +213,6 @@ func DefaultGasCosts() GasCosts { tfhe.FheUint32: 40000 + AdjustFHEGas, tfhe.FheUint64: 43000 + AdjustFHEGas, }, - // TODO: As of now, only support FheUint8. All optimistic require predicates are - // downcast to FheUint8 at the solidity level. Eventually move to ebool. - // If there is at least one optimistic require, we need to decrypt it as it was a normal FHE require. - // For every subsequent optimistic require, we need to bitand it with the current require value - that - // works, because we assume requires have a value of 0 or 1. - FheOptRequire: map[tfhe.FheUintType]uint64{ - tfhe.FheUint4: 170000, - tfhe.FheUint8: 170000, - tfhe.FheUint16: 180000, - tfhe.FheUint32: 190000, - }, - FheOptRequireBitAnd: map[tfhe.FheUintType]uint64{ - tfhe.FheUint4: 20000, - tfhe.FheUint8: 20000, - tfhe.FheUint16: 20000, - tfhe.FheUint32: 20000, - }, FheGetCiphertext: map[tfhe.FheUintType]uint64{ tfhe.FheUint8: 12000, tfhe.FheUint16: 14000,