github.com/core-coin/go-core/v2@v2.1.9/cmd/cvm/internal/t8ntool/transition.go (about)

     1  // Copyright 2020 by the Authors
     2  // This file is part of go-core.
     3  //
     4  // go-core is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // go-core is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-core. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package t8ntool
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"io/ioutil"
    23  	"math/big"
    24  	"os"
    25  	"path"
    26  
    27  	"gopkg.in/urfave/cli.v1"
    28  
    29  	"github.com/core-coin/go-core/v2/common"
    30  	"github.com/core-coin/go-core/v2/core"
    31  	"github.com/core-coin/go-core/v2/core/state"
    32  	"github.com/core-coin/go-core/v2/core/types"
    33  	"github.com/core-coin/go-core/v2/core/vm"
    34  	"github.com/core-coin/go-core/v2/log"
    35  	"github.com/core-coin/go-core/v2/params"
    36  	"github.com/core-coin/go-core/v2/tests"
    37  )
    38  
    39  const (
    40  	ErrorCVM              = 2
    41  	ErrorVMConfig         = 3
    42  	ErrorMissingBlockhash = 4
    43  
    44  	ErrorJson = 10
    45  	ErrorIO   = 11
    46  
    47  	stdinSelector = "stdin"
    48  )
    49  
    50  type NumberedError struct {
    51  	errorCode int
    52  	err       error
    53  }
    54  
    55  func NewError(errorCode int, err error) *NumberedError {
    56  	return &NumberedError{errorCode, err}
    57  }
    58  
    59  func (n *NumberedError) Error() string {
    60  	return fmt.Sprintf("ERROR(%d): %v", n.errorCode, n.err.Error())
    61  }
    62  
    63  func (n *NumberedError) Code() int {
    64  	return n.errorCode
    65  }
    66  
    67  type input struct {
    68  	Alloc core.GenesisAlloc  `json:"alloc,omitempty"`
    69  	Env   *stEnv             `json:"env,omitempty"`
    70  	Txs   types.Transactions `json:"txs,omitempty"`
    71  }
    72  
    73  func Main(ctx *cli.Context) error {
    74  	// Configure the go-core logger
    75  	glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
    76  	glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
    77  	log.Root().SetHandler(glogger)
    78  
    79  	var (
    80  		err     error
    81  		tracer  vm.Tracer
    82  		baseDir = ""
    83  	)
    84  	var getTracer func(txIndex int, txHash common.Hash) (vm.Tracer, error)
    85  
    86  	// If user specified a basedir, make sure it exists
    87  	if ctx.IsSet(OutputBasedir.Name) {
    88  		if base := ctx.String(OutputBasedir.Name); len(base) > 0 {
    89  			err := os.MkdirAll(base, 0755) // //rw-r--r--
    90  			if err != nil {
    91  				return NewError(ErrorIO, fmt.Errorf("failed creating output basedir: %v", err))
    92  			}
    93  			baseDir = base
    94  		}
    95  	}
    96  	if ctx.Bool(TraceFlag.Name) {
    97  		// Configure the CVM logger
    98  		logConfig := &vm.LogConfig{
    99  			DisableStack:      ctx.Bool(TraceDisableStackFlag.Name),
   100  			DisableMemory:     ctx.Bool(TraceDisableMemoryFlag.Name),
   101  			DisableReturnData: ctx.Bool(TraceDisableReturnDataFlag.Name),
   102  			Debug:             true,
   103  		}
   104  		var prevFile *os.File
   105  		// This one closes the last file
   106  		defer func() {
   107  			if prevFile != nil {
   108  				prevFile.Close()
   109  			}
   110  		}()
   111  		getTracer = func(txIndex int, txHash common.Hash) (vm.Tracer, error) {
   112  			if prevFile != nil {
   113  				prevFile.Close()
   114  			}
   115  			traceFile, err := os.Create(path.Join(baseDir, fmt.Sprintf("trace-%d-%v.jsonl", txIndex, txHash.String())))
   116  			if err != nil {
   117  				return nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err))
   118  			}
   119  			prevFile = traceFile
   120  			return vm.NewJSONLogger(logConfig, traceFile), nil
   121  		}
   122  	} else {
   123  		getTracer = func(txIndex int, txHash common.Hash) (tracer vm.Tracer, err error) {
   124  			return nil, nil
   125  		}
   126  	}
   127  	// We need to load three things: alloc, env and transactions. May be either in
   128  	// stdin input or in files.
   129  	// Check if anything needs to be read from stdin
   130  	var (
   131  		prestate Prestate
   132  		txs      types.Transactions // txs to apply
   133  		allocStr = ctx.String(InputAllocFlag.Name)
   134  
   135  		envStr    = ctx.String(InputEnvFlag.Name)
   136  		txStr     = ctx.String(InputTxsFlag.Name)
   137  		inputData = &input{}
   138  	)
   139  
   140  	if allocStr == stdinSelector || envStr == stdinSelector || txStr == stdinSelector {
   141  		decoder := json.NewDecoder(os.Stdin)
   142  		decoder.Decode(inputData)
   143  	}
   144  	if allocStr != stdinSelector {
   145  		inFile, err := os.Open(allocStr)
   146  		if err != nil {
   147  			return NewError(ErrorIO, fmt.Errorf("failed reading alloc file: %v", err))
   148  		}
   149  		defer inFile.Close()
   150  		decoder := json.NewDecoder(inFile)
   151  		if err := decoder.Decode(&inputData.Alloc); err != nil {
   152  			return NewError(ErrorJson, fmt.Errorf("Failed unmarshaling alloc-file: %v", err))
   153  		}
   154  	}
   155  
   156  	if envStr != stdinSelector {
   157  		inFile, err := os.Open(envStr)
   158  		if err != nil {
   159  			return NewError(ErrorIO, fmt.Errorf("failed reading env file: %v", err))
   160  		}
   161  		defer inFile.Close()
   162  		decoder := json.NewDecoder(inFile)
   163  		var env stEnv
   164  		if err := decoder.Decode(&env); err != nil {
   165  			return NewError(ErrorJson, fmt.Errorf("Failed unmarshaling env-file: %v", err))
   166  		}
   167  		inputData.Env = &env
   168  	}
   169  
   170  	if txStr != stdinSelector {
   171  		inFile, err := os.Open(txStr)
   172  		if err != nil {
   173  			return NewError(ErrorIO, fmt.Errorf("failed reading txs file: %v", err))
   174  		}
   175  		defer inFile.Close()
   176  		decoder := json.NewDecoder(inFile)
   177  		var txs types.Transactions
   178  		if err := decoder.Decode(&txs); err != nil {
   179  			return NewError(ErrorJson, fmt.Errorf("Failed unmarshaling txs-file: %v", err))
   180  		}
   181  		inputData.Txs = txs
   182  	}
   183  
   184  	prestate.Pre = inputData.Alloc
   185  	prestate.Env = *inputData.Env
   186  	txs = inputData.Txs
   187  
   188  	// Iterate over all the tests, run them and aggregate the results
   189  	vmConfig := vm.Config{
   190  		Tracer: tracer,
   191  		Debug:  (tracer != nil),
   192  	}
   193  	// Construct the chainconfig
   194  	var chainConfig *params.ChainConfig
   195  	if cConf, _, err := tests.GetChainConfig(ctx.String(ForknameFlag.Name)); err != nil {
   196  		return NewError(ErrorVMConfig, fmt.Errorf("Failed constructing chain configuration: %v", err))
   197  	} else {
   198  		chainConfig = cConf
   199  	}
   200  	// Set the chain id
   201  	chainConfig.NetworkID = big.NewInt(ctx.Int64(NetworkIDFlag.Name))
   202  
   203  	// Run the test and aggregate the result
   204  	state, result, err := prestate.Apply(vmConfig, chainConfig, txs, ctx.Int64(RewardFlag.Name), getTracer)
   205  	if err != nil {
   206  		return err
   207  	}
   208  	// Dump the excution result
   209  	//postAlloc := state.DumpGenesisFormat(false, false, false)
   210  	collector := make(Alloc)
   211  	state.DumpToCollector(collector, false, false, false, nil, -1)
   212  	return dispatchOutput(ctx, baseDir, result, collector)
   213  
   214  }
   215  
   216  type Alloc map[common.Address]core.GenesisAccount
   217  
   218  func (g Alloc) OnRoot(common.Hash) {}
   219  
   220  func (g Alloc) OnAccount(addr common.Address, dumpAccount state.DumpAccount) {
   221  	balance, _ := new(big.Int).SetString(dumpAccount.Balance, 10)
   222  	var storage map[common.Hash]common.Hash
   223  	if dumpAccount.Storage != nil {
   224  		storage = make(map[common.Hash]common.Hash)
   225  		for k, v := range dumpAccount.Storage {
   226  			storage[k] = common.HexToHash(v)
   227  		}
   228  	}
   229  	genesisAccount := core.GenesisAccount{
   230  		Code:    common.FromHex(dumpAccount.Code),
   231  		Storage: storage,
   232  		Balance: balance,
   233  		Nonce:   dumpAccount.Nonce,
   234  	}
   235  	g[addr] = genesisAccount
   236  }
   237  
   238  // saveFile marshalls the object to the given file
   239  func saveFile(baseDir, filename string, data interface{}) error {
   240  	b, err := json.MarshalIndent(data, "", " ")
   241  	if err != nil {
   242  		return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
   243  	}
   244  	if err = ioutil.WriteFile(path.Join(baseDir, filename), b, 0644); err != nil {
   245  		return NewError(ErrorIO, fmt.Errorf("failed writing output: %v", err))
   246  	}
   247  	return nil
   248  }
   249  
   250  // dispatchOutput writes the output data to either stderr or stdout, or to the specified
   251  // files
   252  func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, alloc Alloc) error {
   253  	stdOutObject := make(map[string]interface{})
   254  	stdErrObject := make(map[string]interface{})
   255  	dispatch := func(baseDir, fName, name string, obj interface{}) error {
   256  		switch fName {
   257  		case "stdout":
   258  			stdOutObject[name] = obj
   259  		case "stderr":
   260  			stdErrObject[name] = obj
   261  		default: // save to file
   262  			if err := saveFile(baseDir, fName, obj); err != nil {
   263  				return err
   264  			}
   265  		}
   266  		return nil
   267  	}
   268  	if err := dispatch(baseDir, ctx.String(OutputAllocFlag.Name), "alloc", alloc); err != nil {
   269  		return err
   270  	}
   271  	if err := dispatch(baseDir, ctx.String(OutputResultFlag.Name), "result", result); err != nil {
   272  		return err
   273  	}
   274  	if len(stdOutObject) > 0 {
   275  		b, err := json.MarshalIndent(stdOutObject, "", " ")
   276  		if err != nil {
   277  			return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
   278  		}
   279  		os.Stdout.Write(b)
   280  	}
   281  	if len(stdErrObject) > 0 {
   282  		b, err := json.MarshalIndent(stdErrObject, "", " ")
   283  		if err != nil {
   284  			return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
   285  		}
   286  		os.Stderr.Write(b)
   287  	}
   288  	return nil
   289  }