Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: missing gas costs and memory issues #121

Merged
merged 1 commit into from
Jun 19, 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
137 changes: 0 additions & 137 deletions fhevm/contracts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package fhevm
import (
"bytes"
"context"
"encoding/hex"
"errors"
"fmt"
"math/big"
"os"
Expand Down Expand Up @@ -164,46 +162,6 @@ func toPrecompileInputNoScalar(isScalar bool, hashes ...common.Hash) []byte {
return ret
}

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
if environment.IsCommitting() && !environment.IsEthCall() && environment.FhevmParams().DisableDecryptionsInTransaction {
msg := "decryptions during transaction are disabled"
logger.Error(msg, "input", hex.EncodeToString(input))
return nil, errors.New(msg)
}
if len(input) != 32 {
msg := "decrypt input len must be 32 bytes"
logger.Error(msg, "input", hex.EncodeToString(input), "len", len(input))
return nil, errors.New(msg)
}
ct, _ := loadCiphertext(environment, common.BytesToHash(input))
if ct == nil {
msg := "decrypt unverified handle"
logger.Error(msg, "input", hex.EncodeToString(input))
return nil, errors.New(msg)
}

// If we are doing gas estimation, skip decryption and make sure we return the maximum possible value.
// We need that, because non-zero bytes cost more than zero bytes in some contexts (e.g. SSTORE or memory operations).
if !environment.IsCommitting() && !environment.IsEthCall() {
return bytes.Repeat([]byte{0xFF}, 32), nil
}

plaintext, err := ct.Decrypt()
if err != nil {
logger.Error("decrypt failed", "err", err)
return nil, err
}

logger.Info("decrypt success", "plaintext", plaintext)

// Always return a 32-byte big-endian integer.
ret := make([]byte, 32)
plaintext.FillBytes(ret)
return ret, nil
}

var scalarBytePadding = make([]byte, 31)

func toLibPrecompileInput(method string, isScalar bool, hashes ...common.Hash) []byte {
Expand Down Expand Up @@ -1943,31 +1901,6 @@ func LibDecrypt(t *testing.T, fheUintType tfhe.FheUintType) {
}
}

// TODO: can be enabled if mocking kms or running a kms during tests
// func TestLibReencrypt(t *testing.T) {
// signature := "reencrypt(uint256,uint256)"
// hashRes := crypto.Keccak256([]byte(signature))
// signatureBytes := hashRes[0:4]
// depth := 1
// environment := newTestEVMEnvironment()
// environment.depth = depth
// environment.ethCall = true
// toEncrypt := 7
// fheUintType := tfhe.FheUint8
// encCiphertext := loadCiphertextInTestMemory(environment, uint64(toEncrypt), depth, fheUintType).getHash()
// addr := tfheExecutorContractAddress
// readOnly := false
// input := make([]byte, 0)
// input = append(input, signatureBytes...)
// input = append(input, encCiphertext.Bytes()...)
// // just append twice not to generate public key
// input = append(input, encCiphertext.Bytes()...)
// _, err := FheLibRun(environment, addr, addr, input, readOnly)
// if err != nil {
// t.Fatalf("Reencrypt error: %s", err.Error())
// }
// }

func TestLibCast(t *testing.T) {
signature := "cast(uint256,bytes1)"
hashRes := crypto.Keccak256([]byte(signature))
Expand Down Expand Up @@ -3122,41 +3055,6 @@ func FheIfThenElse(t *testing.T, fheUintType tfhe.FheUintType, condition uint64)
}
}

func Decrypt(t *testing.T, fheUintType tfhe.FheUintType) {
var value uint64
switch fheUintType {
case tfhe.FheBool:
value = 1
case tfhe.FheUint4:
value = 2
case tfhe.FheUint8:
value = 2
case tfhe.FheUint16:
value = 4283
case tfhe.FheUint32:
value = 1333337
case tfhe.FheUint64:
value = 133333777777777
}
depth := 1
environment := newTestEVMEnvironment()
environment.depth = depth
addr := tfheExecutorContractAddress
readOnly := false
hash := loadCiphertextInTestMemory(environment, value, depth, fheUintType).GetHash()
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))
}
result := big.Int{}
result.SetBytes(out)
if result.Uint64() != value {
t.Fatalf("decrypt result not equal to value, result %v != value %v", result.Uint64(), value)
}
}

