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

Commit 147a68b

Browse files
committed
feat: integrate fhevm-go
1 parent 916d6a4 commit 147a68b

File tree

13 files changed

+403
-516
lines changed

13 files changed

+403
-516
lines changed

core/state_transition.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/ethereum/go-ethereum/core/types"
2828
"github.com/ethereum/go-ethereum/core/vm"
2929
"github.com/ethereum/go-ethereum/params"
30+
"github.com/zama-ai/fhevm-go/fhevm"
3031
)
3132

3233
// ExecutionResult includes all output after executing given evm
@@ -111,7 +112,7 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation b
111112
gas += uint64(len(accessList)) * params.TxAccessListAddressGas
112113
gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas
113114
}
114-
return gas, nil
115+
return fhevm.TxDataFractionalGas(gas), nil
115116
}
116117

117118
// toWordSize returns the ceiled word size required for init code payment calculation.

core/vm/contracts.go

Lines changed: 102 additions & 80 deletions
Large diffs are not rendered by default.

core/vm/contracts_test.go

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,14 @@ import (
2020
"bytes"
2121
"encoding/json"
2222
"fmt"
23+
"math/big"
2324
"os"
2425
"testing"
2526
"time"
2627

2728
"github.com/ethereum/go-ethereum/common"
29+
"github.com/ethereum/go-ethereum/core/rawdb"
30+
"github.com/ethereum/go-ethereum/core/state"
2831
)
2932

3033
// precompiledTest defines the input/output pairs for precompiled contract tests.
@@ -93,12 +96,44 @@ var blake2FMalformedInputTests = []precompiledFailureTest{
9396
},
9497
}
9598

99+
type statefulPrecompileAccessibleState struct {
100+
interpreter *EVMInterpreter
101+
}
102+
103+
func (s *statefulPrecompileAccessibleState) Interpreter() *EVMInterpreter {
104+
return s.interpreter
105+
}
106+
107+
// FIXME: create it correctly
108+
func newTestInterpreter() *EVMInterpreter {
109+
// cfg := Config{IsGasEstimation: false}
110+
evm := &EVM{}
111+
evm.Context = BlockContext{}
112+
evm.Context.Transfer = func(StateDB, common.Address, common.Address, *big.Int) {}
113+
evm.Context.CanTransfer = func(StateDB, common.Address, *big.Int) bool { return true }
114+
interpreter := NewEVMInterpreter(evm)
115+
evm.interpreter = interpreter
116+
db := rawdb.NewMemoryDatabase()
117+
state, _ := state.New(common.Hash{}, state.NewDatabase(db), nil)
118+
interpreter.evm.StateDB = state
119+
return interpreter
120+
}
121+
122+
func newTestState() *statefulPrecompileAccessibleState {
123+
s := new(statefulPrecompileAccessibleState)
124+
interpreter := newTestInterpreter()
125+
s.interpreter = interpreter
126+
return s
127+
}
128+
96129
func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
97130
p := allPrecompiles[common.HexToAddress(addr)]
98131
in := common.Hex2Bytes(test.Input)
99-
gas := p.RequiredGas(in)
132+
state := newTestState()
133+
state.interpreter.readOnly = false
134+
gas := p.RequiredGas(state, in)
100135
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
101-
if res, _, err := RunPrecompiledContract(p, in, gas); err != nil {
136+
if res, _, err := RunPrecompiledContract(p, state, common.HexToAddress(addr), common.HexToAddress(addr), in, gas); err != nil {
102137
t.Error(err)
103138
} else if common.Bytes2Hex(res) != test.Expected {
104139
t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res))
@@ -117,10 +152,12 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
117152
func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
118153
p := allPrecompiles[common.HexToAddress(addr)]
119154
in := common.Hex2Bytes(test.Input)
120-
gas := p.RequiredGas(in) - 1
155+
state := newTestState()
156+
state.interpreter.readOnly = false
157+
gas := p.RequiredGas(state, in) - 1
121158

