diff --git a/execution/evm/account.go b/execution/evm/account.go new file mode 100644 index 0000000..c21efd9 --- /dev/null +++ b/execution/evm/account.go @@ -0,0 +1,185 @@ +package evm +import( + "sync" + "fmt" + "encoding/json" + "reflect") +type Account struct { + Info AccountInfo + Storage EvmStorage + Status AccountStatus +} +type AccountInfo struct { + Balance U256 + Nonce uint64 + CodeHash B256 + Code *Bytecode +} +func NewAccountInfo(balance U256, nonce uint64, codeHash B256, code Bytecode) AccountInfo { + return AccountInfo{ + Balance: balance, + Nonce: nonce, + CodeHash: codeHash, + Code: &code, + } +} +type EvmStorage map[U256]EvmStorageSlot +type EvmStorageSlot struct { + OriginalValue U256 + PresentValue U256 + IsCold bool +} +type AccountStatus uint8 +const ( + Loaded AccountStatus = 0b00000000 // Account is loaded but not interacted with + Created AccountStatus = 0b00000001 // Account is newly created, no DB fetch required + SelfDestructed AccountStatus = 0b00000010 // Account is marked for self-destruction + Touched AccountStatus = 0b00000100 // Account is touched and will be saved to DB + LoadedAsNotExisting AccountStatus = 0b0001000 // Pre-spurious state; account loaded but marked as non-existing + Cold AccountStatus = 0b0010000 // Account is marked as cold (not frequently accessed) +) +type BytecodeKind int +const ( + LegacyRawKind BytecodeKind = iota + LegacyAnalyzedKind + EofKind +) +// BytecodeKind serves as discriminator for Bytecode to identify which variant of enum is being used +// 1 , 2 or 3 in this case +type Bytecode struct { + Kind BytecodeKind + LegacyRaw []byte // For LegacyRaw variant + LegacyAnalyzed *LegacyAnalyzedBytecode // For LegacyAnalyzed variant + Eof *Eof // For Eof variant +} +type LegacyAnalyzedBytecode struct { + Bytecode []byte + OriginalLen uint64 + JumpTable JumpTable +} + +// JumpTable equivalent in Go, using a sync.Once pointer to simulate Arc and BitVec. +type JumpTable struct { + BitVector *Bitvector // Simulating BitVec as []byte + once sync.Once // Lazy initialization if needed +} +type Bitvector struct { + bits []uint8 + size int // Total number of bits represented +} +type Opcode struct { + InitCode Eof `json:"initcode"` + Input Bytes `json:"input"` + CreatedAddress Address `json:"created_address"` +} +type Eof struct { + Header EofHeader `json:"header"` + Body EofBody `json:"body"` + Raw Bytes `json:"raw"` +} + +// EofHeader represents the header of the EOF. +type EofHeader struct { + TypesSize uint16 `json:"types_size"` + CodeSizes []uint16 `json:"code_sizes"` + ContainerSizes []uint16 `json:"container_sizes"` + DataSize uint16 `json:"data_size"` + SumCodeSizes int `json:"sum_code_sizes"` + SumContainerSizes int `json:"sum_container_sizes"` +} + +// Marshaler interface for custom marshaling +type Marshaler interface { + MarshalType(val reflect.Value, b []byte, lastWrittenIdx uint64) (nextIdx uint64, err error) +} + +// Unmarshaler interface for custom unmarshaling +type Unmarshaler interface { + UnmarshalType(target reflect.Value, b []byte, lastReadIdx uint64) (nextIdx uint64, err error) +} + +// Implement Marshaler for EOFCreateInputs +func (eci EOFCreateInputs) MarshalType(val reflect.Value, b []byte, lastWrittenIdx uint64) (nextIdx uint64, err error) { + jsonData, err := json.Marshal(eci) + if err != nil { + return lastWrittenIdx, err + } + + return uint64(len(jsonData)), nil +} + +// Implement Unmarshaler for EOFCreateInputs +func (eci *EOFCreateInputs) UnmarshalType(target reflect.Value, b []byte, lastReadIdx uint64) (nextIdx uint64, err error) { + err = json.Unmarshal(b, eci) + if err != nil { + return lastReadIdx, err + } + + return lastReadIdx + uint64(len(b)), nil +} + +type EofBody struct { + TypesSection []TypesSection `json:"types_section"` + CodeSection []Bytes `json:"code_section"` // Using [][]byte for Vec + ContainerSection []Bytes `json:"container_section"` // Using [][]byte for Vec + DataSection Bytes `json:"data_section"` // Using []byte for Bytes + IsDataFilled bool `json:"is_data_filled"` +} + +// TypesSection represents a section describing the types used in the EOF body. +type TypesSection struct { + Inputs uint8 `json:"inputs"` // 1 byte + Outputs uint8 `json:"outputs"` // 1 byte + MaxStackSize uint16 `json:"max_stack_size"` // 2 bytes +} + +// MarshalJSON customizes the JSON marshalling for EOFCreateKind. +func (e *EOFCreateKind) MarshalJSON() ([]byte, error) { + switch e.Kind { + case TxK: + return json.Marshal(map[string]interface{}{ + "type": "Tx", + "data": e.Data, + }) + case OpcodeK: + return json.Marshal(map[string]interface{}{ + "type": "Opcode", + "data": e.Data, + }) + default: + return nil, fmt.Errorf("unknown EOFCreateKind type") + } +} + +// UnmarshalJSON customizes the JSON unmarshalling for EOFCreateKind. +func (e *EOFCreateKind) UnmarshalJSON(data []byte) error { + var aux struct { + Type string `json:"type"` + Data json.RawMessage `json:"data"` + } + + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + switch aux.Type { + case "Tx": + var tx Tx + if err := json.Unmarshal(aux.Data, &tx); err != nil { + return err + } + e.Kind = TxK + e.Data = tx + case "Opcode": + var opcode Opcode + if err := json.Unmarshal(aux.Data, &opcode); err != nil { + return err + } + e.Kind = OpcodeK + e.Data = opcode + default: + return fmt.Errorf("unknown EOFCreateKind type: %s", aux.Type) + } + + return nil +} diff --git a/execution/evm/call_inputs.go b/execution/evm/call_inputs.go new file mode 100644 index 0000000..871b312 --- /dev/null +++ b/execution/evm/call_inputs.go @@ -0,0 +1,170 @@ +package evm +import( + "encoding/json" +) +func (c *CallInputs) New(txEnv *TxEnv, gasLimit uint64) *CallInputs { + // Check if the transaction kind is Call and extract the target address. + if txEnv.TransactTo.Type != Call2 { + return nil + } + targetAddress := txEnv.TransactTo.Address + // Create and return the CallInputs instance. + return &CallInputs{ + Input: txEnv.Data, + GasLimit: gasLimit, + TargetAddress: *targetAddress, + BytecodeAddress: *targetAddress, // Set to target_address as in Rust code. + Caller: txEnv.Caller, + Value: CallValue{ValueType: "transfer", Amount: txEnv.Value}, + Scheme: ICall, // Assuming CallScheme.Call is represented by Call. + IsStatic: false, + IsEof: false, + ReturnMemoryOffset: Range{Start: 0, End: 0}, + } +} +func (c *CallInputs) NewBoxed(txEnv *TxEnv, gasLimit uint64) *CallInputs { + return c.New(txEnv, gasLimit) // Returns a pointer or nil, similar to Option> in Rust. +} + +func (e EOFCreateInputs) NewTx(tx *TxEnv, gasLimit uint64) EOFCreateInputs { + return EOFCreateInputs{ + Caller: tx.Caller, + Value: tx.Value, + GasLimit: gasLimit, + Kind: EOFCreateKind{ + Kind: TxK, + Data: tx.Data, + }, + } +} + +func (c *CreateInputs) New(txEnv *TxEnv, gasLimit uint64) *CreateInputs { + if txEnv.TransactTo.Type != Create2 { + return nil + } + return &CreateInputs{ + Caller: txEnv.Caller, + Scheme: CreateScheme{ + SchemeType: SchemeTypeCreate, + }, + Value: txEnv.Value, + InitCode: txEnv.Data, + GasLimit: gasLimit, + } +} + +func (c *CreateInputs) NewBoxed(txEnv *TxEnv, gasLimit uint64) *CreateInputs { + return c.New(txEnv, gasLimit) +} + +type CallValue struct { + ValueType string `json:"value_type"` + Amount U256 `json:"amount,omitempty"` +} + +// Transfer creates a CallValue representing a transfer. +func Transfer(amount U256) CallValue { + return CallValue{ValueType: "transfer", Amount: amount} +} + +// Apparent creates a CallValue representing an apparent value. +func Apparent(amount U256) CallValue { + return CallValue{ValueType: "apparent", Amount: amount} +} + +type CallScheme int +const ( + ICall CallScheme = iota + ICallCode + IDelegateCall + IStaticCall + IExtCall + IExtStaticCall + IExtDelegateCall +) +type CallInputs struct { + Input Bytes `json:"input"` + ReturnMemoryOffset Range `json:"return_memory_offset"` + GasLimit uint64 `json:"gas_limit"` + BytecodeAddress Address `json:"bytecode_address"` + TargetAddress Address `json:"target_address"` + Caller Address `json:"caller"` + Value CallValue `json:"value"` + Scheme CallScheme `json:"scheme"` + IsStatic bool `json:"is_static"` + IsEof bool `json:"is_eof"` +} + +// JSON serialization for CallValue +func (cv CallValue) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + ValueType string `json:"value_type"` + Amount *U256 `json:"amount,omitempty"` + }{ + ValueType: cv.ValueType, + Amount: &cv.Amount, + }) +} + +// JSON deserialization for CallValue +func (cv *CallValue) UnmarshalJSON(data []byte) error { + var aux struct { + ValueType string `json:"value_type"` + Amount *U256 `json:"amount,omitempty"` + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + cv.ValueType = aux.ValueType + if aux.ValueType == "transfer" { + cv.Amount = *aux.Amount + } else if aux.ValueType == "apparent" { + cv.Amount = *aux.Amount + } + return nil +} + +type CreateInputs struct { + Caller Address `json:"caller"` + Scheme CreateScheme `json:"scheme"` + Value U256 `json:"value"` + InitCode Bytes `json:"init_code"` + GasLimit uint64 `json:"gas_limit"` +} +type CreateScheme struct { + SchemeType SchemeType `json:"scheme_type"` // can be "create" or "create2" + Salt *U256 `json:"salt,omitempty"` // salt is optional for Create +} + +// SchemeType represents the type of creation scheme +type SchemeType int + +const ( + SchemeTypeCreate SchemeType = iota + SchemeTypeCreate2 +) + +type EOFCreateKindType int + +const ( + TxK EOFCreateKindType = iota + OpcodeK +) + +// / Inputs for EOF create call. +type EOFCreateInputs struct { + Caller Address `json:"caller"` + Value U256 `json:"value"` + GasLimit uint64 `json:"gas_limit"` + Kind EOFCreateKind `json:"kind"` +} + +type EOFCreateKind struct { + Kind EOFCreateKindType + Data interface{} // Use an interface to hold the associated data (Tx or Opcode) +} + +// TxKind represents a transaction-based EOF create kind. +type Tx struct { + InitData Bytes `json:"initdata"` +} diff --git a/execution/evm/common.go b/execution/evm/common.go new file mode 100644 index 0000000..f91ce66 --- /dev/null +++ b/execution/evm/common.go @@ -0,0 +1,11 @@ +package evm +import( + "github.com/BlocSoc-iitr/selene/common" + Common "github.com/ethereum/go-ethereum/common" + "math/big" +) +type BlockTag = common.BlockTag +type U256 = *big.Int +type B256 = Common.Hash +type Address = common.Address +type Bytes=[]byte \ No newline at end of file diff --git a/execution/evm/context.go b/execution/evm/context.go new file mode 100644 index 0000000..1446b3d --- /dev/null +++ b/execution/evm/context.go @@ -0,0 +1,136 @@ +package evm +import ( + "math/big" +) +var BLOCK_HASH_HISTORY = big.NewInt(256) +type Context[EXT interface{}, DB Database] struct { + Evm EvmContext[DB] + External interface{} +} +func DefaultContext[EXT interface{}]() Context[EXT, *EmptyDB] { + return Context[EXT, *EmptyDB]{ + Evm: NewEvmContext(new(EmptyDB)), + External: nil, + } +} +func NewContext[EXT interface{}, DB Database](evm EvmContext[DB], external EXT) Context[EXT, DB] { + return Context[EXT, DB]{ + Evm: evm, + External: external, + } +} +type ContextWithHandlerCfg[EXT interface{}, DB Database] struct { + Context Context[EXT, DB] + Cfg HandlerCfg +} +func (js JournaledState) SetSpecId(spec SpecId) { + js.Spec = spec +} +//Not being used +func (c EvmContext[DB]) WithDB(db DB) EvmContext[DB] { + return EvmContext[DB]{ + Inner: c.Inner.WithDB(db), + Precompiles: DefaultContextPrecompiles[DB](), + } +} +//////////////////////////////////// +/// impl Host for Context ///////// +//////////////////////////////////// + +// func (c *Context[EXT, DB]) Env() *Env { +// return c.Evm.Inner.Env +// } + +// func (c *Context[EXT, DB]) EnvMut() *Env { +// return c.Evm.Inner.Env +// } + +// func (c *Context[EXT, DB]) LoadAccount(address Address) (LoadAccountResult, bool) { +// res, err := c.Evm.Inner.LoadAccountExist(address) +// if err != nil { +// c.Evm.Inner.Error = err +// return LoadAccountResult{}, false +// } +// return res, true +// } + +// //Get the block hash of the given block `number`. +// func (c *Context[EXT, DB]) BlockHash(number uint64) (B256, bool) { +// blockNumber := c.Evm.Inner.Env.Block.Number +// requestedNumber := big.NewInt(int64(number)) + +// diff := new(big.Int).Sub(blockNumber, requestedNumber) + +// if diff.Cmp(big.NewInt(0)) == 0 { +// return common.Hash{}, true +// } + +// if diff.Cmp(BLOCK_HASH_HISTORY) <= 0 { +// hash, err := c.Evm.Inner.DB.BlockHash(number) +// if err != nil { +// c.Evm.Inner.Error = err +// return common.Hash{}, false +// } +// return hash, true +// } + +// return common.Hash{}, true +// } + +// // Get balance of `address` and if the account is cold. +// func (c *Context[EXT, DB]) Balance(address Address) (U256, bool, bool) + +// // Get code of `address` and if the account is cold. +// func (c *Context[EXT, DB]) Code(address Address) (Bytes, bool, bool) + +// // Get code hash of `address` and if the account is cold. +// func (c *Context[EXT, DB]) CodeHash(address Address) (B256, bool, bool) + +// // Get storage value of `address` at `index` and if the account is cold. +// func (c *Context[EXT, DB]) SLoad(address Address, index U256) (U256, bool, bool) + +// // Set storage value of account address at index. +// // Returns (original, present, new, is_cold). +// func (c *Context[EXT, DB]) SStore(address Address, index U256, value U256) (SStoreResult, bool) + +// // Get the transient storage value of `address` at `index`. +// func (c *Context[EXT, DB]) TLoad(address Address, index U256) U256 + +// // Set the transient storage value of `address` at `index`. +// func (c *Context[EXT, DB]) TStore(address Address, index U256, value U256) + +// // Emit a log owned by `address` with given `LogData`. +// func (c *Context[EXT, DB]) Log(log Log [any]) + +// // Mark `address` to be deleted, with funds transferred to `target`. +// func (c *Context[EXT, DB]) SelfDestruct(address Address, target Address) (SelfDestructResult, bool) + +//////////////////////////////////// +//////////////////////////////////// +//////////////////////////////////// + +/* +func (c EvmContext[DB1]) WithNewEvmDB[DB2 Database](db DB2) *EvmContext[ DB2] { + return &EvmContext[DB2]{ + Inner: c.Inner.WithDB(db), + context: NewContext[EXT, DB2]( + eb.context.Evm.WithNewDB(db), + eb.context.External.(EXT), + ), + handler: Handler[Context[EXT, DB2], EXT, DB2]{Cfg: eb.handler.Cfg}, + phantom: struct{}{}, + } +}*/ + + + + + + + + + + + + + diff --git a/execution/evm/database.go b/execution/evm/database.go new file mode 100644 index 0000000..a89b024 --- /dev/null +++ b/execution/evm/database.go @@ -0,0 +1,37 @@ +package evm +import( + "errors" + "math/big" +) + type Database interface { + Basic(address Address) (AccountInfo, error) + BlockHash(number uint64) (B256, error) + Storage(address Address, index U256) (U256, error) + CodeByHash(codeHash B256) (Bytecode, error) + } + type EmptyDB struct{} + func (db *EmptyDB) Basic(address Address) (AccountInfo, error) { + return AccountInfo{}, errors.New("EmptyDB: Basic not implemented") + } + func (db *EmptyDB) BlockHash(number uint64) (B256, error) { + return B256{}, errors.New("EmptyDB: BlockHash not implemented") + } + func (db *EmptyDB) Storage(address Address, index U256) (U256, error) { + return big.NewInt(0), errors.New("EmptyDB: Storage not implemented") + } + func (db *EmptyDB) CodeByHash(codeHash B256) (Bytecode, error) { + return Bytecode{}, errors.New("EmptyDB: CodeByHash not implemented") + } + func NewEmptyDB() *EmptyDB { + return &EmptyDB{} + } +type DatabaseError struct { + msg string + err error +} +func (e *DatabaseError) Error() string { + if e.err != nil { + return e.err.Error() + } + return e.msg +} \ No newline at end of file diff --git a/execution/evm/env.go b/execution/evm/env.go new file mode 100644 index 0000000..cd0e15b --- /dev/null +++ b/execution/evm/env.go @@ -0,0 +1,180 @@ +package evm + +import ( + "encoding/json" + "fmt" + "math/big" +) +type Env struct{ + Cfg CfgEnv + Block BlockEnv + Tx TxEnv +} +func NewEnv() *Env { + return &Env{} +} + +type AccessListItem struct { + Address Address + StorageKeys []B256 +} +type Signature struct { + V uint8 // Recovery ID + R *big.Int // R component of signature + S *big.Int // S component of signature +} +type AuthorizationListType int +const ( + Signed AuthorizationListType = iota + Recovered +) +type AuthorizationList struct { + Type AuthorizationListType + SignedAuthorizations []SignedAuthorization + RecoveredAuthorizations []RecoveredAuthorization +} +type ChainID uint64 +type OptionalNonce struct { + Nonce *uint64 // nil if no nonce is provided +} +type Authorization struct { + ChainID ChainID // The chain ID of the authorization + Address Address // The address of the authorization + Nonce OptionalNonce // The nonce for the authorization +} +type SignedAuthorization struct { + Inner Authorization // Embedded authorization data + Signature Signature // Signature associated with authorization +} + +type RecoveredAuthorization struct { + Inner Authorization // Embedded authorization data + Authority *Address // Optional authority address +} +type TransactTo = TxKind; +type TxKind struct { + Type TxKindType + Address *Address // Address is nil for Create type +} +type TxKindType int +const ( + Create2 TxKindType = iota + Call2 +) +type CfgEnv struct { + ChainID uint64 `json:"chain_id"` + KzgSettings *EnvKzgSettings `json:"kzg_settings,omitempty"` + PerfAnalyseCreatedBytecodes AnalysisKind `json:"perf_analyse_created_bytecodes"` + LimitContractCodeSize *uint64 `json:"limit_contract_code_size,omitempty"` + MemoryLimit uint64 `json:"memory_limit,omitempty"` // Consider using a pointer if optional + DisableBalanceCheck bool `json:"disable_balance_check,omitempty"` + DisableBlockGasLimit bool `json:"disable_block_gas_limit,omitempty"` + DisableEIP3607 bool `json:"disable_eip3607,omitempty"` + DisableGasRefund bool `json:"disable_gas_refund,omitempty"` + DisableBaseFee bool `json:"disable_base_fee,omitempty"` + DisableBeneficiaryReward bool `json:"disable_beneficiary_reward,omitempty"` +} +type BlockEnv struct { + Number U256 `json:"number"` + Coinbase Address `json:"coinbase"` + Timestamp U256 `json:"timestamp"` + GasLimit U256 `json:"gas_limit"` + BaseFee U256 `json:"basefee"` + Difficulty U256 `json:"difficulty"` + Prevrandao *B256 `json:"prevrandao,omitempty"` + BlobExcessGasAndPrice *BlobExcessGasAndPrice `json:"blob_excess_gas_and_price,omitempty"` +} +type BlobExcessGasAndPrice struct { + ExcessGas uint64 `json:"excess_gas"` + BlobGasPrice uint64 `json:"blob_gas_price"` +} +type EnvKzgSettings struct { + Mode string `json:"mode"` + Custom *KzgSettings `json:"custom,omitempty"` +} +// KzgSettings represents custom KZG settings. +type KzgSettings struct { + // Define fields for KzgSettings based on your requirements. +} +// AnalysisKind represents the type of analysis for created bytecodes. +type AnalysisKind int +const ( + Raw AnalysisKind = iota + Analyse +) + +// MarshalJSON customizes the JSON marshalling for AnalysisKind. +func (ak AnalysisKind) MarshalJSON() ([]byte, error) { + switch ak { + case Raw: + return json.Marshal("raw") + case Analyse: + return json.Marshal("analyse") + default: + return json.Marshal("unknown") + } +} + +// UnmarshalJSON customizes the JSON unmarshalling for AnalysisKind. +func (ak *AnalysisKind) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + switch s { + case "raw": + *ak = Raw + case "analyse": + *ak = Analyse + default: + return fmt.Errorf("unknown AnalysisKind: %s", s) + } + return nil +} +//Not in use : To be verified +func NewSignature(v uint8, r, s *big.Int) *Signature { + return &Signature{ + V: v, + R: new(big.Int).Set(r), + S: new(big.Int).Set(s), + } +} +//Not in use : To be verified +func (s *Signature) ToRawSignature() []byte { + rBytes := s.R.Bytes() + sBytes := s.S.Bytes() + signature := make([]byte, 64) + copy(signature[32-len(rBytes):32], rBytes) + copy(signature[64-len(sBytes):], sBytes) + return signature +} +//Not in use : To be verified +func FromRawSignature(data []byte, v uint8) (*Signature, error) { + if len(data) != 64 { + return nil, fmt.Errorf("invalid signature length: got %d, want 64", len(data)) + } + + r := new(big.Int).SetBytes(data[:32]) + s := new(big.Int).SetBytes(data[32:]) + + return &Signature{ + V: v, + R: r, + S: s, + }, nil +} + +// Verify verifies the signature against a message hash and public key +/* +func (s *Signature) Verify(pubKey *ecdsa.PublicKey, hash []byte) bool { + // Check if r and s are in the valid range + if s.R.Sign() <= 0 || s.S.Sign() <= 0 { + return false + } + if s.R.Cmp(pubKey.Params().N) >= 0 || s.S.Cmp(pubKey.Params().N) >= 0 { + return false + } + + // Verify the signature + return ecdsa.Verify(pubKey, hash, s.R, s.S) +}*/ diff --git a/execution/evm/evm.go b/execution/evm/evm.go new file mode 100644 index 0000000..1580e70 --- /dev/null +++ b/execution/evm/evm.go @@ -0,0 +1,211 @@ +package evm +import "bytes" +type Evm[EXT interface{}, DB Database] struct { + Context Context[EXT, DB] + Handler Handler[Context[EXT, DB], EXT, DB] +} +func NewEvm[EXT any, DB Database](context Context[EXT, DB], handler Handler[Context[EXT, DB], EXT, DB]) Evm[EXT, DB] { + context.Evm.Inner.JournaledState.SetSpecId(handler.Cfg.specID) + return Evm[EXT, DB]{ + Context: context, + Handler: handler, + } +} +func (e Evm[EXT, DB]) specId() SpecId { + return e.Handler.Cfg.specID +} +func (e Evm[EXT, DB]) IntoContextWithHandlerCfg() ContextWithHandlerCfg[EXT, DB] { + return ContextWithHandlerCfg[EXT, DB]{ + Context: e.Context, + Cfg: e.Handler.Cfg, + } +} +var EOF_MAGIC_BYTES = []byte{0xef, 0x00} +type EVMResult[D any] EVMResultGeneric[ResultAndState, D] +func (e *Evm[EXT, DB]) Transact() EVMResult[DatabaseError] { + initialGasSpend, err := e.preverifyTransactionInner() + if len(err.Message) != 0 { + e.clear() + return EVMResult[DatabaseError]{Err: err} + } + output := e.TransactPreverifiedInner(initialGasSpend) + output2, err := e.Handler.PostExecution.End(&e.Context, EVMResultGeneric[ResultAndState, EvmError](output)) + e.clear() + return EVMResult[DatabaseError]{Err: err, Value: output2} +} +func (e *Evm[EXT, DB]) preverifyTransactionInner() (uint64, EvmError) { + err := e.Handler.Validation.Env(e.Context.Evm.Inner.Env) //? + if err != nil { + return 0, EvmError{Message: err.Error()} + } + initialGasSpend, err := e.Handler.Validation.InitialTxGas(e.Context.Evm.Inner.Env) + err = e.Handler.Validation.TxAgainstState(&e.Context) + return initialGasSpend, EvmError{} +} +func (e *Evm[EXT, DB]) clear() { + e.Handler.PostExecution.Clear(&e.Context) +} +func (e *Evm[EXT, DB]) TransactPreverifiedInner(initialGasSpend uint64) EVMResult[DatabaseError] { + specId := e.Handler.Cfg.specID + ctx := &e.Context + preExec := e.Handler.PreExecution + err := preExec.LoadAccounts(ctx) + if err != nil { + return EVMResult[DatabaseError]{Err: err} + } + precompiles := preExec.LoadPrecompilesFunction() + ctx.Evm.SetPrecompiles(precompiles) + err_caller := preExec.DeductCaller(ctx) //? + if err_caller.Err != nil { + return EVMResult[DatabaseError](err_caller) + } + gasLimit := ctx.Evm.Inner.Env.Tx.GasLimit - initialGasSpend + exec := e.Handler.Execution + var firstFrameOrResult FrameOrResult + if ctx.Evm.Inner.Env.Tx.TransactTo.Type == Call2 { + result, err := exec.Call(ctx, (&CallInputs{}).NewBoxed(&ctx.Evm.Inner.Env.Tx, gasLimit)) + if len(err.Message) != 0 { + return EVMResult[DatabaseError]{Err: err} + } + firstFrameOrResult = result + } else { + if specId.IsEnabledIn(PRAGUE_EOF) && bytes.Equal(ctx.Evm.Inner.Env.Tx.Data[:2], EOF_MAGIC_BYTES) { + result, err := exec.EOFCreate(ctx, EOFCreateInputs{}.NewTx(&ctx.Evm.Inner.Env.Tx, gasLimit)) + if len(err.Message) != 0 { + return EVMResult[DatabaseError]{Err: err} + } + firstFrameOrResult = result + } else { + result, err := exec.Create(ctx, (&CreateInputs{}).NewBoxed(&ctx.Evm.Inner.Env.Tx, gasLimit)) + if len(err.Message) != 0 { + return EVMResult[DatabaseError]{Err: err} + } + firstFrameOrResult = result + } + } + var result FrameResult + if firstFrameOrResult.Type == Frame_FrameOrResult { + result_new, err_new := e.RunTheLoop(firstFrameOrResult.Frame) + if len(err_new.Message) != 0 { + return EVMResult[DatabaseError]{Err: err_new} + } + result = result_new + } else { + result = firstFrameOrResult.Result + } + ctx = &e.Context + err = e.Handler.Execution.LastFrameReturn(ctx, &result) + postExec := e.Handler.PostExecution + postExec.ReimburseCaller(ctx, result.Gas()) //! + postExec.RewardBeneficiary(ctx, result.Gas()) //! + res, err := postExec.Output(ctx, result) + return EVMResult[DatabaseError]{Value: res, Err: err} +} +func (e *Evm[EXT, DB]) RunTheLoop(firstFrame Frame) (FrameResult, EvmError) { + callStack := make([]Frame, 0, 1025) + callStack = append(callStack, firstFrame) + + var sharedMemory SharedMemory + if e.Context.Evm.Inner.Env.Cfg.MemoryLimit > 0 { + sharedMemory = SharedMemory{}.NewWithMemoryLimit(e.Context.Evm.Inner.Env.Cfg.MemoryLimit) + } else { + sharedMemory = SharedMemory{}.New() + } + sharedMemory.NewContext() + + stackFrame := &callStack[len(callStack) - 1] + + for { + nextAction, err := e.Handler.ExecuteFrame(stackFrame,&sharedMemory,&e.Context) + if err != nil { + return FrameResult{}, EvmError{Message: err.Error()} + } + // Take error from the context, if any + if err := e.Context.Evm.Inner.TakeError(); len(err.Message) != 0 { + return FrameResult{}, err + } + exec := &e.Handler.Execution + var frameOrResult FrameOrResult + { + if nextAction.actionType == ActionTypeCall { + res, err := exec.Call(&e.Context, nextAction.callInputs) + if len(err.Message) != 0 { + return FrameResult{}, err + } + frameOrResult = res + } else if nextAction.actionType == ActionTypeCreate { + res, err := exec.Create(&e.Context, nextAction.createInputs) + if len(err.Message) != 0 { + return FrameResult{},err + } + frameOrResult = res + } else if nextAction.actionType == ActionTypeEOFCreate { + res, err := exec.EOFCreate(&e.Context, *nextAction.eofCreateInputs) + if len(err.Message) != 0 { + return FrameResult{},err + } + frameOrResult = res + } else if nextAction.actionType == ActionTypeReturn { + sharedMemory.FreeContext() + returnedFrame := callStack[len(callStack) - 1] + callStack = callStack[:len(callStack) - 1] + ctx := &e.Context + + var result FrameResult + frameOrResult2 := FrameOrResult{ + Type: Result_FrameOrResult, + Result: result, + } + switch returnedFrame.Type { + case Call_Frame: + result.ResultType = Call + res, err := exec.CallReturn(ctx, returnedFrame.Call, *nextAction.result) + if len(err.Message) != 0 { + return FrameResult{}, err + } + result.Call = &res + case Create_Frame: + result.ResultType = Create + res, err := exec.CreateReturn(ctx, returnedFrame.Create, *nextAction.result) + if len(err.Message) != 0 { + return FrameResult{}, err + } + result.Create = &res + case EOFCreate_Frame: + result.ResultType = EOFCreate + res, err := exec.EOFCreateReturn(ctx, returnedFrame.EOFCreate, *nextAction.result) + if len(err.Message) != 0 { + return FrameResult{}, err + } + result.Create = &res + } + frameOrResult = frameOrResult2 + } else { + return FrameResult{}, EvmError{ + Message: "InterpreterAction::None is not expected", + } + } + } + switch frameOrResult.Type { + case Frame_FrameOrResult: + sharedMemory.NewContext() + callStack = append(callStack, frameOrResult.Frame) + stackFrame = &callStack[len(callStack) - 1] + case Result_FrameOrResult: + topFrame := &callStack[len(callStack) - 1] + if topFrame == nil { + return frameOrResult.Result, EvmError{} + } + stackFrame = topFrame + ctx := &e.Context + switch frameOrResult.Result.ResultType { + case Call: + exec.InsertCallOutcome(ctx, stackFrame, &sharedMemory, *frameOrResult.Result.Call) + case Create: + exec.InsertCreateOutcome(ctx, stackFrame, *frameOrResult.Result.Create) + case EOFCreate: + exec.InsertCreateOutcome(ctx, stackFrame, *frameOrResult.Result.Create) + } + } + } +} \ No newline at end of file diff --git a/execution/evm/evmBuilder.go b/execution/evm/evmBuilder.go new file mode 100644 index 0000000..c0ec0c1 --- /dev/null +++ b/execution/evm/evmBuilder.go @@ -0,0 +1,77 @@ +package evm +type EvmBuilder[EXT interface{}, DB Database] struct { + context Context[EXT, DB] + handler Handler[Context[EXT, DB], EXT, DB] + phantom struct{} +} +func NewDefaultEvmBuilder[EXT interface{}]() *EvmBuilder[EXT, *EmptyDB] { + handlerCfg := NewHandlerCfg(LATEST) + return &EvmBuilder[EXT, *EmptyDB]{ + context: DefaultContext[EXT](), + handler: Handler[Context[EXT, *EmptyDB], EXT, *EmptyDB]{Cfg: handlerCfg}, + phantom: struct{}{}, + } +} +func WithNewDB[EXT interface{}, DB1, DB2 Database]( + eb *EvmBuilder[EXT, DB1], + db DB2, +) *EvmBuilder[EXT, DB2] { + return &EvmBuilder[EXT, DB2]{ + context: NewContext[EXT, DB2]( + WithNewEvmDB(eb.context.Evm, db), + eb.context.External.(EXT), + ), + handler: Handler[Context[EXT, DB2], EXT, DB2]{Cfg: eb.handler.Cfg}, + phantom: struct{}{}, + } +} +func (eb *EvmBuilder[EXT, DB]) WithEnv(env *Env) *EvmBuilder[EXT, DB] { + eb.context.Evm.Inner.Env = env + return eb +} + +func (eb *EvmBuilder[EXT, DB]) Build() Evm[EXT,DB] { + return NewEvm(eb.context, eb.handler) +} + +func WithContextWithHandlerCfg[EXT interface{},DB1,DB2 Database](eb *EvmBuilder[EXT, DB1],contextWithHandlerCfg ContextWithHandlerCfg[EXT, DB2]) *EvmBuilder[EXT, DB2] { + return &EvmBuilder[EXT, DB2]{ + context: contextWithHandlerCfg.Context, + handler: Handler[Context[EXT, DB2], EXT, DB2]{Cfg: contextWithHandlerCfg.Cfg}, + phantom: struct{}{}, + } +} +//Not in use +func (eb EvmBuilder[EXT, DB]) WithDB(db DB) *EvmBuilder[EXT, DB] { + return &EvmBuilder[EXT, DB]{ + context: NewContext[EXT, DB](eb.context.Evm.WithDB(db), eb.context.External.(EXT)), //Doubt + handler: Handler[Context[EXT, DB], EXT, DB]{Cfg: eb.handler.Cfg}, //Doubt + phantom: struct{}{}, + } +} +//Not in use +type PhantomData[T any] struct{} +//Not in use +/* +func WithContextWithHandlerCfg[EXT interface{}, DB Database](eb *EvmBuilder[EXT, Database], contextWithHandlerCfg ContextWithHandlerCfg[EXT, Database]) *EvmBuilder[EXT, Database] { + return &EvmBuilder[EXT, Database]{ + context: contextWithHandlerCfg.Context, + handler: Handler[Context[EXT, Database], EXT, Database]{Cfg: contextWithHandlerCfg.Cfg}, + phantom: struct{}{}, + } +}*/ +/* +func (eb EvmBuilder[EXT, DB]) WithContextWithHandlerCfg(contextWithHandlerCfg ContextWithHandlerCfg[EXT, DB]) *EvmBuilder[EXT, DB] { + return &EvmBuilder[EXT, DB]{ + context: contextWithHandlerCfg.Context, + handler: Handler[Context[EXT, DB], EXT, DB]{Cfg: contextWithHandlerCfg.Cfg}, + phantom: struct{}{}, + } +}*/ + + +// Tuple implementation in golang: + +// LogData represents the data structure of a log entry. + + diff --git a/execution/evm/evm_context.go b/execution/evm/evm_context.go new file mode 100644 index 0000000..7a8b4b3 --- /dev/null +++ b/execution/evm/evm_context.go @@ -0,0 +1,20 @@ +package evm +type EvmContext[DB Database] struct { + Inner InnerEvmContext[DB] + Precompiles ContextPrecompiles[DB] +} +func NewEvmContext[DB Database](db DB) EvmContext[DB] { + return EvmContext[DB]{ + Inner: NewInnerEvmContext[DB](db), + Precompiles: DefaultContextPrecompiles[DB](), + } +} +func WithNewEvmDB[DB1, DB2 Database]( + ec EvmContext[DB1], + db DB2, +) EvmContext[DB2] { + return EvmContext[DB2]{ + Inner: WithNewInnerDB(ec.Inner,db), + Precompiles: DefaultContextPrecompiles[DB2](), + } +} diff --git a/execution/evm/execution.go b/execution/evm/execution.go new file mode 100644 index 0000000..e70114d --- /dev/null +++ b/execution/evm/execution.go @@ -0,0 +1,28 @@ +package evm + +type LastFrameReturnHandle[EXT any, DB Database] func(*Context[EXT, DB], *FrameResult) EvmError +type ExecuteFrameHandle[EXT any, DB Database] func(*Frame, *SharedMemory, *InstructionTables[Context[EXT, DB]], *Context[EXT, DB]) (InterpreterAction, EvmError) +type FrameCallHandle[EXT any, DB Database] func(*Context[EXT, DB], *CallInputs) (FrameOrResult, EvmError) +type FrameCallReturnHandle[EXT any, DB Database] func(*Context[EXT, DB], *CallFrame, InterpreterResult) (CallOutcome, EvmError) +type InsertCallOutcomeHandle[EXT any, DB Database] func(*Context[EXT, DB], *Frame, *SharedMemory, CallOutcome) EvmError +type FrameCreateHandle[EXT any, DB Database] func(*Context[EXT, DB], *CreateInputs) (FrameOrResult, EvmError) +type FrameCreateReturnHandle[EXT any, DB Database] func(*Context[EXT, DB], *CreateFrame, InterpreterResult) (CreateOutcome, EvmError) +type InsertCreateOutcomeHandle[EXT any, DB Database] func(*Context[EXT, DB], *Frame, CreateOutcome) EvmError +type FrameEOFCreateHandle[EXT any, DB Database] func(*Context[EXT, DB], EOFCreateInputs) (FrameOrResult, EvmError) +type FrameEOFCreateReturnHandle[EXT any, DB Database] func(*Context[EXT, DB], *EOFCreateFrame, InterpreterResult) (CreateOutcome, EvmError) +type InsertEOFCreateOutcomeHandle[EXT any, DB Database] func(*Context[EXT, DB], *CallFrame, InterpreterResult) (CallOutcome, EvmError) + +type ExecutionHandler[EXT interface{}, DB Database] struct { + LastFrameReturn LastFrameReturnHandle[EXT, DB] + ExecuteFrame ExecuteFrameHandle[EXT, DB] + Call FrameCallHandle[EXT, DB] + CallReturn FrameCallReturnHandle[EXT, DB] + InsertCallOutcome InsertCallOutcomeHandle[EXT, DB] + Create FrameCreateHandle[EXT, DB] + CreateReturn FrameCreateReturnHandle[EXT, DB] + InsertCreateOutcome InsertCreateOutcomeHandle[EXT, DB] + EOFCreate FrameEOFCreateHandle[EXT, DB] + EOFCreateReturn FrameEOFCreateReturnHandle[EXT, DB] + InsertEOFCreateOutcome InsertEOFCreateOutcomeHandle[EXT, DB] +} + diff --git a/execution/evm/frame.go b/execution/evm/frame.go new file mode 100644 index 0000000..71744bb --- /dev/null +++ b/execution/evm/frame.go @@ -0,0 +1,87 @@ +package evm +type CallFrame struct { + ReturnMemoryRange Range + FrameData FrameData +} +type CreateFrame struct { + CreatedAddress Address + FrameData FrameData +} + +type EOFCreateFrame struct { + CreatedAddress Address + FrameData FrameData +} + +type FrameData struct { + Checkpoint JournalCheckpoint + Interpreter Interpreter +} + +type CallOutcome struct { + Result InterpreterResult + MemoryOffset Range +} + +type CreateOutcome struct { + Result InterpreterResult + Address *Address +} + +type FrameType uint8 + +const ( + Call_Frame FrameType = iota + Create_Frame + EOFCreate_Frame +) + +type Frame struct { + Type FrameType + Call *CallFrame + Create *CreateFrame + EOFCreate *EOFCreateFrame +} + +// FrameResultType represents the type of FrameResult. +type FrameResultType int + +const ( + Call FrameResultType = iota + Create + EOFCreate +) + +// FrameResult represents the outcome of a frame, which can be Call, Create, or EOFCreate. +type FrameResult struct { + ResultType FrameResultType + Call *CallOutcome + Create *CreateOutcome +} + +type FrameOrResultType uint8 + +const ( + Frame_FrameOrResult FrameOrResultType = iota + Result_FrameOrResult +) + +type FrameOrResult struct { + Type FrameOrResultType + Frame Frame + Result FrameResult +} + +func (f *FrameResult) Gas() *Gas { + if f.ResultType == Call { + return &f.Call.Result.Gas + } else if f.ResultType == Create { + return &f.Create.Result.Gas + } else { + return &f.Create.Result.Gas + } +} +type Range struct { + Start int `json:"start"` + End int `json:"end"` +} \ No newline at end of file diff --git a/execution/evm/handler.go b/execution/evm/handler.go new file mode 100644 index 0000000..43694a7 --- /dev/null +++ b/execution/evm/handler.go @@ -0,0 +1,397 @@ +package evm +type Handler[H Host, EXT any, DB Database] struct { + Cfg HandlerCfg + InstructionTable InstructionTables[Context[EXT, DB]] + Registers []HandleRegisters[H, EXT, DB] + Validation ValidationHandler[EXT, DB] + PreExecution PreExecutionHandler[EXT, DB] + PostExecution PostExecutionHandler[EXT, DB] + Execution ExecutionHandler[EXT, DB] +} +func NewEvmHandler[H Host, EXT any, DB Database](cfg HandlerCfg) (*EvmHandler[H, EXT, DB], error) { + h := &EvmHandler[H, EXT, DB]{Cfg: cfg} + if h.Cfg.isOptimism { + return h.optimismWithSpec(cfg.specID) + } + return h.mainnetWithSpec(cfg.specID) +} +func (h *EvmHandler[H, EXT, DB]) optimismWithSpec(specid SpecId) (*EvmHandler[H, EXT, DB], error) { + return &EvmHandler[H, EXT, DB]{ + Cfg: HandlerCfg{ + specID: specid, + isOptimism: true, + }, + InstructionTable: NewPlainInstructionTable(InstructionTable[Context[EXT, DB]]{}), + Registers: []HandleRegisters[H, EXT, DB]{}, + Validation: ValidationHandler[EXT, DB]{}, + PreExecution: PreExecutionHandler[EXT, DB]{}, + PostExecution: PostExecutionHandler[EXT, DB]{}, + Execution: ExecutionHandler[EXT, DB]{}, + }, nil +} +func (h *EvmHandler[H, EXT, DB]) mainnetWithSpec(specid SpecId) (*EvmHandler[H, EXT, DB], error) { + return &EvmHandler[H, EXT, DB]{ + Cfg: HandlerCfg{ + specID: specid, + isOptimism: false, + }, + InstructionTable: NewPlainInstructionTable(InstructionTable[Context[EXT, DB]]{}), + Registers: []HandleRegisters[H, EXT, DB]{}, + Validation: ValidationHandler[EXT, DB]{}, + PreExecution: PreExecutionHandler[EXT, DB]{}, + PostExecution: PostExecutionHandler[EXT, DB]{}, + Execution: ExecutionHandler[EXT, DB]{}, + }, nil +} +type HandlerCfg struct { + specID SpecId + isOptimism bool +} +func NewHandlerCfg(specID SpecId) HandlerCfg { + return HandlerCfg{ + specID: specID, + isOptimism: getDefaultOptimismSetting(), + } +} +//Note there might be error in specToGeneric needs to be well tested + +/* +Enabling run time flexibility +# Run with optimism disabled (default) +go run main.go + +# Run with optimism enabled +OPTIMISM_ENABLED=true go run main.go*/ +// SpecId is the enumeration of various Ethereum hard fork specifications. + +// PhantomData is a Go struct that simulates the behavior of Rust's PhantomData. +func (s SpecId) Enabled(a SpecId, b SpecId) bool { + return uint8(a) >= uint8(b) +} +func (h *Handler[H, EXT, DB]) ExecuteFrame(frame *Frame, sharedMemory *SharedMemory, context *Context[EXT, DB]) (InterpreterAction, error) { + return h.Execution.ExecuteFrame(frame, sharedMemory, &h.InstructionTable, context) +} +func (h *EvmHandler[H, EXT, DB]) specId() SpecId { + return h.Cfg.specID +} +func (h *EvmHandler[H, EXT, DB]) IsOptimism() bool { + return isOptimismEnabled && h.Cfg.isOptimism +} + +// EvmHandler is a type alias for Handler with specific type parameters. +type EvmHandler[H Host, EXT any, DB Database] Handler[H, EXT, DB] + +// HandleRegister defines a function type that accepts a pointer to EvmHandler. +type HandleRegister[H Host, EXT any, DB Database] func(handler *EvmHandler[H, EXT, DB]) + +// HandleRegisterBox defines a function type as a boxed register. +type HandleRegisterBox[H Host, EXT any, DB Database] func(handler *EvmHandler[H, EXT, DB]) + +// HandleRegisters is an interface that defines the `Register` method. +type HandleRegisters[H Host, EXT any, DB Database] interface { + Register(handler *EvmHandler[H, EXT, DB]) +} + +// PlainRegister struct implements HandleRegisters with a plain function register. +type PlainRegister[H Host, EXT any, DB Database] struct { + RegisterFn HandleRegister[H, EXT, DB] +} + +// BoxRegister struct implements HandleRegisters with a boxed function register. +type BoxRegister[H Host, EXT any, DB Database] struct { + RegisterFn HandleRegisterBox[H, EXT, DB] +} + +// Register method for PlainRegister, to satisfy HandleRegisters interface. +func (p PlainRegister[H, EXT, DB]) Register(handler *EvmHandler[H, EXT, DB]) { + p.RegisterFn(handler) +} + +// Register method for BoxRegister, to satisfy HandleRegisters interface. +func (b BoxRegister[H, EXT, DB]) Register(handler *EvmHandler[H, EXT, DB]) { + b.RegisterFn(handler) +} +/* +func (s SpecId) IsEnabledIn(spec SpecId) bool { + return s.Enabled(s, spec) +} +*/ +// String method to provide a string representation for each CallScheme variant. +// func (cs CallScheme) String() string { +// switch cs { +// case Call_Scheme: +// return "Call" +// case CallCode: +// return "CallCode" +// case DelegateCall: +// return "DelegateCall" +// case StaticCall: +// return "StaticCall" +// case ExtCall: +// return "ExtCall" +// case ExtStaticCall: +// return "ExtStaticCall" +// case ExtDelegateCall: +// return "ExtDelegateCall" +// default: +// return "Unknown" +// } +// } + +/* +func (s Spec) SpecID() SpecId { + return s.SPEC_ID +} +*/ +// IsEnabled checks if a specific SpecId is enabled. +/*func (s Spec) IsEnabled(specId SpecId) bool { + return SpecIdEnabled(s.SpecID(), specId) +} +func SpecIdEnabled(our, other SpecId) bool { + return our >= other +}*/ +/* +func NewHandler[H Host, EXT any, DB Database](cfg HandlerCfg) Handler[H, EXT, DB] { + return createHandlerWithConfig[H, EXT, DB](cfg) +}*/ +/* +func createHandlerWithConfig[H Host, EXT any, DB Database](cfg HandlerCfg) Handler[H, EXT, DB] { + spec := GetSpecforID[H, EXT, DB](cfg.specID) + + if cfg.isOptimism { + return createOptimismHandler(spec) + } + return createMainnetHandler(spec) +} +*/ + +/* +func (h *EvmHandler[H,EXT, DB])optimism() *EvmHandler[H,EXT, DB]{ + handler:= +}*/ +//Doubt in newplaininstructiontable +/* +func (h *EvmHandler[H,EXT, DB])mainnet(spec Spec) *EvmHandler[H,EXT, DB]{ + specID := spec.SpecID() + + return &EvmHandler[H, EXT, DB]{ + Cfg: NewHandlerCfg(specID), + InstructionTable: NewPlainInstructionTable(InstructionTable[H]{}), + Registers: []HandleRegister[H, EXT, DB]{}, + + + } +}*/ +/* +func specToGeneric[H Host, EXT any, DB Database]( + specID SpecId, + h *EvmHandler[H, EXT, DB], + isOptimism bool, +) (*EvmHandler[H, EXT, DB], error) { + switch specID { + case FRONTIER, FRONTIER_THAWING: + return createSpecHandler[H, EXT, DB](h, "frontier", isOptimism) + case HOMESTEAD, DAO_FORK: + return createSpecHandler[H, EXT, DB](h, "homestead", isOptimism) + case TANGERINE: + return createSpecHandler[H, EXT, DB](h, "tangerine", isOptimism) + case SPURIOUS_DRAGON: + return createSpecHandler[H, EXT, DB](h, "spurious_dragon", isOptimism) + case BYZANTIUM: + return createSpecHandler[H, EXT, DB](h, "byzantium", isOptimism) + case PETERSBURG, CONSTANTINOPLE: + return createSpecHandler[H, EXT, DB](h, "petersburg", isOptimism) + case ISTANBUL, MUIR_GLACIER: + return createSpecHandler[H, EXT, DB](h, "istanbul", isOptimism) + case BERLIN: + return createSpecHandler[H, EXT, DB](h, "berlin", isOptimism) + case LONDON, ARROW_GLACIER, GRAY_GLACIER: + return createSpecHandler[H, EXT, DB](h, "london", isOptimism) + case MERGE: + return createSpecHandler[H, EXT, DB](h, "merge", isOptimism) + case SHANGHAI: + return createSpecHandler[H, EXT, DB](h, "shanghai", isOptimism) + case CANCUN: + return createSpecHandler[H, EXT, DB](h, "cancun", isOptimism) + case PRAGUE: + return createSpecHandler[H, EXT, DB](h, "prague", isOptimism) + case PRAGUE_EOF: + return createSpecHandler[H, EXT, DB](h, "prague_eof", isOptimism) + case LATEST: + return createSpecHandler[H, EXT, DB](h, "latest", isOptimism) + } + + // Optimism-specific specs + if isOptimism { + switch specID { + case BEDROCK: + return createSpecHandler[H, EXT, DB](h, "bedrock", true) + case REGOLITH: + return createSpecHandler[H, EXT, DB](h, "regolith", true) + case CANYON: + return createSpecHandler[H, EXT, DB](h, "canyon", true) + case ECOTONE: + return createSpecHandler[H, EXT, DB](h, "ecotone", true) + case FJORD: + return createSpecHandler[H, EXT, DB](h, "fjord", true) + } + } + + return nil, fmt.Errorf("unsupported spec ID: %d", specID) +} + +*/ +/* +// Concrete spec implementations +type FrontierSpec struct{} +type HomesteadSpec struct{} +type TangerineSpec struct{} +type SpuriousDragonSpec struct{} +type ByzantiumSpec struct{} +type PetersburgSpec struct{} +type IstanbulSpec struct{} +type BerlinSpec struct{} +type LondonSpec struct{} +type MergeSpec struct{} +type ShanghaiSpec struct{} +type CancunSpec struct{} +type PragueSpec struct{} +type PragueEofSpec struct{} +type LatestSpec struct{} + +// Optimism-specific specs +type BedrockSpec struct{} +type RegolithSpec struct{} +type CanyonSpec struct{} +type EcotoneSpec struct{} +type FjordSpec struct{} + +func (s *FrontierSpec) Name() string { return "frontier" } +func (s *FrontierSpec) Version() uint { return 1 } +func (s *HomesteadSpec) Name() string { return "homestead" } +func (s *HomesteadSpec) Version() uint { return 1 } +func (s *TangerineSpec) Name() string { return "tangerine" } +func (s *TangerineSpec) Version() uint { return 1 } +func (s *SpuriousDragonSpec) Name() string { return "spurious_dragon" } +func (s *SpuriousDragonSpec) Version() uint { return 1 } +func (s *ByzantiumSpec) Name() string { return "byzantium" } +func (s *ByzantiumSpec) Version() uint { return 1 } +func (s *PetersburgSpec) Name() string { return "petersburg" } +func (s *PetersburgSpec) Version() uint { return 1 } +func (s *IstanbulSpec) Name() string { return "istanbul" } +func (s *IstanbulSpec) Version() uint { return 1 } +func (s *BerlinSpec) Name() string { return "berlin" } +func (s *BerlinSpec) Version() uint { return 1 } +func (s *LondonSpec) Name() string { return "london" } +func (s *LondonSpec) Version() uint { return 1 } +func (s *MergeSpec) Name() string { return "merge" } +func (s *MergeSpec) Version() uint { return 1 } +func (s *ShanghaiSpec) Name() string { return "shanghai" } +func (s *ShanghaiSpec) Version() uint { return 1 } +func (s *CancunSpec) Name() string { return "cancun" } +func (s *CancunSpec) Version() uint { return 1 } +func (s *PragueSpec) Name() string { return "prague" } +func (s *PragueSpec) Version() uint { return 1 } +func (s *PragueEofSpec) Name() string { return "prague_eof" } +func (s *PragueEofSpec) Version() uint { return 1 } +func (s *LatestSpec) Name() string { return "latest" } +func (s *LatestSpec) Version() uint { return 1 } +func (s *BedrockSpec) Name() string { return "bedrock" } +func (s *BedrockSpec) Version() uint { return 1 } +func (s *RegolithSpec) Name() string { return "regolith" } +func (s *RegolithSpec) Version() uint { return 1 } +func (s *CanyonSpec) Name() string { return "canyon" } +func (s *CanyonSpec) Version() uint { return 1 } +func (s *EcotoneSpec) Name() string { return "ecotone" } +func (s *EcotoneSpec) Version() uint { return 1 } +func (s *FjordSpec) Name() string { return "fjord" } +func (s *FjordSpec) Version() uint { return 1 } +*/ +// createSpecHandler now creates the appropriate spec implementation +/* +func createSpecHandler[H Host, EXT any, DB Database]( + h *EvmHandler[H, EXT, DB], + specName string, + isOptimism bool, +) (*EvmHandler[H, EXT, DB], error) { + newHandler := *h + + var spec Spec + switch specName { + case "frontier": + spec = &FrontierSpec{} + case "homestead": + spec = &HomesteadSpec{} + case "tangerine": + spec = &TangerineSpec{} + case "spurious_dragon": + spec = &SpuriousDragonSpec{} + case "byzantium": + spec = &ByzantiumSpec{} + case "petersburg": + spec = &PetersburgSpec{} + case "istanbul": + spec = &IstanbulSpec{} + case "berlin": + spec = &BerlinSpec{} + case "london": + spec = &LondonSpec{} + case "merge": + spec = &MergeSpec{} + case "shanghai": + spec = &ShanghaiSpec{} + case "cancun": + spec = &CancunSpec{} + case "prague": + spec = &PragueSpec{} + case "prague_eof": + spec = &PragueEofSpec{} + case "latest": + spec = &LatestSpec{} + /* + case "bedrock": + spec = &BedrockSpec{} + case "regolith": + spec = &RegolithSpec{} + case "canyon": + spec = &CanyonSpec{} + case "ecotone": + spec = &EcotoneSpec{} + case "fjord": + spec = &FjordSpec{} +*/ +/* + default: + return nil, fmt.Errorf("unknown spec: %s", specName) + } + + // Add the spec to the handler + newHandler = spec + return &newHandler, nil +} +*/ +/* +func withSpec[EXT any, DB Database, S Spec](h *EVMHandler[EXT, DB]) (*EVMHandler[EXT, DB], error) { + var spec S + rules := spec.Rules() + + // Create a new handler with the specific spec configuration + newHandler := &EVMHandler[EXT, DB]{ + cfg: h.cfg, + spec: spec, + rules: *rules, + } + + return newHandler, nil +}*/ + +/* +type HandleRegister[EXT any, DB Database] func(handler *EvmHandler[EXT, DB]) +type EvmHandler[EXT any, DB Database] Handler[EXT, DB] +type HandleRegisterBox[EXT any, DB Database] interface { + Call(handler *EvmHandler[EXT, DB]) +} +type HandleRegisters[EXT any, DB Database] struct { + Plain HandleRegister[EXT, DB] // Plain function register + Box HandleRegisterBox[EXT, DB] // Boxed function register +}*/ diff --git a/execution/evm/host.go b/execution/evm/host.go new file mode 100644 index 0000000..d73074b --- /dev/null +++ b/execution/evm/host.go @@ -0,0 +1,72 @@ +package evm + +type Host interface { + // // Returns a reference to the environment. + // Env() *Env + + // // Returns a mutable reference to the environment. + // EnvMut() *Env + + // // Load an account. + // // Returns (is_cold, is_new_account) + // LoadAccount(address Address) (LoadAccountResult, bool) + + // // Get the block hash of the given block `number`. + // BlockHash(number uint64) (B256, bool) + + // // Get balance of `address` and if the account is cold. + // Balance(address Address) (U256, bool, bool) + + // // Get code of `address` and if the account is cold. + // Code(address Address) (Bytes, bool, bool) + + // // Get code hash of `address` and if the account is cold. + // CodeHash(address Address) (B256, bool, bool) + + // // Get storage value of `address` at `index` and if the account is cold. + // SLoad(address Address, index U256) (U256, bool, bool) + + // // Set storage value of account address at index. + // // Returns (original, present, new, is_cold). + // SStore(address Address, index U256, value U256) (SStoreResult, bool) + + // // Get the transient storage value of `address` at `index`. + // TLoad(address Address, index U256) U256 + + // // Set the transient storage value of `address` at `index`. + // TStore(address Address, index U256, value U256) + + // // Emit a log owned by `address` with given `LogData`. + // Log(log Log [any]) + + // // Mark `address` to be deleted, with funds transferred to `target`. + // SelfDestruct(address Address, target Address) (SelfDestructResult, bool) +} + +// SStoreResult represents the result of an `sstore` operation. +type SStoreResult struct { + // Value of the storage when it is first read + OriginalValue U256 + // Current value of the storage + PresentValue U256 + // New value that is set + NewValue U256 + // Is storage slot loaded from database + IsCold bool +} + +// LoadAccountResult represents the result of loading an account from the journal state. +type LoadAccountResult struct { + // Is account cold loaded + IsCold bool + // Is account empty; if true, the account is not created. + IsEmpty bool +} + +// SelfDestructResult represents the result of a selfdestruct instruction. +type SelfDestructResult struct { + HadValue bool + TargetExists bool + IsCold bool + PreviouslyDestroyed bool +} \ No newline at end of file diff --git a/execution/evm/inner_evm_context.go b/execution/evm/inner_evm_context.go new file mode 100644 index 0000000..e4fafef --- /dev/null +++ b/execution/evm/inner_evm_context.go @@ -0,0 +1,62 @@ +package evm +type InnerEvmContext[DB Database] struct { + Env *Env + JournaledState JournaledState + DB DB + Error error + ValidAuthorizations []Address + L1BlockInfo *L1BlockInfo // For optimism feature +} +func NewInnerEvmContext[DB Database](db DB) InnerEvmContext[DB] { + SpecId:=DefaultSpecId() + return InnerEvmContext[DB]{ + Env: &Env{}, + JournaledState: NewJournalState(SpecId,make(map[Address]struct{})),//to be changed + DB: db, + Error: nil, + ValidAuthorizations: nil, + L1BlockInfo: nil, + } +} +func WithNewInnerDB[DB1, DB2 Database]( + inner InnerEvmContext[DB1], + db DB2, +) InnerEvmContext[DB2] { + return InnerEvmContext[DB2]{ + Env: inner.Env, + JournaledState: inner.JournaledState, + DB: db, + Error: inner.Error, + ValidAuthorizations: nil, + L1BlockInfo: inner.L1BlockInfo, + } +} +func (i *InnerEvmContext[DB]) TakeError() EvmError { + currentError := i.Error + i.Error = nil + return EvmError{Message: currentError.Error()} +} +type L1BlockInfo struct { + L1BaseFee U256 + L1FeeOverhead *U256 + L1BaseFeeScalar U256 + L1BlobBaseFee *U256 + L1BlobBaseFeeScalar *U256 + EmptyScalars bool +} +//Not being used in the code +func (inner InnerEvmContext[DB]) WithDB(db DB) InnerEvmContext[DB] { + return InnerEvmContext[DB]{ + Env: inner.Env, + JournaledState: inner.JournaledState, + DB: db, + Error: inner.Error, + ValidAuthorizations: nil, + L1BlockInfo: inner.L1BlockInfo, + } +} +//Not being used in the code +func (inner InnerEvmContext[DB]) specId() SpecId { + return inner.JournaledState.Spec +} + diff --git a/execution/evm/interpreter.go b/execution/evm/interpreter.go new file mode 100644 index 0000000..05288ce --- /dev/null +++ b/execution/evm/interpreter.go @@ -0,0 +1,228 @@ +package evm +import ( + "encoding/json" + "math" +) +type Interpreter struct { + InstructionPointer *byte // Using *byte as equivalent to *const u8 in Rust + Gas Gas + Contract Contract + InstructionResult InstructionResult + Bytecode Bytes + IsEof bool + // Is init flag for eof create + IsEofInit bool + // Shared memory. + // Note: This field is only set while running the interpreter loop. + // Otherwise, it is taken and replaced with empty shared memory. + SharedMemory SharedMemory + // Stack. + Stack Stack + // EOF function stack. + FunctionStack FunctionStack + // The return data buffer for internal calls. + // It has multi usage: + // - It contains the output bytes of call sub call. + // - When this interpreter finishes execution it contains the output bytes of this contract. + ReturnDataBuffer Bytes + // Whether the interpreter is in "staticcall" mode, meaning no state changes can happen. + IsStatic bool + // Actions that the EVM should do. + // Set inside CALL or CREATE instructions and RETURN or REVERT instructions. + // Additionally, those instructions will set InstructionResult to CallOrCreate/Return/Revert so we know the reason. + NextAction InterpreterAction +} +type Gas struct { + Limit uint64 + Remaining uint64 + Refunded int64 +} +type Contract struct { + Input Bytes + Bytecode Bytecode + Hash *B256 + TargetAddress Address + BytecodeAddress *Address + Caller Address + CallValue U256 +} +type InstructionResult uint8 +const ( + // Success codes + Continue InstructionResult = iota // 0x00 + Stop + Return + SelfDestruct + ReturnContract + + // Revert codes + Revert = 0x10 // revert opcode + CallTooDeep + OutOfFunds + CreateInitCodeStartingEF00 + InvalidEOFInitCode + InvalidExtDelegateCallTarget + + // Actions + CallOrCreate = 0x20 + + // Error codes + OutOfGas = 0x50 + MemoryOOG + MemoryLimitOOG + PrecompileOOG + InvalidOperandOOG + OpcodeNotFound + CallNotAllowedInsideStatic + StateChangeDuringStaticCall + InvalidFEOpcode + InvalidJump + NotActivated + StackUnderflow + StackOverflow + OutOfOffset + CreateCollision + OverflowPayment + PrecompileError + NonceOverflow + CreateContractSizeLimit + CreateContractStartingWithEF + CreateInitCodeSizeLimit + FatalExternalError + ReturnContractInNotInitEOF + EOFOpcodeDisabledInLegacy + EOFFunctionStackOverflow + EofAuxDataOverflow + EofAuxDataTooSmall + InvalidEXTCALLTarget +) +// MarshalJSON customizes the JSON serialization of InstructionResult. +func (ir InstructionResult) MarshalJSON() ([]byte, error) { + return json.Marshal(uint8(ir)) +} + +// UnmarshalJSON customizes the JSON deserialization of InstructionResult. +func (ir *InstructionResult) UnmarshalJSON(data []byte) error { + var value uint8 + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *ir = InstructionResult(value) + return nil +} +type SharedMemory struct { + Buffer []byte + Checkpoints []int + LastCheckpoint int + MemoryLimit uint64 //although implemented as feature memory_limit but kept p=optional for now +} +func (s *SharedMemory) NewContext() { + newCheckpoint := len(s.Buffer) + s.Checkpoints = append(s.Checkpoints, newCheckpoint) + s.LastCheckpoint = newCheckpoint +} +func (s SharedMemory) New() SharedMemory { + return s.WithCapacity(4 * 1024) +} +func (s SharedMemory) WithCapacity(capacity uint) SharedMemory { + return SharedMemory{ + Buffer: make([]byte, capacity), + Checkpoints: make([]int, 32), + LastCheckpoint: 0, + MemoryLimit: math.MaxUint64, + } +} + +func (s SharedMemory) NewWithMemoryLimit(memoryLimit uint64) SharedMemory { + return SharedMemory{ + Buffer: make([]byte, 4 * 1024), + Checkpoints: make([]int, 32), + LastCheckpoint: 0, + MemoryLimit: memoryLimit, + } +} +func (s *SharedMemory) FreeContext() { + if len(s.Checkpoints) > 0 { + // Pop the last element from Checkpoints + oldCheckpoint := s.Checkpoints[len(s.Checkpoints)-1] + s.Checkpoints = s.Checkpoints[:len(s.Checkpoints)-1] + + // Update LastCheckpoint to the last value in Checkpoints, or 0 if empty + if len(s.Checkpoints) > 0 { + s.LastCheckpoint = s.Checkpoints[len(s.Checkpoints)-1] + } else { + s.LastCheckpoint = 0 // default value if no checkpoints are left + } + + // Safely resize Buffer to the old checkpoint length + if oldCheckpoint < len(s.Buffer) { + s.Buffer = s.Buffer[:oldCheckpoint] + } + } +} +type Stack struct { + Data []U256 +} +type FunctionStack struct { + ReturnStack []FunctionReturnFrame `json:"return_stack"` + CurrentCodeIdx uint64 `json:"current_code_idx"` // Changed to uint64 to match Rust's usize +} + +// MarshalJSON customizes the JSON serialization of FunctionStack. +func (fs FunctionStack) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + ReturnStack []FunctionReturnFrame `json:"return_stack"` + CurrentCodeIdx uint64 `json:"current_code_idx"` + }{ + ReturnStack: fs.ReturnStack, + CurrentCodeIdx: fs.CurrentCodeIdx, + }) +} + +// UnmarshalJSON customizes the JSON deserialization of FunctionStack. +func (fs *FunctionStack) UnmarshalJSON(data []byte) error { + var temp struct { + ReturnStack []FunctionReturnFrame `json:"return_stack"` + CurrentCodeIdx uint64 `json:"current_code_idx"` + } + if err := json.Unmarshal(data, &temp); err != nil { + return err + } + fs.ReturnStack = temp.ReturnStack + fs.CurrentCodeIdx = temp.CurrentCodeIdx + return nil +} + +// FunctionReturnFrame represents a frame in the function return stack for the EVM interpreter. +type FunctionReturnFrame struct { + // The index of the code container that this frame is executing. + Idx uint64 `json:"idx"` // Changed to uint64 to match Rust's usize + // The program counter where frame execution should continue. + PC uint64 `json:"pc"` // Changed to uint64 to match Rust's usize +} + +// MarshalJSON customizes the JSON serialization of FunctionReturnFrame. +func (frf FunctionReturnFrame) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Idx uint64 `json:"idx"` + PC uint64 `json:"pc"` + }{ + Idx: frf.Idx, + PC: frf.PC, + }) +} + +// UnmarshalJSON customizes the JSON deserialization of FunctionReturnFrame. +func (frf *FunctionReturnFrame) UnmarshalJSON(data []byte) error { + var temp struct { + Idx uint64 `json:"idx"` + PC uint64 `json:"pc"` + } + if err := json.Unmarshal(data, &temp); err != nil { + return err + } + frf.Idx = temp.Idx + frf.PC = temp.PC + return nil +} + diff --git a/execution/evm/interpreter_action.go b/execution/evm/interpreter_action.go new file mode 100644 index 0000000..26ff9bb --- /dev/null +++ b/execution/evm/interpreter_action.go @@ -0,0 +1,141 @@ +package evm +import( + "fmt" + "encoding/json" + +) +type InterpreterAction struct { + // We use a type field to distinguish between different actions + actionType ActionType + callInputs *CallInputs + createInputs *CreateInputs + eofCreateInputs *EOFCreateInputs + result *InterpreterResult +} +type InterpreterResult struct { + // The result of the execution. + Result InstructionResult + Output Bytes + Gas Gas +} +//Check marshal unmarshal remove if not required +type jsonInterpreterAction struct { + Type string `json:"type"` + CallInputs *CallInputs `json:"call_inputs,omitempty"` + CreateInputs *CreateInputs `json:"create_inputs,omitempty"` + EOFInputs *EOFCreateInputs `json:"eof_inputs,omitempty"` + Result *InterpreterResult `json:"result,omitempty"` +} + +// MarshalJSON implements the json.Marshaler interface +func (a *InterpreterAction) MarshalJSON() ([]byte, error) { + var jsonAction jsonInterpreterAction + + switch a.actionType { + case ActionTypeCall: + jsonAction.Type = "Call" + jsonAction.CallInputs = a.callInputs + case ActionTypeCreate: + jsonAction.Type = "Create" + jsonAction.CreateInputs = a.createInputs + case ActionTypeEOFCreate: + jsonAction.Type = "EOFCreate" + jsonAction.EOFInputs = a.eofCreateInputs + case ActionTypeReturn: + jsonAction.Type = "Return" + jsonAction.Result = a.result + case ActionTypeNone: + jsonAction.Type = "None" + default: + return nil, fmt.Errorf("unknown action type: %v", a.actionType) + } + + return json.Marshal(jsonAction) +} + +// UnmarshalJSON implements the json.Unmarshaler interface +func (a *InterpreterAction) UnmarshalJSON(data []byte) error { + var jsonAction jsonInterpreterAction + if err := json.Unmarshal(data, &jsonAction); err != nil { + return err + } + + switch jsonAction.Type { + case "Call": + if jsonAction.CallInputs == nil { + return fmt.Errorf("Call action missing inputs") + } + *a = *NewCallAction(jsonAction.CallInputs) + case "Create": + if jsonAction.CreateInputs == nil { + return fmt.Errorf("Create action missing inputs") + } + *a = *NewCreateAction(jsonAction.CreateInputs) + case "EOFCreate": + if jsonAction.EOFInputs == nil { + return fmt.Errorf("EOFCreate action missing inputs") + } + *a = *NewEOFCreateAction(jsonAction.EOFInputs) + case "Return": + if jsonAction.Result == nil { + return fmt.Errorf("Return action missing result") + } + *a = *NewReturnAction(jsonAction.Result) + case "None": + *a = *NewNoneAction() + default: + return fmt.Errorf("unknown action type: %s", jsonAction.Type) + } + + return nil +} + +// ActionType represents the type of interpreter action +type ActionType int + +const ( + ActionTypeNone ActionType = iota + ActionTypeCall + ActionTypeCreate + ActionTypeEOFCreate + ActionTypeReturn +) + +// NewCallAction creates a new Call action +func NewCallAction(inputs *CallInputs) *InterpreterAction { + return &InterpreterAction{ + actionType: ActionTypeCall, + callInputs: inputs, + } +} + +// NewCreateAction creates a new Create action +func NewCreateAction(inputs *CreateInputs) *InterpreterAction { + return &InterpreterAction{ + actionType: ActionTypeCreate, + createInputs: inputs, + } +} + +// NewEOFCreateAction creates a new EOFCreate action +func NewEOFCreateAction(inputs *EOFCreateInputs) *InterpreterAction { + return &InterpreterAction{ + actionType: ActionTypeEOFCreate, + eofCreateInputs: inputs, + } +} + +// NewReturnAction creates a new Return action +func NewReturnAction(result *InterpreterResult) *InterpreterAction { + return &InterpreterAction{ + actionType: ActionTypeReturn, + result: result, + } +} + +// NewNoneAction creates a new None action +func NewNoneAction() *InterpreterAction { + return &InterpreterAction{ + actionType: ActionTypeNone, + } +} \ No newline at end of file diff --git a/execution/evm/journal.go b/execution/evm/journal.go new file mode 100644 index 0000000..36c9f9c --- /dev/null +++ b/execution/evm/journal.go @@ -0,0 +1,50 @@ +package evm +import ( + "github.com/BlocSoc-iitr/selene/common" +) +type JournaledState struct { + State EvmState + TransientStorage TransientStorage + Logs []Log[LogData] + Depth uint + Journal [][]JournalEntry + Spec SpecId + WarmPreloadedAddresses map[Address]struct{} +} +func NewJournalState(spec SpecId,warmPreloadedAddresses map[Address]struct{}) JournaledState { + return JournaledState{ + State: nil, + TransientStorage: nil, + Logs: []Log[LogData] {}, + Depth: 0, + Journal: [][]JournalEntry{}, + Spec: spec, + WarmPreloadedAddresses: warmPreloadedAddresses, + } +} +type JournalCheckpoint struct { + Log_i uint + Journal_i uint +} +func (j JournaledState)setSpecId(spec SpecId) { + j.Spec = spec +} +type TransientStorage map[Key]U256 +type EvmState map[common.Address]Account +type Key struct { + Account common.Address + Slot U256 +} +type Log[T any] struct { + // The address which emitted this log. + Address Address `json:"address"` + // The log data. + Data T `json:"data"` +} +type LogData struct { + // The indexed topic list. + Topics []B256 `json:"topics"` + // The plain data. + Data Bytes `json:"data"` +} + diff --git a/execution/evm/journal_entry.go b/execution/evm/journal_entry.go new file mode 100644 index 0000000..cee45bd --- /dev/null +++ b/execution/evm/journal_entry.go @@ -0,0 +1,148 @@ +package evm +import( + "encoding/json" + "math/big" + ) +type JournalEntryType uint8 +const ( + AccountWarmedType JournalEntryType = iota + AccountDestroyedType + AccountTouchedType + BalanceTransferType + NonceChangeType + AccountCreatedType + StorageChangedType + StorageWarmedType + TransientStorageChangeType + CodeChangeType +) + +// Creating a struct for JournalEntry containing all possible fields that might be needed +type JournalEntry struct { + Type JournalEntryType + Address Address + Target Address // Used for AccountDestroyed + WasDestroyed bool // Used for AccountDestroyed + HadBalance U256 // Used for AccountDestroyed + Balance U256 // Used for BalanceTransfer + From Address // Used for BalanceTransfer + To Address // Used for BalanceTransfer + Key U256 // Used for Storage operations + HadValue U256 // Used for Storage operations +} + +func NewAccountWarmedEntry(address Address) *JournalEntry { + return &JournalEntry{ + Type: AccountWarmedType, + Address: address, + } +} + +// NewAccountDestroyedEntry creates a new journal entry for destroying an account +func NewAccountDestroyedEntry(address, target Address, wasDestroyed bool, hadBalance U256) *JournalEntry { + return &JournalEntry{ + Type: AccountDestroyedType, + Address: address, + Target: target, + WasDestroyed: wasDestroyed, + HadBalance: new(big.Int).Set(hadBalance), //to avoid mutating the original value (had balance not written directly) + } +} + +// NewAccountTouchedEntry creates a new journal entry for touching an account +func NewAccountTouchedEntry(address Address) *JournalEntry { + return &JournalEntry{ + Type: AccountTouchedType, + Address: address, + } +} + +// NewBalanceTransferEntry creates a new journal entry for balance transfer +func NewBalanceTransferEntry(from, to Address, balance U256) *JournalEntry { + return &JournalEntry{ + Type: BalanceTransferType, + From: from, + To: to, + Balance: new(big.Int).Set(balance), + } +} + +// NewNonceChangeEntry creates a new journal entry for nonce change +func NewNonceChangeEntry(address Address) *JournalEntry { + return &JournalEntry{ + Type: NonceChangeType, + Address: address, + } +} + +// NewAccountCreatedEntry creates a new journal entry for account creation +func NewAccountCreatedEntry(address Address) *JournalEntry { + return &JournalEntry{ + Type: AccountCreatedType, + Address: address, + } +} + +// NewStorageChangedEntry creates a new journal entry for storage change +func NewStorageChangedEntry(address Address, key, hadValue U256) *JournalEntry { + return &JournalEntry{ + Type: StorageChangedType, + Address: address, + Key: new(big.Int).Set(key), + HadValue: new(big.Int).Set(hadValue), + } +} + +// NewStorageWarmedEntry creates a new journal entry for storage warming +func NewStorageWarmedEntry(address Address, key U256) *JournalEntry { + return &JournalEntry{ + Type: StorageWarmedType, + Address: address, + Key: new(big.Int).Set(key), + } +} + +// NewTransientStorageChangeEntry creates a new journal entry for transient storage change +func NewTransientStorageChangeEntry(address Address, key, hadValue U256) *JournalEntry { + return &JournalEntry{ + Type: TransientStorageChangeType, + Address: address, + Key: new(big.Int).Set(key), + HadValue: new(big.Int).Set(hadValue), + } +} + +// NewCodeChangeEntry creates a new journal entry for code change +func NewCodeChangeEntry(address Address) *JournalEntry { + return &JournalEntry{ + Type: CodeChangeType, + Address: address, + } +} + +// MarshalJSON implements the json.Marshaler interface +func (j JournalEntry) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type JournalEntryType `json:"type"` + Address Address `json:"address"` + Target Address `json:"target,omitempty"` + WasDestroyed bool `json:"was_destroyed,omitempty"` + HadBalance U256 `json:"had_balance,omitempty"` + Balance U256 `json:"balance,omitempty"` + From Address `json:"from,omitempty"` + To Address `json:"to,omitempty"` + Key U256 `json:"key,omitempty"` + HadValue U256 `json:"had_value,omitempty"` + }{ + Type: j.Type, + Address: j.Address, + Target: j.Target, + WasDestroyed: j.WasDestroyed, + HadBalance: j.HadBalance, + Balance: j.Balance, + From: j.From, + To: j.To, + Key: j.Key, + HadValue: j.HadValue, + }) +} \ No newline at end of file diff --git a/execution/evm/non_optimism.go b/execution/evm/non_optimism.go new file mode 100644 index 0000000..9c0a9cc --- /dev/null +++ b/execution/evm/non_optimism.go @@ -0,0 +1,8 @@ +//go:build !optimism_default_handler || negate_optimism_default_handler +// +build !optimism_default_handler negate_optimism_default_handler + +package evm + +func getDefaultOptimismSetting() bool { + return false +} \ No newline at end of file diff --git a/execution/evm/optimism.go b/execution/evm/optimism.go new file mode 100644 index 0000000..9333844 --- /dev/null +++ b/execution/evm/optimism.go @@ -0,0 +1,8 @@ +//go:build optimism_default_handler && !negate_optimism_default_handler +// +build optimism_default_handler,!negate_optimism_default_handler + +package evm + +func getDefaultOptimismSetting() bool { + return true +} \ No newline at end of file diff --git a/execution/evm/post_execution.go b/execution/evm/post_execution.go new file mode 100644 index 0000000..2a42398 --- /dev/null +++ b/execution/evm/post_execution.go @@ -0,0 +1,20 @@ +package evm +type PostExecutionHandler[EXT any, DB Database] struct { + ReimburseCaller ReimburseCallerHandle[EXT, DB] + RewardBeneficiary RewardBeneficiaryHandle[EXT, DB] + Output OutputHandle[EXT, DB] + End EndHandle[EXT, DB] + Clear ClearHandle[EXT, DB] +} +type ReimburseCallerHandle[EXT any, DB Database] func(ctx *Context[EXT, DB], gas *Gas) EVMResultGeneric[struct{}, any] +type RewardBeneficiaryHandle[EXT any, DB Database] ReimburseCallerHandle[EXT, DB] +type OutputHandle[EXT any, DB Database] func(ctx *Context[EXT, DB], frameResult FrameResult) (ResultAndState, EvmError) +type EndHandle[EXT any, DB Database] func(ctx *Context[EXT, DB], result EVMResultGeneric[ResultAndState, EvmError]) (ResultAndState, EvmError) +type ClearHandle[EXT any, DB Database] func(ctx *Context[EXT, DB]) +type LoadPrecompilesHandle[DB Database] func() ContextPrecompiles[DB] +type LoadAccountsHandle[EXT any, DB Database] func(ctx *Context[EXT, DB]) error +type DeductCallerHandle[EXT any, DB Database] func(ctx *Context[EXT, DB]) EVMResultGeneric[ ResultAndState, DatabaseError] +type EVMResultGeneric[T any, DBError any] struct { + Value T + Err error +} diff --git a/execution/evm/pre_execution.go b/execution/evm/pre_execution.go new file mode 100644 index 0000000..b17073d --- /dev/null +++ b/execution/evm/pre_execution.go @@ -0,0 +1,17 @@ +package evm +type PreExecutionHandler[EXT any, DB Database] struct { + LoadPrecompiles LoadPrecompilesHandle[DB] + LoadAccounts LoadAccountsHandle[EXT, DB] + DeductCaller DeductCallerHandle[EXT, DB] +} +type ValidationHandler[EXT any, DB Database] struct { + InitialTxGas ValidateInitialTxGasHandle[DB] + TxAgainstState ValidateTxEnvAgainstState[EXT, DB] + Env ValidateEnvHandle[DB] +} +type ValidateEnvHandle[DB Database] func(env *Env) error +type ValidateTxEnvAgainstState[EXT any, DB Database] func(ctx *Context[EXT, DB]) error +type ValidateInitialTxGasHandle[DB Database] func(env *Env) (uint64, error) +func (p *PreExecutionHandler[EXT, DB]) LoadPrecompilesFunction() ContextPrecompiles[DB] { + return p.LoadPrecompiles() +} diff --git a/execution/evm/precompiles.go b/execution/evm/precompiles.go new file mode 100644 index 0000000..4eddd8b --- /dev/null +++ b/execution/evm/precompiles.go @@ -0,0 +1,121 @@ +package evm +import ( + "sync" +) + func (e *EvmContext[DB]) SetPrecompiles(precompiles ContextPrecompiles[DB]) { + for i, address := range precompiles.Inner.StaticRef.Addresses { + e.Inner.JournaledState.WarmPreloadedAddresses[i] = address + } + e.Precompiles = precompiles + } + type ContextPrecompiles[DB Database] struct { + Inner PrecompilesCow[DB] + } + func DefaultContextPrecompiles[DB Database]() ContextPrecompiles[DB] { + return ContextPrecompiles[DB]{ + Inner: NewPrecompilesCow[DB](), + } + } + func NewPrecompilesCow[DB Database]() PrecompilesCow[DB] { + return PrecompilesCow[DB]{ + Owned: make(map[Address]ContextPrecompile[DB]), + } + } + type PrecompilesCow[DB Database] struct { + isStatic bool + StaticRef *Precompiles + Owned map[Address]ContextPrecompile[DB] + } + type Precompiles struct { + Inner map[Address]Precompile + Addresses map[Address]struct{} + } + type Precompile struct { + precompileType string // "Standard", "Env", "Stateful", or "StatefulMut" + standard StandardPrecompileFn + env EnvPrecompileFn + stateful *StatefulPrecompileArc + statefulMut *StatefulPrecompileBox + } + type StandardPrecompileFn func(input *Bytes, gasLimit uint64) PrecompileResult + type EnvPrecompileFn func(input *Bytes, gasLimit uint64, env *Env) PrecompileResult + type StatefulPrecompile interface { + Call(bytes *Bytes, gasLimit uint64, env *Env) PrecompileResult + } + type StatefulPrecompileMut interface { + CallMut(bytes *Bytes, gasLimit uint64, env *Env) PrecompileResult + Clone() StatefulPrecompileMut + } + + // Doubt + type StatefulPrecompileArc struct { + sync.RWMutex + impl StatefulPrecompile + } + + // StatefulPrecompileBox is a mutable reference to a StatefulPrecompileMut + type StatefulPrecompileBox struct { + impl StatefulPrecompileMut + } + type ContextPrecompile[DB Database] struct { + precompileType string // "Ordinary", "ContextStateful", or "ContextStatefulMut" + ordinary *Precompile + contextStateful *ContextStatefulPrecompileArc[DB] + contextStatefulMut *ContextStatefulPrecompileBox[DB] + } + + // ContextStatefulPrecompileArc is a thread-safe reference to a ContextStatefulPrecompile + type ContextStatefulPrecompileArc[DB Database] struct { + sync.RWMutex + impl ContextStatefulPrecompile[DB] + } + + // ContextStatefulPrecompileBox is a mutable reference to a ContextStatefulPrecompileMut + type ContextStatefulPrecompileBox[DB Database] struct { + impl ContextStatefulPrecompileMut[DB] + } + type ContextStatefulPrecompile[DB Database] interface { + Call(bytes *Bytes, gasLimit uint64, evmCtx *InnerEvmContext[DB]) PrecompileResult + } + + // ContextStatefulPrecompileMut interface for mutable stateful precompiles with context + type ContextStatefulPrecompileMut[DB Database] interface { + CallMut(bytes *Bytes, gasLimit uint64, evmCtx *InnerEvmContext[DB]) PrecompileResult + Clone() ContextStatefulPrecompileMut[DB] + } + type PrecompileResult struct { + output *PrecompileOutput + err PrecompileErrorStruct //Doubt + } + type PrecompileOutput struct { + GasUsed uint64 + Bytes []byte + } + type PrecompileErrorStruct struct { + errorType string + message string + } + + const ( + ErrorOutOfGas = "OutOfGas" + ErrorBlake2WrongLength = "Blake2WrongLength" + ErrorBlake2WrongFinalFlag = "Blake2WrongFinalIndicatorFlag" + ErrorModexpExpOverflow = "ModexpExpOverflow" + ErrorModexpBaseOverflow = "ModexpBaseOverflow" + ErrorModexpModOverflow = "ModexpModOverflow" + ErrorBn128FieldPointNotMember = "Bn128FieldPointNotAMember" + ErrorBn128AffineGFailedCreate = "Bn128AffineGFailedToCreate" + ErrorBn128PairLength = "Bn128PairLength" + ErrorBlobInvalidInputLength = "BlobInvalidInputLength" + ErrorBlobMismatchedVersion = "BlobMismatchedVersion" + ErrorBlobVerifyKzgProofFailed = "BlobVerifyKzgProofFailed" + ErrorOther = "Other" + ) + + // Error implementation for PrecompileError + func (e PrecompileErrorStruct) Error() string { + if e.message != "" { + return e.message + } + return e.errorType + } \ No newline at end of file diff --git a/execution/evm/result.go b/execution/evm/result.go new file mode 100644 index 0000000..776f332 --- /dev/null +++ b/execution/evm/result.go @@ -0,0 +1,60 @@ +package evm +import "github.com/ethereum/go-ethereum/core/types" +type ResultAndState struct{ + Result ExecutionResult + State EvmState +} +type ExecutionResult struct { + Type string // "Success", "Revert", or "Halt" + Reason interface{} // SuccessReason or HaltReason + GasUsed uint64 + GasRefunded uint64 // Only for Success + Logs []types.Log // Only for Success + Output Output // Only for Success and Revert +} +type SuccessReason string +const ( + SStop SuccessReason = "Stop" + SReturn SuccessReason = "Return" + SSelfDestruct SuccessReason = "SelfDestruct" + SEofReturnContract SuccessReason = "EofReturnContract" +) +type Output struct { + Type string + Data []byte + Address *Address // Only for Create type +} +type HaltReason string +const ( + HOutOfGas HaltReason = "OutOfGas" + HOpcodeNotFound HaltReason = "OpcodeNotFound" + HInvalidFEOpcode HaltReason = "InvalidFEOpcode" + HInvalidJump HaltReason = "InvalidJump" + HNotActivated HaltReason = "NotActivated" + HStackUnderflow HaltReason = "StackUnderflow" + HStackOverflow HaltReason = "StackOverflow" + HOutOfOffset HaltReason = "OutOfOffset" + HCreateCollision HaltReason = "CreateCollision" + HPrecompileError HaltReason = "PrecompileError" + HNonceOverflow HaltReason = "NonceOverflow" + HCreateContractSizeLimit HaltReason = "CreateContractSizeLimit" + HCreateContractStartingWithEF HaltReason = "CreateContractStartingWithEF" + HCreateInitCodeSizeLimit HaltReason = "CreateInitCodeSizeLimit" + HOverflowPayment HaltReason = "OverflowPayment" + HStateChangeDuringStaticCall HaltReason = "StateChangeDuringStaticCall" + HCallNotAllowedInsideStatic HaltReason = "CallNotAllowedInsideStatic" + HOutOfFunds HaltReason = "OutOfFunds" + HCallTooDeep HaltReason = "CallTooDeep" + HEofAuxDataOverflow HaltReason = "EofAuxDataOverflow" + HEofAuxDataTooSmall HaltReason = "EofAuxDataTooSmall" + HEOFFunctionStackOverflow HaltReason = "EOFFunctionStackOverflow" +) +type EvmError struct { + Message string + Data []byte +} + +// Error implements the error interface +func (e EvmError) Error() string { + return e.Message +} diff --git a/execution/evm/specs_common.go b/execution/evm/specs_common.go new file mode 100644 index 0000000..bd910f3 --- /dev/null +++ b/execution/evm/specs_common.go @@ -0,0 +1,33 @@ +package evm +func (s SpecId) IsEnabledIn(other SpecId) bool { + return s >= other +} +type Spec interface { + SpecID() SpecId + Enabled(specID SpecId) bool +} +type BaseSpec struct { + id SpecId +} +func (s BaseSpec) SpecID() SpecId { + return s.id +} + +func (s BaseSpec) Enabled(specID SpecId) bool { + return s.id >= specID +} +func NewSpec(id SpecId) BaseSpec { + return BaseSpec{id: id} +} +func TryFromUint8(specID uint8) (SpecId, bool) { + if specID > uint8(PRAGUE_EOF) && specID != uint8(LATEST) { + return 0, false + } + return SpecId(specID), true +} +type OptimismFields struct { + SourceHash *B256 `json:"source_hash,omitempty"` + Mint *uint64 `json:"mint,omitempty"` + IsSystemTransaction *bool `json:"is_system_transaction,omitempty"` + EnvelopedTx Bytes `json:"enveloped_tx,omitempty"` +} diff --git a/execution/evm/specs_default.go b/execution/evm/specs_default.go new file mode 100644 index 0000000..6b0f0ea --- /dev/null +++ b/execution/evm/specs_default.go @@ -0,0 +1,431 @@ +//go:build !optimism +// +build !optimism + +package evm + +import ( + "encoding/json" + "fmt" + "math/big" + +) +type TxEnv struct { + // Caller aka Author aka transaction signer + Caller Address `json:"caller"` + // The gas limit of the transaction + GasLimit uint64 `json:"gas_limit"` + // The gas price of the transaction + GasPrice *big.Int `json:"gas_price"` + // The destination of the transaction + TransactTo TxKind `json:"transact_to"` + // The value sent to TransactTo + Value *big.Int `json:"value"` + // The data of the transaction + Data Bytes `json:"data"` + // The nonce of the transaction + // If nil, nonce validation against the account's nonce is skipped + Nonce *uint64 `json:"nonce,omitempty"` + // The chain ID of the transaction + // If nil, no checks are performed (EIP-155) + ChainID *uint64 `json:"chain_id,omitempty"` + // List of addresses and storage keys that the transaction plans to access (EIP-2930) + AccessList []AccessListItem `json:"access_list"` + // The priority fee per gas (EIP-1559) + GasPriorityFee *big.Int `json:"gas_priority_fee,omitempty"` + // The list of blob versioned hashes (EIP-4844) + BlobHashes []B256 `json:"blob_hashes"` + // The max fee per blob gas (EIP-4844) + MaxFeePerBlobGas *big.Int `json:"max_fee_per_blob_gas,omitempty"` + // List of authorizations for EOA account code (EIP-7702) + AuthorizationList *AuthorizationList `json:"authorization_list,omitempty"` +} +const isOptimismEnabled = false + +// SpecId represents the specification IDs and their activation block. +type SpecId uint8 +const ( + FRONTIER SpecId = 0 // Frontier 0 + FRONTIER_THAWING SpecId = 1 // Frontier Thawing 200000 + HOMESTEAD SpecId = 2 // Homestead 1150000 + DAO_FORK SpecId = 3 // DAO Fork 1920000 + TANGERINE SpecId = 4 // Tangerine Whistle 2463000 + SPURIOUS_DRAGON SpecId = 5 // Spurious Dragon 2675000 + BYZANTIUM SpecId = 6 // Byzantium 4370000 + CONSTANTINOPLE SpecId = 7 // Constantinople 7280000 is overwritten with PETERSBURG + PETERSBURG SpecId = 8 // Petersburg 7280000 + ISTANBUL SpecId = 9 // Istanbul 9069000 + MUIR_GLACIER SpecId = 10 // Muir Glacier 9200000 + BERLIN SpecId = 11 // Berlin 12244000 + LONDON SpecId = 12 // London 12965000 + ARROW_GLACIER SpecId = 13 // Arrow Glacier 13773000 + GRAY_GLACIER SpecId = 14 // Gray Glacier 15050000 + MERGE SpecId = 15 // Paris/Merge 15537394 (TTD: 58750000000000000000000) + SHANGHAI SpecId = 16 // Shanghai 17034870 (Timestamp: 1681338455) + CANCUN SpecId = 17 // Cancun 19426587 (Timestamp: 1710338135) + PRAGUE SpecId = 18 // Prague TBD + PRAGUE_EOF SpecId = 19 // Prague+EOF TBD + LATEST SpecId = 255 // LATEST = u8::MAX +) + +// String method to convert SpecId to string +func (s SpecId) String() string { + switch s { + case FRONTIER: + return "FRONTIER" + case FRONTIER_THAWING: + return "FRONTIER_THAWING" + case HOMESTEAD: + return "HOMESTEAD" + case DAO_FORK: + return "DAO_FORK" + case TANGERINE: + return "TANGERINE" + case SPURIOUS_DRAGON: + return "SPURIOUS_DRAGON" + case BYZANTIUM: + return "BYZANTIUM" + case CONSTANTINOPLE: + return "CONSTANTINOPLE" + case PETERSBURG: + return "PETERSBURG" + case ISTANBUL: + return "ISTANBUL" + case MUIR_GLACIER: + return "MUIR_GLACIER" + case BERLIN: + return "BERLIN" + case LONDON: + return "LONDON" + case ARROW_GLACIER: + return "ARROW_GLACIER" + case GRAY_GLACIER: + return "GRAY_GLACIER" + case MERGE: + return "MERGE" + case SHANGHAI: + return "SHANGHAI" + case CANCUN: + return "CANCUN" + case PRAGUE: + return "PRAGUE" + case PRAGUE_EOF: + return "PRAGUE_EOF" + default: + return "UNKNOWN" + } +} +func DefaultSpecId() SpecId { + return LATEST +} + +type ( + FrontierSpec struct{ BaseSpec } + HomesteadSpec struct{ BaseSpec } + TangerineSpec struct{ BaseSpec } + SpuriousDragonSpec struct{ BaseSpec } + ByzantiumSpec struct{ BaseSpec } + PetersburgSpec struct{ BaseSpec } + IstanbulSpec struct{ BaseSpec } + BerlinSpec struct{ BaseSpec } + LondonSpec struct{ BaseSpec } + MergeSpec struct{ BaseSpec } + ShanghaiSpec struct{ BaseSpec } + CancunSpec struct{ BaseSpec } + PragueSpec struct{ BaseSpec } + PragueEofSpec struct{ BaseSpec } + LatestSpec struct{ BaseSpec } +) +func SpecToGeneric(specID SpecId) Spec { + switch specID { + case FRONTIER, FRONTIER_THAWING: + return FrontierSpec{NewSpec(FRONTIER)} + case HOMESTEAD, DAO_FORK: + return HomesteadSpec{NewSpec(HOMESTEAD)} + case TANGERINE: + return TangerineSpec{NewSpec(TANGERINE)} + case SPURIOUS_DRAGON: + return SpuriousDragonSpec{NewSpec(SPURIOUS_DRAGON)} + case BYZANTIUM: + return ByzantiumSpec{NewSpec(BYZANTIUM)} + case PETERSBURG, CONSTANTINOPLE: + return PetersburgSpec{NewSpec(PETERSBURG)} + case ISTANBUL, MUIR_GLACIER: + return IstanbulSpec{NewSpec(ISTANBUL)} + case BERLIN: + return BerlinSpec{NewSpec(BERLIN)} + case LONDON, ARROW_GLACIER, GRAY_GLACIER: + return LondonSpec{NewSpec(LONDON)} + case MERGE: + return MergeSpec{NewSpec(MERGE)} + case SHANGHAI: + return ShanghaiSpec{NewSpec(SHANGHAI)} + case CANCUN: + return CancunSpec{NewSpec(CANCUN)} + case PRAGUE: + return PragueSpec{NewSpec(PRAGUE)} + case PRAGUE_EOF: + return PragueEofSpec{NewSpec(PRAGUE_EOF)} + default: + return LatestSpec{NewSpec(LATEST)} + } +} + + +// MarshalJSON implements the json.Marshaler interface for SpecId. +// MarshalJSON for serialization +func (s SpecId) MarshalJSON() ([]byte, error) { + return json.Marshal(s.String()) +} + +// UnmarshalJSON for deserialization +func (s *SpecId) UnmarshalJSON(data []byte) error { + var name string + if err := json.Unmarshal(data, &name); err != nil { + return err + } + + switch name { + case "FRONTIER": + *s = FRONTIER + case "FRONTIER_THAWING": + *s = FRONTIER_THAWING + case "HOMESTEAD": + *s = HOMESTEAD + case "DAO_FORK": + *s = DAO_FORK + case "TANGERINE": + *s = TANGERINE + case "SPURIOUS_DRAGON": + *s = SPURIOUS_DRAGON + case "BYZANTIUM": + *s = BYZANTIUM + case "CONSTANTINOPLE": + *s = CONSTANTINOPLE + case "PETERSBURG": + *s = PETERSBURG + case "ISTANBUL": + *s = ISTANBUL + case "MUIR_GLACIER": + *s = MUIR_GLACIER + case "BERLIN": + *s = BERLIN + case "LONDON": + *s = LONDON + case "ARROW_GLACIER": + *s = ARROW_GLACIER + case "GRAY_GLACIER": + *s = GRAY_GLACIER + case "MERGE": + *s = MERGE + case "SHANGHAI": + *s = SHANGHAI + case "CANCUN": + *s = CANCUN + case "PRAGUE": + *s = PRAGUE + case "PRAGUE_EOF": + *s = PRAGUE_EOF + default: + return fmt.Errorf("unknown SpecId: %s", name) + } + return nil +} + +// func SpecToGeneric(specID SpecId) Spec { +// switch specID { +// case FRONTIER, FRONTIER_THAWING: +// return FrontierSpec{NewSpec(FRONTIER)} +// case HOMESTEAD, DAO_FORK: +// return HomesteadSpec{NewSpec(HOMESTEAD)} +// case TANGERINE: +// return TangerineSpec{NewSpec(TANGERINE)} +// case SPURIOUS_DRAGON: +// return SpuriousDragonSpec{NewSpec(SPURIOUS_DRAGON)} +// case BYZANTIUM: +// return ByzantiumSpec{NewSpec(BYZANTIUM)} +// case PETERSBURG, CONSTANTINOPLE: +// return PetersburgSpec{NewSpec(PETERSBURG)} +// case ISTANBUL, MUIR_GLACIER: +// return IstanbulSpec{NewSpec(ISTANBUL)} +// case BERLIN: +// return BerlinSpec{NewSpec(BERLIN)} +// case LONDON, ARROW_GLACIER, GRAY_GLACIER: +// return LondonSpec{NewSpec(LONDON)} +// case MERGE: +// return MergeSpec{NewSpec(MERGE)} +// case SHANGHAI: +// return ShanghaiSpec{NewSpec(SHANGHAI)} +// case CANCUN: +// return CancunSpec{NewSpec(CANCUN)} +// case PRAGUE: +// return PragueSpec{NewSpec(PRAGUE)} +// case PRAGUE_EOF: +// return PragueEofSpec{NewSpec(PRAGUE_EOF)} +// default: +// return LatestSpec{NewSpec(LATEST)} +// } +// } +/* + func specToGeneric[H Host, EXT any, DB Database]( + specID SpecId, + h *EvmHandler[H, EXT, DB], + isOptimism bool, + ) (*EvmHandler[H, EXT, DB], error) { + switch specID { + case FRONTIER, FRONTIER_THAWING: + return createSpecHandler[H, EXT, DB](h, "frontier", isOptimism) + case HOMESTEAD, DAO_FORK: + return createSpecHandler[H, EXT, DB](h, "homestead", isOptimism) + case TANGERINE: + return createSpecHandler[H, EXT, DB](h, "tangerine", isOptimism) + case SPURIOUS_DRAGON: + return createSpecHandler[H, EXT, DB](h, "spurious_dragon", isOptimism) + case BYZANTIUM: + return createSpecHandler[H, EXT, DB](h, "byzantium", isOptimism) + case PETERSBURG, CONSTANTINOPLE: + return createSpecHandler[H, EXT, DB](h, "petersburg", isOptimism) + case ISTANBUL, MUIR_GLACIER: + return createSpecHandler[H, EXT, DB](h, "istanbul", isOptimism) + case BERLIN: + return createSpecHandler[H, EXT, DB](h, "berlin", isOptimism) + case LONDON, ARROW_GLACIER, GRAY_GLACIER: + return createSpecHandler[H, EXT, DB](h, "london", isOptimism) + case MERGE: + return createSpecHandler[H, EXT, DB](h, "merge", isOptimism) + case SHANGHAI: + return createSpecHandler[H, EXT, DB](h, "shanghai", isOptimism) + case CANCUN: + return createSpecHandler[H, EXT, DB](h, "cancun", isOptimism) + case PRAGUE: + return createSpecHandler[H, EXT, DB](h, "prague", isOptimism) + case PRAGUE_EOF: + return createSpecHandler[H, EXT, DB](h, "prague_eof", isOptimism) + case LATEST: + return createSpecHandler[H, EXT, DB](h, "latest", isOptimism) + } + + // Optimism-specific specs + /* + if isOptimism { + switch specID { + case BEDROCK: + return createSpecHandler[H, EXT, DB](h, "bedrock", true) + case REGOLITH: + return createSpecHandler[H, EXT, DB](h, "regolith", true) + case CANYON: + return createSpecHandler[H, EXT, DB](h, "canyon", true) + case ECOTONE: + return createSpecHandler[H, EXT, DB](h, "ecotone", true) + case FJORD: + return createSpecHandler[H, EXT, DB](h, "fjord", true) + } + }*/ + + //return nil, fmt.Errorf("unsupported spec ID: %d", specID) + //}*/ + +/* +// Spec interface defining the behavior of Ethereum specs. +type Spec interface { + GetSpecID() SpecId +} + +// CreateSpec creates a new specification struct based on the provided SpecId. +func CreateSpec(specId SpecId) Spec { + switch specId { + case FRONTIER: + return &FrontierSpec{} + case FRONTIER_THAWING: + // No changes for EVM spec + return nil + case HOMESTEAD: + return &HomesteadSpec{} + case DAO_FORK: + // No changes for EVM spec + return nil + case TANGERINE: + return &TangerineSpec{} + case SPURIOUS_DRAGON: + return &SpuriousDragonSpec{} + case BYZANTIUM: + return &ByzantiumSpec{} + case PETERSBURG: + return &PetersburgSpec{} + case ISTANBUL: + return &IstanbulSpec{} + case MUIR_GLACIER: + // No changes for EVM spec + return nil + case BERLIN: + return &BerlinSpec{} + case LONDON: + return &LondonSpec{} + case ARROW_GLACIER: + // No changes for EVM spec + return nil + case GRAY_GLACIER: + // No changes for EVM spec + return nil + case MERGE: + return &MergeSpec{} + case SHANGHAI: + return &ShanghaiSpec{} + case CANCUN: + return &CancunSpec{} + case PRAGUE: + return &PragueSpec{} + case PRAGUE_EOF: + return &PragueEofSpec{} + case LATEST: + return &LatestSpec{} + default: + return nil + } +} + +// Spec structures +type FrontierSpec struct{} +func (s *FrontierSpec) GetSpecID() SpecId { return FRONTIER } + +type HomesteadSpec struct{} +func (s *HomesteadSpec) GetSpecID() SpecId { return HOMESTEAD } + +type TangerineSpec struct{} +func (s *TangerineSpec) GetSpecID() SpecId { return TANGERINE } + +type SpuriousDragonSpec struct{} +func (s *SpuriousDragonSpec) GetSpecID() SpecId { return SPURIOUS_DRAGON } + +type ByzantiumSpec struct{} +func (s *ByzantiumSpec) GetSpecID() SpecId { return BYZANTIUM } + +type PetersburgSpec struct{} +func (s *PetersburgSpec) GetSpecID() SpecId { return PETERSBURG } + +type IstanbulSpec struct{} +func (s *IstanbulSpec) GetSpecID() SpecId { return ISTANBUL } + +type BerlinSpec struct{} +func (s *BerlinSpec) GetSpecID() SpecId { return BERLIN } + +type LondonSpec struct{} +func (s *LondonSpec) GetSpecID() SpecId { return LONDON } + +type MergeSpec struct{} +func (s *MergeSpec) GetSpecID() SpecId { return MERGE } + +type ShanghaiSpec struct{} +func (s *ShanghaiSpec) GetSpecID() SpecId { return SHANGHAI } + +type CancunSpec struct{} +func (s *CancunSpec) GetSpecID() SpecId { return CANCUN } + +type PragueSpec struct{} +func (s *PragueSpec) GetSpecID() SpecId { return PRAGUE } + +type PragueEofSpec struct{} +func (s *PragueEofSpec) GetSpecID() SpecId { return PRAGUE_EOF } + +type LatestSpec struct{} +func (s *LatestSpec) GetSpecID() SpecId { return LATEST } +*/ diff --git a/execution/evm/specs_optimism.go b/execution/evm/specs_optimism.go new file mode 100644 index 0000000..34b5c68 --- /dev/null +++ b/execution/evm/specs_optimism.go @@ -0,0 +1,282 @@ +//go:build optimism +// +build optimism + +package evm + +const isOptimismEnabled = true +import ( + "encoding/json" +) +type TxEnv struct { + // Caller aka Author aka transaction signer + Caller Address `json:"caller"` + // The gas limit of the transaction + GasLimit uint64 `json:"gas_limit"` + // The gas price of the transaction + GasPrice *big.Int `json:"gas_price"` + // The destination of the transaction + TransactTo TxKind `json:"transact_to"` + // The value sent to TransactTo + Value *big.Int `json:"value"` + // The data of the transaction + Data Bytes `json:"data"` + // The nonce of the transaction + // If nil, nonce validation against the account's nonce is skipped + Nonce *uint64 `json:"nonce,omitempty"` + // The chain ID of the transaction + // If nil, no checks are performed (EIP-155) + ChainID *uint64 `json:"chain_id,omitempty"` + // List of addresses and storage keys that the transaction plans to access (EIP-2930) + AccessList []AccessListItem `json:"access_list"` + // The priority fee per gas (EIP-1559) + GasPriorityFee *big.Int `json:"gas_priority_fee,omitempty"` + // The list of blob versioned hashes (EIP-4844) + BlobHashes []B256 `json:"blob_hashes"` + // The max fee per blob gas (EIP-4844) + MaxFeePerBlobGas *big.Int `json:"max_fee_per_blob_gas,omitempty"` + // List of authorizations for EOA account code (EIP-7702) + AuthorizationList *AuthorizationList `json:"authorization_list,omitempty"` + // Optimism fields (only included when build tag is set) + Optimism OptimismFields `json:"optimism,omitempty"` +} +func TryFromUint8(specID uint8) (SpecId, bool) { + if specID > uint8(PRAGUE_EOF) && specID != uint8(LATEST) { + return 0, false + } + return SpecId(specID), true +} +type SpecId uint8 + +const ( + FRONTIER SpecId = 0 // Frontier + FRONTIER_THAWING SpecId = 1 // Frontier Thawing + HOMESTEAD SpecId = 2 // Homestead + DAO_FORK SpecId = 3 // DAO Fork + TANGERINE SpecId = 4 // Tangerine Whistle + SPURIOUS_DRAGON SpecId = 5 // Spurious Dragon + BYZANTIUM SpecId = 6 // Byzantium + CONSTANTINOPLE SpecId = 7 // Constantinople + PETERSBURG SpecId = 8 // Petersburg + ISTANBUL SpecId = 9 // Istanbul + MUIR_GLACIER SpecId = 10 // Muir Glacier + BERLIN SpecId = 11 // Berlin + LONDON SpecId = 12 // London + ARROW_GLACIER SpecId = 13 // Arrow Glacier + GRAY_GLACIER SpecId = 14 // Gray Glacier + MERGE SpecId = 15 // Paris/Merge + BEDROCK SpecId = 16 // Bedrock + REGOLITH SpecId = 17 // Regolith + SHANGHAI SpecId = 18 // Shanghai + CANYON SpecId = 19 // Canyon + CANCUN SpecId = 20 // Cancun + ECOTONE SpecId = 21 // Ecotone + FJORD SpecId = 22 // Fjord + PRAGUE SpecId = 23 // Prague + PRAGUE_EOF SpecId = 24 // Prague+EOF + LATEST SpecId = 255 // LATEST = u8::MAX +) + +// Default value for SpecId +func DefaultSpecId() SpecId { + return LATEST +} + +// String method to convert SpecId to string +func (s SpecId) String() string { + switch s { + case FRONTIER: + return "FRONTIER" + case FRONTIER_THAWING: + return "FRONTIER_THAWING" + case HOMESTEAD: + return "HOMESTEAD" + case DAO_FORK: + return "DAO_FORK" + case TANGERINE: + return "TANGERINE" + case SPURIOUS_DRAGON: + return "SPURIOUS_DRAGON" + case BYZANTIUM: + return "BYZANTIUM" + case CONSTANTINOPLE: + return "CONSTANTINOPLE" + case PETERSBURG: + return "PETERSBURG" + case ISTANBUL: + return "ISTANBUL" + case MUIR_GLACIER: + return "MUIR_GLACIER" + case BERLIN: + return "BERLIN" + case LONDON: + return "LONDON" + case ARROW_GLACIER: + return "ARROW_GLACIER" + case GRAY_GLACIER: + return "GRAY_GLACIER" + case MERGE: + return "MERGE" + case BEDROCK: + return "BEDROCK" + case REGOLITH: + return "REGOLITH" + case SHANGHAI: + return "SHANGHAI" + case CANYON: + return "CANYON" + case CANCUN: + return "CANCUN" + case ECOTONE: + return "ECOTONE" + case FJORD: + return "FJORD" + case PRAGUE: + return "PRAGUE" + case PRAGUE_EOF: + return "PRAGUE_EOF" + default: + return "UNKNOWN" + } +} +type ( + FrontierSpec struct{ BaseSpec } + FrontierThawingSpec struct{ BaseSpec } + HomesteadSpec struct{ BaseSpec } + DaoForkSpec struct{ BaseSpec } + TangerineSpec struct{ BaseSpec } + SpuriousDragonSpec struct{ BaseSpec } + ByzantiumSpec struct{ BaseSpec } + ConstantinopleSpec struct{ BaseSpec } + PetersburgSpec struct{ BaseSpec } + IstanbulSpec struct{ BaseSpec } + MuirGlacierSpec struct{ BaseSpec } + BerlinSpec struct{ BaseSpec } + LondonSpec struct{ BaseSpec } + ArrowGlacierSpec struct{ BaseSpec } + GrayGlacierSpec struct{ BaseSpec } + MergeSpec struct{ BaseSpec } + BedrockSpec struct{ BaseSpec } + RegolithSpec struct{ BaseSpec } + ShanghaiSpec struct{ BaseSpec } + CanyonSpec struct{ BaseSpec } + CancunSpec struct{ BaseSpec } + EcotoneSpec struct{ BaseSpec } + FjordSpec struct{ BaseSpec } + PragueSpec struct{ BaseSpec } + PragueEofSpec struct{ BaseSpec } + LatestSpec struct{ BaseSpec } +) +// MarshalJSON for serialization +func (s SpecId) MarshalJSON() ([]byte, error) { + return json.Marshal(s.String()) +} + + + +func SpecToGeneric(specID SpecId) interface{} { + switch specID { + case FRONTIER, FRONTIER_THAWING: + return FrontierSpec{NewSpec(FRONTIER)} + case HOMESTEAD, DAO_FORK: + return HomesteadSpec{NewSpec(HOMESTEAD)} + case TANGERINE: + return TangerineSpec{NewSpec(TANGERINE)} + case SPURIOUS_DRAGON: + return SpuriousDragonSpec{NewSpec(SPURIOUS_DRAGON)} + case BYZANTIUM: + return ByzantiumSpec{NewSpec(BYZANTIUM)} + case PETERSBURG, CONSTANTINOPLE: + return PetersburgSpec{NewSpec(PETERSBURG)} + case ISTANBUL, MUIR_GLACIER: + return IstanbulSpec{NewSpec(ISTANBUL)} + case BERLIN: + return BerlinSpec{NewSpec(BERLIN)} + case LONDON, ARROW_GLACIER, GRAY_GLACIER: + return LondonSpec{NewSpec(LONDON)} + case MERGE: + return MergeSpec{NewSpec(MERGE)} + case SHANGHAI: + return ShanghaiSpec{NewSpec(SHANGHAI)} + case CANCUN: + return CancunSpec{NewSpec(CANCUN)} + case PRAGUE: + return PragueSpec{NewSpec(PRAGUE)} + case PRAGUE_EOF: + return PragueEofSpec{NewSpec(PRAGUE_EOF)} + case BEDROCK: + return BedrockSpec{NewSpec(BEDROCK)} + case REGOLITH: + return RegolithSpec{NewSpec(REGOLITH)} + case CANYON: + return CanyonSpec{NewSpec(CANYON)} + case ECOTONE: + return EcotoneSpec{NewSpec(ECOTONE)} + case FJORD: + return FjordSpec{NewSpec(FJORD)} + default: + return LatestSpec{NewSpec(LATEST)} + } +} + +func (s *SpecId) UnmarshalJSON(data []byte) error { + var name string + if err := json.Unmarshal(data, &name); err != nil { + return err + } + + switch name { + case "FRONTIER": + *s = FRONTIER + case "FRONTIER_THAWING": + *s = FRONTIER_THAWING + case "HOMESTEAD": + *s = HOMESTEAD + case "DAO_FORK": + *s = DAO_FORK + case "TANGERINE": + *s = TANGERINE + case "SPURIOUS_DRAGON": + *s = SPURIOUS_DRAGON + case "BYZANTIUM": + *s = BYZANTIUM + case "CONSTANTINOPLE": + *s = CONSTANTINOPLE + case "PETERSBURG": + *s = PETERSBURG + case "ISTANBUL": + *s = ISTANBUL + case "MUIR_GLACIER": + *s = MUIR_GLACIER + case "BERLIN": + *s = BERLIN + case "LONDON": + *s = LONDON + case "ARROW_GLACIER": + *s = ARROW_GLACIER + case "GRAY_GLACIER": + *s = GRAY_GLACIER + case "MERGE": + *s = MERGE + case "SHANGHAI": + *s = SHANGHAI + case "CANCUN": + *s = CANCUN + case "PRAGUE": + *s = PRAGUE + case "PRAGUE_EOF": + *s = PRAGUE_EOF + case "BEDROCK": + *s = BEDROCK + case "REGOLITH": + *s = REGOLITH + case "CANYON": + *s = CANYON + case "ECOTONE": + *s = ECOTONE + case "FJORD": + *s = FJORD + default: + return fmt.Errorf("unknown SpecId: %s", name) + } + return nil +} \ No newline at end of file diff --git a/execution/evm/tables.go b/execution/evm/tables.go new file mode 100644 index 0000000..4e35584 --- /dev/null +++ b/execution/evm/tables.go @@ -0,0 +1,35 @@ +package evm +// Instruction is a function type that takes an Interpreter and a generic type H. +type Instruction[H any] func(interpreter *Interpreter, h *H) + +// InstructionTable is a list of 256 instructions mapped to EVM opcodes. +type InstructionTable[H any] [256]Instruction[H] + +// DynInstruction is a function type signature for dynamic instructions. +type DynInstruction[H any] func(interpreter *Interpreter, h *H) + +// BoxedInstruction wraps a DynInstruction in a pointer, enabling dynamic dispatch. +type BoxedInstruction[H any] *DynInstruction[H] + +// BoxedInstructionTable is an array of 256 boxed instructions. +type BoxedInstructionTable[H any] [256]BoxedInstruction[H] + +const ( + PlainTableMode = iota + BoxedTableMode +) +type InstructionTables[H any] struct { + PlainTable *InstructionTable[H] + BoxedTable *BoxedInstructionTable[H] + Mode int // Indicates which table is in use (Plain or Boxed) +} +//Mode contains 0 and 1 +// NewPlainInstructionTable creates an InstructionTables instance with a PlainTable. +func NewPlainInstructionTable[H any](table InstructionTable[H]) InstructionTables[H] { + return InstructionTables[H]{PlainTable: &table} +} + +// NewBoxedInstructionTable creates an InstructionTables instance with a BoxedTable. +func NewBoxedInstructionTable[H any](table BoxedInstructionTable[H]) InstructionTables[H] { + return InstructionTables[H]{BoxedTable: &table} +}