github.com/ethereum/go-ethereum@v1.16.1/cmd/evm/internal/t8ntool/execution.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 "fmt" 21 "math/big" 22 23 "github.com/ethereum/go-ethereum/common" 24 "github.com/ethereum/go-ethereum/common/hexutil" 25 "github.com/ethereum/go-ethereum/common/math" 26 "github.com/ethereum/go-ethereum/consensus/ethash" 27 "github.com/ethereum/go-ethereum/consensus/misc" 28 "github.com/ethereum/go-ethereum/consensus/misc/eip4844" 29 "github.com/ethereum/go-ethereum/core" 30 "github.com/ethereum/go-ethereum/core/rawdb" 31 "github.com/ethereum/go-ethereum/core/state" 32 "github.com/ethereum/go-ethereum/core/tracing" 33 "github.com/ethereum/go-ethereum/core/types" 34 "github.com/ethereum/go-ethereum/core/vm" 35 "github.com/ethereum/go-ethereum/crypto" 36 "github.com/ethereum/go-ethereum/ethdb" 37 "github.com/ethereum/go-ethereum/log" 38 "github.com/ethereum/go-ethereum/params" 39 "github.com/ethereum/go-ethereum/rlp" 40 "github.com/ethereum/go-ethereum/trie" 41 "github.com/ethereum/go-ethereum/triedb" 42 "github.com/holiman/uint256" 43 "golang.org/x/crypto/sha3" 44 ) 45 46 type Prestate struct { 47 Env stEnv `json:"env"` 48 Pre types.GenesisAlloc `json:"pre"` 49 } 50 51 //go:generate go run github.com/fjl/gencodec -type ExecutionResult -field-override executionResultMarshaling -out gen_execresult.go 52 53 // ExecutionResult contains the execution status after running a state test, any 54 // error that might have occurred and a dump of the final state if requested. 55 type ExecutionResult struct { 56 StateRoot common.Hash `json:"stateRoot"` 57 TxRoot common.Hash `json:"txRoot"` 58 ReceiptRoot common.Hash `json:"receiptsRoot"` 59 LogsHash common.Hash `json:"logsHash"` 60 Bloom types.Bloom `json:"logsBloom" gencodec:"required"` 61 Receipts types.Receipts `json:"receipts"` 62 Rejected []*rejectedTx `json:"rejected,omitempty"` 63 Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"` 64 GasUsed math.HexOrDecimal64 `json:"gasUsed"` 65 BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"` 66 WithdrawalsRoot *common.Hash `json:"withdrawalsRoot,omitempty"` 67 CurrentExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"` 68 CurrentBlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed,omitempty"` 69 RequestsHash *common.Hash `json:"requestsHash,omitempty"` 70 Requests [][]byte `json:"requests"` 71 } 72 73 type executionResultMarshaling struct { 74 Requests []hexutil.Bytes `json:"requests"` 75 } 76 77 type ommer struct { 78 Delta uint64 `json:"delta"` 79 Address common.Address `json:"address"` 80 } 81 82 //go:generate go run github.com/fjl/gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go 83 type stEnv struct { 84 Coinbase common.Address `json:"currentCoinbase" gencodec:"required"` 85 Difficulty *big.Int `json:"currentDifficulty"` 86 Random *big.Int `json:"currentRandom"` 87 ParentDifficulty *big.Int `json:"parentDifficulty"` 88 ParentBaseFee *big.Int `json:"parentBaseFee,omitempty"` 89 ParentGasUsed uint64 `json:"parentGasUsed,omitempty"` 90 ParentGasLimit uint64 `json:"parentGasLimit,omitempty"` 91 GasLimit uint64 `json:"currentGasLimit" gencodec:"required"` 92 Number uint64 `json:"currentNumber" gencodec:"required"` 93 Timestamp uint64 `json:"currentTimestamp" gencodec:"required"` 94 ParentTimestamp uint64 `json:"parentTimestamp,omitempty"` 95 BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` 96 Ommers []ommer `json:"ommers,omitempty"` 97 Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"` 98 BaseFee *big.Int `json:"currentBaseFee,omitempty"` 99 ParentUncleHash common.Hash `json:"parentUncleHash"` 100 ExcessBlobGas *uint64 `json:"currentExcessBlobGas,omitempty"` 101 ParentExcessBlobGas *uint64 `json:"parentExcessBlobGas,omitempty"` 102 ParentBlobGasUsed *uint64 `json:"parentBlobGasUsed,omitempty"` 103 ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"` 104 } 105 106 type stEnvMarshaling struct { 107 Coinbase common.UnprefixedAddress 108 Difficulty *math.HexOrDecimal256 109 Random *math.HexOrDecimal256 110 ParentDifficulty *math.HexOrDecimal256 111 ParentBaseFee *math.HexOrDecimal256 112 ParentGasUsed math.HexOrDecimal64 113 ParentGasLimit math.HexOrDecimal64 114 GasLimit math.HexOrDecimal64 115 Number math.HexOrDecimal64 116 Timestamp math.HexOrDecimal64 117 ParentTimestamp math.HexOrDecimal64 118 BaseFee *math.HexOrDecimal256 119 ExcessBlobGas *math.HexOrDecimal64 120 ParentExcessBlobGas *math.HexOrDecimal64 121 ParentBlobGasUsed *math.HexOrDecimal64 122 } 123 124 type rejectedTx struct { 125 Index int `json:"index"` 126 Err string `json:"error"` 127 } 128 129 // Apply applies a set of transactions to a pre-state 130 func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, txIt txIterator, miningReward int64) (*state.StateDB, *ExecutionResult, []byte, error) { 131 // Capture errors for BLOCKHASH operation, if we haven't been supplied the 132 // required blockhashes 133 var hashError error 134 getHash := func(num uint64) common.Hash { 135 if pre.Env.BlockHashes == nil { 136 hashError = fmt.Errorf("getHash(%d) invoked, no blockhashes provided", num) 137 return common.Hash{} 138 } 139 h, ok := pre.Env.BlockHashes[math.HexOrDecimal64(num)] 140 if !ok { 141 hashError = fmt.Errorf("getHash(%d) invoked, blockhash for that block not provided", num) 142 } 143 return h 144 } 145 var ( 146 statedb = MakePreState(rawdb.NewMemoryDatabase(), pre.Pre) 147 signer = types.MakeSigner(chainConfig, new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp) 148 gaspool = new(core.GasPool) 149 blockHash = common.Hash{0x13, 0x37} 150 rejectedTxs []*rejectedTx 151 includedTxs types.Transactions 152 gasUsed = uint64(0) 153 blobGasUsed = uint64(0) 154 receipts = make(types.Receipts, 0) 155 txIndex = 0 156 ) 157 gaspool.AddGas(pre.Env.GasLimit) 158 vmContext := vm.BlockContext{ 159 CanTransfer: core.CanTransfer, 160 Transfer: core.Transfer, 161 Coinbase: pre.Env.Coinbase, 162 BlockNumber: new(big.Int).SetUint64(pre.Env.Number), 163 Time: pre.Env.Timestamp, 164 Difficulty: pre.Env.Difficulty, 165 GasLimit: pre.Env.GasLimit, 166 GetHash: getHash, 167 } 168 // If currentBaseFee is defined, add it to the vmContext. 169 if pre.Env.BaseFee != nil { 170 vmContext.BaseFee = new(big.Int).Set(pre.Env.BaseFee) 171 } 172 // If random is defined, add it to the vmContext. 173 if pre.Env.Random != nil { 174 rnd := common.BigToHash(pre.Env.Random) 175 vmContext.Random = &rnd 176 } 177 // Calculate the BlobBaseFee 178 var excessBlobGas uint64 179 if pre.Env.ExcessBlobGas != nil { 180 excessBlobGas = *pre.Env.ExcessBlobGas 181 header := &types.Header{ 182 Time: pre.Env.Timestamp, 183 ExcessBlobGas: pre.Env.ExcessBlobGas, 184 } 185 vmContext.BlobBaseFee = eip4844.CalcBlobFee(chainConfig, header) 186 } else { 187 // If it is not explicitly defined, but we have the parent values, we try 188 // to calculate it ourselves. 189 parentExcessBlobGas := pre.Env.ParentExcessBlobGas 190 parentBlobGasUsed := pre.Env.ParentBlobGasUsed 191 if parentExcessBlobGas != nil && parentBlobGasUsed != nil { 192 parent := &types.Header{ 193 Time: pre.Env.ParentTimestamp, 194 ExcessBlobGas: pre.Env.ParentExcessBlobGas, 195 BlobGasUsed: pre.Env.ParentBlobGasUsed, 196 } 197 header := &types.Header{ 198 Time: pre.Env.Timestamp, 199 ExcessBlobGas: &excessBlobGas, 200 } 201 excessBlobGas = eip4844.CalcExcessBlobGas(chainConfig, parent, header.Time) 202 vmContext.BlobBaseFee = eip4844.CalcBlobFee(chainConfig, header) 203 } 204 } 205 // If DAO is supported/enabled, we need to handle it here. In geth 'proper', it's 206 // done in StateProcessor.Process(block, ...), right before transactions are applied. 207 if chainConfig.DAOForkSupport && 208 chainConfig.DAOForkBlock != nil && 209 chainConfig.DAOForkBlock.Cmp(new(big.Int).SetUint64(pre.Env.Number)) == 0 { 210 misc.ApplyDAOHardFork(statedb) 211 } 212 evm := vm.NewEVM(vmContext, statedb, chainConfig, vmConfig) 213 if beaconRoot := pre.Env.ParentBeaconBlockRoot; beaconRoot != nil { 214 core.ProcessBeaconBlockRoot(*beaconRoot, evm) 215 } 216 if pre.Env.BlockHashes != nil && chainConfig.IsPrague(new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp) { 217 var ( 218 prevNumber = pre.Env.Number - 1 219 prevHash = pre.Env.BlockHashes[math.HexOrDecimal64(prevNumber)] 220 ) 221 core.ProcessParentBlockHash(prevHash, evm) 222 } 223 for i := 0; txIt.Next(); i++ { 224 tx, err := txIt.Tx() 225 if err != nil { 226 log.Warn("rejected tx", "index", i, "error", err) 227 rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()}) 228 continue 229 } 230 if tx.Type() == types.BlobTxType && vmContext.BlobBaseFee == nil { 231 errMsg := "blob tx used but field env.ExcessBlobGas missing" 232 log.Warn("rejected tx", "index", i, "hash", tx.Hash(), "error", errMsg) 233 rejectedTxs = append(rejectedTxs, &rejectedTx{i, errMsg}) 234 continue 235 } 236 msg, err := core.TransactionToMessage(tx, signer, pre.Env.BaseFee) 237 if err != nil { 238 log.Warn("rejected tx", "index", i, "hash", tx.Hash(), "error", err) 239 rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()}) 240 continue 241 } 242 txBlobGas := uint64(0) 243 if tx.Type() == types.BlobTxType { 244 txBlobGas = uint64(params.BlobTxBlobGasPerBlob * len(tx.BlobHashes())) 245 max := eip4844.MaxBlobGasPerBlock(chainConfig, pre.Env.Timestamp) 246 if used := blobGasUsed + txBlobGas; used > max { 247 err := fmt.Errorf("blob gas (%d) would exceed maximum allowance %d", used, max) 248 log.Warn("rejected tx", "index", i, "err", err) 249 rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()}) 250 continue 251 } 252 } 253 statedb.SetTxContext(tx.Hash(), txIndex) 254 var ( 255 snapshot = statedb.Snapshot() 256 prevGas = gaspool.Gas() 257 ) 258 if evm.Config.Tracer != nil && evm.Config.Tracer.OnTxStart != nil { 259 evm.Config.Tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) 260 } 261 // (ret []byte, usedGas uint64, failed bool, err error) 262 msgResult, err := core.ApplyMessage(evm, msg, gaspool) 263 if err != nil { 264 statedb.RevertToSnapshot(snapshot) 265 log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err) 266 rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()}) 267 gaspool.SetGas(prevGas) 268 if evm.Config.Tracer != nil && evm.Config.Tracer.OnTxEnd != nil { 269 evm.Config.Tracer.OnTxEnd(nil, err) 270 } 271 continue 272 } 273 includedTxs = append(includedTxs, tx) 274 if hashError != nil { 275 return nil, nil, nil, NewError(ErrorMissingBlockhash, hashError) 276 } 277 blobGasUsed += txBlobGas 278 gasUsed += msgResult.UsedGas 279 280 // Receipt: 281 { 282 var root []byte 283 if chainConfig.IsByzantium(vmContext.BlockNumber) { 284 statedb.Finalise(true) 285 } else { 286 root = statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber)).Bytes() 287 } 288 289 // Create a new receipt for the transaction, storing the intermediate root and 290 // gas used by the tx. 291 receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: gasUsed} 292 if msgResult.Failed() { 293 receipt.Status = types.ReceiptStatusFailed 294 } else { 295 receipt.Status = types.ReceiptStatusSuccessful 296 } 297 receipt.TxHash = tx.Hash() 298 receipt.GasUsed = msgResult.UsedGas 299 300 // If the transaction created a contract, store the creation address in the receipt. 301 if msg.To == nil { 302 receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce()) 303 } 304 305 // Set the receipt logs and create the bloom filter. 306 receipt.Logs = statedb.GetLogs(tx.Hash(), vmContext.BlockNumber.Uint64(), blockHash, vmContext.Time) 307 receipt.Bloom = types.CreateBloom(receipt) 308 309 // These three are non-consensus fields: 310 //receipt.BlockHash 311 //receipt.BlockNumber 312 receipt.TransactionIndex = uint(txIndex) 313 receipts = append(receipts, receipt) 314 if evm.Config.Tracer != nil && evm.Config.Tracer.OnTxEnd != nil { 315 evm.Config.Tracer.OnTxEnd(receipt, nil) 316 } 317 } 318 319 txIndex++ 320 } 321 statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber)) 322 // Add mining reward? (-1 means rewards are disabled) 323 if miningReward >= 0 { 324 // Add mining reward. The mining reward may be `0`, which only makes a difference in the cases 325 // where 326 // - the coinbase self-destructed, or 327 // - there are only 'bad' transactions, which aren't executed. In those cases, 328 // the coinbase gets no txfee, so isn't created, and thus needs to be touched 329 var ( 330 blockReward = big.NewInt(miningReward) 331 minerReward = new(big.Int).Set(blockReward) 332 perOmmer = new(big.Int).Rsh(blockReward, 5) 333 ) 334 for _, ommer := range pre.Env.Ommers { 335 // Add 1/32th for each ommer included 336 minerReward.Add(minerReward, perOmmer) 337 // Add (8-delta)/8 338 reward := big.NewInt(8) 339 reward.Sub(reward, new(big.Int).SetUint64(ommer.Delta)) 340 reward.Mul(reward, blockReward) 341 reward.Rsh(reward, 3) 342 statedb.AddBalance(ommer.Address, uint256.MustFromBig(reward), tracing.BalanceIncreaseRewardMineUncle) 343 } 344 statedb.AddBalance(pre.Env.Coinbase, uint256.MustFromBig(minerReward), tracing.BalanceIncreaseRewardMineBlock) 345 } 346 // Apply withdrawals 347 for _, w := range pre.Env.Withdrawals { 348 // Amount is in gwei, turn into wei 349 amount := new(big.Int).Mul(new(big.Int).SetUint64(w.Amount), big.NewInt(params.GWei)) 350 statedb.AddBalance(w.Address, uint256.MustFromBig(amount), tracing.BalanceIncreaseWithdrawal) 351 } 352 353 // Gather the execution-layer triggered requests. 354 var requests [][]byte 355 if chainConfig.IsPrague(vmContext.BlockNumber, vmContext.Time) { 356 requests = [][]byte{} 357 // EIP-6110 358 var allLogs []*types.Log 359 for _, receipt := range receipts { 360 allLogs = append(allLogs, receipt.Logs...) 361 } 362 if err := core.ParseDepositLogs(&requests, allLogs, chainConfig); err != nil { 363 return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not parse requests logs: %v", err)) 364 } 365 // EIP-7002 366 if err := core.ProcessWithdrawalQueue(&requests, evm); err != nil { 367 return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not process withdrawal requests: %v", err)) 368 } 369 // EIP-7251 370 if err := core.ProcessConsolidationQueue(&requests, evm); err != nil { 371 return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not process consolidation requests: %v", err)) 372 } 373 } 374 375 // Commit block 376 root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber), chainConfig.IsCancun(vmContext.BlockNumber, vmContext.Time)) 377 if err != nil { 378 return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not commit state: %v", err)) 379 } 380 execRs := &ExecutionResult{ 381 StateRoot: root, 382 TxRoot: types.DeriveSha(includedTxs, trie.NewStackTrie(nil)), 383 ReceiptRoot: types.DeriveSha(receipts, trie.NewStackTrie(nil)), 384 Bloom: types.MergeBloom(receipts), 385 LogsHash: rlpHash(statedb.Logs()), 386 Receipts: receipts, 387 Rejected: rejectedTxs, 388 Difficulty: (*math.HexOrDecimal256)(vmContext.Difficulty), 389 GasUsed: (math.HexOrDecimal64)(gasUsed), 390 BaseFee: (*math.HexOrDecimal256)(vmContext.BaseFee), 391 } 392 if pre.Env.Withdrawals != nil { 393 h := types.DeriveSha(types.Withdrawals(pre.Env.Withdrawals), trie.NewStackTrie(nil)) 394 execRs.WithdrawalsRoot = &h 395 } 396 if vmContext.BlobBaseFee != nil { 397 execRs.CurrentExcessBlobGas = (*math.HexOrDecimal64)(&excessBlobGas) 398 execRs.CurrentBlobGasUsed = (*math.HexOrDecimal64)(&blobGasUsed) 399 } 400 if requests != nil { 401 // Set requestsHash on block. 402 h := types.CalcRequestsHash(requests) 403 execRs.RequestsHash = &h 404 for i := range requests { 405 // remove prefix 406 requests[i] = requests[i][1:] 407 } 408 execRs.Requests = requests 409 } 410 411 // Re-create statedb instance with new root upon the updated database 412 // for accessing latest states. 413 statedb, err = state.New(root, statedb.Database()) 414 if err != nil { 415 return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not reopen state: %v", err)) 416 } 417 body, _ := rlp.EncodeToBytes(includedTxs) 418 return statedb, execRs, body, nil 419 } 420 421 func MakePreState(db ethdb.Database, accounts types.GenesisAlloc) *state.StateDB { 422 tdb := triedb.NewDatabase(db, &triedb.Config{Preimages: true}) 423 sdb := state.NewDatabase(tdb, nil) 424 statedb, _ := state.New(types.EmptyRootHash, sdb) 425 for addr, a := range accounts { 426 statedb.SetCode(addr, a.Code) 427 statedb.SetNonce(addr, a.Nonce, tracing.NonceChangeGenesis) 428 statedb.SetBalance(addr, uint256.MustFromBig(a.Balance), tracing.BalanceIncreaseGenesisBalance) 429 for k, v := range a.Storage { 430 statedb.SetState(addr, k, v) 431 } 432 } 433 // Commit and re-open to start with a clean state. 434 root, _ := statedb.Commit(0, false, false) 435 statedb, _ = state.New(root, sdb) 436 return statedb 437 } 438 439 func rlpHash(x interface{}) (h common.Hash) { 440 hw := sha3.NewLegacyKeccak256() 441 rlp.Encode(hw, x) 442 hw.Sum(h[:0]) 443 return h 444 } 445 446 // calcDifficulty is based on ethash.CalcDifficulty. This method is used in case 447 // the caller does not provide an explicit difficulty, but instead provides only 448 // parent timestamp + difficulty. 449 // Note: this method only works for ethash engine. 450 func calcDifficulty(config *params.ChainConfig, number, currentTime, parentTime uint64, 451 parentDifficulty *big.Int, parentUncleHash common.Hash) *big.Int { 452 uncleHash := parentUncleHash 453 if uncleHash == (common.Hash{}) { 454 uncleHash = types.EmptyUncleHash 455 } 456 parent := &types.Header{ 457 ParentHash: common.Hash{}, 458 UncleHash: uncleHash, 459 Difficulty: parentDifficulty, 460 Number: new(big.Int).SetUint64(number - 1), 461 Time: parentTime, 462 } 463 return ethash.CalcDifficulty(config, currentTime, parent) 464 }