diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 29f8eb03c..26f68b0f3 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -71,7 +71,6 @@ var PrecompiledContractsHomestead = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{66}): &verifyCiphertext{}, common.BytesToAddress([]byte{67}): &reencrypt{}, common.BytesToAddress([]byte{68}): &fhePubKey{}, - common.BytesToAddress([]byte{69}): &require{}, common.BytesToAddress([]byte{70}): &fheLe{}, common.BytesToAddress([]byte{71}): &fheSub{}, common.BytesToAddress([]byte{72}): &fheMul{}, @@ -115,7 +114,6 @@ var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{66}): &verifyCiphertext{}, common.BytesToAddress([]byte{67}): &reencrypt{}, common.BytesToAddress([]byte{68}): &fhePubKey{}, - common.BytesToAddress([]byte{69}): &require{}, common.BytesToAddress([]byte{70}): &fheLe{}, common.BytesToAddress([]byte{71}): &fheSub{}, common.BytesToAddress([]byte{72}): &fheMul{}, @@ -160,7 +158,6 @@ var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{66}): &verifyCiphertext{}, common.BytesToAddress([]byte{67}): &reencrypt{}, common.BytesToAddress([]byte{68}): &fhePubKey{}, - common.BytesToAddress([]byte{69}): &require{}, common.BytesToAddress([]byte{70}): &fheLe{}, common.BytesToAddress([]byte{71}): &fheSub{}, common.BytesToAddress([]byte{72}): &fheMul{}, @@ -205,7 +202,6 @@ var PrecompiledContractsBerlin = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{66}): &verifyCiphertext{}, common.BytesToAddress([]byte{67}): &reencrypt{}, common.BytesToAddress([]byte{68}): &fhePubKey{}, - common.BytesToAddress([]byte{69}): &require{}, common.BytesToAddress([]byte{70}): &fheLe{}, common.BytesToAddress([]byte{71}): &fheSub{}, common.BytesToAddress([]byte{72}): &fheMul{}, @@ -250,7 +246,6 @@ var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{66}): &verifyCiphertext{}, common.BytesToAddress([]byte{67}): &reencrypt{}, common.BytesToAddress([]byte{68}): &fhePubKey{}, - common.BytesToAddress([]byte{69}): &require{}, common.BytesToAddress([]byte{70}): &fheLe{}, common.BytesToAddress([]byte{71}): &fheSub{}, common.BytesToAddress([]byte{72}): &fheMul{}, @@ -1460,12 +1455,6 @@ var fheVerifyGasCosts = map[fheUintType]uint64{ FheUint32: params.FheUint32VerifyGas, } -var fheRequireGasCosts = map[fheUintType]uint64{ - FheUint8: params.FheUint8RequireGas, - FheUint16: params.FheUint16RequireGas, - FheUint32: params.FheUint32RequireGas, -} - var fheTrivialEncryptGasCosts = map[fheUintType]uint64{ FheUint8: params.FheUint8TrivialEncryptGas, FheUint16: params.FheUint16TrivialEncryptGas, @@ -1708,6 +1697,13 @@ func (e *reencrypt) Run(accessibleState PrecompileAccessibleState, caller common } ct := getVerifiedCiphertext(accessibleState, common.BytesToHash(input[0:32])) if ct != nil { + // Make sure we don't decrypt before any optimistic requires are checked. + optReqResult, optReqErr := evaluateRemainingOptimisticRequires(accessibleState.Interpreter()) + if optReqErr != nil { + return nil, optReqErr + } else if !optReqResult { + return nil, ErrExecutionReverted + } decryptedValue, err := ct.ciphertext.decrypt() if err != nil { logger.Error("reencrypt decryption failed", "err", err) @@ -1876,57 +1872,6 @@ func evaluateRemainingOptimisticRequires(in *EVMInterpreter) (bool, error) { return true, nil } -type require struct{} - -func (e *require) RequiredGas(accessibleState PrecompileAccessibleState, input []byte) uint64 { - logger := accessibleState.Interpreter().evm.Logger - if len(input) != 32 { - logger.Error("require RequiredGas() input len must be 32 bytes", "input", hex.EncodeToString(input), "len", len(input)) - return 0 - } - ct := getVerifiedCiphertext(accessibleState, common.BytesToHash(input)) - if ct == nil { - logger.Error("require RequiredGas() input doesn't point to verified ciphertext", "input", hex.EncodeToString(input)) - return 0 - } - return fheRequireGasCosts[ct.ciphertext.fheUintType] -} - -func (e *require) Run(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, readOnly bool) ([]byte, error) { - logger := accessibleState.Interpreter().evm.Logger - if len(input) != 32 { - msg := "require input len must be 32 bytes" - logger.Error(msg, "input", hex.EncodeToString(input), "len", len(input)) - return nil, errors.New(msg) - } - ct := getVerifiedCiphertext(accessibleState, common.BytesToHash(input)) - if ct == nil { - msg := "require unverified handle" - logger.Error(msg, "input", hex.EncodeToString(input)) - return nil, errors.New(msg) - } - // If we are doing gas estimation, assume the require is true. - if !accessibleState.Interpreter().evm.Commit && !accessibleState.Interpreter().evm.EthCall { - return nil, nil - } - // Make sure we don't decrypt before any optimistic requires are checked. - optReqResult, optReqErr := evaluateRemainingOptimisticRequires(accessibleState.Interpreter()) - if optReqErr != nil { - return nil, optReqErr - } else if !optReqResult { - return nil, ErrExecutionReverted - } - value, err := decryptValue(ct.ciphertext, accessibleState.Interpreter()) - if err != nil { - logger.Error("require failed to decrypt, reverting", "err", err) - return nil, err - } else if value == 0 { - logger.Error("require value is false, reverting") - return nil, ErrExecutionReverted - } - return nil, nil -} - type optimisticRequire struct{} func (e *optimisticRequire) RequiredGas(accessibleState PrecompileAccessibleState, input []byte) uint64 { diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index ddb6a397d..ed8305287 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -1555,40 +1555,6 @@ func newStopOpcodeContract() *Contract { return c } -func TestRequire(t *testing.T) { - var value uint64 = 1 - c := &require{} - depth := 1 - state := newTestState() - state.interpreter.evm.depth = depth - addr := common.Address{} - readOnly := false - hash := verifyCiphertextInTestMemory(state.interpreter, value, depth, FheUint8).getHash() - out, err := c.Run(state, addr, addr, hash.Bytes(), 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)) - } -} - -func TestRequireRevert(t *testing.T) { - var value uint64 = 0 - c := &require{} - depth := 1 - state := newTestState() - state.interpreter.evm.depth = depth - addr := common.Address{} - readOnly := false - hash := verifyCiphertextInTestMemory(state.interpreter, value, depth, FheUint8).getHash() - out, err := c.Run(state, addr, addr, hash.Bytes(), readOnly) - if err == nil || err != ErrExecutionReverted { - t.Fatalf("require expected reversal on value 0") - } else if len(out) != 0 { - t.Fatalf("require expected output len of 0, got %v", len(out)) - } -} - func TestOneTrueOptimisticRequire(t *testing.T) { var value uint64 = 1 c := &optimisticRequire{} @@ -1785,62 +1751,6 @@ func TestDecryptWithTrueOptimisticRequire(t *testing.T) { } } -func TestRequireWithFalseOptimisticRequire(t *testing.T) { - opt := &optimisticRequire{} - req := &require{} - depth := 0 - state := newTestState() - state.interpreter.evm.depth = depth - addr := common.Address{} - readOnly := false - // Call optimistic require with a false value and expect it succeeds. - optHash := verifyCiphertextInTestMemory(state.interpreter, 0, depth, FheUint8).getHash() - out, err := opt.Run(state, addr, addr, optHash.Bytes(), 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)) - } - // Call require with a true value, but expect it to fail due to the optimistic require being false. - reqHash := verifyCiphertextInTestMemory(state.interpreter, 1, depth, FheUint8).getHash() - _, err = req.Run(state, addr, addr, reqHash.Bytes(), readOnly) - if err == nil { - t.Fatalf("expected require fails due to false optimistic require") - } - // Make sure there are no more optimistic requires after the require call. - if len(state.interpreter.optimisticRequires) != 0 { - t.Fatalf("expected that there are no optimistic requires after require") - } -} - -func TestRequireWithTrueOptimisticRequire(t *testing.T) { - opt := &optimisticRequire{} - req := &require{} - depth := 0 - state := newTestState() - state.interpreter.evm.depth = depth - addr := common.Address{} - readOnly := false - // Call optimistic require with a true value and expect it succeeds. - optHash := verifyCiphertextInTestMemory(state.interpreter, 1, depth, FheUint8).getHash() - out, err := opt.Run(state, addr, addr, optHash.Bytes(), 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)) - } - // Call require with a true value and expect it to succeed due to the optimistic require being true. - reqHash := verifyCiphertextInTestMemory(state.interpreter, 1, depth, FheUint8).getHash() - _, err = req.Run(state, addr, addr, reqHash.Bytes(), readOnly) - if err != nil { - t.Fatalf(err.Error()) - } - // Make sure there are no more optimistic requires after the require call. - if len(state.interpreter.optimisticRequires) != 0 { - t.Fatalf("expected that there are no optimistic requires after require") - } -} - func TestVerifyCiphertextInvalidType(t *testing.T) { c := &verifyCiphertext{} depth := 1