good morning!!!!

Skip to content
Snippets Groups Projects
transition.go 7.82 KiB
Newer Older
  • Learn to ignore specific revisions
  • // Copyright 2020 The go-ethereum Authors
    // This file is part of go-ethereum.
    //
    // go-ethereum is free software: you can redistribute it and/or modify
    // it under the terms of the GNU General Public License as published by
    // the Free Software Foundation, either version 3 of the License, or
    // (at your option) any later version.
    //
    // go-ethereum is distributed in the hope that it will be useful,
    // but WITHOUT ANY WARRANTY; without even the implied warranty of
    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    // GNU General Public License for more details.
    //
    // You should have received a copy of the GNU General Public License
    // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    
    package t8ntool
    
    import (
    	"encoding/json"
    	"fmt"
    	"io/ioutil"
    	"math/big"
    	"os"
    
    	"github.com/ethereum/go-ethereum/common"
    	"github.com/ethereum/go-ethereum/core"
    	"github.com/ethereum/go-ethereum/core/state"
    	"github.com/ethereum/go-ethereum/core/types"
    	"github.com/ethereum/go-ethereum/core/vm"
    	"github.com/ethereum/go-ethereum/log"
    	"github.com/ethereum/go-ethereum/params"
    	"github.com/ethereum/go-ethereum/tests"
    	"gopkg.in/urfave/cli.v1"
    )
    
    const (
    	ErrorEVM              = 2
    	ErrorVMConfig         = 3
    	ErrorMissingBlockhash = 4
    
    	ErrorJson = 10
    	ErrorIO   = 11
    
    	stdinSelector = "stdin"
    )
    
    type NumberedError struct {
    	errorCode int
    	err       error
    }
    
    func NewError(errorCode int, err error) *NumberedError {
    	return &NumberedError{errorCode, err}
    }
    
    func (n *NumberedError) Error() string {
    	return fmt.Sprintf("ERROR(%d): %v", n.errorCode, n.err.Error())
    }
    
    func (n *NumberedError) Code() int {
    	return n.errorCode
    }
    
    type input struct {
    	Alloc core.GenesisAlloc  `json:"alloc,omitempty"`
    	Env   *stEnv             `json:"env,omitempty"`
    	Txs   types.Transactions `json:"txs,omitempty"`
    }
    
    func Main(ctx *cli.Context) error {
    	// Configure the go-ethereum logger
    	glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
    	glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
    	log.Root().SetHandler(glogger)
    
    	var (
    		err    error
    		tracer vm.Tracer
    	)
    	var getTracer func(txIndex int) (vm.Tracer, error)
    
    	if ctx.Bool(TraceFlag.Name) {
    		// Configure the EVM logger
    		logConfig := &vm.LogConfig{
    			DisableStack:  ctx.Bool(TraceDisableStackFlag.Name),
    			DisableMemory: ctx.Bool(TraceDisableMemoryFlag.Name),
    			Debug:         true,
    		}
    		var prevFile *os.File
    		// This one closes the last file
    		defer func() {
    			if prevFile != nil {
    				prevFile.Close()
    			}
    		}()
    		getTracer = func(txIndex int) (vm.Tracer, error) {
    			if prevFile != nil {
    				prevFile.Close()
    			}
    			traceFile, err := os.Create(fmt.Sprintf("trace-%d.jsonl", txIndex))
    			if err != nil {
    				return nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err))
    			}
    			prevFile = traceFile
    			return vm.NewJSONLogger(logConfig, traceFile), nil
    		}
    	} else {
    		getTracer = func(txIndex int) (tracer vm.Tracer, err error) {
    			return nil, nil
    		}
    	}
    	// We need to load three things: alloc, env and transactions. May be either in
    	// stdin input or in files.
    	// Check if anything needs to be read from stdin
    	var (
    		prestate Prestate
    		txs      types.Transactions // txs to apply
    		allocStr = ctx.String(InputAllocFlag.Name)
    
    		envStr    = ctx.String(InputEnvFlag.Name)
    		txStr     = ctx.String(InputTxsFlag.Name)
    		inputData = &input{}
    	)
    
    	if allocStr == stdinSelector || envStr == stdinSelector || txStr == stdinSelector {
    		decoder := json.NewDecoder(os.Stdin)
    		decoder.Decode(inputData)
    	}
    	if allocStr != stdinSelector {
    		inFile, err := os.Open(allocStr)
    		if err != nil {
    			return NewError(ErrorIO, fmt.Errorf("failed reading alloc file: %v", err))
    		}
    		defer inFile.Close()
    		decoder := json.NewDecoder(inFile)
    		if err := decoder.Decode(&inputData.Alloc); err != nil {
    			return NewError(ErrorJson, fmt.Errorf("Failed unmarshaling alloc-file: %v", err))
    		}
    	}
    
    	if envStr != stdinSelector {
    		inFile, err := os.Open(envStr)
    		if err != nil {
    			return NewError(ErrorIO, fmt.Errorf("failed reading env file: %v", err))
    		}
    		defer inFile.Close()
    		decoder := json.NewDecoder(inFile)
    		var env stEnv
    		if err := decoder.Decode(&env); err != nil {
    			return NewError(ErrorJson, fmt.Errorf("Failed unmarshaling env-file: %v", err))
    		}
    		inputData.Env = &env
    	}
    
    	if txStr != stdinSelector {
    		inFile, err := os.Open(txStr)
    		if err != nil {
    			return NewError(ErrorIO, fmt.Errorf("failed reading txs file: %v", err))
    		}
    		defer inFile.Close()
    		decoder := json.NewDecoder(inFile)
    		var txs types.Transactions
    		if err := decoder.Decode(&txs); err != nil {
    			return NewError(ErrorJson, fmt.Errorf("Failed unmarshaling txs-file: %v", err))
    		}
    		inputData.Txs = txs
    	}
    
    	prestate.Pre = inputData.Alloc
    	prestate.Env = *inputData.Env
    	txs = inputData.Txs
    
    	// Iterate over all the tests, run them and aggregate the results
    	vmConfig := vm.Config{
    		Tracer: tracer,
    		Debug:  (tracer != nil),
    	}
    	// Construct the chainconfig
    	var chainConfig *params.ChainConfig
    	if cConf, extraEips, err := tests.GetChainConfig(ctx.String(ForknameFlag.Name)); err != nil {
    		return NewError(ErrorVMConfig, fmt.Errorf("Failed constructing chain configuration: %v", err))
    	} else {
    		chainConfig = cConf
    		vmConfig.ExtraEips = extraEips
    	}
    	// Set the chain id
    	chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name))
    
    	// Run the test and aggregate the result
    	state, result, err := prestate.Apply(vmConfig, chainConfig, txs, ctx.Int64(RewardFlag.Name), getTracer)
    	if err != nil {
    		return err
    	}
    	// Dump the excution result
    	//postAlloc := state.DumpGenesisFormat(false, false, false)
    	collector := make(Alloc)
    	state.DumpToCollector(collector, false, false, false, nil, -1)
    	return dispatchOutput(ctx, result, collector)
    
    }
    
    type Alloc map[common.Address]core.GenesisAccount
    
    func (g Alloc) OnRoot(common.Hash) {}
    
    func (g Alloc) OnAccount(addr common.Address, dumpAccount state.DumpAccount) {
    	balance, _ := new(big.Int).SetString(dumpAccount.Balance, 10)
    	var storage map[common.Hash]common.Hash
    	if dumpAccount.Storage != nil {
    		storage = make(map[common.Hash]common.Hash)
    		for k, v := range dumpAccount.Storage {
    			storage[k] = common.HexToHash(v)
    		}
    	}
    	genesisAccount := core.GenesisAccount{
    		Code:    common.FromHex(dumpAccount.Code),
    		Storage: storage,
    		Balance: balance,
    		Nonce:   dumpAccount.Nonce,
    	}
    	g[addr] = genesisAccount
    }
    
    // saveFile marshalls the object to the given file
    func saveFile(filename string, data interface{}) error {
    	b, err := json.MarshalIndent(data, "", " ")
    	if err != nil {
    		return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
    	}
    	if err = ioutil.WriteFile(filename, b, 0644); err != nil {
    		return NewError(ErrorIO, fmt.Errorf("failed writing output: %v", err))
    	}
    	return nil
    }
    
    // dispatchOutput writes the output data to either stderr or stdout, or to the specified
    // files
    func dispatchOutput(ctx *cli.Context, result *ExecutionResult, alloc Alloc) error {
    	stdOutObject := make(map[string]interface{})
    	stdErrObject := make(map[string]interface{})
    	dispatch := func(fName, name string, obj interface{}) error {
    		switch fName {
    		case "stdout":
    			stdOutObject[name] = obj
    		case "stderr":
    			stdErrObject[name] = obj
    		default: // save to file
    			if err := saveFile(fName, obj); err != nil {
    				return err
    			}
    		}
    		return nil
    	}
    	if err := dispatch(ctx.String(OutputAllocFlag.Name), "alloc", alloc); err != nil {
    		return err
    	}
    	if err := dispatch(ctx.String(OutputResultFlag.Name), "result", result); err != nil {
    		return err
    	}
    	if len(stdOutObject) > 0 {
    		b, err := json.MarshalIndent(stdOutObject, "", " ")
    		if err != nil {
    			return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
    		}
    		os.Stdout.Write(b)
    	}
    	if len(stdErrObject) > 0 {
    		b, err := json.MarshalIndent(stdErrObject, "", " ")
    		if err != nil {
    			return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
    		}
    		os.Stderr.Write(b)
    	}
    	return nil
    }