122159
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
123-
_, _, err := RunPrecompiledContract(p, in, gas)
160+
_, _, err := RunPrecompiledContract(p, state, common.HexToAddress(addr), common.HexToAddress(addr), in, gas)
124161
if err.Error() != "out of gas" {
125162
t.Errorf("Expected error [out of gas], got [%v]", err)
126163
}
@@ -135,9 +172,11 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
135172
func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing.T) {
136173
p := allPrecompiles[common.HexToAddress(addr)]
137174
in := common.Hex2Bytes(test.Input)
138-
gas := p.RequiredGas(in)
175+
state := newTestState()
176+
state.interpreter.readOnly = false
177+
gas := p.RequiredGas(state, in)
139178
t.Run(test.Name, func(t *testing.T) {
140-
_, _, err := RunPrecompiledContract(p, in, gas)
179+
_, _, err := RunPrecompiledContract(p, state, common.HexToAddress(addr), common.HexToAddress(addr), in, gas)
141180
if err.Error() != test.ExpectedError {
142181
t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err)
143182
}
@@ -155,7 +194,9 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
155194
}
156195
p := allPrecompiles[common.HexToAddress(addr)]
157196
in := common.Hex2Bytes(test.Input)
158-
reqGas := p.RequiredGas(in)
197+
state := newTestState()
198+
state.interpreter.readOnly = false
199+
reqGas := p.RequiredGas(state, in)
159200

160201
var (
161202
res []byte
@@ -169,7 +210,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
169210
bench.ResetTimer()
170211
for i := 0; i < bench.N; i++ {
171212
copy(data, in)
172-
res, _, err = RunPrecompiledContract(p, data, reqGas)
213+
res, _, err = RunPrecompiledContract(p, state, common.HexToAddress(addr), common.HexToAddress(addr), data, reqGas)
173214
}
174215
bench.StopTimer()
175216
elapsed := uint64(time.Since(start))

core/vm/errors.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ package vm
1919
import (
2020
"errors"
2121
"fmt"
22+
23+
"github.com/zama-ai/fhevm-go/fhevm"
2224
)
2325

2426
// List evm execution errors
@@ -43,6 +45,13 @@ var (
4345
errStopToken = errors.New("stop token")
4446
)
4547

48+
func init() {
49+
fhevm.RegisterErrors(ErrOutOfGas, ErrCodeStoreOutOfGas, ErrDepth, ErrInsufficientBalance,
50+
ErrContractAddressCollision, ErrExecutionReverted, ErrMaxInitCodeSizeExceeded, ErrMaxCodeSizeExceeded,
51+
ErrInvalidJump, ErrWriteProtection, ErrReturnDataOutOfBounds, ErrGasUintOverflow, ErrInvalidCode,
52+
ErrNonceUintOverflow, nil, nil, nil)
53+
}
54+
4655
// ErrStackUnderflow wraps an evm error when the items on the stack less
4756
// than the minimal requirement.
4857
type ErrStackUnderflow struct {

core/vm/evm.go

Lines changed: 93 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/ethereum/go-ethereum/crypto"
2626
"github.com/ethereum/go-ethereum/params"
2727
"github.com/holiman/uint256"
28+
"github.com/zama-ai/fhevm-go/fhevm"
2829
)
2930

3031
type (
@@ -120,7 +121,17 @@ type EVM struct {
120121
// callGasTemp holds the gas available for the current call. This is needed because the
121122
// available gas is calculated in gasCall* according to the 63/64 rule and later
122123
// applied in opCall*.
123-
callGasTemp uint64
124+
callGasTemp uint64
125+
fhevmEnvironment FhevmImplementation
126+
isGasEstimation bool
127+
isEthCall bool
128+
}
129+
130+
type FhevmImplementation struct {
131+
interpreter *EVMInterpreter
132+
data fhevm.FhevmData
133+
logger fhevm.Logger
134+
params fhevm.FhevmParams
124135
}
125136

126137
// NewEVM returns a new EVM. The returned EVM is not thread safe and should
@@ -138,14 +149,19 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig
138149
}
139150
}
140151
evm := &EVM{
141-
Context: blockCtx,
142-
TxContext: txCtx,
143-
StateDB: statedb,
144-
Config: config,
145-
chainConfig: chainConfig,
146-
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time),
152+
Context: blockCtx,
153+
TxContext: txCtx,
154+
StateDB: statedb,
155+
Config: config,
156+
chainConfig: chainConfig,
157+
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time),
158+
fhevmEnvironment: FhevmImplementation{interpreter: nil, logger: &fhevm.DefaultLogger{}, data: fhevm.NewFhevmData(), params: fhevm.DefaultFhevmParams()},
159+
isGasEstimation: config.IsGasEstimation,
160+
isEthCall: config.IsEthCall,
147161
}
148162
evm.interpreter = NewEVMInterpreter(evm)
163+
evm.fhevmEnvironment.interpreter = evm.interpreter
164+
fhevm.InitFhevm(&evm.fhevmEnvironment)
149165
return evm
150166
}
151167

@@ -224,7 +240,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
224240
}
225241