func FheRand(t *testing.T, fheUintType tfhe.FheUintType) {
depth := 1
environment := newTestEVMEnvironment()
Expand Down Expand Up @@ -4677,22 +4575,6 @@ func TestFheScalarMax64(t *testing.T) {
FheMax(t, tfhe.FheUint64, true)
}

func TestDecrypt8(t *testing.T) {
Decrypt(t, tfhe.FheUint8)
}

func TestDecrypt16(t *testing.T) {
Decrypt(t, tfhe.FheUint16)
}

func TestDecrypt32(t *testing.T) {
Decrypt(t, tfhe.FheUint32)
}

func TestDecrypt64(t *testing.T) {
Decrypt(t, tfhe.FheUint64)
}

func TestFheRand8(t *testing.T) {
FheRand(t, tfhe.FheUint8)
}
Expand Down Expand Up @@ -4883,25 +4765,6 @@ func newInterpreterFromEnvironment(environment *MockEVMEnvironment) *vm.EVMInter
return interpreter
}

func TestDecryptInTransactionDisabled(t *testing.T) {
depth := 0
environment := newTestEVMEnvironment()
environment.depth = depth
environment.commit = true
environment.ethCall = false
environment.fhevmParams.DisableDecryptionsInTransaction = true
addr := tfheExecutorContractAddress
readOnly := false
hash := loadCiphertextInTestMemory(environment, 1, depth, tfhe.FheUint8).GetHash()
// Call decrypt and expect it to fail due to disabling of decryptions during commit
_, err := decryptRunWithoutKms(environment, addr, addr, hash.Bytes(), readOnly)
if err == nil {
t.Fatalf("expected to error out in test")
} else if err.Error() != "decryptions during transaction are disabled" {
t.Fatalf("unexpected error for disabling decryption transactions, got %s", err.Error())
}
}

func TestFheLibGetCiphertextInvalidInputSize(t *testing.T) {
environment := newTestEVMEnvironment()
addr := tfheExecutorContractAddress
Expand Down
12 changes: 0 additions & 12 deletions fhevm/fhelib.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,18 +227,6 @@ var fhelibMethods = []*FheLibMethod{
requiredGasFunction: trivialEncryptRequiredGas,
runFunction: trivialEncryptRun,
},
{
name: "decrypt",
argTypes: "(uint256)",
requiredGasFunction: decryptRequiredGas,
runFunction: decryptRun,
},
{
name: "reencrypt",
argTypes: "(uint256,uint256)",
requiredGasFunction: reencryptRequiredGas,
runFunction: reencryptRun,
},
{
name: "verifyCiphertext",
argTypes: "(bytes32,address,bytes,bytes1)",
Expand Down
129 changes: 0 additions & 129 deletions fhevm/operators_crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,135 +180,6 @@ func verifyCiphertextRun(environment EVMEnvironment, caller common.Address, addr
return handle[:], nil
}

func reencryptRun(environment EVMEnvironment, caller common.Address, addr common.Address, input []byte, readOnly bool, runSpan trace.Span) ([]byte, error) {
input = input[:minInt(64, len(input))]
// precompileBytes, err := reencryptRun(environment, caller, addr, bwCompatBytes, readOnly)

logger := environment.GetLogger()
if !environment.IsEthCall() {
msg := "reencrypt only supported on EthCall"
logger.Error(msg)
return nil, errors.New(msg)
}
if len(input) != 64 {
msg := "reencrypt input len must be 64 bytes"
logger.Error(msg, "input", hex.EncodeToString(input), "len", len(input))
return nil, errors.New(msg)
}
handle := common.BytesToHash(input[0:32])
ct, _ := loadCiphertext(environment, handle)
if ct != nil {
otelDescribeOperandsFheTypes(runSpan, ct.Type())

var fheType kms.FheType
switch ct.Type() {
case tfhe.FheBool:
fheType = kms.FheType_Bool
case tfhe.FheUint4:
fheType = kms.FheType_Euint4
case tfhe.FheUint8:
fheType = kms.FheType_Euint8
case tfhe.FheUint16:
fheType = kms.FheType_Euint16
case tfhe.FheUint32:
fheType = kms.FheType_Euint32
case tfhe.FheUint64:
fheType = kms.FheType_Euint64
case tfhe.FheUint160:
fheType = kms.FheType_Euint160
}

pubKey := input[32:64]

// TODO: generate merkle proof for some data
proof := &kms.Proof{
Height: 3,
MerklePatriciaProof: []byte{},
}

reencryptionRequest := &kms.ReencryptionRequest{
FheType: fheType,
Ciphertext: ct.Serialize(),
Request: pubKey, // TODO: change according to the structure of `Request`
Proof: proof,
}

conn, err := grpc.Dial(kms.KmsEndpointAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return nil, errors.New("kms unreachable")
}
defer conn.Close()

ep := kms.NewKmsEndpointClient(conn)

ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()

res, err := ep.Reencrypt(ctx, reencryptionRequest)
if err != nil {
return nil, err
}

// TODO: decide if `res.Signature` should be verified here

var reencryptedValue = res.ReencryptedCiphertext

logger.Info("reencrypt success", "input", hex.EncodeToString(input), "callerAddr", caller, "reencryptedValue", reencryptedValue, "len", len(reencryptedValue))
reencryptedValue = toEVMBytes(reencryptedValue)
// pad according to abi specification, first add offset to the dynamic bytes argument
outputBytes := make([]byte, 32, len(reencryptedValue)+32)
outputBytes[31] = 0x20
outputBytes = append(outputBytes, reencryptedValue...)
return padArrayTo32Multiple(outputBytes), nil
}
msg := "reencrypt could not load ciphertext handle"
logger.Error(msg, "input", hex.EncodeToString(input))
return nil, errors.New(msg)
}

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))]

