github.com/core-coin/go-core/v2@v2.1.9/cmd/cvm/internal/t8ntool/execution.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 "fmt" 21 "math/big" 22 "os" 23 24 "golang.org/x/crypto/sha3" 25 26 "github.com/core-coin/go-core/v2/xcbdb" 27 28 "github.com/core-coin/go-core/v2/common" 29 "github.com/core-coin/go-core/v2/common/math" 30 "github.com/core-coin/go-core/v2/core" 31 "github.com/core-coin/go-core/v2/core/rawdb" 32 "github.com/core-coin/go-core/v2/core/state" 33 "github.com/core-coin/go-core/v2/core/types" 34 "github.com/core-coin/go-core/v2/core/vm" 35 "github.com/core-coin/go-core/v2/crypto" 36 "github.com/core-coin/go-core/v2/log" 37 "github.com/core-coin/go-core/v2/params" 38 "github.com/core-coin/go-core/v2/rlp" 39 "github.com/core-coin/go-core/v2/trie" 40 ) 41 42 type Prestate struct { 43 Env stEnv `json:"env"` 44 Pre core.GenesisAlloc `json:"pre"` 45 } 46 47 // ExecutionResult contains the execution status after running a state test, any 48 // error that might have occurred and a dump of the final state if requested. 49 type ExecutionResult struct { 50 StateRoot common.Hash `json:"stateRoot"` 51 TxRoot common.Hash `json:"txRoot"` 52 ReceiptRoot common.Hash `json:"receiptRoot"` 53 LogsHash common.Hash `json:"logsHash"` 54 Bloom types.Bloom `json:"logsBloom" gencodec:"required"` 55 Receipts types.Receipts `json:"receipts"` 56 Rejected []int `json:"rejected,omitempty"` 57 } 58 59 type ommer struct { 60 Delta uint64 `json:"delta"` 61 Address common.Address `json:"address"` 62 } 63 64 //go:generate gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go 65 type stEnv struct { 66 Coinbase common.Address `json:"currentCoinbase" gencodec:"required"` 67 Difficulty *big.Int `json:"currentDifficulty" gencodec:"required"` 68 EnergyLimit uint64 `json:"currentEnergyLimit" gencodec:"required"` 69 Number uint64 `json:"currentNumber" gencodec:"required"` 70 Timestamp uint64 `json:"currentTimestamp" gencodec:"required"` 71 BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` 72 Ommers []ommer `json:"ommers,omitempty"` 73 } 74 75 type stEnvMarshaling struct { 76 Coinbase common.UnprefixedAddress 77 Difficulty *math.HexOrDecimal256 78 EnergyLimit math.HexOrDecimal64 79 Number math.HexOrDecimal64 80 Timestamp math.HexOrDecimal64 81 } 82 83 // Apply applies a set of transactions to a pre-state 84 func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, 85 txs types.Transactions, miningReward int64, 86 getTracerFn func(txIndex int, txHash common.Hash) (tracer vm.Tracer, err error)) (*state.StateDB, *ExecutionResult, error) { 87 88 // Capture errors for BLOCKHASH operation, if we haven't been supplied the 89 // required blockhashes 90 var hashError error 91 getHash := func(num uint64) common.Hash { 92 if pre.Env.BlockHashes == nil { 93 hashError = fmt.Errorf("getHash(%d) invoked, no blockhashes provided", num) 94 return common.Hash{} 95 } 96 h, ok := pre.Env.BlockHashes[math.HexOrDecimal64(num)] 97 if !ok { 98 hashError = fmt.Errorf("getHash(%d) invoked, blockhash for that block not provided", num) 99 } 100 return h 101 } 102 var ( 103 statedb = MakePreState(rawdb.NewMemoryDatabase(), pre.Pre) 104 signer = types.MakeSigner(chainConfig.NetworkID) 105 energypool = new(core.EnergyPool) 106 blockHash = common.Hash{0x13, 0x37} 107 rejectedTxs []int 108 includedTxs types.Transactions 109 energyUsed = uint64(0) 110 receipts = make(types.Receipts, 0) 111 txIndex = 0 112 ) 113 energypool.AddEnergy(pre.Env.EnergyLimit) 114 vmContext := vm.BlockContext{ 115 CanTransfer: core.CanTransfer, 116 Transfer: core.Transfer, 117 Coinbase: pre.Env.Coinbase, 118 BlockNumber: new(big.Int).SetUint64(pre.Env.Number), 119 Time: new(big.Int).SetUint64(pre.Env.Timestamp), 120 Difficulty: pre.Env.Difficulty, 121 EnergyLimit: pre.Env.EnergyLimit, 122 GetHash: getHash, 123 } 124 125 for i, tx := range txs { 126 msg, err := tx.AsMessage(signer) 127 if err != nil { 128 log.Info("rejected tx", "index", i, "hash", tx.Hash(), "error", err) 129 rejectedTxs = append(rejectedTxs, i) 130 continue 131 } 132 tracer, err := getTracerFn(txIndex, tx.Hash()) 133 if err != nil { 134 return nil, nil, err 135 } 136 vmConfig.Tracer = tracer 137 vmConfig.Debug = (tracer != nil) 138 statedb.Prepare(tx.Hash(), blockHash, txIndex) 139 txContext := core.NewCVMTxContext(msg) 140 141 cvm := vm.NewCVM(vmContext, txContext, statedb, chainConfig, vmConfig) 142 snapshot := statedb.Snapshot() 143 // (ret []byte, usedEnergy uint64, failed bool, err error) 144 msgResult, err := core.ApplyMessage(cvm, msg, energypool) 145 if err != nil { 146 statedb.RevertToSnapshot(snapshot) 147 log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From(), "error", err) 148 rejectedTxs = append(rejectedTxs, i) 149 continue 150 } 151 includedTxs = append(includedTxs, tx) 152 if hashError != nil { 153 return nil, nil, NewError(ErrorMissingBlockhash, hashError) 154 } 155 energyUsed += msgResult.UsedEnergy 156 // Create a new receipt for the transaction, storing the intermediate root and energy used by the tx 157 { 158 var root []byte 159 statedb.Finalise(true) 160 161 receipt := types.NewReceipt(root, msgResult.Failed(), energyUsed) 162 receipt.TxHash = tx.Hash() 163 receipt.EnergyUsed = msgResult.UsedEnergy 164 // if the transaction created a contract, store the creation address in the receipt. 165 if msg.To() == nil { 166 receipt.ContractAddress = crypto.CreateAddress(cvm.TxContext.Origin, tx.Nonce()) 167 } 168 // Set the receipt logs and create a bloom for filtering 169 receipt.Logs = statedb.GetLogs(tx.Hash()) 170 receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) 171 // These three are non-consensus fields 172 //receipt.BlockHash 173 //receipt.BlockNumber = 174 receipt.TransactionIndex = uint(txIndex) 175 receipts = append(receipts, receipt) 176 } 177 txIndex++ 178 } 179 statedb.IntermediateRoot(true) 180 // Add mining reward? 181 if miningReward > 0 { 182 // Add mining reward. The mining reward may be `0`, which only makes a difference in the cases 183 // where 184 // - the coinbase suicided, or 185 // - there are only 'bad' transactions, which aren't executed. In those cases, 186 // the coinbase gets no txfee, so isn't created, and thus needs to be touched 187 var ( 188 blockReward = big.NewInt(miningReward) 189 minerReward = new(big.Int).Set(blockReward) 190 perOmmer = new(big.Int).Div(blockReward, big.NewInt(32)) 191 ) 192 for _, ommer := range pre.Env.Ommers { 193 // Add 1/32th for each ommer included 194 minerReward.Add(minerReward, perOmmer) 195 // Add (8-delta)/8 196 reward := big.NewInt(8) 197 reward.Sub(reward, big.NewInt(0).SetUint64(ommer.Delta)) 198 reward.Mul(reward, blockReward) 199 reward.Div(reward, big.NewInt(8)) 200 statedb.AddBalance(ommer.Address, reward) 201 } 202 statedb.AddBalance(pre.Env.Coinbase, minerReward) 203 } 204 // Commit block 205 root, err := statedb.Commit(true) 206 if err != nil { 207 fmt.Fprintf(os.Stderr, "Could not commit state: %v", err) 208 return nil, nil, NewError(ErrorCVM, fmt.Errorf("could not commit state: %v", err)) 209 } 210 execRs := &ExecutionResult{ 211 StateRoot: root, 212 TxRoot: types.DeriveSha(includedTxs, new(trie.Trie)), 213 ReceiptRoot: types.DeriveSha(receipts, new(trie.Trie)), 214 Bloom: types.CreateBloom(receipts), 215 LogsHash: rlpHash(statedb.Logs()), 216 Receipts: receipts, 217 Rejected: rejectedTxs, 218 } 219 return statedb, execRs, nil 220 } 221 222 func MakePreState(db xcbdb.Database, accounts core.GenesisAlloc) *state.StateDB { 223 sdb := state.NewDatabase(db) 224 statedb, _ := state.New(common.Hash{}, sdb, nil) 225 for addr, a := range accounts { 226 statedb.SetCode(addr, a.Code) 227 statedb.SetNonce(addr, a.Nonce) 228 statedb.SetBalance(addr, a.Balance) 229 for k, v := range a.Storage { 230 statedb.SetState(addr, k, v) 231 } 232 } 233 // Commit and re-open to start with a clean state. 234 root, _ := statedb.Commit(false) 235 statedb, _ = state.New(root, sdb, nil) 236 return statedb 237 } 238 239 func rlpHash(x interface{}) (h common.Hash) { 240 hw := sha3.New256() 241 rlp.Encode(hw, x) 242 hw.Sum(h[:0]) 243 return h 244 }