Skip to content

Commit

Permalink
fix(precompiles): remove require (#158)
Browse files Browse the repository at this point in the history
  • Loading branch information
tremblaythibaultl committed Sep 7, 2023
1 parent 0fe94b1 commit d9b1715
Show file tree
Hide file tree
Showing 2 changed files with 7 additions and 152 deletions.
69 changes: 7 additions & 62 deletions core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{},
Expand Down Expand Up @@ -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{},
Expand Down Expand Up @@ -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{},
Expand Down Expand Up @@ -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{},
Expand Down Expand Up @@ -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{},
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down
90 changes: 0 additions & 90 deletions core/vm/contracts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit d9b1715

Please sign in to comment.