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

refactor: remove optReq #98

Merged
merged 1 commit into from
Mar 12, 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
20 changes: 12 additions & 8 deletions docs/getting_started/Integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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" %}
Expand Down Expand Up @@ -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)
```
Expand Down Expand Up @@ -260,18 +266,21 @@ 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)
```

#### 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())
Expand All @@ -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)
```
Expand Down Expand Up @@ -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
Expand All @@ -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

Expand Down
242 changes: 0 additions & 242 deletions fhevm/contracts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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
}
Expand All @@ -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()
Expand Down
6 changes: 0 additions & 6 deletions fhevm/fhelib.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,12 +226,6 @@ var fhelibMethods = []*FheLibMethod{
requiredGasFunction: verifyCiphertextRequiredGas,
runFunction: verifyCiphertextRun,
},
{
name: "optimisticRequire",
argTypes: "(uint256)",
requiredGasFunction: optimisticRequireRequiredGas,
runFunction: optimisticRequireRun,
},
{
name: "getCiphertext",
argTypes: "(address,uint256)",
Expand Down
Loading
Loading