226242
if isPrecompile {
227-
ret, gas, err = RunPrecompiledContract(p, input, gas)
243+
ret, gas, err = RunPrecompiledContract(p, evm, caller.Address(), addr, input, gas)
228244
} else {
229245
// Initialise a new contract and set the code that is to be used by the EVM.
230246
// The contract is a scoped environment for this execution context only.
@@ -287,7 +303,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
287303

288304
// It is allowed to call precompiles, even via delegatecall
289305
if p, isPrecompile := evm.precompile(addr); isPrecompile {
290-
ret, gas, err = RunPrecompiledContract(p, input, gas)
306+
ret, gas, err = RunPrecompiledContract(p, evm, caller.Address(), addr, input, gas)
291307
} else {
292308
addrCopy := addr
293309
// Initialise a new contract and set the code that is to be used by the EVM.
@@ -332,7 +348,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
332348

333349
// It is allowed to call precompiles, even via delegatecall
334350
if p, isPrecompile := evm.precompile(addr); isPrecompile {
335-
ret, gas, err = RunPrecompiledContract(p, input, gas)
351+
ret, gas, err = RunPrecompiledContract(p, evm, caller.Address(), addr, input, gas)
336352
} else {
337353
addrCopy := addr
338354
// Initialise a new contract and make initialise the delegate values
@@ -381,7 +397,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
381397
}
382398

383399
if p, isPrecompile := evm.precompile(addr); isPrecompile {
384-
ret, gas, err = RunPrecompiledContract(p, input, gas)
400+
ret, gas, err = RunPrecompiledContract(p, evm, caller.Address(), addr, input, gas)
385401
} else {
386402
// At this point, we use a copy of address. If we don't, the go compiler will
387403
// leak the 'contract' to the outer scope, and make allocation for 'contract'
@@ -511,19 +527,80 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
511527

512528
// Create creates a new contract using code as deployment code.
513529
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
514-
contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))
515-
return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, CREATE)
530+
return fhevm.Create(evm.FhevmEnvironment(), caller.Address(), code, gas, value)
516531
}
517532

518533
// Create2 creates a new contract using code as deployment code.
519534
//
520535
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
521536
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
522537
func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
523-
codeAndHash := &codeAndHash{code: code}
524-
contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes())
525-
return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2)
538+
return fhevm.Create2(evm.FhevmEnvironment(), caller.Address(), code, gas, endowment, salt)
526539
}
527540

528541
// ChainConfig returns the environment's chain configuration
529542
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
543+
544+
func (evm *EVM) FhevmEnvironment() fhevm.EVMEnvironment { return &evm.fhevmEnvironment }
545+
546+
func (evm *FhevmImplementation) GetState(addr common.Address, hash common.Hash) common.Hash {
547+
return evm.interpreter.evm.StateDB.GetState(addr, hash)
548+
}
549+
550+
func (evm *FhevmImplementation) SetState(addr common.Address, hash common.Hash, input common.Hash) {
551+
evm.interpreter.evm.StateDB.SetState(addr, hash, input)
552+
}
553+
554+
func (evm *FhevmImplementation) GetNonce(addr common.Address) uint64 {
555+
return evm.interpreter.evm.StateDB.GetNonce(addr)
556+
}
557+
558+
func (evm *FhevmImplementation) AddBalance(addr common.Address, value *big.Int) {
559+
evm.interpreter.evm.StateDB.AddBalance(addr, value)
560+
}
561+
562+
func (evm *FhevmImplementation) GetBalance(addr common.Address) *big.Int {
563+
return evm.interpreter.evm.StateDB.GetBalance(addr)
564+
}
565+
566+
// FIXME: Maybe change the interface to SelfDestruct (new version of geth)
567+
func (evm *FhevmImplementation) Suicide(addr common.Address) bool {
568+
evm.interpreter.evm.StateDB.SelfDestruct(addr)
569+
return evm.interpreter.evm.StateDB.HasSelfDestructed(addr)
570+
}
571+
572+
func (evm *FhevmImplementation) GetDepth() int {
573+
return evm.interpreter.evm.depth
574+
}
575+
576+
func (evm *FhevmImplementation) IsCommitting() bool {
577+
return !evm.interpreter.evm.isGasEstimation
578+
}
579+
580+
func (evm *FhevmImplementation) IsEthCall() bool {
581+
return evm.interpreter.evm.isEthCall
582+
}
583+
584+
func (evm *FhevmImplementation) IsReadOnly() bool {
585+
return evm.interpreter.readOnly
586+
}
587+
588+
func (evm *FhevmImplementation) GetLogger() fhevm.Logger {
589+
return evm.logger
590+
}
591+
592+
func (evm *FhevmImplementation) FhevmData() *fhevm.FhevmData {
593+
return &evm.data
594+
}
595+
596+
func (evm *FhevmImplementation) FhevmParams() *fhevm.FhevmParams {
597+
return &evm.params
598+
}
599+
600+
func (evm *FhevmImplementation) CreateContract(caller common.Address, code []byte, gas uint64, value *big.Int, address common.Address) ([]byte, common.Address, uint64, error) {
601+
return evm.interpreter.evm.create(AccountRef(caller), &codeAndHash{code: code}, gas, value, address, CREATE)
602+
}
603+
604+
func (evm *FhevmImplementation) CreateContract2(caller common.Address, code []byte, codeHash common.Hash, gas uint64, value *big.Int, address common.Address) ([]byte, common.Address, uint64, error) {
605+
return evm.interpreter.evm.create(AccountRef(caller), &codeAndHash{code: code, hash: codeHash}, gas, value, address, CREATE2)
606+
}

0 commit comments

Comments
 (0)