github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/core/multivm_processor.go (about) 1 // +build sputnikvm 2 3 package core 4 5 import ( 6 "math/big" 7 8 "github.com/ETCDEVTeam/sputnikvm-ffi/go/sputnikvm" 9 "github.com/ethereumproject/go-ethereum/common" 10 "github.com/ethereumproject/go-ethereum/core/state" 11 "github.com/ethereumproject/go-ethereum/core/types" 12 evm "github.com/ethereumproject/go-ethereum/core/vm" 13 "github.com/ethereumproject/go-ethereum/crypto" 14 "github.com/ethereumproject/go-ethereum/logger" 15 "github.com/ethereumproject/go-ethereum/logger/glog" 16 ) 17 18 const SputnikVMExists = true 19 20 // UseSputnikVM determines whether the VM will be Sputnik or Geth's native one. 21 // Awkward though it is to use a string variable, go's -ldflags relies on it being a constant string in order to be settable via -X from the command line, 22 // eg. -ldflags "-X core.UseSputnikVM=true". 23 var UseSputnikVM string = "false" 24 25 // Apply a transaction using the SputnikVM processor with the given 26 // chain config and state. Note that we use the name of the chain 27 // config to determine which hard fork to use so ClassicVM's gas table 28 // would not be used. 29 func ApplyMultiVmTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, totalUsedGas *big.Int) (*types.Receipt, evm.Logs, *big.Int, error) { 30 tx.SetSigner(config.GetSigner(header.Number)) 31 32 from, err := tx.From() 33 if err != nil { 34 return nil, nil, nil, err 35 } 36 vmtx := sputnikvm.Transaction{ 37 Caller: from, 38 GasPrice: tx.GasPrice(), 39 GasLimit: tx.Gas(), 40 Address: tx.To(), 41 Value: tx.Value(), 42 Input: tx.Data(), 43 Nonce: new(big.Int).SetUint64(tx.Nonce()), 44 } 45 vmheader := sputnikvm.HeaderParams{ 46 Beneficiary: header.Coinbase, 47 Timestamp: header.Time.Uint64(), 48 Number: header.Number, 49 Difficulty: header.Difficulty, 50 GasLimit: header.GasLimit, 51 } 52 53 currentNumber := header.Number 54 homesteadFork := config.ForkByName("Homestead") 55 eip150Fork := config.ForkByName("GasReprice") 56 eip160Fork := config.ForkByName("Diehard") 57 58 var vm *sputnikvm.VM 59 if state.StartingNonce == 0 { 60 if eip160Fork.Block != nil && currentNumber.Cmp(eip160Fork.Block) >= 0 { 61 vm = sputnikvm.NewEIP160(&vmtx, &vmheader) 62 } else if eip150Fork.Block != nil && currentNumber.Cmp(eip150Fork.Block) >= 0 { 63 vm = sputnikvm.NewEIP150(&vmtx, &vmheader) 64 } else if homesteadFork.Block != nil && currentNumber.Cmp(homesteadFork.Block) >= 0 { 65 vm = sputnikvm.NewHomestead(&vmtx, &vmheader) 66 } else { 67 vm = sputnikvm.NewFrontier(&vmtx, &vmheader) 68 } 69 } else if state.StartingNonce == 1048576 { 70 if eip160Fork.Block != nil && currentNumber.Cmp(eip160Fork.Block) >= 0 { 71 vm = sputnikvm.NewMordenEIP160(&vmtx, &vmheader) 72 } else if eip150Fork.Block != nil && currentNumber.Cmp(eip150Fork.Block) >= 0 { 73 vm = sputnikvm.NewMordenEIP150(&vmtx, &vmheader) 74 } else if homesteadFork.Block != nil && currentNumber.Cmp(homesteadFork.Block) >= 0 { 75 vm = sputnikvm.NewMordenHomestead(&vmtx, &vmheader) 76 } else { 77 vm = sputnikvm.NewMordenFrontier(&vmtx, &vmheader) 78 } 79 } else { 80 sputnikvm.SetCustomInitialNonce(big.NewInt(int64(state.StartingNonce))) 81 if eip160Fork.Block != nil && currentNumber.Cmp(eip160Fork.Block) >= 0 { 82 vm = sputnikvm.NewCustomEIP160(&vmtx, &vmheader) 83 } else if eip150Fork.Block != nil && currentNumber.Cmp(eip150Fork.Block) >= 0 { 84 vm = sputnikvm.NewCustomEIP150(&vmtx, &vmheader) 85 } else if homesteadFork.Block != nil && currentNumber.Cmp(homesteadFork.Block) >= 0 { 86 vm = sputnikvm.NewCustomHomestead(&vmtx, &vmheader) 87 } else { 88 vm = sputnikvm.NewCustomFrontier(&vmtx, &vmheader) 89 } 90 } 91 92 Loop: 93 for { 94 ret := vm.Fire() 95 switch ret.Typ() { 96 case sputnikvm.RequireNone: 97 break Loop 98 case sputnikvm.RequireAccount: 99 address := ret.Address() 100 if statedb.Exist(address) { 101 vm.CommitAccount(address, new(big.Int).SetUint64(statedb.GetNonce(address)), 102 statedb.GetBalance(address), statedb.GetCode(address)) 103 break 104 } 105 vm.CommitNonexist(address) 106 case sputnikvm.RequireAccountCode: 107 address := ret.Address() 108 if statedb.Exist(address) { 109 vm.CommitAccountCode(address, statedb.GetCode(address)) 110 break 111 } 112 vm.CommitNonexist(address) 113 case sputnikvm.RequireAccountStorage: 114 address := ret.Address() 115 key := common.BigToHash(ret.StorageKey()) 116 if statedb.Exist(address) { 117 value := statedb.GetState(address, key).Big() 118 sKey := ret.StorageKey() 119 vm.CommitAccountStorage(address, sKey, value) 120 break 121 } 122 vm.CommitNonexist(address) 123 case sputnikvm.RequireBlockhash: 124 number := ret.BlockNumber() 125 hash := common.Hash{} 126 if block := bc.GetBlockByNumber(number.Uint64()); block != nil && block.Number().Cmp(number) == 0 { 127 hash = block.Hash() 128 } 129 vm.CommitBlockhash(number, hash) 130 } 131 } 132 133 // VM execution is finished at this point. We apply changes to the statedb. 134 135 for _, account := range vm.AccountChanges() { 136 switch account.Typ() { 137 case sputnikvm.AccountChangeIncreaseBalance: 138 address := account.Address() 139 amount := account.ChangedAmount() 140 statedb.AddBalance(address, amount) 141 case sputnikvm.AccountChangeDecreaseBalance: 142 address := account.Address() 143 amount := account.ChangedAmount() 144 balance := new(big.Int).Sub(statedb.GetBalance(address), amount) 145 statedb.SetBalance(address, balance) 146 case sputnikvm.AccountChangeRemoved: 147 address := account.Address() 148 statedb.Suicide(address) 149 case sputnikvm.AccountChangeFull: 150 address := account.Address() 151 code := account.Code() 152 nonce := account.Nonce() 153 balance := account.Balance() 154 statedb.SetBalance(address, balance) 155 statedb.SetNonce(address, nonce.Uint64()) 156 statedb.SetCode(address, code) 157 for _, item := range account.ChangedStorage() { 158 statedb.SetState(address, common.BigToHash(item.Key), common.BigToHash(item.Value)) 159 } 160 case sputnikvm.AccountChangeCreate: 161 address := account.Address() 162 code := account.Code() 163 nonce := account.Nonce() 164 balance := account.Balance() 165 statedb.SetBalance(address, balance) 166 statedb.SetNonce(address, nonce.Uint64()) 167 statedb.SetCode(address, code) 168 for _, item := range account.Storage() { 169 statedb.SetState(address, common.BigToHash(item.Key), common.BigToHash(item.Value)) 170 } 171 default: 172 panic("unreachable") 173 } 174 } 175 for _, log := range vm.Logs() { 176 statelog := evm.NewLog(log.Address, log.Topics, log.Data, header.Number.Uint64()) 177 statedb.AddLog(*statelog) 178 } 179 usedGas := vm.UsedGas() 180 totalUsedGas.Add(totalUsedGas, usedGas) 181 182 receipt := types.NewReceipt(statedb.IntermediateRoot(false).Bytes(), totalUsedGas) 183 receipt.TxHash = tx.Hash() 184 receipt.GasUsed = new(big.Int).Set(totalUsedGas) 185 if vm.Failed() { 186 receipt.Status = types.TxFailure 187 } else { 188 receipt.Status = types.TxSuccess 189 } 190 if MessageCreatesContract(tx) { 191 receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce()) 192 } 193 194 logs := statedb.GetLogs(tx.Hash()) 195 receipt.Logs = logs 196 receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) 197 198 glog.V(logger.Debug).Infoln(receipt) 199 200 vm.Free() 201 return receipt, logs, totalUsedGas, nil 202 }