From bbb3cc978fbe40549e4e496261af6ddfd54d486e Mon Sep 17 00:00:00 2001 From: Alexandr Borodulin <sashaborodulin@gmail.com> Date: Mon, 6 Dec 2021 17:58:53 +0300 Subject: [PATCH] Starknet getcode (#3038) * deploy_cairo_smartcontract * deploy_cairo_smartcontract / 2 Add new transaction type for cairo and vm factory * starknet_getcode * deploy_cairo_smartcontract / 3 * deploy_cairo_smartcontract / 4 * deploy_cairo_smartcontract / 5 Co-authored-by: Aleksandr Borodulin <a.borodulin@axioma.lv> --- cmd/evm/internal/t8ntool/execution.go | 2 +- cmd/rpcdaemon/commands/daemon.go | 8 + cmd/rpcdaemon/commands/erigon_api.go | 1 - cmd/rpcdaemon/commands/starknet_accounts.go | 38 ++ cmd/rpcdaemon/commands/starknet_api.go | 30 ++ .../commands/starknet_send_transaction.go | 47 ++ cmd/rpcdaemon/commands/trace_adhoc.go | 10 +- cmd/starknet/README.md | 19 + cmd/starknet/cmd/generate_raw_tx.go | 64 +++ cmd/starknet/cmd/root.go | 19 + cmd/starknet/main.go | 7 + cmd/starknet/services/raw_tx_generator.go | 67 +++ .../services/raw_tx_generator_test.go | 103 +++++ cmd/state/commands/opcode_tracer.go | 2 +- core/state_processor.go | 18 +- core/state_transition.go | 34 +- core/types/access_list_tx.go | 4 + core/types/cairo_tx.go | 433 ++++++++++++++++++ core/types/dynamic_fee_tx.go | 5 +- core/types/legacy_tx.go | 8 + core/types/transaction.go | 16 + core/types/transaction_signing.go | 7 + core/vm/cvm.go | 40 ++ core/vm/cvm_adapter.go | 50 ++ core/vm/cvm_test.go | 1 + core/vm/eips.go | 6 +- core/vm/evm.go | 197 ++++---- core/vm/gas_table.go | 58 +-- core/vm/gas_table_test.go | 2 +- core/vm/instructions.go | 60 +-- core/vm/instructions_test.go | 10 +- core/vm/interface.go | 12 + core/vm/interpreter.go | 34 +- core/vm/logger.go | 6 +- core/vm/logger_json.go | 2 +- core/vm/operations_acl.go | 44 +- core/vm/runtime/runtime.go | 6 +- eth/tracers/tracer.go | 12 +- tests/state_test_util.go | 4 +- turbo/transactions/tracing.go | 4 +- 40 files changed, 1248 insertions(+), 242 deletions(-) create mode 100644 cmd/rpcdaemon/commands/starknet_accounts.go create mode 100644 cmd/rpcdaemon/commands/starknet_api.go create mode 100644 cmd/rpcdaemon/commands/starknet_send_transaction.go create mode 100644 cmd/starknet/README.md create mode 100644 cmd/starknet/cmd/generate_raw_tx.go create mode 100644 cmd/starknet/cmd/root.go create mode 100644 cmd/starknet/main.go create mode 100644 cmd/starknet/services/raw_tx_generator.go create mode 100644 cmd/starknet/services/raw_tx_generator_test.go create mode 100644 core/types/cairo_tx.go create mode 100644 core/vm/cvm.go create mode 100644 core/vm/cvm_adapter.go create mode 100644 core/vm/cvm_test.go diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 0a9d075b4a..620767ce5a 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -200,7 +200,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, // If the transaction created a contract, store the creation address in the receipt. if msg.To() == nil { - receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, txn.GetNonce()) + receipt.ContractAddress = crypto.CreateAddress(evm.TxContext().Origin, txn.GetNonce()) } // Set the receipt logs and create a bloom for filtering diff --git a/cmd/rpcdaemon/commands/daemon.go b/cmd/rpcdaemon/commands/daemon.go index c15a534de9..67974fa681 100644 --- a/cmd/rpcdaemon/commands/daemon.go +++ b/cmd/rpcdaemon/commands/daemon.go @@ -27,6 +27,7 @@ func APIList(ctx context.Context, db kv.RoDB, } ethImpl := NewEthAPI(base, db, eth, txPool, mining, cfg.Gascap) erigonImpl := NewErigonAPI(base, db, eth) + starknetImpl := NewStarknetAPI(base, db, txPool) txpoolImpl := NewTxPoolAPI(base, db, txPool) netImpl := NewNetAPIImpl(eth) debugImpl := NewPrivateDebugAPI(base, db, cfg.Gascap) @@ -94,6 +95,13 @@ func APIList(ctx context.Context, db kv.RoDB, Service: ErigonAPI(erigonImpl), Version: "1.0", }) + case "starknet": + defaultAPIList = append(defaultAPIList, rpc.API{ + Namespace: "starknet", + Public: true, + Service: StarknetAPI(starknetImpl), + Version: "1.0", + }) case "engine": defaultAPIList = append(defaultAPIList, rpc.API{ Namespace: "engine", diff --git a/cmd/rpcdaemon/commands/erigon_api.go b/cmd/rpcdaemon/commands/erigon_api.go index c95d96312b..b43dc393b4 100644 --- a/cmd/rpcdaemon/commands/erigon_api.go +++ b/cmd/rpcdaemon/commands/erigon_api.go @@ -2,7 +2,6 @@ package commands import ( "context" - "github.com/ledgerwatch/erigon-lib/kv" "github.com/ledgerwatch/erigon/cmd/rpcdaemon/services" "github.com/ledgerwatch/erigon/common" diff --git a/cmd/rpcdaemon/commands/starknet_accounts.go b/cmd/rpcdaemon/commands/starknet_accounts.go new file mode 100644 index 0000000000..0c456316cb --- /dev/null +++ b/cmd/rpcdaemon/commands/starknet_accounts.go @@ -0,0 +1,38 @@ +package commands + +import ( + "context" + "fmt" + "github.com/ledgerwatch/erigon/common" + "github.com/ledgerwatch/erigon/common/hexutil" + "github.com/ledgerwatch/erigon/rpc" + "github.com/ledgerwatch/erigon/turbo/adapter" + "github.com/ledgerwatch/erigon/turbo/rpchelper" +) + +// GetCode implements starknet_getCode. Returns the byte code at a given address (if it's a smart contract). +func (api *StarknetImpl) GetCode(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) { + tx, err1 := api.db.BeginRo(ctx) + if err1 != nil { + return nil, fmt.Errorf("getCode cannot open tx: %w", err1) + } + defer tx.Rollback() + blockNumber, _, err := rpchelper.GetBlockNumber(blockNrOrHash, tx, api.filters) + if err != nil { + return nil, err + } + + reader := adapter.NewStateReader(tx, blockNumber) + acc, err := reader.ReadAccountData(address) + if acc == nil || err != nil { + return hexutil.Bytes(""), nil + } + res, err := reader.ReadAccountCode(address, acc.Incarnation, acc.CodeHash) + if res == nil || err != nil { + return hexutil.Bytes(""), nil + } + if res == nil { + return hexutil.Bytes(""), nil + } + return res, nil +} diff --git a/cmd/rpcdaemon/commands/starknet_api.go b/cmd/rpcdaemon/commands/starknet_api.go new file mode 100644 index 0000000000..7e598881e2 --- /dev/null +++ b/cmd/rpcdaemon/commands/starknet_api.go @@ -0,0 +1,30 @@ +package commands + +import ( + "context" + "github.com/ledgerwatch/erigon-lib/gointerfaces/txpool" + "github.com/ledgerwatch/erigon/common/hexutil" + "github.com/ledgerwatch/erigon/rpc" + + "github.com/ledgerwatch/erigon-lib/kv" + "github.com/ledgerwatch/erigon/common" +) + +type StarknetAPI interface { + SendRawTransaction(ctx context.Context, encodedTx hexutil.Bytes) (common.Hash, error) + GetCode(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) +} + +type StarknetImpl struct { + *BaseAPI + txPool txpool.TxpoolClient + db kv.RoDB +} + +func NewStarknetAPI(base *BaseAPI, db kv.RoDB, txPool txpool.TxpoolClient) *StarknetImpl { + return &StarknetImpl{ + BaseAPI: base, + db: db, + txPool: txPool, + } +} diff --git a/cmd/rpcdaemon/commands/starknet_send_transaction.go b/cmd/rpcdaemon/commands/starknet_send_transaction.go new file mode 100644 index 0000000000..8a8aa73edd --- /dev/null +++ b/cmd/rpcdaemon/commands/starknet_send_transaction.go @@ -0,0 +1,47 @@ +package commands + +import ( + "bytes" + "context" + "errors" + "fmt" + txPoolProto "github.com/ledgerwatch/erigon-lib/gointerfaces/txpool" + "github.com/ledgerwatch/erigon/common" + "github.com/ledgerwatch/erigon/common/hexutil" + "github.com/ledgerwatch/erigon/core/types" + "github.com/ledgerwatch/erigon/rlp" +) + +var ( + ErrOnlyStarknetTx = errors.New("only support starknet transactions") + ErrOnlyContractDeploy = errors.New("only support contract creation") +) + +// SendRawTransaction deploy new cairo contract +func (api *StarknetImpl) SendRawTransaction(ctx context.Context, encodedTx hexutil.Bytes) (common.Hash, error) { + txn, err := types.DecodeTransaction(rlp.NewStream(bytes.NewReader(encodedTx), uint64(len(encodedTx)))) + + if err != nil { + return common.Hash{}, err + } + + if !txn.IsStarkNet() { + return common.Hash{}, ErrOnlyStarknetTx + } + + if !txn.IsContractDeploy() { + return common.Hash{}, ErrOnlyContractDeploy + } + + hash := txn.Hash() + res, err := api.txPool.Add(ctx, &txPoolProto.AddRequest{RlpTxs: [][]byte{encodedTx}}) + if err != nil { + return common.Hash{}, err + } + + if res.Imported[0] != txPoolProto.ImportResult_SUCCESS { + return hash, fmt.Errorf("%s: %s", txPoolProto.ImportResult_name[int32(res.Imported[0])], res.Errors[0]) + } + + return common.Hash{}, err +} diff --git a/cmd/rpcdaemon/commands/trace_adhoc.go b/cmd/rpcdaemon/commands/trace_adhoc.go index c12562c717..a8b6ff71f9 100644 --- a/cmd/rpcdaemon/commands/trace_adhoc.go +++ b/cmd/rpcdaemon/commands/trace_adhoc.go @@ -959,7 +959,7 @@ func (api *TraceAPIImpl) Call(ctx context.Context, args TraceCallParam, traceTyp sdMap := make(map[common.Address]*StateDiffAccount) traceResult.StateDiff = sdMap sd := &StateDiff{sdMap: sdMap} - if err = ibs.FinalizeTx(evm.ChainRules, sd); err != nil { + if err = ibs.FinalizeTx(evm.ChainRules(), sd); err != nil { return nil, err } // Create initial IntraBlockState, we will compare it with ibs (IntraBlockState after the transaction) @@ -1182,18 +1182,18 @@ func (api *TraceAPIImpl) doCallMany(ctx context.Context, dbtx kv.Tx, msgs []type sdMap := make(map[common.Address]*StateDiffAccount) traceResult.StateDiff = sdMap sd := &StateDiff{sdMap: sdMap} - if err = ibs.FinalizeTx(evm.ChainRules, sd); err != nil { + if err = ibs.FinalizeTx(evm.ChainRules(), sd); err != nil { return nil, err } sd.CompareStates(initialIbs, ibs) - if err = ibs.CommitBlock(evm.ChainRules, cachedWriter); err != nil { + if err = ibs.CommitBlock(evm.ChainRules(), cachedWriter); err != nil { return nil, err } } else { - if err = ibs.FinalizeTx(evm.ChainRules, noop); err != nil { + if err = ibs.FinalizeTx(evm.ChainRules(), noop); err != nil { return nil, err } - if err = ibs.CommitBlock(evm.ChainRules, cachedWriter); err != nil { + if err = ibs.CommitBlock(evm.ChainRules(), cachedWriter); err != nil { return nil, err } } diff --git a/cmd/starknet/README.md b/cmd/starknet/README.md new file mode 100644 index 0000000000..7d248d45f4 --- /dev/null +++ b/cmd/starknet/README.md @@ -0,0 +1,19 @@ +# How to deploy cairo smart contract + +1. Compile cairo smart contract + + `starknet-compile contract.cairo --output contract_compiled.json --abi contract_abi.json` + + +2. Generate payload for `starknet_sendRawTransaction` PRC method + + `go run ./cmd/starknet/main.go generateRawTx -c ./cairo/contract.json -o /cairo/send_raw_transaction -k b9a8b19ff082a7f4b943fcbe0da6cce6ce2c860090f05d031f463412ab534e95` + + Command syntax: `go run main.go generateRawTx --help` + + +3. Use command output in RPC call + +```json +"params":["0x03f86583127ed80180800180019637623232363136323639323233613230356235643764c080a0b44c2f4e18ca27e621171da5cf3a0c875c0749c7b998ec2759974280d987143aa04f01823122d972baa1a03b113535d9f9057fd9366fd8770e766b91f835b88ea6"], +``` diff --git a/cmd/starknet/cmd/generate_raw_tx.go b/cmd/starknet/cmd/generate_raw_tx.go new file mode 100644 index 0000000000..67a8277c78 --- /dev/null +++ b/cmd/starknet/cmd/generate_raw_tx.go @@ -0,0 +1,64 @@ +package cmd + +import ( + "bytes" + "encoding/hex" + "fmt" + "github.com/ledgerwatch/erigon/cmd/starknet/services" + "github.com/spf13/cobra" + "os" + "strings" +) + +type Flags struct { + Contract string + PrivateKey string + Output string +} + +var generateRawTxCmd = &cobra.Command{ + Use: "generateRawTx", + Short: "Generate data for starknet_sendRawTransaction RPC method", +} + +func init() { + flags := Flags{} + + generateRawTxCmd.Flags().StringVarP(&flags.Contract, "contract", "c", "", "Path to compiled cairo contract in JSON format") + generateRawTxCmd.MarkFlagRequired("contract") + + generateRawTxCmd.Flags().StringVarP(&flags.PrivateKey, "private_key", "k", "", "Private key") + generateRawTxCmd.MarkFlagRequired("private_key") + + generateRawTxCmd.Flags().StringVarP(&flags.Output, "output", "o", "", "Path to file where sign transaction will be saved") + + generateRawTxCmd.RunE = func(cmd *cobra.Command, args []string) error { + rawTxGenerator := services.NewRawTxGenerator(flags.PrivateKey) + + fs := os.DirFS("/") + buf := bytes.NewBuffer(nil) + err := rawTxGenerator.CreateFromFS(fs, strings.Trim(flags.Contract, "/"), buf) + if err != nil { + return err + } + + if flags.Output != "" { + outputFile, err := os.Create(flags.Output) + if err != nil { + return fmt.Errorf("could not create output file: %v", flags.Output) + } + defer outputFile.Close() + + _, err = outputFile.WriteString(hex.EncodeToString(buf.Bytes())) + if err != nil { + return fmt.Errorf("could not write to output file: %v", flags.Output) + } + } else { + fmt.Println(hex.EncodeToString(buf.Bytes())) + } + + return err + } + + rootCmd.AddCommand(generateRawTxCmd) +} diff --git a/cmd/starknet/cmd/root.go b/cmd/starknet/cmd/root.go new file mode 100644 index 0000000000..a7d1d3ab35 --- /dev/null +++ b/cmd/starknet/cmd/root.go @@ -0,0 +1,19 @@ +package cmd + +import ( + "github.com/spf13/cobra" +) + +// rootCmd represents the base command when called without any subcommands +var rootCmd = &cobra.Command{ + Use: "starknet", + Short: "Starknet cli commands", +} + +func Execute() { + cobra.CheckErr(rootCmd.Execute()) +} + +func init() { + cobra.OnInitialize() +} diff --git a/cmd/starknet/main.go b/cmd/starknet/main.go new file mode 100644 index 0000000000..47a140a703 --- /dev/null +++ b/cmd/starknet/main.go @@ -0,0 +1,7 @@ +package main + +import "github.com/ledgerwatch/erigon/cmd/starknet/cmd" + +func main() { + cmd.Execute() +} diff --git a/cmd/starknet/services/raw_tx_generator.go b/cmd/starknet/services/raw_tx_generator.go new file mode 100644 index 0000000000..8c528c6567 --- /dev/null +++ b/cmd/starknet/services/raw_tx_generator.go @@ -0,0 +1,67 @@ +package services + +import ( + "encoding/hex" + "errors" + "github.com/holiman/uint256" + "github.com/ledgerwatch/erigon/core/types" + "github.com/ledgerwatch/erigon/crypto" + "github.com/ledgerwatch/erigon/params" + "golang.org/x/crypto/sha3" + "io" + "io/fs" +) + +var ( + ErrReadContract = errors.New("contract read error") + ErrInvalidPrivateKey = errors.New("invalid private key") +) + +func NewRawTxGenerator(privateKey string) RawTxGenerator { + return RawTxGenerator{ + privateKey: privateKey, + } +} + +type RawTxGenerator struct { + privateKey string +} + +func (g RawTxGenerator) CreateFromFS(fileSystem fs.FS, contractFileName string, writer io.Writer) error { + privateKey, err := crypto.HexToECDSA(g.privateKey) + if err != nil { + return ErrInvalidPrivateKey + } + + contract, err := fs.ReadFile(fileSystem, contractFileName) + if err != nil { + return ErrReadContract + } + + enc := make([]byte, hex.EncodedLen(len(contract))) + hex.Encode(enc, contract) + + tx := types.CairoTransaction{ + CommonTx: types.CommonTx{ + Nonce: 1, + Value: uint256.NewInt(1), + Gas: 1, + Data: enc, + }, + } + + signature, _ := crypto.Sign(sha3.New256().Sum(nil), privateKey) + signer := types.MakeSigner(params.FermionChainConfig, 1) + + signedTx, err := tx.WithSignature(*signer, signature) + if err != nil { + return err + } + + err = signedTx.MarshalBinary(writer) + if err != nil { + return errors.New("can not save signed tx") + } + + return nil +} diff --git a/cmd/starknet/services/raw_tx_generator_test.go b/cmd/starknet/services/raw_tx_generator_test.go new file mode 100644 index 0000000000..bd912a3462 --- /dev/null +++ b/cmd/starknet/services/raw_tx_generator_test.go @@ -0,0 +1,103 @@ +package services + +import ( + "bytes" + "encoding/hex" + "github.com/ledgerwatch/erigon/crypto" + "testing" + "testing/fstest" +) + +func TestCreate(t *testing.T) { + privateKey := "26e86e45f6fc45ec6e2ecd128cec80fa1d1505e5507dcd2ae58c3130a7a97b48" + + var cases = []struct { + name string + privateKey string + fileName string + want string + }{ + {name: "success", privateKey: privateKey, fileName: "contract_test.json", want: "03f86583127ed80180800180019637623232363136323639323233613230356235643764c080a0ceb955e6039bf37dbf77e4452a10b4a47906bbbd2f6dcf0c15bccb052d3bbb60a03de24d584a0a20523f55a137ebc651e2b092fbc3728d67c9fda09da9f0edd154"}, + } + + fs := fstest.MapFS{ + "contract_test.json": {Data: []byte("{\"abi\": []}")}, + } + + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + rawTxGenerator := RawTxGenerator{ + privateKey: tt.privateKey, + } + + buf := bytes.NewBuffer(nil) + err := rawTxGenerator.CreateFromFS(fs, tt.fileName, buf) + + assertNoError(t, err) + + if hex.EncodeToString(buf.Bytes()) != tt.want { + t.Error("got not equals want") + } + }) + } +} + +func TestErrorCreate(t *testing.T) { + var cases = []struct { + name string + privateKey string + fileName string + error error + }{ + {name: "invalid private key", privateKey: "abc", fileName: "not_exist.json", error: ErrInvalidPrivateKey}, + {name: "contract file not found", privateKey: generatePrivateKey(t), fileName: "not_exist.json", error: ErrReadContract}, + } + + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + fs := fstest.MapFS{} + + rawTxGenerator := RawTxGenerator{ + privateKey: tt.privateKey, + } + + buf := bytes.NewBuffer(nil) + err := rawTxGenerator.CreateFromFS(fs, tt.fileName, buf) + + if tt.error != nil { + assertError(t, err, tt.error) + } + }) + } +} + +func generatePrivateKey(t testing.TB) string { + t.Helper() + + privateKey, err := crypto.GenerateKey() + if err != nil { + t.Error(err) + } + + return hex.EncodeToString(crypto.FromECDSA(privateKey)) +} + +func assertNoError(t testing.TB, got error) { + t.Helper() + + if got != nil { + t.Fatal("got an error but didn't want one") + } +} + +func assertError(t testing.TB, got error, want error) { + t.Helper() + + if got == nil { + t.Fatal("didn't get an error but wanted one") + } + + if got != want { + t.Errorf("got %q, want %q", got, want) + } +} diff --git a/cmd/state/commands/opcode_tracer.go b/cmd/state/commands/opcode_tracer.go index e7cdebbda2..e14c848d8f 100644 --- a/cmd/state/commands/opcode_tracer.go +++ b/cmd/state/commands/opcode_tracer.go @@ -228,7 +228,7 @@ func (ot *opcodeTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, } pc16 := uint16(pc) - currentTxHash := env.TxHash + currentTxHash := env.TxContext().TxHash currentTxDepth := opDepth - 1 ls := len(ot.stack) diff --git a/core/state_processor.go b/core/state_processor.go index 0a2fdca0a5..691edd4755 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -84,7 +84,7 @@ func FormatLogs(logs []vm.StructLog) []StructLogRes { // and uses the input parameters for its environment. It returns the receipt // for the transaction, gas used and an error if the transaction failed, // indicating the block was invalid. -func applyTransaction(config *params.ChainConfig, gp *GasPool, statedb *state.IntraBlockState, stateWriter state.StateWriter, header *types.Header, tx types.Transaction, usedGas *uint64, evm *vm.EVM, cfg vm.Config) (*types.Receipt, []byte, error) { +func applyTransaction(config *params.ChainConfig, gp *GasPool, statedb *state.IntraBlockState, stateWriter state.StateWriter, header *types.Header, tx types.Transaction, usedGas *uint64, evm vm.VMInterface, cfg vm.Config) (*types.Receipt, []byte, error) { msg, err := tx.AsMessage(*types.MakeSigner(config, header.Number.Uint64()), header.BaseFee) if err != nil { return nil, nil, err @@ -103,7 +103,7 @@ func applyTransaction(config *params.ChainConfig, gp *GasPool, statedb *state.In return nil, nil, err } // Update the state with pending changes - if err = statedb.FinalizeTx(evm.ChainRules, stateWriter); err != nil { + if err = statedb.FinalizeTx(evm.ChainRules(), stateWriter); err != nil { return nil, nil, err } @@ -124,7 +124,7 @@ func applyTransaction(config *params.ChainConfig, gp *GasPool, statedb *state.In receipt.GasUsed = result.UsedGas // if the transaction created a contract, store the creation address in the receipt. if msg.To() == nil { - receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.GetNonce()) + receipt.ContractAddress = crypto.CreateAddress(evm.TxContext().Origin, tx.GetNonce()) } // Set the receipt logs and create a bloom for filtering receipt.Logs = statedb.GetLogs(tx.Hash()) @@ -141,8 +141,16 @@ func applyTransaction(config *params.ChainConfig, gp *GasPool, statedb *state.In // indicating the block was invalid. func ApplyTransaction(config *params.ChainConfig, getHeader func(hash common.Hash, number uint64) *types.Header, engine consensus.Engine, author *common.Address, gp *GasPool, ibs *state.IntraBlockState, stateWriter state.StateWriter, header *types.Header, tx types.Transaction, usedGas *uint64, cfg vm.Config, contractHasTEVM func(contractHash common.Hash) (bool, error)) (*types.Receipt, []byte, error) { // Create a new context to be used in the EVM environment - blockContext := NewEVMBlockContext(header, getHeader, engine, author, contractHasTEVM) - vmenv := vm.NewEVM(blockContext, vm.TxContext{}, ibs, config, cfg) + + var vmenv vm.VMInterface + + if tx.IsStarkNet() { + vmenv = &vm.CVMAdapter{Cvm: vm.NewCVM(ibs)} + } else { + blockContext := NewEVMBlockContext(header, getHeader, engine, author, contractHasTEVM) + vmenv = vm.NewEVM(blockContext, vm.TxContext{}, ibs, config, cfg) + } + // Add addresses to access list if applicable // about the transaction and calling mechanisms. cfg.SkipAnalysis = SkipAnalysis(config, header.Number.Uint64()) diff --git a/core/state_transition.go b/core/state_transition.go index 58bf689ebd..f6b16e7f6e 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -60,7 +60,7 @@ type StateTransition struct { value *uint256.Int data []byte state vm.IntraBlockState - evm *vm.EVM + evm vm.VMInterface //some pre-allocated intermediate variables sharedBuyGas *uint256.Int @@ -161,7 +161,7 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation b } // NewStateTransition initialises and returns a new state transition object. -func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition { +func NewStateTransition(evm vm.VMInterface, msg Message, gp *GasPool) *StateTransition { return &StateTransition{ gp: gp, evm: evm, @@ -171,7 +171,7 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition tip: msg.Tip(), value: msg.Value(), data: msg.Data(), - state: evm.IntraBlockState, + state: evm.IntraBlockState(), sharedBuyGas: uint256.NewInt(0), sharedBuyGasBalance: uint256.NewInt(0), @@ -188,7 +188,7 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition // `refunds` is false when it is not required to apply gas refunds // `gasBailout` is true when it is not required to fail transaction if the balance is not enough to pay gas. // for trace_call to replicate OE/Pariry behaviour -func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool, refunds bool, gasBailout bool) (*ExecutionResult, error) { +func ApplyMessage(evm vm.VMInterface, msg Message, gp *GasPool, refunds bool, gasBailout bool) (*ExecutionResult, error) { return NewStateTransition(evm, msg, gp).TransitionDb(refunds, gasBailout) } @@ -261,9 +261,9 @@ func (st *StateTransition) preCheck(gasBailout bool) error { } // Make sure the transaction gasFeeCap is greater than the block's baseFee. - if st.evm.ChainRules.IsLondon { + if st.evm.ChainRules().IsLondon { // Skip the checks if gas fields are zero and baseFee was explicitly disabled (eth_call) - if !st.evm.Config.NoBaseFee || !st.gasFeeCap.IsZero() || !st.tip.IsZero() { + if !st.evm.Config().NoBaseFee || !st.gasFeeCap.IsZero() || !st.tip.IsZero() { if l := st.gasFeeCap.BitLen(); l > 256 { return fmt.Errorf("%w: address %v, gasFeeCap bit length: %d", ErrFeeCapVeryHigh, st.msg.From().Hex(), l) @@ -276,9 +276,9 @@ func (st *StateTransition) preCheck(gasBailout bool) error { return fmt.Errorf("%w: address %v, tip: %s, gasFeeCap: %s", ErrTipAboveFeeCap, st.msg.From().Hex(), st.gasFeeCap, st.tip) } - if st.gasFeeCap.Cmp(st.evm.Context.BaseFee) < 0 { + if st.gasFeeCap.Cmp(st.evm.Context().BaseFee) < 0 { return fmt.Errorf("%w: address %v, gasFeeCap: %d baseFee: %d", ErrFeeCapTooLow, - st.msg.From().Hex(), st.gasFeeCap.Uint64(), st.evm.Context.BaseFee.Uint64()) + st.msg.From().Hex(), st.gasFeeCap.Uint64(), st.evm.Context().BaseFee.Uint64()) } } } @@ -315,9 +315,9 @@ func (st *StateTransition) TransitionDb(refunds bool, gasBailout bool) (*Executi } msg := st.msg sender := vm.AccountRef(msg.From()) - homestead := st.evm.ChainRules.IsHomestead - istanbul := st.evm.ChainRules.IsIstanbul - london := st.evm.ChainRules.IsLondon + homestead := st.evm.ChainRules().IsHomestead + istanbul := st.evm.ChainRules().IsIstanbul + london := st.evm.ChainRules().IsLondon contractCreation := msg.To() == nil // Check clauses 4-5, subtract intrinsic gas if everything is correct @@ -333,14 +333,14 @@ func (st *StateTransition) TransitionDb(refunds bool, gasBailout bool) (*Executi var bailout bool // Gas bailout (for trace_call) should only be applied if there is not sufficient balance to perform value transfer if gasBailout { - if !msg.Value().IsZero() && !st.evm.Context.CanTransfer(st.state, msg.From(), msg.Value()) { + if !msg.Value().IsZero() && !st.evm.Context().CanTransfer(st.state, msg.From(), msg.Value()) { bailout = true } } // Set up the initial access list. - if st.evm.ChainRules.IsBerlin { - st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(st.evm.ChainRules), msg.AccessList()) + if st.evm.ChainRules().IsBerlin { + st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(st.evm.ChainRules()), msg.AccessList()) } var ( @@ -368,10 +368,10 @@ func (st *StateTransition) TransitionDb(refunds bool, gasBailout bool) (*Executi } } effectiveTip := st.gasPrice - if st.evm.ChainRules.IsLondon { - effectiveTip = cmath.Min256(st.tip, new(uint256.Int).Sub(st.gasFeeCap, st.evm.Context.BaseFee)) + if st.evm.ChainRules().IsLondon { + effectiveTip = cmath.Min256(st.tip, new(uint256.Int).Sub(st.gasFeeCap, st.evm.Context().BaseFee)) } - st.state.AddBalance(st.evm.Context.Coinbase, new(uint256.Int).Mul(new(uint256.Int).SetUint64(st.gasUsed()), effectiveTip)) + st.state.AddBalance(st.evm.Context().Coinbase, new(uint256.Int).Mul(new(uint256.Int).SetUint64(st.gasUsed()), effectiveTip)) return &ExecutionResult{ UsedGas: st.gasUsed(), diff --git a/core/types/access_list_tx.go b/core/types/access_list_tx.go index d46031efdb..b60d891fe9 100644 --- a/core/types/access_list_tx.go +++ b/core/types/access_list_tx.go @@ -628,3 +628,7 @@ func (tx AccessListTx) GetSender() (common.Address, bool) { func (tx *AccessListTx) SetSender(addr common.Address) { tx.from.Store(addr) } + +func (tx AccessListTx) IsStarkNet() bool { + return false +} diff --git a/core/types/cairo_tx.go b/core/types/cairo_tx.go new file mode 100644 index 0000000000..bfa9cf7e6b --- /dev/null +++ b/core/types/cairo_tx.go @@ -0,0 +1,433 @@ +package types + +import ( + "encoding/binary" + "fmt" + "github.com/holiman/uint256" + "github.com/ledgerwatch/erigon/common" + "github.com/ledgerwatch/erigon/rlp" + "io" + "math/big" + "math/bits" +) + +type CairoTransaction struct { + CommonTx + + ChainID *uint256.Int + Tip *uint256.Int + FeeCap *uint256.Int + AccessList AccessList +} + +func (tx CairoTransaction) Type() byte { + return CairoType +} + +func (tx CairoTransaction) IsStarkNet() bool { + return true +} + +func (tx *CairoTransaction) DecodeRLP(s *rlp.Stream) error { + _, err := s.List() + if err != nil { + return err + } + var b []byte + if b, err = s.Bytes(); err != nil { + return err + } + if len(b) > 32 { + return fmt.Errorf("wrong size for ChainID: %d", len(b)) + } + //tx.ChainID = new(uint256.Int).SetBytes(b) + if tx.Nonce, err = s.Uint(); err != nil { + return err + } + if b, err = s.Bytes(); err != nil { + return err + } + if len(b) > 32 { + return fmt.Errorf("wrong size for MaxPriorityFeePerGas: %d", len(b)) + } + tx.Tip = new(uint256.Int).SetBytes(b) + if b, err = s.Bytes(); err != nil { + return err + } + if len(b) > 32 { + return fmt.Errorf("wrong size for MaxFeePerGas: %d", len(b)) + } + tx.FeeCap = new(uint256.Int).SetBytes(b) + if tx.Gas, err = s.Uint(); err != nil { + return err + } + if b, err = s.Bytes(); err != nil { + return err + } + if len(b) > 0 && len(b) != 20 { + return fmt.Errorf("wrong size for To: %d", len(b)) + } + if len(b) > 0 { + tx.To = &common.Address{} + copy((*tx.To)[:], b) + } + if b, err = s.Bytes(); err != nil { + return err + } + if len(b) > 32 { + return fmt.Errorf("wrong size for Value: %d", len(b)) + } + tx.Value = new(uint256.Int).SetBytes(b) + if tx.Data, err = s.Bytes(); err != nil { + return err + } + // decode AccessList + tx.AccessList = AccessList{} + if err = decodeAccessList(&tx.AccessList, s); err != nil { + return err + } + // decode V + if b, err = s.Bytes(); err != nil { + return err + } + if len(b) > 32 { + return fmt.Errorf("wrong size for V: %d", len(b)) + } + tx.V.SetBytes(b) + if b, err = s.Bytes(); err != nil { + return err + } + if len(b) > 32 { + return fmt.Errorf("wrong size for R: %d", len(b)) + } + tx.R.SetBytes(b) + if b, err = s.Bytes(); err != nil { + return err + } + if len(b) > 32 { + return fmt.Errorf("wrong size for S: %d", len(b)) + } + tx.S.SetBytes(b) + return s.ListEnd() +} + +func (tx CairoTransaction) GetChainID() *uint256.Int { + panic("implement me") +} + +func (tx CairoTransaction) GetPrice() *uint256.Int { + panic("implement me") +} + +func (tx CairoTransaction) GetTip() *uint256.Int { + panic("implement me") +} + +func (tx CairoTransaction) GetEffectiveGasTip(baseFee *uint256.Int) *uint256.Int { + panic("implement me") +} + +func (tx CairoTransaction) GetFeeCap() *uint256.Int { + panic("implement me") +} + +func (tx CairoTransaction) Cost() *uint256.Int { + panic("implement me") +} + +func (tx CairoTransaction) AsMessage(s Signer, baseFee *big.Int) (Message, error) { + panic("implement me") +} + +func (tx *CairoTransaction) WithSignature(signer Signer, sig []byte) (Transaction, error) { + cpy := tx.copy() + r, s, v, err := signer.SignatureValues(tx, sig) + if err != nil { + return nil, err + } + cpy.R.Set(r) + cpy.S.Set(s) + cpy.V.Set(v) + cpy.ChainID = signer.ChainID() + return cpy, nil +} + +func (tx CairoTransaction) FakeSign(address common.Address) (Transaction, error) { + panic("implement me") +} + +func (tx CairoTransaction) Hash() common.Hash { + if hash := tx.hash.Load(); hash != nil { + return *hash.(*common.Hash) + } + hash := prefixedRlpHash(DynamicFeeTxType, []interface{}{ + tx.ChainID, + tx.Nonce, + tx.Tip, + tx.FeeCap, + tx.Gas, + tx.To, + tx.Value, + tx.Data, + tx.AccessList, + tx.V, tx.R, tx.S, + }) + tx.hash.Store(&hash) + return hash +} + +func (tx CairoTransaction) SigningHash(chainID *big.Int) common.Hash { + panic("implement me") +} + +func (tx CairoTransaction) Size() common.StorageSize { + panic("implement me") +} + +func (tx CairoTransaction) GetAccessList() AccessList { + panic("implement me") +} + +func (tx CairoTransaction) Protected() bool { + panic("implement me") +} + +func (tx CairoTransaction) RawSignatureValues() (*uint256.Int, *uint256.Int, *uint256.Int) { + panic("implement me") +} + +func (tx CairoTransaction) MarshalBinary(w io.Writer) error { + payloadSize, nonceLen, gasLen, accessListLen := tx.payloadSize() + var b [33]byte + b[0] = CairoType + if _, err := w.Write(b[:1]); err != nil { + return err + } + if err := tx.encodePayload(w, b[:], payloadSize, nonceLen, gasLen, accessListLen); err != nil { + return err + } + return nil +} + +func (tx CairoTransaction) Sender(signer Signer) (common.Address, error) { + panic("implement me") +} + +func (tx CairoTransaction) SetSender(address common.Address) { + panic("implement me") +} + +func (tx CairoTransaction) encodePayload(w io.Writer, b []byte, payloadSize, nonceLen, gasLen, accessListLen int) error { + // prefix + if err := EncodeStructSizePrefix(payloadSize, w, b); err != nil { + return err + } + // encode ChainID + if err := tx.ChainID.EncodeRLP(w); err != nil { + return err + } + // encode Nonce + if tx.Nonce > 0 && tx.Nonce < 128 { + b[0] = byte(tx.Nonce) + if _, err := w.Write(b[:1]); err != nil { + return err + } + } else { + binary.BigEndian.PutUint64(b[1:], tx.Nonce) + b[8-nonceLen] = 128 + byte(nonceLen) + if _, err := w.Write(b[8-nonceLen : 9]); err != nil { + return err + } + } + // encode MaxPriorityFeePerGas + if err := tx.Tip.EncodeRLP(w); err != nil { + return err + } + // encode MaxFeePerGas + if err := tx.FeeCap.EncodeRLP(w); err != nil { + return err + } + // encode Gas + if tx.Gas > 0 && tx.Gas < 128 { + b[0] = byte(tx.Gas) + if _, err := w.Write(b[:1]); err != nil { + return err + } + } else { + binary.BigEndian.PutUint64(b[1:], tx.Gas) + b[8-gasLen] = 128 + byte(gasLen) + if _, err := w.Write(b[8-gasLen : 9]); err != nil { + return err + } + } + // encode To + if tx.To == nil { + b[0] = 128 + } else { + b[0] = 128 + 20 + } + if _, err := w.Write(b[:1]); err != nil { + return err + } + if tx.To != nil { + if _, err := w.Write(tx.To.Bytes()); err != nil { + return err + } + } + // encode Value + if err := tx.Value.EncodeRLP(w); err != nil { + return err + } + // encode Data + if err := EncodeString(tx.Data, w, b); err != nil { + return err + } + // prefix + if err := EncodeStructSizePrefix(accessListLen, w, b); err != nil { + return err + } + // encode AccessList + if err := encodeAccessList(tx.AccessList, w, b); err != nil { + return err + } + // encode V + if err := tx.V.EncodeRLP(w); err != nil { + return err + } + // encode R + if err := tx.R.EncodeRLP(w); err != nil { + return err + } + // encode S + if err := tx.S.EncodeRLP(w); err != nil { + return err + } + return nil +} + +func (tx CairoTransaction) payloadSize() (payloadSize int, nonceLen, gasLen, accessListLen int) { + // size of ChainID + payloadSize++ + var chainIdLen int + if tx.ChainID.BitLen() >= 8 { + chainIdLen = (tx.ChainID.BitLen() + 7) / 8 + } + payloadSize += chainIdLen + // size of Nonce + payloadSize++ + if tx.Nonce >= 128 { + nonceLen = (bits.Len64(tx.Nonce) + 7) / 8 + } + payloadSize += nonceLen + // size of MaxPriorityFeePerGas + payloadSize++ + var tipLen int + if tx.Tip.BitLen() >= 8 { + tipLen = (tx.Tip.BitLen() + 7) / 8 + } + payloadSize += tipLen + // size of MaxFeePerGas + payloadSize++ + var feeCapLen int + if tx.FeeCap.BitLen() >= 8 { + feeCapLen = (tx.FeeCap.BitLen() + 7) / 8 + } + payloadSize += feeCapLen + // size of Gas + payloadSize++ + if tx.Gas >= 128 { + gasLen = (bits.Len64(tx.Gas) + 7) / 8 + } + payloadSize += gasLen + // size of To + payloadSize++ + if tx.To != nil { + payloadSize += 20 + } + // size of Value + payloadSize++ + var valueLen int + if tx.Value.BitLen() >= 8 { + valueLen = (tx.Value.BitLen() + 7) / 8 + } + payloadSize += valueLen + // size of Data + payloadSize++ + switch len(tx.Data) { + case 0: + case 1: + if tx.Data[0] >= 128 { + payloadSize++ + } + default: + if len(tx.Data) >= 56 { + payloadSize += (bits.Len(uint(len(tx.Data))) + 7) / 8 + } + payloadSize += len(tx.Data) + } + // size of AccessList + payloadSize++ + accessListLen = accessListSize(tx.AccessList) + if accessListLen >= 56 { + payloadSize += (bits.Len(uint(accessListLen)) + 7) / 8 + } + payloadSize += accessListLen + // size of V + payloadSize++ + var vLen int + if tx.V.BitLen() >= 8 { + vLen = (tx.V.BitLen() + 7) / 8 + } + payloadSize += vLen + // size of R + payloadSize++ + var rLen int + if tx.R.BitLen() >= 8 { + rLen = (tx.R.BitLen() + 7) / 8 + } + payloadSize += rLen + // size of S + payloadSize++ + var sLen int + if tx.S.BitLen() >= 8 { + sLen = (tx.S.BitLen() + 7) / 8 + } + payloadSize += sLen + return payloadSize, nonceLen, gasLen, accessListLen +} + +func (tx CairoTransaction) copy() *CairoTransaction { + cpy := &CairoTransaction{ + CommonTx: CommonTx{ + TransactionMisc: TransactionMisc{ + time: tx.time, + }, + Nonce: tx.Nonce, + To: tx.To, + Data: common.CopyBytes(tx.Data), + Gas: tx.Gas, + Value: new(uint256.Int), + }, + AccessList: make(AccessList, len(tx.AccessList)), + ChainID: new(uint256.Int), + Tip: new(uint256.Int), + FeeCap: new(uint256.Int), + } + copy(cpy.AccessList, tx.AccessList) + if tx.Value != nil { + cpy.Value.Set(tx.Value) + } + if tx.ChainID != nil { + cpy.ChainID.Set(tx.ChainID) + } + if tx.Tip != nil { + cpy.Tip.Set(tx.Tip) + } + if tx.FeeCap != nil { + cpy.FeeCap.Set(tx.FeeCap) + } + cpy.V.Set(&tx.V) + cpy.R.Set(&tx.R) + cpy.S.Set(&tx.S) + return cpy +} diff --git a/core/types/dynamic_fee_tx.go b/core/types/dynamic_fee_tx.go index ed8b99d446..42d0f6c62f 100644 --- a/core/types/dynamic_fee_tx.go +++ b/core/types/dynamic_fee_tx.go @@ -432,7 +432,6 @@ func (tx *DynamicFeeTransaction) DecodeRLP(s *rlp.Stream) error { } tx.S.SetBytes(b) return s.ListEnd() - } // AsMessage returns the transaction as a core.Message. @@ -535,6 +534,10 @@ func (tx *DynamicFeeTransaction) SetSender(addr common.Address) { tx.from.Store(addr) } +func (tx DynamicFeeTransaction) IsStarkNet() bool { + return false +} + // NewTransaction creates an unsigned eip1559 transaction. func NewEIP1559Transaction(chainID uint256.Int, nonce uint64, to common.Address, amount *uint256.Int, gasLimit uint64, gasPrice *uint256.Int, gasTip *uint256.Int, gasFeeCap *uint256.Int, data []byte) *DynamicFeeTransaction { return &DynamicFeeTransaction{ diff --git a/core/types/legacy_tx.go b/core/types/legacy_tx.go index 24a4db88b6..07871d5a79 100644 --- a/core/types/legacy_tx.go +++ b/core/types/legacy_tx.go @@ -59,6 +59,10 @@ func (ct CommonTx) GetData() []byte { return ct.Data } +func (ct CommonTx) IsContractDeploy() bool { + return ct.GetTo() == nil +} + // LegacyTx is the transaction data of regular Ethereum transactions. type LegacyTx struct { CommonTx @@ -524,3 +528,7 @@ func (tx LegacyTx) GetSender() (common.Address, bool) { func (tx *LegacyTx) SetSender(addr common.Address) { tx.from.Store(addr) } + +func (tx LegacyTx) IsStarkNet() bool { + return false +} diff --git a/core/types/transaction.go b/core/types/transaction.go index b01550ec7c..65269eb4b1 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -44,6 +44,7 @@ const ( LegacyTxType = iota AccessListTxType DynamicFeeTxType + CairoType ) // Transaction is an Ethereum transaction. @@ -81,6 +82,8 @@ type Transaction interface { Sender(Signer) (common.Address, error) GetSender() (common.Address, bool) SetSender(common.Address) + IsContractDeploy() bool + IsStarkNet() bool } // TransactionMisc is collection of miscelaneous fields for transaction that is supposed to be embedded into concrete @@ -112,6 +115,13 @@ func (tm TransactionMisc) From() *atomic.Value { return &tm.from } +func (tm TransactionMisc) GetSender() (common.Address, bool) { + if sc := tm.from.Load(); sc != nil { + return sc.(common.Address), true + } + return common.Address{}, false +} + func DecodeTransaction(s *rlp.Stream) (Transaction, error) { kind, size, err := s.Kind() if err != nil { @@ -148,6 +158,12 @@ func DecodeTransaction(s *rlp.Stream) (Transaction, error) { return nil, err } tx = t + case CairoType: + t := &CairoTransaction{} + if err = t.DecodeRLP(s); err != nil { + return nil, err + } + tx = t default: return nil, fmt.Errorf("%w, got: %d", rlp.ErrUnknownTxTypePrefix, b[0]) } diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go index ee6af01679..09126ecb68 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -271,6 +271,13 @@ func (sg Signer) SignatureValues(tx Transaction, sig []byte) (R, S, V *uint256.I return nil, nil, nil, ErrInvalidChainId } R, S, V = decodeSignature(sig) + case *CairoTransaction: + // Check that chain ID of tx matches the signer. We also accept ID zero here, + // because it indicates that the chain ID was not specified in the tx. + if t.ChainID != nil && !t.ChainID.IsZero() && !t.ChainID.Eq(&sg.chainID) { + return nil, nil, nil, ErrInvalidChainId + } + R, S, V = decodeSignature(sig) default: return nil, nil, nil, ErrTxTypeNotSupported } diff --git a/core/vm/cvm.go b/core/vm/cvm.go new file mode 100644 index 0000000000..e307d3714e --- /dev/null +++ b/core/vm/cvm.go @@ -0,0 +1,40 @@ +package vm + +import ( + "github.com/ledgerwatch/erigon/common" +) + +func NewCVM(state IntraBlockState) *CVM { + cvm := &CVM{ + intraBlockState: state, + } + + return cvm +} + +type CVM struct { + config Config + intraBlockState IntraBlockState +} + +func (cvm *CVM) Create(code []byte) ([]byte, common.Address, error) { + address := common.Address{} + cvm.intraBlockState.SetCode(address, code) + return code, common.Address{}, nil + + //TODO:: execute cairo construct + //ret, err := cvm.run(code) +} + +func (cvm *CVM) Config() Config { + return cvm.config +} + +func (cvm *CVM) IntraBlockState() IntraBlockState { + return cvm.intraBlockState +} + +//func (cvm *CVM) run(code []byte) ([]byte, error) { +// // TODO:: call grpc cairo +// return code, nil +//} diff --git a/core/vm/cvm_adapter.go b/core/vm/cvm_adapter.go new file mode 100644 index 0000000000..30150d5e21 --- /dev/null +++ b/core/vm/cvm_adapter.go @@ -0,0 +1,50 @@ +package vm + +import ( + "fmt" + "github.com/holiman/uint256" + "github.com/ledgerwatch/erigon/common" + "github.com/ledgerwatch/erigon/params" +) + +const CairoNotImplemented = "the method is currently not implemented for cvm: %s" + +type CVMAdapter struct { + Cvm *CVM +} + +func (c *CVMAdapter) Reset(txCtx TxContext, ibs IntraBlockState) { + panic("implement me") +} + +func (c *CVMAdapter) Create(caller ContractRef, code []byte, gas uint64, value *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { + leftOverGas = 0 + + ret, contractAddr, err = c.Cvm.Create(code) + + return ret, contractAddr, leftOverGas, err +} + +func (cvm *CVMAdapter) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int, bailout bool) (ret []byte, leftOverGas uint64, err error) { + return nil, 0, fmt.Errorf(CairoNotImplemented, "Call") +} + +func (cvm *CVMAdapter) Config() Config { + return cvm.Cvm.Config() +} + +func (cvm *CVMAdapter) ChainRules() params.Rules { + return params.Rules{} +} + +func (cvm *CVMAdapter) Context() BlockContext { + return BlockContext{} +} + +func (cvm *CVMAdapter) IntraBlockState() IntraBlockState { + return cvm.Cvm.IntraBlockState() +} + +func (cvm *CVMAdapter) TxContext() TxContext { + return TxContext{} +} diff --git a/core/vm/cvm_test.go b/core/vm/cvm_test.go new file mode 100644 index 0000000000..830a8f69d9 --- /dev/null +++ b/core/vm/cvm_test.go @@ -0,0 +1 @@ +package vm diff --git a/core/vm/eips.go b/core/vm/eips.go index b428a3a823..12968ffb54 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -80,7 +80,7 @@ func enable1884(jt *JumpTable) { } func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - balance := interpreter.evm.IntraBlockState.GetBalance(callContext.contract.Address()) + balance := interpreter.evm.IntraBlockState().GetBalance(callContext.contract.Address()) callContext.stack.Push(balance) return nil, nil } @@ -99,7 +99,7 @@ func enable1344(jt *JumpTable) { // opChainID implements CHAINID opcode func opChainID(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - chainId, _ := uint256.FromBig(interpreter.evm.ChainRules.ChainID) + chainId, _ := uint256.FromBig(interpreter.evm.ChainRules().ChainID) callContext.stack.Push(chainId) return nil, nil } @@ -167,7 +167,7 @@ func enable3198(jt *JumpTable) { // opBaseFee implements BASEFEE opcode func opBaseFee(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - baseFee := interpreter.evm.Context.BaseFee + baseFee := interpreter.evm.Context().BaseFee callContext.stack.Push(baseFee) return nil, nil } diff --git a/core/vm/evm.go b/core/vm/evm.go index 44255555ef..d44b1975d3 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -46,11 +46,11 @@ type ( func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) { var precompiles map[common.Address]PrecompiledContract switch { - case evm.ChainRules.IsBerlin: + case evm.chainRules.IsBerlin: precompiles = PrecompiledContractsBerlin - case evm.ChainRules.IsIstanbul: + case evm.chainRules.IsIstanbul: precompiles = PrecompiledContractsIstanbul - case evm.ChainRules.IsByzantium: + case evm.chainRules.IsByzantium: precompiles = PrecompiledContractsByzantium default: precompiles = PrecompiledContractsHomestead @@ -132,20 +132,20 @@ type TxContext struct { // The EVM should never be reused and is not thread safe. type EVM struct { // Context provides auxiliary blockchain related information - Context BlockContext - TxContext + context BlockContext + txContext TxContext // IntraBlockState gives access to the underlying state - IntraBlockState IntraBlockState + intraBlockState IntraBlockState // Depth is the current call stack depth int // chainConfig contains information about the current chain chainConfig *params.ChainConfig // chain rules contains the chain rules for the current epoch - ChainRules params.Rules + chainRules params.Rules // virtual machine configuration options used to initialise the // evm. - Config Config + config Config // global (to this context) ethereum virtual machine // used throughout the execution of the tx. interpreters []Interpreter @@ -163,12 +163,12 @@ type EVM struct { // only ever be used *once*. func NewEVM(blockCtx BlockContext, txCtx TxContext, state IntraBlockState, chainConfig *params.ChainConfig, vmConfig Config) *EVM { evm := &EVM{ - Context: blockCtx, - TxContext: txCtx, - IntraBlockState: state, - Config: vmConfig, + context: blockCtx, + txContext: txCtx, + intraBlockState: state, + config: vmConfig, chainConfig: chainConfig, - ChainRules: chainConfig.Rules(blockCtx.BlockNumber), + chainRules: chainConfig.Rules(blockCtx.BlockNumber), } evmInterp := NewEVMInterpreter(evm, vmConfig) @@ -184,8 +184,8 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, state IntraBlockState, chain // Reset resets the EVM with a new transaction context.Reset // This is not threadsafe and should only be done very cautiously. func (evm *EVM) Reset(txCtx TxContext, ibs IntraBlockState) { - evm.TxContext = txCtx - evm.IntraBlockState = ibs + evm.txContext = txCtx + evm.intraBlockState = ibs } // Cancel cancels any running EVM operation. This may be called concurrently and @@ -209,7 +209,7 @@ func (evm *EVM) Interpreter() Interpreter { // the necessary steps to create accounts and reverses the state in case of an // execution error or failed value transfer. func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int, bailout bool) (ret []byte, leftOverGas uint64, err error) { - if evm.Config.NoRecursion && evm.depth > 0 { + if evm.config.NoRecursion && evm.depth > 0 { return nil, gas, nil } // Fail if we're trying to execute above the call depth limit @@ -217,7 +217,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas return nil, gas, ErrDepth } // Fail if we're trying to transfer more than the available balance - if !value.IsZero() && !evm.Context.CanTransfer(evm.IntraBlockState, caller.Address(), value) { + if !value.IsZero() && !evm.context.CanTransfer(evm.intraBlockState, caller.Address(), value) { if !bailout { return nil, gas, ErrInsufficientBalance } @@ -225,27 +225,27 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas p, isPrecompile := evm.precompile(addr) var code []byte if !isPrecompile { - code = evm.IntraBlockState.GetCode(addr) + code = evm.intraBlockState.GetCode(addr) } // Capture the tracer start/end events in debug mode - if evm.Config.Debug { - _ = evm.Config.Tracer.CaptureStart(evm.depth, caller.Address(), addr, isPrecompile, false /* create */, CALLT, input, gas, value.ToBig(), code) + if evm.config.Debug { + _ = evm.config.Tracer.CaptureStart(evm.depth, caller.Address(), addr, isPrecompile, false /* create */, CALLT, input, gas, value.ToBig(), code) defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters - evm.Config.Tracer.CaptureEnd(evm.depth, ret, startGas, gas, time.Since(startTime), err) //nolint:errcheck + evm.config.Tracer.CaptureEnd(evm.depth, ret, startGas, gas, time.Since(startTime), err) //nolint:errcheck }(gas, time.Now()) } var ( to = AccountRef(addr) - snapshot = evm.IntraBlockState.Snapshot() + snapshot = evm.intraBlockState.Snapshot() ) - if !evm.IntraBlockState.Exist(addr) { - if !isPrecompile && evm.ChainRules.IsEIP158 && value.IsZero() { + if !evm.intraBlockState.Exist(addr) { + if !isPrecompile && evm.chainRules.IsEIP158 && value.IsZero() { return nil, gas, nil } - evm.IntraBlockState.CreateAccount(addr, false) + evm.intraBlockState.CreateAccount(addr, false) } - evm.Context.Transfer(evm.IntraBlockState, caller.Address(), to.Address(), value, bailout) + evm.context.Transfer(evm.intraBlockState, caller.Address(), to.Address(), value, bailout) if isPrecompile { ret, gas, err = RunPrecompiledContract(p, input, gas) @@ -258,13 +258,13 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas addrCopy := addr // If the account has no code, we can abort here // The depth-check is already done, and precompiles handled above - codehash := evm.IntraBlockState.GetCodeHash(addrCopy) + codehash := evm.intraBlockState.GetCodeHash(addrCopy) var contractHasTEVM bool - contractHasTEVM, err = evm.Context.ContractHasTEVM(codehash) + contractHasTEVM, err = evm.context.ContractHasTEVM(codehash) if err == nil { - contract := NewContract(caller, AccountRef(addrCopy), value, gas, evm.Config.SkipAnalysis, contractHasTEVM) + contract := NewContract(caller, AccountRef(addrCopy), value, gas, evm.config.SkipAnalysis, contractHasTEVM) contract.SetCallCode(&addrCopy, codehash, code) ret, err = run(evm, contract, input, false) gas = contract.Gas @@ -275,7 +275,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // above we revert to the snapshot and consume any gas remaining. Additionally // when we're in homestead this also counts for code storage gas errors. if err != nil { - evm.IntraBlockState.RevertToSnapshot(snapshot) + evm.intraBlockState.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { gas = 0 } @@ -294,7 +294,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // CallCode differs from Call in the sense that it executes the given address' // code with the caller as context. func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) { - if evm.Config.NoRecursion && evm.depth > 0 { + if evm.config.NoRecursion && evm.depth > 0 { return nil, gas, nil } // Fail if we're trying to execute above the call depth limit @@ -305,23 +305,23 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // Note although it's noop to transfer X ether to caller itself. But // if caller doesn't have enough balance, it would be an error to allow // over-charging itself. So the check here is necessary. - if !evm.Context.CanTransfer(evm.IntraBlockState, caller.Address(), value) { + if !evm.context.CanTransfer(evm.intraBlockState, caller.Address(), value) { return nil, gas, ErrInsufficientBalance } p, isPrecompile := evm.precompile(addr) var code []byte if !isPrecompile { - code = evm.IntraBlockState.GetCode(addr) + code = evm.intraBlockState.GetCode(addr) } // Capture the tracer start/end events in debug mode - if evm.Config.Debug { - _ = evm.Config.Tracer.CaptureStart(evm.depth, caller.Address(), addr, isPrecompile, false /* create */, CALLCODET, input, gas, value.ToBig(), code) + if evm.config.Debug { + _ = evm.config.Tracer.CaptureStart(evm.depth, caller.Address(), addr, isPrecompile, false /* create */, CALLCODET, input, gas, value.ToBig(), code) defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters - evm.Config.Tracer.CaptureEnd(evm.depth, ret, startGas, gas, time.Since(startTime), err) //nolint:errcheck + evm.config.Tracer.CaptureEnd(evm.depth, ret, startGas, gas, time.Since(startTime), err) //nolint:errcheck }(gas, time.Now()) } var ( - snapshot = evm.IntraBlockState.Snapshot() + snapshot = evm.intraBlockState.Snapshot() ) // It is allowed to call precompiles, even via delegatecall @@ -333,18 +333,18 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // The contract is a scoped environment for this execution context only. var isTEVM bool - codeHash := evm.IntraBlockState.GetCodeHash(addrCopy) - isTEVM, err = evm.Context.ContractHasTEVM(codeHash) + codeHash := evm.intraBlockState.GetCodeHash(addrCopy) + isTEVM, err = evm.context.ContractHasTEVM(codeHash) if err == nil { - contract := NewContract(caller, AccountRef(caller.Address()), value, gas, evm.Config.SkipAnalysis, isTEVM) + contract := NewContract(caller, AccountRef(caller.Address()), value, gas, evm.config.SkipAnalysis, isTEVM) contract.SetCallCode(&addrCopy, codeHash, code) ret, err = run(evm, contract, input, false) gas = contract.Gas } } if err != nil { - evm.IntraBlockState.RevertToSnapshot(snapshot) + evm.intraBlockState.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { gas = 0 } @@ -358,7 +358,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // DelegateCall differs from CallCode in the sense that it executes the given address' // code with the caller as context and the caller is set to the caller of the caller. func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) { - if evm.Config.NoRecursion && evm.depth > 0 { + if evm.config.NoRecursion && evm.depth > 0 { return nil, gas, nil } // Fail if we're trying to execute above the call depth limit @@ -368,16 +368,16 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by p, isPrecompile := evm.precompile(addr) var code []byte if !isPrecompile { - code = evm.IntraBlockState.GetCode(addr) + code = evm.intraBlockState.GetCode(addr) } // Capture the tracer start/end events in debug mode - if evm.Config.Debug { - _ = evm.Config.Tracer.CaptureStart(evm.depth, caller.Address(), addr, isPrecompile, false /* create */, DELEGATECALLT, input, gas, big.NewInt(-1), code) + if evm.config.Debug { + _ = evm.config.Tracer.CaptureStart(evm.depth, caller.Address(), addr, isPrecompile, false /* create */, DELEGATECALLT, input, gas, big.NewInt(-1), code) defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters - evm.Config.Tracer.CaptureEnd(evm.depth, ret, startGas, gas, time.Since(startTime), err) //nolint:errcheck + evm.config.Tracer.CaptureEnd(evm.depth, ret, startGas, gas, time.Since(startTime), err) //nolint:errcheck }(gas, time.Now()) } - snapshot := evm.IntraBlockState.Snapshot() + snapshot := evm.intraBlockState.Snapshot() // It is allowed to call precompiles, even via delegatecall if isPrecompile { @@ -386,18 +386,18 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by addrCopy := addr // Initialise a new contract and make initialise the delegate values var isTEVM bool - codeHash := evm.IntraBlockState.GetCodeHash(addrCopy) - isTEVM, err = evm.Context.ContractHasTEVM(codeHash) + codeHash := evm.intraBlockState.GetCodeHash(addrCopy) + isTEVM, err = evm.context.ContractHasTEVM(codeHash) if err == nil { - contract := NewContract(caller, AccountRef(caller.Address()), nil, gas, evm.Config.SkipAnalysis, isTEVM).AsDelegate() + contract := NewContract(caller, AccountRef(caller.Address()), nil, gas, evm.config.SkipAnalysis, isTEVM).AsDelegate() contract.SetCallCode(&addrCopy, codeHash, code) ret, err = run(evm, contract, input, false) gas = contract.Gas } } if err != nil { - evm.IntraBlockState.RevertToSnapshot(snapshot) + evm.intraBlockState.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { gas = 0 } @@ -410,7 +410,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by // Opcodes that attempt to perform such modifications will result in exceptions // instead of performing the modifications. func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) { - if evm.Config.NoRecursion && evm.depth > 0 { + if evm.config.NoRecursion && evm.depth > 0 { return nil, gas, nil } // Fail if we're trying to execute above the call depth limit @@ -420,13 +420,13 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte p, isPrecompile := evm.precompile(addr) var code []byte if !isPrecompile { - code = evm.IntraBlockState.GetCode(addr) + code = evm.intraBlockState.GetCode(addr) } // Capture the tracer start/end events in debug mode - if evm.Config.Debug { - _ = evm.Config.Tracer.CaptureStart(evm.depth, caller.Address(), addr, isPrecompile, false, STATICCALLT, input, gas, big.NewInt(-2), code) + if evm.config.Debug { + _ = evm.config.Tracer.CaptureStart(evm.depth, caller.Address(), addr, isPrecompile, false, STATICCALLT, input, gas, big.NewInt(-2), code) defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters - evm.Config.Tracer.CaptureEnd(evm.depth, ret, startGas, gas, time.Since(startTime), err) //nolint:errcheck + evm.config.Tracer.CaptureEnd(evm.depth, ret, startGas, gas, time.Since(startTime), err) //nolint:errcheck }(gas, time.Now()) } // We take a snapshot here. This is a bit counter-intuitive, and could probably be skipped. @@ -434,13 +434,13 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // after all empty accounts were deleted, so this is not required. However, if we omit this, // then certain tests start failing; stRevertTest/RevertPrecompiledTouchExactOOG.json. // We could change this, but for now it's left for legacy reasons - var snapshot = evm.IntraBlockState.Snapshot() + var snapshot = evm.intraBlockState.Snapshot() // We do an AddBalance of zero here, just in order to trigger a touch. // This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium, // but is the correct thing to do and matters on other networks, in tests, and potential // future scenarios - evm.IntraBlockState.AddBalance(addr, u256.Num0) + evm.intraBlockState.AddBalance(addr, u256.Num0) if isPrecompile { ret, gas, err = RunPrecompiledContract(p, input, gas) @@ -452,11 +452,11 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. var isTEVM bool - codeHash := evm.IntraBlockState.GetCodeHash(addrCopy) - isTEVM, err = evm.Context.ContractHasTEVM(codeHash) + codeHash := evm.intraBlockState.GetCodeHash(addrCopy) + isTEVM, err = evm.context.ContractHasTEVM(codeHash) if err == nil { - contract := NewContract(caller, AccountRef(addrCopy), new(uint256.Int), gas, evm.Config.SkipAnalysis, isTEVM) + contract := NewContract(caller, AccountRef(addrCopy), new(uint256.Int), gas, evm.config.SkipAnalysis, isTEVM) contract.SetCallCode(&addrCopy, codeHash, code) // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally @@ -466,7 +466,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte } } if err != nil { - evm.IntraBlockState.RevertToSnapshot(snapshot) + evm.intraBlockState.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { gas = 0 } @@ -495,53 +495,53 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, if evm.depth > int(params.CallCreateDepth) { return nil, common.Address{}, gas, ErrDepth } - if !evm.Context.CanTransfer(evm.IntraBlockState, caller.Address(), value) { + if !evm.context.CanTransfer(evm.intraBlockState, caller.Address(), value) { return nil, common.Address{}, gas, ErrInsufficientBalance } - if evm.Config.Debug || evm.Config.EnableTEMV { - _ = evm.Config.Tracer.CaptureStart(evm.depth, caller.Address(), address, false /* precompile */, true /* create */, calltype, codeAndHash.code, gas, value.ToBig(), nil) + if evm.config.Debug || evm.config.EnableTEMV { + _ = evm.config.Tracer.CaptureStart(evm.depth, caller.Address(), address, false /* precompile */, true /* create */, calltype, codeAndHash.code, gas, value.ToBig(), nil) defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters - evm.Config.Tracer.CaptureEnd(evm.depth, ret, startGas, gas, time.Since(startTime), err) //nolint:errcheck + evm.config.Tracer.CaptureEnd(evm.depth, ret, startGas, gas, time.Since(startTime), err) //nolint:errcheck }(gas, time.Now()) } - nonce := evm.IntraBlockState.GetNonce(caller.Address()) - evm.IntraBlockState.SetNonce(caller.Address(), nonce+1) + nonce := evm.intraBlockState.GetNonce(caller.Address()) + evm.intraBlockState.SetNonce(caller.Address(), nonce+1) // We add this to the access list _before_ taking a snapshot. Even if the creation fails, // the access-list change should not be rolled back - if evm.ChainRules.IsBerlin { - evm.IntraBlockState.AddAddressToAccessList(address) + if evm.chainRules.IsBerlin { + evm.intraBlockState.AddAddressToAccessList(address) } // Ensure there's no existing contract already at the designated address - contractHash := evm.IntraBlockState.GetCodeHash(address) - if evm.IntraBlockState.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) { + contractHash := evm.intraBlockState.GetCodeHash(address) + if evm.intraBlockState.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) { err = ErrContractAddressCollision return nil, common.Address{}, 0, err } // Create a new account on the state - snapshot := evm.IntraBlockState.Snapshot() - evm.IntraBlockState.CreateAccount(address, true) - if evm.ChainRules.IsEIP158 { - evm.IntraBlockState.SetNonce(address, 1) + snapshot := evm.intraBlockState.Snapshot() + evm.intraBlockState.CreateAccount(address, true) + if evm.chainRules.IsEIP158 { + evm.intraBlockState.SetNonce(address, 1) } - evm.Context.Transfer(evm.IntraBlockState, caller.Address(), address, value, false /* bailout */) + evm.context.Transfer(evm.intraBlockState, caller.Address(), address, value, false /* bailout */) // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. - contract := NewContract(caller, AccountRef(address), value, gas, evm.Config.SkipAnalysis, false) + contract := NewContract(caller, AccountRef(address), value, gas, evm.config.SkipAnalysis, false) contract.SetCodeOptionalHash(&address, codeAndHash) - if evm.Config.NoRecursion && evm.depth > 0 { + if evm.config.NoRecursion && evm.depth > 0 { return nil, address, gas, nil } ret, err = run(evm, contract, nil, false) // check whether the max code size has been exceeded - maxCodeSizeExceeded := evm.ChainRules.IsEIP158 && len(ret) > params.MaxCodeSize + maxCodeSizeExceeded := evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize // Reject code starting with 0xEF if EIP-3541 is enabled. if err == nil && !maxCodeSizeExceeded { - if evm.ChainRules.IsLondon && len(ret) >= 1 && ret[0] == 0xEF { + if evm.chainRules.IsLondon && len(ret) >= 1 && ret[0] == 0xEF { err = ErrInvalidCode } } @@ -552,8 +552,8 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, if err == nil && !maxCodeSizeExceeded { createDataGas := uint64(len(ret)) * params.CreateDataGas if contract.UseGas(createDataGas) { - evm.IntraBlockState.SetCode(address, ret) - } else if evm.ChainRules.IsHomestead { + evm.intraBlockState.SetCode(address, ret) + } else if evm.chainRules.IsHomestead { err = ErrCodeStoreOutOfGas } } @@ -561,8 +561,8 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally // when we're in homestead this also counts for code storage gas errors. - if maxCodeSizeExceeded || (err != nil && (evm.ChainRules.IsHomestead || err != ErrCodeStoreOutOfGas)) { - evm.IntraBlockState.RevertToSnapshot(snapshot) + if maxCodeSizeExceeded || (err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas)) { + evm.intraBlockState.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { contract.UseGas(contract.Gas) } @@ -580,7 +580,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // Create creates a new contract using code as deployment code. // DESCRIBED: docs/programmers_guide/guide.md#nonce func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { - contractAddr = crypto.CreateAddress(caller.Address(), evm.IntraBlockState.GetNonce(caller.Address())) + contractAddr = crypto.CreateAddress(caller.Address(), evm.intraBlockState.GetNonce(caller.Address())) return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, CREATET) } @@ -596,4 +596,27 @@ func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment * } // ChainConfig returns the environment's chain configuration -func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } +func (evm *EVM) Config() Config { + return evm.config +} + +// ChainConfig returns the environment's chain configuration +func (evm *EVM) ChainConfig() *params.ChainConfig { + return evm.chainConfig +} + +func (evm *EVM) ChainRules() params.Rules { + return evm.chainRules +} + +func (evm *EVM) Context() BlockContext { + return evm.context +} + +func (evm *EVM) TxContext() TxContext { + return evm.txContext +} + +func (evm *EVM) IntraBlockState() IntraBlockState { + return evm.intraBlockState +} diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 2691471974..cf29469a3c 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -100,11 +100,11 @@ func gasSStore(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, me value, x := stack.Back(1), stack.Back(0) key := common.Hash(x.Bytes32()) var current uint256.Int - evm.IntraBlockState.GetState(contract.Address(), &key, ¤t) + evm.IntraBlockState().GetState(contract.Address(), &key, ¤t) // The legacy gas metering only takes into consideration the current state // Legacy rules should be applied if we are in Petersburg (removal of EIP-1283) // OR Constantinople is not active - if evm.ChainRules.IsPetersburg || !evm.ChainRules.IsConstantinople { + if evm.ChainRules().IsPetersburg || !evm.ChainRules().IsConstantinople { // This checks for 3 scenario's and calculates gas accordingly: // // 1. From a zero-value address to a non-zero value (NEW VALUE) @@ -114,7 +114,7 @@ func gasSStore(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, me case current.IsZero() && !value.IsZero(): // 0 => non 0 return params.SstoreSetGas, nil case !current.IsZero() && value.IsZero(): // non 0 => 0 - evm.IntraBlockState.AddRefund(params.SstoreRefundGas) + evm.IntraBlockState().AddRefund(params.SstoreRefundGas) return params.SstoreClearGas, nil default: // non 0 => non 0 (or 0 => 0) return params.SstoreResetGas, nil @@ -138,28 +138,28 @@ func gasSStore(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, me return params.NetSstoreNoopGas, nil } var original uint256.Int - evm.IntraBlockState.GetCommittedState(contract.Address(), &key, &original) + evm.IntraBlockState().GetCommittedState(contract.Address(), &key, &original) if original == current { if original.IsZero() { // create slot (2.1.1) return params.NetSstoreInitGas, nil } if value.IsZero() { // delete slot (2.1.2b) - evm.IntraBlockState.AddRefund(params.NetSstoreClearRefund) + evm.IntraBlockState().AddRefund(params.NetSstoreClearRefund) } return params.NetSstoreCleanGas, nil // write existing slot (2.1.2) } if !original.IsZero() { if current.IsZero() { // recreate slot (2.2.1.1) - evm.IntraBlockState.SubRefund(params.NetSstoreClearRefund) + evm.IntraBlockState().SubRefund(params.NetSstoreClearRefund) } else if value.IsZero() { // delete slot (2.2.1.2) - evm.IntraBlockState.AddRefund(params.NetSstoreClearRefund) + evm.IntraBlockState().AddRefund(params.NetSstoreClearRefund) } } if original.Eq(value) { if original.IsZero() { // reset to original inexistent slot (2.2.2.1) - evm.IntraBlockState.AddRefund(params.NetSstoreResetClearRefund) + evm.IntraBlockState().AddRefund(params.NetSstoreResetClearRefund) } else { // reset to original existing slot (2.2.2.2) - evm.IntraBlockState.AddRefund(params.NetSstoreResetRefund) + evm.IntraBlockState().AddRefund(params.NetSstoreResetRefund) } } @@ -188,35 +188,35 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *stack.Stack, mem *Mem value, x := stack.Back(1), stack.Back(0) key := common.Hash(x.Bytes32()) var current uint256.Int - evm.IntraBlockState.GetState(contract.Address(), &key, ¤t) + evm.IntraBlockState().GetState(contract.Address(), &key, ¤t) if current.Eq(value) { // noop (1) return params.SloadGasEIP2200, nil } var original uint256.Int - evm.IntraBlockState.GetCommittedState(contract.Address(), &key, &original) + evm.IntraBlockState().GetCommittedState(contract.Address(), &key, &original) if original == current { if original.IsZero() { // create slot (2.1.1) return params.SstoreSetGasEIP2200, nil } if value.IsZero() { // delete slot (2.1.2b) - evm.IntraBlockState.AddRefund(params.SstoreClearsScheduleRefundEIP2200) + evm.IntraBlockState().AddRefund(params.SstoreClearsScheduleRefundEIP2200) } return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2) } if !original.IsZero() { if current.IsZero() { // recreate slot (2.2.1.1) - evm.IntraBlockState.SubRefund(params.SstoreClearsScheduleRefundEIP2200) + evm.IntraBlockState().SubRefund(params.SstoreClearsScheduleRefundEIP2200) } else if value.IsZero() { // delete slot (2.2.1.2) - evm.IntraBlockState.AddRefund(params.SstoreClearsScheduleRefundEIP2200) + evm.IntraBlockState().AddRefund(params.SstoreClearsScheduleRefundEIP2200) } } if original.Eq(value) { if original.IsZero() { // reset to original inexistent slot (2.2.2.1) - evm.IntraBlockState.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200) + evm.IntraBlockState().AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200) } else { // reset to original existing slot (2.2.2.2) - evm.IntraBlockState.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200) + evm.IntraBlockState().AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200) } } return params.SloadGasEIP2200, nil // dirty update (2.2) @@ -336,11 +336,11 @@ func gasCall(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, memo transfersValue = !stack.Back(2).IsZero() address = common.Address(stack.Back(1).Bytes20()) ) - if evm.ChainRules.IsEIP158 { - if transfersValue && evm.IntraBlockState.Empty(address) { + if evm.ChainRules().IsEIP158 { + if transfersValue && evm.IntraBlockState().Empty(address) { gas += params.CallNewAccountGas } - } else if !evm.IntraBlockState.Exist(address) { + } else if !evm.IntraBlockState().Exist(address) { gas += params.CallNewAccountGas } if transfersValue { @@ -355,7 +355,7 @@ func gasCall(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, memo return 0, ErrGasUintOverflow } - evm.callGasTemp, err = callGas(evm.ChainRules.IsEIP150, contract.Gas, gas, stack.Back(0)) + evm.callGasTemp, err = callGas(evm.ChainRules().IsEIP150, contract.Gas, gas, stack.Back(0)) if err != nil { return 0, err } @@ -380,7 +380,7 @@ func gasCallCode(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { return 0, ErrGasUintOverflow } - evm.callGasTemp, err = callGas(evm.ChainRules.IsEIP150, contract.Gas, gas, stack.Back(0)) + evm.callGasTemp, err = callGas(evm.ChainRules().IsEIP150, contract.Gas, gas, stack.Back(0)) if err != nil { return 0, err } @@ -395,7 +395,7 @@ func gasDelegateCall(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memo if err != nil { return 0, err } - evm.callGasTemp, err = callGas(evm.ChainRules.IsEIP150, contract.Gas, gas, stack.Back(0)) + evm.callGasTemp, err = callGas(evm.ChainRules().IsEIP150, contract.Gas, gas, stack.Back(0)) if err != nil { return 0, err } @@ -411,7 +411,7 @@ func gasStaticCall(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory if err != nil { return 0, err } - evm.callGasTemp, err = callGas(evm.ChainRules.IsEIP150, contract.Gas, gas, stack.Back(0)) + evm.callGasTemp, err = callGas(evm.ChainRules().IsEIP150, contract.Gas, gas, stack.Back(0)) if err != nil { return 0, err } @@ -425,22 +425,22 @@ func gasStaticCall(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory func gasSelfdestruct(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) { var gas uint64 // EIP150 homestead gas reprice fork: - if evm.ChainRules.IsEIP150 { + if evm.ChainRules().IsEIP150 { gas = params.SelfdestructGasEIP150 var address = common.Address(stack.Back(0).Bytes20()) - if evm.ChainRules.IsEIP158 { + if evm.ChainRules().IsEIP158 { // if empty and transfers value - if evm.IntraBlockState.Empty(address) && !evm.IntraBlockState.GetBalance(contract.Address()).IsZero() { + if evm.IntraBlockState().Empty(address) && !evm.IntraBlockState().GetBalance(contract.Address()).IsZero() { gas += params.CreateBySelfdestructGas } - } else if !evm.IntraBlockState.Exist(address) { + } else if !evm.IntraBlockState().Exist(address) { gas += params.CreateBySelfdestructGas } } - if !evm.IntraBlockState.HasSuicided(contract.Address()) { - evm.IntraBlockState.AddRefund(params.SelfdestructRefundGas) + if !evm.IntraBlockState().HasSuicided(contract.Address()) { + evm.IntraBlockState().AddRefund(params.SelfdestructRefundGas) } return gas, nil } diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go index d02fe88c63..8d6eb2bc95 100644 --- a/core/vm/gas_table_test.go +++ b/core/vm/gas_table_test.go @@ -109,7 +109,7 @@ func TestEIP2200(t *testing.T) { if used := tt.gaspool - gas; used != tt.used { t.Errorf("test %d: gas used mismatch: have %v, want %v", i, used, tt.used) } - if refund := vmenv.IntraBlockState.GetRefund(); refund != tt.refund { + if refund := vmenv.IntraBlockState().GetRefund(); refund != tt.refund { t.Errorf("test %d: gas refund mismatch: have %v, want %v", i, refund, tt.refund) } }) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index d61e6faed0..4f60c0e0f7 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -287,12 +287,12 @@ func opAddress(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ func opBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { slot := callContext.stack.Peek() address := common.Address(slot.Bytes20()) - slot.Set(interpreter.evm.IntraBlockState.GetBalance(address)) + slot.Set(interpreter.evm.IntraBlockState().GetBalance(address)) return nil, nil } func opOrigin(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.Push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes())) + callContext.stack.Push(new(uint256.Int).SetBytes(interpreter.evm.TxContext().Origin.Bytes())) return nil, nil } func opCaller(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { @@ -371,7 +371,7 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *call func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { slot := callContext.stack.Peek() - slot.SetUint64(uint64(interpreter.evm.IntraBlockState.GetCodeSize(common.Address(slot.Bytes20())))) + slot.SetUint64(uint64(interpreter.evm.IntraBlockState().GetCodeSize(common.Address(slot.Bytes20())))) return nil, nil } @@ -407,7 +407,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx ) addr := common.Address(a.Bytes20()) len64 := length.Uint64() - codeCopy := getDataBig(interpreter.evm.IntraBlockState.GetCode(addr), &codeOffset, len64) + codeCopy := getDataBig(interpreter.evm.IntraBlockState().GetCode(addr), &codeOffset, len64) callContext.memory.Set(memOffset.Uint64(), len64, codeCopy) return nil, nil } @@ -441,16 +441,16 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { slot := callContext.stack.Peek() address := common.Address(slot.Bytes20()) - if interpreter.evm.IntraBlockState.Empty(address) { + if interpreter.evm.IntraBlockState().Empty(address) { slot.Clear() } else { - slot.SetBytes(interpreter.evm.IntraBlockState.GetCodeHash(address).Bytes()) + slot.SetBytes(interpreter.evm.IntraBlockState().GetCodeHash(address).Bytes()) } return nil, nil } func opGasprice(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - v, overflow := uint256.FromBig(interpreter.evm.GasPrice) + v, overflow := uint256.FromBig(interpreter.evm.TxContext().GasPrice) if overflow { return nil, fmt.Errorf("interpreter.evm.GasPrice higher than 2^256-1") } @@ -466,14 +466,14 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) return nil, nil } var upper, lower uint64 - upper = interpreter.evm.Context.BlockNumber + upper = interpreter.evm.Context().BlockNumber if upper < 257 { lower = 0 } else { lower = upper - 256 } if num64 >= lower && num64 < upper { - num.SetBytes(interpreter.evm.Context.GetHash(num64).Bytes()) + num.SetBytes(interpreter.evm.Context().GetHash(num64).Bytes()) } else { num.Clear() } @@ -481,24 +481,24 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) } func opCoinbase(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.Push(new(uint256.Int).SetBytes(interpreter.evm.Context.Coinbase.Bytes())) + callContext.stack.Push(new(uint256.Int).SetBytes(interpreter.evm.Context().Coinbase.Bytes())) return nil, nil } func opTimestamp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - v := new(uint256.Int).SetUint64(interpreter.evm.Context.Time) + v := new(uint256.Int).SetUint64(interpreter.evm.Context().Time) callContext.stack.Push(v) return nil, nil } func opNumber(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - v := new(uint256.Int).SetUint64(interpreter.evm.Context.BlockNumber) + v := new(uint256.Int).SetUint64(interpreter.evm.Context().BlockNumber) callContext.stack.Push(v) return nil, nil } func opDifficulty(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - v, overflow := uint256.FromBig(interpreter.evm.Context.Difficulty) + v, overflow := uint256.FromBig(interpreter.evm.Context().Difficulty) if overflow { return nil, fmt.Errorf("interpreter.evm.Context.Difficulty higher than 2^256-1") } @@ -507,10 +507,10 @@ func opDifficulty(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) } func opGasLimit(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - if interpreter.evm.Context.MaxGasLimit { + if interpreter.evm.Context().MaxGasLimit { callContext.stack.Push(new(uint256.Int).SetAllOne()) } else { - callContext.stack.Push(new(uint256.Int).SetUint64(interpreter.evm.Context.GasLimit)) + callContext.stack.Push(new(uint256.Int).SetUint64(interpreter.evm.Context().GasLimit)) } return nil, nil } @@ -542,7 +542,7 @@ func opMstore8(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ func opSload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { loc := callContext.stack.Peek() interpreter.hasherBuf = loc.Bytes32() - interpreter.evm.IntraBlockState.GetState(callContext.contract.Address(), &interpreter.hasherBuf, loc) + interpreter.evm.IntraBlockState().GetState(callContext.contract.Address(), &interpreter.hasherBuf, loc) return nil, nil } @@ -550,7 +550,7 @@ func opSstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] loc := callContext.stack.Pop() val := callContext.stack.Pop() interpreter.hasherBuf = loc.Bytes32() - interpreter.evm.IntraBlockState.SetState(callContext.contract.Address(), &interpreter.hasherBuf, val) + interpreter.evm.IntraBlockState().SetState(callContext.contract.Address(), &interpreter.hasherBuf, val) return nil, nil } @@ -559,8 +559,8 @@ func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by if valid, usedBitmap := callContext.contract.validJumpdest(&pos); !valid { if usedBitmap && interpreter.cfg.TraceJumpDest { log.Warn("Code Bitmap used for detecting invalid jump", - "tx", fmt.Sprintf("0x%x", interpreter.evm.TxContext.TxHash), - "block_num", interpreter.evm.Context.BlockNumber, + "tx", fmt.Sprintf("0x%x", interpreter.evm.TxContext().TxHash), + "block_num", interpreter.evm.Context().BlockNumber, ) } return nil, ErrInvalidJump @@ -575,8 +575,8 @@ func opJumpi(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]b if valid, usedBitmap := callContext.contract.validJumpdest(&pos); !valid { if usedBitmap && interpreter.cfg.TraceJumpDest { log.Warn("Code Bitmap used for detecting invalid jump", - "tx", fmt.Sprintf("0x%x", interpreter.evm.TxContext.TxHash), - "block_num", interpreter.evm.Context.BlockNumber, + "tx", fmt.Sprintf("0x%x", interpreter.evm.TxContext().TxHash), + "block_num", interpreter.evm.Context().BlockNumber, ) } return nil, ErrInvalidJump @@ -615,7 +615,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] input = callContext.memory.GetCopy(offset.Uint64(), size.Uint64()) gas = callContext.contract.Gas ) - if interpreter.evm.ChainRules.IsEIP150 { + if interpreter.evm.ChainRules().IsEIP150 { gas -= gas / 64 } // reuse size int for stackvalue @@ -629,7 +629,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] // homestead we must check for CodeStoreOutOfGasError (homestead only // rule) and treat as an error, if the ruleset is frontier we must // ignore this error and pretend the operation was successful. - if interpreter.evm.ChainRules.IsHomestead && suberr == ErrCodeStoreOutOfGas { + if interpreter.evm.ChainRules().IsHomestead && suberr == ErrCodeStoreOutOfGas { stackvalue.Clear() } else if suberr != nil && suberr != ErrCodeStoreOutOfGas { stackvalue.Clear() @@ -822,12 +822,12 @@ func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ beneficiary := callContext.stack.Pop() callerAddr := callContext.contract.Address() beneficiaryAddr := common.Address(beneficiary.Bytes20()) - balance := interpreter.evm.IntraBlockState.GetBalance(callerAddr) - interpreter.evm.IntraBlockState.AddBalance(beneficiaryAddr, balance) - if interpreter.evm.Config.Debug { - interpreter.evm.Config.Tracer.CaptureSelfDestruct(callerAddr, beneficiaryAddr, balance.ToBig()) + balance := interpreter.evm.IntraBlockState().GetBalance(callerAddr) + interpreter.evm.IntraBlockState().AddBalance(beneficiaryAddr, balance) + if interpreter.evm.Config().Debug { + interpreter.evm.Config().Tracer.CaptureSelfDestruct(callerAddr, beneficiaryAddr, balance.ToBig()) } - interpreter.evm.IntraBlockState.Suicide(callerAddr) + interpreter.evm.IntraBlockState().Suicide(callerAddr) return nil, nil } @@ -845,13 +845,13 @@ func makeLog(size int) executionFunc { } d := callContext.memory.GetCopy(mStart.Uint64(), mSize.Uint64()) - interpreter.evm.IntraBlockState.AddLog(&types.Log{ + interpreter.evm.IntraBlockState().AddLog(&types.Log{ Address: callContext.contract.Address(), Topics: topics, Data: d, // This is a non-consensus field, but assigned here because // core/state doesn't know the current block number. - BlockNumber: interpreter.evm.Context.BlockNumber, + BlockNumber: interpreter.evm.Context().BlockNumber, }) return nil, nil diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index a10f3a41b2..0e49201823 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -200,7 +200,7 @@ func TestAddMod(t *testing.T) { ContractHasTEVM: func(common.Hash) (bool, error) { return false, nil }, }, TxContext{}, nil, params.TestChainConfig, Config{}) stack = stack.New() - evmInterpreter = NewEVMInterpreter(env, env.Config) + evmInterpreter = NewEVMInterpreter(env, env.Config()) pc = uint64(0) ) tests := []struct { @@ -289,7 +289,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) { ContractHasTEVM: func(common.Hash) (bool, error) { return false, nil }, }, TxContext{}, nil, params.TestChainConfig, Config{}) stack = stack.New() - evmInterpreter = NewEVMInterpreter(env, env.Config) + evmInterpreter = NewEVMInterpreter(env, env.Config()) ) env.interpreter = evmInterpreter @@ -526,7 +526,7 @@ func TestOpMstore(t *testing.T) { }, TxContext{}, nil, params.TestChainConfig, Config{}) stack = stack.New() mem = NewMemory() - evmInterpreter = NewEVMInterpreter(env, env.Config) + evmInterpreter = NewEVMInterpreter(env, env.Config()) ) env.interpreter = evmInterpreter @@ -552,7 +552,7 @@ func BenchmarkOpMstore(bench *testing.B) { }, TxContext{}, nil, params.TestChainConfig, Config{}) stack = stack.New() mem = NewMemory() - evmInterpreter = NewEVMInterpreter(env, env.Config) + evmInterpreter = NewEVMInterpreter(env, env.Config()) ) env.interpreter = evmInterpreter @@ -575,7 +575,7 @@ func BenchmarkOpSHA3(bench *testing.B) { }, TxContext{}, nil, params.TestChainConfig, Config{}) stack = stack.New() mem = NewMemory() - evmInterpreter = NewEVMInterpreter(env, env.Config) + evmInterpreter = NewEVMInterpreter(env, env.Config()) ) env.interpreter = evmInterpreter mem.Resize(32) diff --git a/core/vm/interface.go b/core/vm/interface.go index f03fd8220c..57d4f28d5e 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -17,6 +17,7 @@ package vm import ( + "github.com/ledgerwatch/erigon/params" "math/big" "github.com/holiman/uint256" @@ -87,3 +88,14 @@ type CallContext interface { // Create a new contract Create(env *EVM, me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) } + +type VMInterface interface { + Reset(txCtx TxContext, ibs IntraBlockState) + Create(caller ContractRef, code []byte, gas uint64, value *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) + Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int, bailout bool) (ret []byte, leftOverGas uint64, err error) + Config() Config + ChainRules() params.Rules + Context() BlockContext + IntraBlockState() IntraBlockState + TxContext() TxContext +} diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index a6694c2aaa..6fb884a5fb 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -90,21 +90,21 @@ type VM struct { func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { var jt *JumpTable switch { - case evm.ChainRules.IsLondon: + case evm.ChainRules().IsLondon: jt = &londonInstructionSet - case evm.ChainRules.IsBerlin: + case evm.ChainRules().IsBerlin: jt = &berlinInstructionSet - case evm.ChainRules.IsIstanbul: + case evm.ChainRules().IsIstanbul: jt = &istanbulInstructionSet - case evm.ChainRules.IsConstantinople: + case evm.ChainRules().IsConstantinople: jt = &constantinopleInstructionSet - case evm.ChainRules.IsByzantium: + case evm.ChainRules().IsByzantium: jt = &byzantiumInstructionSet - case evm.ChainRules.IsEIP158: + case evm.ChainRules().IsEIP158: jt = &spuriousDragonInstructionSet - case evm.ChainRules.IsEIP150: + case evm.ChainRules().IsEIP150: jt = &tangerineWhistleInstructionSet - case evm.ChainRules.IsHomestead: + case evm.ChainRules().IsHomestead: jt = &homesteadInstructionSet default: jt = &frontierInstructionSet @@ -131,21 +131,21 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { func NewEVMInterpreterByVM(vm *VM) *EVMInterpreter { var jt *JumpTable switch { - case vm.evm.ChainRules.IsLondon: + case vm.evm.ChainRules().IsLondon: jt = &londonInstructionSet - case vm.evm.ChainRules.IsBerlin: + case vm.evm.ChainRules().IsBerlin: jt = &berlinInstructionSet - case vm.evm.ChainRules.IsIstanbul: + case vm.evm.ChainRules().IsIstanbul: jt = &istanbulInstructionSet - case vm.evm.ChainRules.IsConstantinople: + case vm.evm.ChainRules().IsConstantinople: jt = &constantinopleInstructionSet - case vm.evm.ChainRules.IsByzantium: + case vm.evm.ChainRules().IsByzantium: jt = &byzantiumInstructionSet - case vm.evm.ChainRules.IsEIP158: + case vm.evm.ChainRules().IsEIP158: jt = &spuriousDragonInstructionSet - case vm.evm.ChainRules.IsEIP150: + case vm.evm.ChainRules().IsEIP150: jt = &tangerineWhistleInstructionSet - case vm.evm.ChainRules.IsHomestead: + case vm.evm.ChainRules().IsHomestead: jt = &homesteadInstructionSet default: jt = &frontierInstructionSet @@ -262,7 +262,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack} } // If the operation is valid, enforce and write restrictions - if in.readOnly && in.evm.ChainRules.IsByzantium { + if in.readOnly && in.evm.ChainRules().IsByzantium { // If the interpreter is operating in readonly mode, make sure no // state-modifying operation is performed. The 3rd stack item // for a call operation is the value. Transferring value from one diff --git a/core/vm/logger.go b/core/vm/logger.go index fa7448f2d2..981aa14dca 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -196,7 +196,7 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui address = common.Hash(stack.Data[stack.Len()-1].Bytes32()) value uint256.Int ) - env.IntraBlockState.GetState(contract.Address(), &address, &value) + env.IntraBlockState().GetState(contract.Address(), &address, &value) l.storage[contract.Address()][address] = common.Hash(value.Bytes32()) } // capture SSTORE opcodes and record the written entry in the local storage. @@ -215,7 +215,7 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui copy(rdata, rData) } // create a new snapshot of the EVM. - log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, env.IntraBlockState.GetRefund(), err} + log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, env.IntraBlockState().GetRefund(), err} l.logs = append(l.logs, log) return nil } @@ -354,7 +354,7 @@ func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64 b := fmt.Sprintf("[%v]", strings.Join(a, ",")) fmt.Fprintf(t.out, "%10v |", b) } - fmt.Fprintf(t.out, "%10v |", env.IntraBlockState.GetRefund()) + fmt.Fprintf(t.out, "%10v |", env.IntraBlockState().GetRefund()) fmt.Fprintln(t.out, "") if err != nil { fmt.Fprintf(t.out, "Error: %v\n", err) diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go index e131c4b551..da9dfcb129 100644 --- a/core/vm/logger_json.go +++ b/core/vm/logger_json.go @@ -56,7 +56,7 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint MemorySize: memory.Len(), Storage: nil, Depth: depth, - RefundCounter: env.IntraBlockState.GetRefund(), + RefundCounter: env.IntraBlockState().GetRefund(), Err: err, } if !l.cfg.DisableMemory { diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index 599963ee36..4b54daa4a8 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -39,12 +39,12 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc { current uint256.Int cost = uint64(0) ) - evm.IntraBlockState.GetState(contract.Address(), &slot, ¤t) + evm.IntraBlockState().GetState(contract.Address(), &slot, ¤t) // Check slot presence in the access list - if addrPresent, slotPresent := evm.IntraBlockState.SlotInAccessList(contract.Address(), slot); !slotPresent { + if addrPresent, slotPresent := evm.IntraBlockState().SlotInAccessList(contract.Address(), slot); !slotPresent { cost = params.ColdSloadCostEIP2929 // If the caller cannot afford the cost, this change will be rolled back - evm.IntraBlockState.AddSlotToAccessList(contract.Address(), slot) + evm.IntraBlockState().AddSlotToAccessList(contract.Address(), slot) if !addrPresent { // Once we're done with YOLOv2 and schedule this for mainnet, might // be good to remove this panic here, which is just really a @@ -62,13 +62,13 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc { } var original uint256.Int slotCommited := common.Hash(x.Bytes32()) - evm.IntraBlockState.GetCommittedState(contract.Address(), &slotCommited, &original) + evm.IntraBlockState().GetCommittedState(contract.Address(), &slotCommited, &original) if original.Eq(¤t) { if original.IsZero() { // create slot (2.1.1) return cost + params.SstoreSetGasEIP2200, nil } if value.IsZero() { // delete slot (2.1.2b) - evm.IntraBlockState.AddRefund(clearingRefund) + evm.IntraBlockState().AddRefund(clearingRefund) } // EIP-2200 original clause: // return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2) @@ -76,23 +76,23 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc { } if !original.IsZero() { if current.IsZero() { // recreate slot (2.2.1.1) - evm.IntraBlockState.SubRefund(clearingRefund) + evm.IntraBlockState().SubRefund(clearingRefund) } else if value.IsZero() { // delete slot (2.2.1.2) - evm.IntraBlockState.AddRefund(clearingRefund) + evm.IntraBlockState().AddRefund(clearingRefund) } } if original.Eq(&value) { if original.IsZero() { // reset to original inexistent slot (2.2.2.1) // EIP 2200 Original clause: //evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200) - evm.IntraBlockState.AddRefund(params.SstoreSetGasEIP2200 - params.WarmStorageReadCostEIP2929) + evm.IntraBlockState().AddRefund(params.SstoreSetGasEIP2200 - params.WarmStorageReadCostEIP2929) } else { // reset to original existing slot (2.2.2.2) // EIP 2200 Original clause: // evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200) // - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST) // - SLOAD_GAS redefined as WARM_STORAGE_READ_COST // Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST - evm.IntraBlockState.AddRefund((params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929) - params.WarmStorageReadCostEIP2929) + evm.IntraBlockState().AddRefund((params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929) - params.WarmStorageReadCostEIP2929) } } // EIP-2200 original clause: @@ -110,10 +110,10 @@ func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memo loc := stack.Peek() slot := common.Hash(loc.Bytes32()) // Check slot presence in the access list - if _, slotPresent := evm.IntraBlockState.SlotInAccessList(contract.Address(), slot); !slotPresent { + if _, slotPresent := evm.IntraBlockState().SlotInAccessList(contract.Address(), slot); !slotPresent { // If the caller cannot afford the cost, this change will be rolled back // If he does afford it, we can skip checking the same thing later on, during execution - evm.IntraBlockState.AddSlotToAccessList(contract.Address(), slot) + evm.IntraBlockState().AddSlotToAccessList(contract.Address(), slot) return params.ColdSloadCostEIP2929, nil } return params.WarmStorageReadCostEIP2929, nil @@ -132,8 +132,8 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *stack.Stack, mem } addr := common.Address(stack.Peek().Bytes20()) // Check slot presence in the access list - if !evm.IntraBlockState.AddressInAccessList(addr) { - evm.IntraBlockState.AddAddressToAccessList(addr) + if !evm.IntraBlockState().AddressInAccessList(addr) { + evm.IntraBlockState().AddAddressToAccessList(addr) var overflow bool // We charge (cold-warm), since 'warm' is already charged as constantGas if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow { @@ -154,9 +154,9 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *stack.Stack, mem func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) { addr := common.Address(stack.Peek().Bytes20()) // Check slot presence in the access list - if !evm.IntraBlockState.AddressInAccessList(addr) { + if !evm.IntraBlockState().AddressInAccessList(addr) { // If the caller cannot afford the cost, this change will be rolled back - evm.IntraBlockState.AddAddressToAccessList(addr) + evm.IntraBlockState().AddAddressToAccessList(addr) // The warm storage read cost is already charged as constantGas return params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929, nil } @@ -167,12 +167,12 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc { return func(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) { addr := common.Address(stack.Back(1).Bytes20()) // Check slot presence in the access list - warmAccess := evm.IntraBlockState.AddressInAccessList(addr) + warmAccess := evm.IntraBlockState().AddressInAccessList(addr) // The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so // the cost to charge for cold access, if any, is Cold - Warm coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929 if !warmAccess { - evm.IntraBlockState.AddAddressToAccessList(addr) + evm.IntraBlockState().AddAddressToAccessList(addr) // Charge the remaining difference here already, to correctly calculate available // gas for call if !contract.UseGas(coldCost) { @@ -232,17 +232,17 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc { gas uint64 address = common.Address(stack.Peek().Bytes20()) ) - if !evm.IntraBlockState.AddressInAccessList(address) { + if !evm.IntraBlockState().AddressInAccessList(address) { // If the caller cannot afford the cost, this change will be rolled back - evm.IntraBlockState.AddAddressToAccessList(address) + evm.IntraBlockState().AddAddressToAccessList(address) gas = params.ColdAccountAccessCostEIP2929 } // if empty and transfers value - if evm.IntraBlockState.Empty(address) && !evm.IntraBlockState.GetBalance(contract.Address()).IsZero() { + if evm.IntraBlockState().Empty(address) && !evm.IntraBlockState().GetBalance(contract.Address()).IsZero() { gas += params.CreateBySelfdestructGas } - if refundsEnabled && !evm.IntraBlockState.HasSuicided(contract.Address()) { - evm.IntraBlockState.AddRefund(params.SelfdestructRefundGas) + if refundsEnabled && !evm.IntraBlockState().HasSuicided(contract.Address()) { + evm.IntraBlockState().AddRefund(params.SelfdestructRefundGas) } return gas, nil } diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 44b372f302..d072dd631b 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -128,7 +128,7 @@ func Execute(code, input []byte, cfg *Config, blockNr uint64) ([]byte, *state.In vmenv = NewEnv(cfg) sender = vm.AccountRef(cfg.Origin) ) - if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsBerlin { + if rules := cfg.ChainConfig.Rules(vmenv.Context().BlockNumber); rules.IsBerlin { cfg.State.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil) } cfg.State.CreateAccount(address, true) @@ -166,7 +166,7 @@ func Create(input []byte, cfg *Config, blockNr uint64) ([]byte, common.Address, vmenv = NewEnv(cfg) sender = vm.AccountRef(cfg.Origin) ) - if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsBerlin { + if rules := cfg.ChainConfig.Rules(vmenv.Context().BlockNumber); rules.IsBerlin { cfg.State.PrepareAccessList(cfg.Origin, nil, vm.ActivePrecompiles(rules), nil) } @@ -192,7 +192,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er sender := cfg.State.GetOrNewStateObject(cfg.Origin) statedb := cfg.State - if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsBerlin { + if rules := cfg.ChainConfig.Rules(vmenv.Context().BlockNumber); rules.IsBerlin { statedb.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil) } diff --git a/eth/tracers/tracer.go b/eth/tracers/tracer.go index 4584e801d2..2d0da2bc3a 100644 --- a/eth/tracers/tracer.go +++ b/eth/tracers/tracer.go @@ -568,13 +568,13 @@ func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost // Initialize the context if it wasn't done yet if !jst.inited { // Update list of precompiles based on current block - rules := env.ChainConfig().Rules(env.Context.BlockNumber) + rules := env.ChainConfig().Rules(env.Context().BlockNumber) jst.activePrecompiles = vm.ActivePrecompiles(rules) - jst.ctx["block"] = env.Context.BlockNumber + jst.ctx["block"] = env.Context().BlockNumber // Compute intrinsic gas - isHomestead := env.ChainRules.IsHomestead - isIstanbul := env.ChainRules.IsIstanbul + isHomestead := env.ChainRules().IsHomestead + isIstanbul := env.ChainRules().IsIstanbul var input []byte if data, ok := jst.ctx["input"].([]byte); ok { input = data @@ -595,13 +595,13 @@ func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost jst.stackWrapper.stack = stack jst.memoryWrapper.memory = memory jst.contractWrapper.contract = contract - jst.dbWrapper.db = env.IntraBlockState + jst.dbWrapper.db = env.IntraBlockState() *jst.pcValue = uint(pc) *jst.gasValue = uint(gas) *jst.costValue = uint(cost) *jst.depthValue = uint(depth) - *jst.refundValue = uint(env.IntraBlockState.GetRefund()) + *jst.refundValue = uint(env.IntraBlockState().GetRefund()) jst.errorValue = nil if err != nil { diff --git a/tests/state_test_util.go b/tests/state_test_util.go index ccfc363b80..860e2f8d87 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -230,10 +230,10 @@ func (t *StateTest) RunNoVerify(rules params.Rules, tx kv.RwTx, subtest StateSub statedb.RevertToSnapshot(snapshot) } - if err = statedb.FinalizeTx(evm.ChainRules, w); err != nil { + if err = statedb.FinalizeTx(evm.ChainRules(), w); err != nil { return nil, common.Hash{}, err } - if err = statedb.CommitBlock(evm.ChainRules, w); err != nil { + if err = statedb.CommitBlock(evm.ChainRules(), w); err != nil { return nil, common.Hash{}, err } // Generate hashed state diff --git a/turbo/transactions/tracing.go b/turbo/transactions/tracing.go index c7341802fb..b09f61ee29 100644 --- a/turbo/transactions/tracing.go +++ b/turbo/transactions/tracing.go @@ -66,7 +66,7 @@ func ComputeTxEnv(ctx context.Context, block *types.Block, cfg *params.ChainConf } // Ensure any modifications are committed to the state // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect - _ = statedb.FinalizeTx(vmenv.ChainRules, state.NewNoopWriter()) + _ = statedb.FinalizeTx(vmenv.ChainRules(), state.NewNoopWriter()) } return nil, vm.BlockContext{}, vm.TxContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %x", txIndex, blockHash) } @@ -239,7 +239,7 @@ func (l *JsonStreamLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, ga address = common.Hash(stack.Data[stack.Len()-1].Bytes32()) value uint256.Int ) - env.IntraBlockState.GetState(contract.Address(), &address, &value) + env.IntraBlockState().GetState(contract.Address(), &address, &value) l.storage[contract.Address()][address] = common.Hash(value.Bytes32()) outputStorage = true } -- GitLab