logger := environment.GetLogger()
// if not gas estimation and not view function fail if decryptions are disabled in transactions
if environment.IsCommitting() && !environment.IsEthCall() && environment.FhevmParams().DisableDecryptionsInTransaction {
msg := "decryptions during transaction are disabled"
logger.Error(msg, "input", hex.EncodeToString(input))
return nil, errors.New(msg)
}
if len(input) != 32 {
msg := "decrypt input len must be 32 bytes"
logger.Error(msg, "input", hex.EncodeToString(input), "len", len(input))
return nil, errors.New(msg)
}
ct, _ := loadCiphertext(environment, common.BytesToHash(input))
if ct == nil {
msg := "decrypt unverified handle"
logger.Error(msg, "input", hex.EncodeToString(input))
return nil, errors.New(msg)
}
otelDescribeOperandsFheTypes(runSpan, ct.Type())

// If we are doing gas estimation, skip decryption and make sure we return the maximum possible value.
// We need that, because non-zero bytes cost more than zero bytes in some contexts (e.g. SSTORE or memory operations).
if !environment.IsCommitting() && !environment.IsEthCall() {
return bytes.Repeat([]byte{0xFF}, 32), nil
}

plaintext, err := decryptValue(environment, ct)
if err != nil {
logger.Error("decrypt failed", "err", err)
return nil, err
}

logger.Info("decrypt success", "plaintext", plaintext)

// Always return a 32-byte big-endian integer.
ret := make([]byte, 32)
plaintext.FillBytes(ret)
return ret, nil
}

func getCiphertextRun(environment EVMEnvironment, caller common.Address, addr common.Address, input []byte, readOnly bool, runSpan trace.Span) ([]byte, error) {
input = input[:minInt(32, len(input))]

Expand Down
32 changes: 0 additions & 32 deletions fhevm/operators_crypto_gas.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,6 @@ func verifyCiphertextRequiredGas(environment EVMEnvironment, input []byte) uint6
return environment.FhevmParams().GasCosts.FheVerify[ct.Type()]
}

func reencryptRequiredGas(environment EVMEnvironment, input []byte) uint64 {
input = input[:minInt(64, len(input))]

logger := environment.GetLogger()
if len(input) != 64 {
logger.Error("reencrypt RequiredGas() input len must be 64 bytes", "input", hex.EncodeToString(input), "len", len(input))
return 0
}
ct, loadGas := loadCiphertext(environment, common.BytesToHash(input[0:32]))
if ct == nil {
logger.Error("reencrypt RequiredGas() input doesn't point to verified ciphertext", "input", hex.EncodeToString(input))
return loadGas
}
return environment.FhevmParams().GasCosts.FheReencrypt[ct.Type()] + loadGas
}

func getCiphertextRequiredGas(environment EVMEnvironment, input []byte) uint64 {
input = input[:minInt(32, len(input))]

Expand Down Expand Up @@ -71,22 +55,6 @@ func castRequiredGas(environment EVMEnvironment, input []byte) uint64 {
return environment.FhevmParams().GasCosts.FheCast + loadGas
}

func decryptRequiredGas(environment EVMEnvironment, input []byte) uint64 {
input = input[:minInt(32, len(input))]

logger := environment.GetLogger()
if len(input) != 32 {
logger.Error("decrypt RequiredGas() input len must be 32 bytes", "input", hex.EncodeToString(input), "len", len(input))
return 0
}
ct, loadGas := loadCiphertext(environment, common.BytesToHash(input))
if ct == nil {
logger.Error("decrypt RequiredGas() input doesn't point to verified ciphertext", "input", hex.EncodeToString(input))
return loadGas
}
return environment.FhevmParams().GasCosts.FheDecrypt[ct.Type()] + loadGas
}

func fhePubKeyRequiredGas(environment EVMEnvironment, input []byte) uint64 {
return environment.FhevmParams().GasCosts.FhePubKey
}
Expand Down
Loading
Loading