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

Commit

Permalink
refactor: remove optReq
Browse files Browse the repository at this point in the history
  • Loading branch information
immortal-tofu committed Mar 9, 2024
1 parent dea7141 commit 00d0c34
Show file tree
Hide file tree
Showing 8 changed files with 12 additions and 403 deletions.
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

0 comments on commit 00d0c34

Please sign in to comment.