github.com/theQRL/go-zond@v0.2.1/cmd/zvm/internal/t8ntool/transition.go (about) 1 // Copyright 2020 The go-ethereum Authors 2 // This file is part of go-ethereum. 3 // 4 // go-ethereum 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-ethereum 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-ethereum. If not, see <http://www.gnu.org/licenses/>. 16 17 package t8ntool 18 19 import ( 20 "encoding/json" 21 "errors" 22 "fmt" 23 "math/big" 24 "os" 25 "path" 26 "strings" 27 28 "github.com/theQRL/go-qrllib/dilithium" 29 "github.com/theQRL/go-zond/common" 30 "github.com/theQRL/go-zond/common/hexutil" 31 "github.com/theQRL/go-zond/consensus/misc/eip1559" 32 "github.com/theQRL/go-zond/core" 33 "github.com/theQRL/go-zond/core/state" 34 "github.com/theQRL/go-zond/core/types" 35 "github.com/theQRL/go-zond/core/vm" 36 "github.com/theQRL/go-zond/crypto/pqcrypto" 37 "github.com/theQRL/go-zond/log" 38 "github.com/theQRL/go-zond/params" 39 "github.com/theQRL/go-zond/rlp" 40 "github.com/theQRL/go-zond/tests" 41 "github.com/theQRL/go-zond/zond/tracers/logger" 42 "github.com/urfave/cli/v2" 43 ) 44 45 const ( 46 ErrorZVM = 2 47 ErrorConfig = 3 48 ErrorMissingBlockhash = 4 49 50 ErrorJson = 10 51 ErrorIO = 11 52 ErrorRlp = 12 53 54 stdinSelector = "stdin" 55 ) 56 57 type NumberedError struct { 58 errorCode int 59 err error 60 } 61 62 func NewError(errorCode int, err error) *NumberedError { 63 return &NumberedError{errorCode, err} 64 } 65 66 func (n *NumberedError) Error() string { 67 return fmt.Sprintf("ERROR(%d): %v", n.errorCode, n.err.Error()) 68 } 69 70 func (n *NumberedError) ExitCode() int { 71 return n.errorCode 72 } 73 74 // compile-time conformance test 75 var ( 76 _ cli.ExitCoder = (*NumberedError)(nil) 77 ) 78 79 type input struct { 80 Alloc core.GenesisAlloc `json:"alloc,omitempty"` 81 Env *stEnv `json:"env,omitempty"` 82 Txs []*txWithKey `json:"txs,omitempty"` 83 TxRlp string `json:"txsRlp,omitempty"` 84 } 85 86 func Transition(ctx *cli.Context) error { 87 // Configure the go-ethereum logger 88 glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) 89 glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name))) 90 log.Root().SetHandler(glogger) 91 92 var ( 93 err error 94 tracer vm.ZVMLogger 95 ) 96 var getTracer func(txIndex int, txHash common.Hash) (vm.ZVMLogger, error) 97 98 baseDir, err := createBasedir(ctx) 99 if err != nil { 100 return NewError(ErrorIO, fmt.Errorf("failed creating output basedir: %v", err)) 101 } 102 if ctx.Bool(TraceFlag.Name) { 103 // Configure the ZVM logger 104 logConfig := &logger.Config{ 105 DisableStack: ctx.Bool(TraceDisableStackFlag.Name), 106 EnableMemory: ctx.Bool(TraceEnableMemoryFlag.Name), 107 EnableReturnData: ctx.Bool(TraceEnableReturnDataFlag.Name), 108 Debug: true, 109 } 110 var prevFile *os.File 111 // This one closes the last file 112 defer func() { 113 if prevFile != nil { 114 prevFile.Close() 115 } 116 }() 117 getTracer = func(txIndex int, txHash common.Hash) (vm.ZVMLogger, error) { 118 if prevFile != nil { 119 prevFile.Close() 120 } 121 traceFile, err := os.Create(path.Join(baseDir, fmt.Sprintf("trace-%d-%v.jsonl", txIndex, txHash.String()))) 122 if err != nil { 123 return nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err)) 124 } 125 prevFile = traceFile 126 return logger.NewJSONLogger(logConfig, traceFile), nil 127 } 128 } else { 129 getTracer = func(txIndex int, txHash common.Hash) (tracer vm.ZVMLogger, err error) { 130 return nil, nil 131 } 132 } 133 // We need to load three things: alloc, env and transactions. May be either in 134 // stdin input or in files. 135 // Check if anything needs to be read from stdin 136 var ( 137 prestate Prestate 138 txs types.Transactions // txs to apply 139 allocStr = ctx.String(InputAllocFlag.Name) 140 141 envStr = ctx.String(InputEnvFlag.Name) 142 txStr = ctx.String(InputTxsFlag.Name) 143 inputData = &input{} 144 ) 145 // Figure out the prestate alloc 146 if allocStr == stdinSelector || envStr == stdinSelector || txStr == stdinSelector { 147 decoder := json.NewDecoder(os.Stdin) 148 if err := decoder.Decode(inputData); err != nil { 149 return NewError(ErrorJson, fmt.Errorf("failed unmarshaling stdin: %v", err)) 150 } 151 } 152 if allocStr != stdinSelector { 153 if err := readFile(allocStr, "alloc", &inputData.Alloc); err != nil { 154 return err 155 } 156 } 157 prestate.Pre = inputData.Alloc 158 159 // Set the block environment 160 if envStr != stdinSelector { 161 var env stEnv 162 if err := readFile(envStr, "env", &env); err != nil { 163 return err 164 } 165 inputData.Env = &env 166 } 167 prestate.Env = *inputData.Env 168 169 vmConfig := vm.Config{ 170 Tracer: tracer, 171 } 172 // Construct the chainconfig 173 var chainConfig *params.ChainConfig 174 if cConf, extraEips, err := tests.GetChainConfig(ctx.String(ForknameFlag.Name)); err != nil { 175 return NewError(ErrorConfig, fmt.Errorf("failed constructing chain configuration: %v", err)) 176 } else { 177 chainConfig = cConf 178 vmConfig.ExtraEips = extraEips 179 } 180 // Set the chain id 181 chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name)) 182 183 if txs, err = loadTransactions(txStr, inputData, chainConfig); err != nil { 184 return err 185 } 186 if err := applyLondonChecks(&prestate.Env, chainConfig); err != nil { 187 return err 188 } 189 if err := applyShanghaiChecks(&prestate.Env, chainConfig); err != nil { 190 return err 191 } 192 if err := applyMergeChecks(&prestate.Env, chainConfig); err != nil { 193 return err 194 } 195 // Run the test and aggregate the result 196 s, result, err := prestate.Apply(vmConfig, chainConfig, txs, ctx.Int64(RewardFlag.Name), getTracer) 197 if err != nil { 198 return err 199 } 200 body, _ := rlp.EncodeToBytes(txs) 201 // Dump the excution result 202 collector := make(Alloc) 203 s.DumpToCollector(collector, nil) 204 return dispatchOutput(ctx, baseDir, result, collector, body) 205 } 206 207 // txWithKey is a helper-struct, to allow us to use the types.Transaction along with 208 // a `seed`-field, for input 209 type txWithKey struct { 210 key *dilithium.Dilithium 211 tx *types.Transaction 212 } 213 214 func (t *txWithKey) UnmarshalJSON(input []byte) error { 215 // Read the metadata, if present 216 type txMetadata struct { 217 Seed *string `json:"seed"` 218 } 219 var data txMetadata 220 if err := json.Unmarshal(input, &data); err != nil { 221 return err 222 } 223 if data.Seed != nil { 224 sd := *data.Seed 225 if dilithiumKey, err := pqcrypto.HexToDilithium(sd[2:]); err != nil { 226 return err 227 } else { 228 t.key = dilithiumKey 229 } 230 } 231 // Now, read the transaction itself 232 var tx types.Transaction 233 if err := json.Unmarshal(input, &tx); err != nil { 234 return err 235 } 236 t.tx = &tx 237 return nil 238 } 239 240 // signUnsignedTransactions converts the input txs to canonical transactions. 241 // 242 // The transactions can have two forms, either 243 // 1. unsigned or 244 // 2. signed 245 // 246 // For (1), signature, need so be zero, and the `seed` needs to be set. 247 // If so, we sign it here and now, with the given `seed` 248 // If the condition above is not met, then it's considered a signed transaction. 249 // 250 // To manage this, we read the transactions twice, first trying to read the seeds, 251 // and secondly to read them with the standard tx json format 252 253 func signUnsignedTransactions(txs []*txWithKey, signer types.Signer) (types.Transactions, error) { 254 var signedTxs []*types.Transaction 255 for i, tx := range txs { 256 var ( 257 signature = tx.tx.RawSignatureValue() 258 signed *types.Transaction 259 err error 260 ) 261 if tx.key == nil || len(signature) != 0 { 262 // Already signed 263 signedTxs = append(signedTxs, tx.tx) 264 continue 265 } 266 signed, err = types.SignTx(tx.tx, signer, tx.key) 267 if err != nil { 268 return nil, NewError(ErrorJson, fmt.Errorf("tx %d: failed to sign tx: %v", i, err)) 269 } 270 signedTxs = append(signedTxs, signed) 271 } 272 return signedTxs, nil 273 } 274 275 func loadTransactions(txStr string, inputData *input, chainConfig *params.ChainConfig) (types.Transactions, error) { 276 var txsWithKeys []*txWithKey 277 var signed types.Transactions 278 if txStr != stdinSelector { 279 data, err := os.ReadFile(txStr) 280 if err != nil { 281 return nil, NewError(ErrorIO, fmt.Errorf("failed reading txs file: %v", err)) 282 } 283 if strings.HasSuffix(txStr, ".rlp") { // A file containing an rlp list 284 var body hexutil.Bytes 285 if err := json.Unmarshal(data, &body); err != nil { 286 return nil, err 287 } 288 // Already signed transactions 289 if err := rlp.DecodeBytes(body, &signed); err != nil { 290 return nil, err 291 } 292 return signed, nil 293 } 294 if err := json.Unmarshal(data, &txsWithKeys); err != nil { 295 return nil, NewError(ErrorJson, fmt.Errorf("failed unmarshaling txs-file: %v", err)) 296 } 297 } else { 298 if len(inputData.TxRlp) > 0 { 299 // Decode the body of already signed transactions 300 body := common.FromHex(inputData.TxRlp) 301 // Already signed transactions 302 if err := rlp.DecodeBytes(body, &signed); err != nil { 303 return nil, err 304 } 305 return signed, nil 306 } 307 // JSON encoded transactions 308 txsWithKeys = inputData.Txs 309 } 310 // We may have to sign the transactions. 311 signer := types.MakeSigner(chainConfig) 312 return signUnsignedTransactions(txsWithKeys, signer) 313 } 314 315 func applyLondonChecks(env *stEnv, chainConfig *params.ChainConfig) error { 316 // Sanity check, to not `panic` in state_transition 317 if env.BaseFee != nil { 318 // Already set, base fee has precedent over parent base fee. 319 return nil 320 } 321 if env.ParentBaseFee == nil || env.Number == 0 { 322 return NewError(ErrorConfig, errors.New("EIP-1559 config but missing 'parentBaseFee' in env section")) 323 } 324 env.BaseFee = eip1559.CalcBaseFee(chainConfig, &types.Header{ 325 Number: new(big.Int).SetUint64(env.Number - 1), 326 BaseFee: env.ParentBaseFee, 327 GasUsed: env.ParentGasUsed, 328 GasLimit: env.ParentGasLimit, 329 }) 330 return nil 331 } 332 333 func applyShanghaiChecks(env *stEnv, chainConfig *params.ChainConfig) error { 334 if env.Withdrawals == nil { 335 return NewError(ErrorConfig, errors.New("shanghai config but missing 'withdrawals' in env section")) 336 } 337 return nil 338 } 339 340 func applyMergeChecks(env *stEnv, chainConfig *params.ChainConfig) error { 341 if env.Random == nil { 342 return NewError(ErrorConfig, errors.New("post-merge requires currentRandom to be defined in env")) 343 } 344 return nil 345 } 346 347 type Alloc map[common.Address]core.GenesisAccount 348 349 func (g Alloc) OnRoot(common.Hash) {} 350 351 func (g Alloc) OnAccount(addr *common.Address, dumpAccount state.DumpAccount) { 352 if addr == nil { 353 return 354 } 355 balance, _ := new(big.Int).SetString(dumpAccount.Balance, 10) 356 var storage map[common.Hash]common.Hash 357 if dumpAccount.Storage != nil { 358 storage = make(map[common.Hash]common.Hash) 359 for k, v := range dumpAccount.Storage { 360 storage[k] = common.HexToHash(v) 361 } 362 } 363 genesisAccount := core.GenesisAccount{ 364 Code: dumpAccount.Code, 365 Storage: storage, 366 Balance: balance, 367 Nonce: dumpAccount.Nonce, 368 } 369 g[*addr] = genesisAccount 370 } 371 372 // saveFile marshals the object to the given file 373 func saveFile(baseDir, filename string, data interface{}) error { 374 b, err := json.MarshalIndent(data, "", " ") 375 if err != nil { 376 return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err)) 377 } 378 location := path.Join(baseDir, filename) 379 if err = os.WriteFile(location, b, 0644); err != nil { 380 return NewError(ErrorIO, fmt.Errorf("failed writing output: %v", err)) 381 } 382 log.Info("Wrote file", "file", location) 383 return nil 384 } 385 386 // dispatchOutput writes the output data to either stderr or stdout, or to the specified 387 // files 388 func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, alloc Alloc, body hexutil.Bytes) error { 389 stdOutObject := make(map[string]interface{}) 390 stdErrObject := make(map[string]interface{}) 391 dispatch := func(baseDir, fName, name string, obj interface{}) error { 392 switch fName { 393 case "stdout": 394 stdOutObject[name] = obj 395 case "stderr": 396 stdErrObject[name] = obj 397 case "": 398 // don't save 399 default: // save to file 400 if err := saveFile(baseDir, fName, obj); err != nil { 401 return err 402 } 403 } 404 return nil 405 } 406 if err := dispatch(baseDir, ctx.String(OutputAllocFlag.Name), "alloc", alloc); err != nil { 407 return err 408 } 409 if err := dispatch(baseDir, ctx.String(OutputResultFlag.Name), "result", result); err != nil { 410 return err 411 } 412 if err := dispatch(baseDir, ctx.String(OutputBodyFlag.Name), "body", body); err != nil { 413 return err 414 } 415 if len(stdOutObject) > 0 { 416 b, err := json.MarshalIndent(stdOutObject, "", " ") 417 if err != nil { 418 return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err)) 419 } 420 os.Stdout.Write(b) 421 os.Stdout.WriteString("\n") 422 } 423 if len(stdErrObject) > 0 { 424 b, err := json.MarshalIndent(stdErrObject, "", " ") 425 if err != nil { 426 return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err)) 427 } 428 os.Stderr.Write(b) 429 os.Stderr.WriteString("\n") 430 } 431 return nil 432 }