github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/core/state_processor.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser 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 // The go-ethereum library 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 Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package core 18 19 import ( 20 "errors" 21 "fmt" 22 23 "github.com/kisexp/xdchain/common" 24 "github.com/kisexp/xdchain/consensus" 25 "github.com/kisexp/xdchain/consensus/misc" 26 "github.com/kisexp/xdchain/core/mps" 27 "github.com/kisexp/xdchain/core/state" 28 "github.com/kisexp/xdchain/core/types" 29 "github.com/kisexp/xdchain/core/vm" 30 "github.com/kisexp/xdchain/crypto" 31 "github.com/kisexp/xdchain/log" 32 "github.com/kisexp/xdchain/params" 33 "github.com/kisexp/xdchain/permission/core" 34 "github.com/kisexp/xdchain/private" 35 ) 36 37 // StateProcessor is a basic Processor, which takes care of transitioning 38 // state from one point to another. 39 // 40 // StateProcessor implements Processor. 41 type StateProcessor struct { 42 config *params.ChainConfig // Chain configuration options 43 bc *BlockChain // Canonical block chain 44 engine consensus.Engine // Consensus engine used for block rewards 45 } 46 47 // NewStateProcessor initialises a new StateProcessor. 48 func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consensus.Engine) *StateProcessor { 49 return &StateProcessor{ 50 config: config, 51 bc: bc, 52 engine: engine, 53 } 54 } 55 56 // Process processes the state changes according to the Ethereum rules by running 57 // the transaction messages using the statedb and applying any rewards to both 58 // the processor (coinbase) and any included uncles. 59 // 60 // Process returns the receipts and logs accumulated during the process and 61 // returns the amount of gas that was used in the process. If any of the 62 // transactions failed to execute due to insufficient gas it will return an error. 63 // 64 // Quorum: Private transactions are handled for the following: 65 // 66 // 1. On original single private state (SPS) design 67 // 2. On multiple private states (MPS) design 68 // 3. Contract extension callback (p.bc.CheckAndSetPrivateState) 69 func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, privateStateRepo mps.PrivateStateRepository, cfg vm.Config) (types.Receipts, types.Receipts, []*types.Log, uint64, error) { 70 71 var ( 72 receipts types.Receipts 73 usedGas = new(uint64) 74 header = block.Header() 75 allLogs []*types.Log 76 gp = new(GasPool).AddGas(block.GasLimit()) 77 78 privateReceipts types.Receipts 79 ) 80 // Mutate the block and state according to any hard-fork specs 81 if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { 82 misc.ApplyDAOHardFork(statedb) 83 } 84 blockContext := NewEVMBlockContext(header, p.bc, nil) 85 // Iterate over and process the individual transactions 86 for i, tx := range block.Transactions() { 87 mpsReceipt, err := handleMPS(i, tx, gp, usedGas, cfg, statedb, privateStateRepo, p.config, p.bc, header, false) 88 if err != nil { 89 return nil, nil, nil, 0, err 90 } 91 92 // handling transaction in 2 scenarios: 93 // 1. For MPS, the target private state being applied would be the EmptyPrivateState. 94 // This must be last to avoid contract address collisions. 95 // 2. For orignal SPS design, the target private state is the single private state 96 // 97 // in both cases, privateStateRepo is responsible to return the appropriate 98 // private state for execution and a bool flag to enable the privacy execution 99 privateStateDB, err := privateStateRepo.DefaultState() 100 if err != nil { 101 return nil, nil, nil, 0, err 102 } 103 privateStateDB.Prepare(tx.Hash(), block.Hash(), i) 104 statedb.Prepare(tx.Hash(), block.Hash(), i) 105 106 privateStateDBToUse := PrivateStateDBForTxn(p.config.IsQuorum, tx, statedb, privateStateDB) 107 108 // Quorum - check for account permissions to execute the transaction 109 if core.IsV2Permission() { 110 if err := core.CheckAccountPermission(tx.From(), tx.To(), tx.Value(), tx.Data(), tx.Gas(), tx.GasPrice()); err != nil { 111 return nil, nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) 112 } 113 } 114 115 if p.config.IsQuorum && tx.GasPrice() != nil && tx.GasPrice().Cmp(common.Big0) > 0 { 116 return nil, nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), ErrInvalidGasPrice) 117 } 118 119 msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number)) 120 if err != nil { 121 return nil, nil, nil, 0, err 122 } 123 124 // Quorum: this tx needs to be applied as if we were not a party 125 msg = msg.WithEmptyPrivateData(privateStateRepo.IsMPS() && tx.IsPrivate()) 126 127 // the same transaction object is used for multiple executions (clear the privacy metadata - it should be updated after privacyManager.receive) 128 // when running in parallel for multiple private states is implemented - a copy of the tx may be used 129 tx.SetTxPrivacyMetadata(nil) 130 131 txContext := NewEVMTxContext(msg) 132 vmenv := vm.NewEVM(blockContext, txContext, statedb, privateStateDBToUse, p.config, cfg) 133 vmenv.SetCurrentTX(tx) 134 receipt, privateReceipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, privateStateDB, header, tx, usedGas, vmenv, cfg, privateStateRepo.IsMPS(), privateStateRepo) 135 if err != nil { 136 return nil, nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) 137 } 138 139 receipts = append(receipts, receipt) 140 allLogs = append(allLogs, receipt.Logs...) 141 142 // if the private receipt is nil this means the tx was public 143 // and we do not need to apply the additional logic. 144 if privateReceipt != nil { 145 newPrivateReceipt, privateLogs := HandlePrivateReceipt(receipt, privateReceipt, mpsReceipt, tx, privateStateDB, privateStateRepo, p.bc) 146 privateReceipts = append(privateReceipts, newPrivateReceipt) 147 allLogs = append(allLogs, privateLogs...) 148 } 149 } 150 // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) 151 p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles()) 152 153 return receipts, privateReceipts, allLogs, *usedGas, nil 154 } 155 156 // Quorum 157 func HandlePrivateReceipt(receipt *types.Receipt, privateReceipt *types.Receipt, mpsReceipt *types.Receipt, tx *types.Transaction, privateStateDB *state.StateDB, privateStateRepo mps.PrivateStateRepository, bc *BlockChain) (*types.Receipt, []*types.Log) { 158 var ( 159 privateLogs []*types.Log 160 ) 161 162 if tx.IsPrivacyMarker() { 163 // This was a public privacy marker transaction, so we need to handle two scenarios: 164 // 1) MPS: privateReceipt is an auxiliary MPS receipt which contains actual private receipts in PSReceipts[] 165 // 2) non-MPS: privateReceipt is the actual receipt for the inner private transaction 166 // In both cases we return a receipt for the public PMT, which holds the private receipt(s) in PSReceipts[], 167 // and we then discard the privateReceipt. 168 if privateStateRepo != nil && privateStateRepo.IsMPS() { 169 receipt.PSReceipts = privateReceipt.PSReceipts 170 privateLogs = append(privateLogs, privateReceipt.Logs...) 171 } else { 172 receipt.PSReceipts = make(map[types.PrivateStateIdentifier]*types.Receipt) 173 receipt.PSReceipts[privateStateRepo.DefaultStateMetadata().ID] = privateReceipt 174 privateLogs = append(privateLogs, privateReceipt.Logs...) 175 bc.CheckAndSetPrivateState(privateReceipt.Logs, privateStateDB, privateStateRepo.DefaultStateMetadata().ID) 176 } 177 178 // There should be no auxiliary receipt from MPS execution, just logging in case this ever occurs 179 if mpsReceipt != nil { 180 log.Error("Unexpected MPS auxiliary receipt, when processing a privacy marker transaction") 181 } 182 return privateReceipt, privateLogs 183 } else { 184 // This was a regular private transaction. 185 privateLogs = append(privateLogs, privateReceipt.Logs...) 186 bc.CheckAndSetPrivateState(privateReceipt.Logs, privateStateDB, privateStateRepo.DefaultStateMetadata().ID) 187 188 // handling the auxiliary receipt from MPS execution 189 if mpsReceipt != nil { 190 privateReceipt.PSReceipts = mpsReceipt.PSReceipts 191 privateLogs = append(privateLogs, mpsReceipt.Logs...) 192 } 193 return privateReceipt, privateLogs 194 } 195 } 196 197 // Quorum 198 // returns the privateStateDB to be used for a transaction 199 func PrivateStateDBForTxn(isQuorum bool, tx *types.Transaction, stateDb, privateStateDB *state.StateDB) *state.StateDB { 200 if isQuorum && (tx.IsPrivate() || tx.IsPrivacyMarker()) { 201 return privateStateDB 202 } 203 return stateDb 204 } 205 206 // Quorum 207 // handling MPS scenario for a private transaction 208 // 209 // handleMPS returns the auxiliary receipt and not the standard receipt 210 func handleMPS(ti int, tx *types.Transaction, gp *GasPool, usedGas *uint64, cfg vm.Config, statedb *state.StateDB, privateStateRepo mps.PrivateStateRepository, config *params.ChainConfig, bc ChainContext, header *types.Header, applyOnPartiesOnly bool) (mpsReceipt *types.Receipt, err error) { 211 if tx.IsPrivate() && privateStateRepo != nil && privateStateRepo.IsMPS() { 212 publicStateDBFactory := func() *state.StateDB { 213 db := statedb.Copy() 214 db.Prepare(tx.Hash(), header.Hash(), ti) 215 return db 216 } 217 privateStateDBFactory := func(psi types.PrivateStateIdentifier) (*state.StateDB, error) { 218 db, err := privateStateRepo.StatePSI(psi) 219 if err != nil { 220 return nil, err 221 } 222 db.Prepare(tx.Hash(), header.Hash(), ti) 223 return db, nil 224 } 225 mpsReceipt, err = ApplyTransactionOnMPS(config, bc, nil, gp, publicStateDBFactory, privateStateDBFactory, header, tx, usedGas, cfg, privateStateRepo, applyOnPartiesOnly) 226 } 227 return 228 } 229 230 // Quorum 231 // ApplyTransactionOnMPS runs the transaction on multiple private states which 232 // the transaction is designated to. 233 // 234 // For each designated private state, the transaction is ran only ONCE. 235 // 236 // ApplyTransactionOnMPS returns the auxiliary receipt which is mainly used to capture 237 // multiple private receipts and logs array. Logs are decorated with types.PrivateStateIdentifier 238 // 239 // The originalGP gas pool will not be modified 240 func ApplyTransactionOnMPS(config *params.ChainConfig, bc ChainContext, author *common.Address, originalGP *GasPool, 241 publicStateDBFactory func() *state.StateDB, privateStateDBFactory func(psi types.PrivateStateIdentifier) (*state.StateDB, error), 242 header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config, privateStateRepo mps.PrivateStateRepository, applyOnPartiesOnly bool) (*types.Receipt, error) { 243 244 mpsReceipt := &types.Receipt{ 245 QuorumReceiptExtraData: types.QuorumReceiptExtraData{ 246 PSReceipts: make(map[types.PrivateStateIdentifier]*types.Receipt), 247 }, 248 Logs: make([]*types.Log, 0), 249 } 250 _, managedParties, _, _, err := private.P.Receive(common.BytesToEncryptedPayloadHash(tx.Data())) 251 if err != nil { 252 return nil, err 253 } 254 targetPsi := make(map[types.PrivateStateIdentifier]struct{}) 255 for _, managedParty := range managedParties { 256 psMetadata, err := bc.PrivateStateManager().ResolveForManagedParty(managedParty) 257 if err != nil { 258 return nil, err 259 } 260 targetPsi[psMetadata.ID] = struct{}{} 261 } 262 // execute in all the managed private states 263 // TODO this could be enhanced to run in parallel 264 for _, psi := range bc.PrivateStateManager().PSIs() { 265 if cfg.ApplyOnPartyOverride != nil && *cfg.ApplyOnPartyOverride != psi { 266 continue 267 } 268 _, applyAsParty := targetPsi[psi] 269 if !applyAsParty && applyOnPartiesOnly { 270 continue 271 } 272 privateStateDB, err := privateStateDBFactory(psi) 273 if err != nil { 274 return nil, err 275 } 276 publicStateDB := publicStateDBFactory() 277 278 // use a clone of the gas pool (as we don't want to consume gas multiple times for each MPS execution, which might blow the block gasLimit on MPS node) 279 gp := new(GasPool).AddGas(originalGP.Gas()) 280 281 _, privateReceipt, err := ApplyTransaction(config, bc, author, gp, publicStateDB, privateStateDB, header, tx, usedGas, cfg, !applyAsParty, privateStateRepo) 282 if err != nil { 283 return nil, err 284 } 285 286 // set the PSI for each log (so that the filter system knows for what private state they are) 287 // we don't care about the empty privateReceipt (as we'll execute the transaction on the empty state anyway) 288 if applyAsParty { 289 for _, log := range privateReceipt.Logs { 290 log.PSI = psi 291 mpsReceipt.Logs = append(mpsReceipt.Logs, log) 292 } 293 mpsReceipt.PSReceipts[psi] = privateReceipt 294 295 bc.CheckAndSetPrivateState(privateReceipt.Logs, privateStateDB, psi) 296 } 297 } 298 299 return mpsReceipt, nil 300 } 301 302 // /Quorum 303 304 func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb, privateStateDB *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, evm *vm.EVM, cfg vm.Config, forceNonParty bool, privateStateRepo mps.PrivateStateRepository) (*types.Receipt, *types.Receipt, error) { 305 // Add addresses to access list if applicable 306 if config.IsYoloV2(header.Number) { 307 statedb.AddAddressToAccessList(msg.From()) 308 if dst := msg.To(); dst != nil { 309 statedb.AddAddressToAccessList(*dst) 310 // If it's a create-tx, the destination will be added inside evm.create 311 } 312 for _, addr := range evm.ActivePrecompiles() { 313 statedb.AddAddressToAccessList(addr) 314 } 315 } 316 317 // Quorum 318 txIndex := statedb.TxIndex() 319 evm.InnerApply = func(innerTx *types.Transaction) error { 320 return ApplyInnerTransaction(bc, author, gp, statedb, privateStateDB, header, tx, usedGas, cfg, forceNonParty, privateStateRepo, evm, innerTx, txIndex) 321 } 322 // End Quorum 323 324 // Apply the transaction to the current state (included in the env) 325 result, err := ApplyMessage(evm, msg, gp) 326 if err != nil { 327 return nil, nil, err 328 } 329 // Update the state with pending changes 330 var root []byte 331 if config.IsByzantium(header.Number) { 332 statedb.Finalise(true) 333 } else { 334 root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes() 335 } 336 *usedGas += result.UsedGas 337 338 // If this is a private transaction, the public receipt should always 339 // indicate success. 340 publicFailed := !(config.IsQuorum && tx.IsPrivate()) && result.Failed() 341 342 // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx 343 // based on the eip phase, we're passing wether the root touch-delete accounts. 344 receipt := types.NewReceipt(root, publicFailed, *usedGas) 345 receipt.TxHash = tx.Hash() 346 receipt.GasUsed = result.UsedGas 347 // if the transaction created a contract, store the creation address in the receipt. 348 if msg.To() == nil { 349 receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce()) 350 } 351 // Set the receipt logs and create a bloom for filtering 352 receipt.Logs = statedb.GetLogs(tx.Hash()) 353 receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) 354 receipt.BlockHash = statedb.BlockHash() 355 receipt.BlockNumber = header.Number 356 receipt.TransactionIndex = uint(statedb.TxIndex()) 357 358 // Quorum 359 var privateReceipt *types.Receipt 360 if config.IsQuorum { 361 if tx.IsPrivate() { 362 var privateRoot []byte 363 if config.IsByzantium(header.Number) { 364 privateStateDB.Finalise(true) 365 } else { 366 privateRoot = privateStateDB.IntermediateRoot(config.IsEIP158(header.Number)).Bytes() 367 } 368 privateReceipt = types.NewReceipt(privateRoot, result.Failed(), *usedGas) 369 privateReceipt.TxHash = tx.Hash() 370 privateReceipt.GasUsed = result.UsedGas 371 if msg.To() == nil { 372 privateReceipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce()) 373 } 374 375 privateReceipt.Logs = privateStateDB.GetLogs(tx.Hash()) 376 privateReceipt.Bloom = types.CreateBloom(types.Receipts{privateReceipt}) 377 } else { 378 // This may have been a privacy marker transaction, in which case need to retrieve the receipt for the 379 // inner private transaction (note that this can be an mpsReceipt, containing private receipts in PSReceipts). 380 if evm.InnerPrivateReceipt != nil { 381 privateReceipt = evm.InnerPrivateReceipt 382 } 383 } 384 } 385 386 // Save revert reason if feature enabled 387 if bc != nil && bc.QuorumConfig().RevertReasonEnabled() { 388 revertReason := result.Revert() 389 if revertReason != nil { 390 if config.IsQuorum && tx.IsPrivate() { 391 privateReceipt.RevertReason = revertReason 392 } else { 393 receipt.RevertReason = revertReason 394 } 395 } 396 } 397 // End Quorum 398 399 return receipt, privateReceipt, err 400 } 401 402 // ApplyTransaction attempts to apply a transaction to the given state database 403 // and uses the input parameters for its environment. It returns the receipt 404 // for the transaction, gas used and an error if the transaction failed, 405 // indicating the block was invalid. 406 func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb, privateStateDB *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config, forceNonParty bool, privateStateRepo mps.PrivateStateRepository) (*types.Receipt, *types.Receipt, error) { 407 // Quorum - decide the privateStateDB to use 408 privateStateDbToUse := PrivateStateDBForTxn(config.IsQuorum, tx, statedb, privateStateDB) 409 // End Quorum 410 411 // Quorum - check for account permissions to execute the transaction 412 if core.IsV2Permission() { 413 if err := core.CheckAccountPermission(tx.From(), tx.To(), tx.Value(), tx.Data(), tx.Gas(), tx.GasPrice()); err != nil { 414 return nil, nil, err 415 } 416 } 417 418 if config.IsQuorum && tx.GasPrice() != nil && tx.GasPrice().Cmp(common.Big0) > 0 { 419 return nil, nil, ErrInvalidGasPrice 420 } 421 422 msg, err := tx.AsMessage(types.MakeSigner(config, header.Number)) 423 if err != nil { 424 return nil, nil, err 425 } 426 // Quorum: this tx needs to be applied as if we were not a party 427 msg = msg.WithEmptyPrivateData(forceNonParty && tx.IsPrivate()) 428 429 // Create a new context to be used in the EVM environment 430 blockContext := NewEVMBlockContext(header, bc, author) 431 txContext := NewEVMTxContext(msg) 432 vmenv := vm.NewEVM(blockContext, txContext, statedb, privateStateDbToUse, config, cfg) 433 434 // the same transaction object is used for multiple executions (clear the privacy metadata - it should be updated after privacyManager.receive) 435 // when running in parallel for multiple private states is implemented - a copy of the tx may be used 436 tx.SetTxPrivacyMetadata(nil) 437 vmenv.SetCurrentTX(tx) 438 439 return applyTransaction(msg, config, bc, author, gp, statedb, privateStateDB, header, tx, usedGas, vmenv, cfg, forceNonParty, privateStateRepo) 440 } 441 442 // Quorum 443 // ApplyInnerTransaction is called from within the Quorum precompile for privacy marker transactions. 444 // It's a call back which essentially duplicates the logic in Process(), 445 // in this case to process the actual private transaction. 446 func ApplyInnerTransaction(bc ChainContext, author *common.Address, gp *GasPool, stateDB *state.StateDB, privateStateDB *state.StateDB, header *types.Header, outerTx *types.Transaction, usedGas *uint64, evmConf vm.Config, forceNonParty bool, privateStateRepo mps.PrivateStateRepository, vmenv *vm.EVM, innerTx *types.Transaction, txIndex int) error { 447 // this should never happen, but added as sanity check 448 if !innerTx.IsPrivate() { 449 return errors.New("attempt to process non-private transaction from within ApplyInnerTransaction()") 450 } 451 452 // create a single use gas pool (as we don't want the gas consumed by the inner tx to blow the block gasLimit on a participant node) 453 singleUseGasPool := new(GasPool).AddGas(innerTx.Gas()) 454 455 if privateStateRepo != nil && privateStateRepo.IsMPS() { 456 mpsReceipt, err := handleMPS(txIndex, innerTx, singleUseGasPool, usedGas, evmConf, stateDB, privateStateRepo, bc.Config(), bc, header, true) 457 if err != nil { 458 return err 459 } 460 461 // Store the auxiliary MPS receipt for the inner private transaction (this contains private receipts in PSReceipts). 462 vmenv.InnerPrivateReceipt = mpsReceipt 463 return nil 464 } 465 466 defer prepareStates(outerTx, stateDB, privateStateDB, txIndex) 467 prepareStates(innerTx, stateDB, privateStateDB, txIndex) 468 469 used := uint64(0) 470 _, innerPrivateReceipt, err := ApplyTransaction(bc.Config(), bc, author, singleUseGasPool, stateDB, privateStateDB, header, innerTx, &used, evmConf, forceNonParty, privateStateRepo) 471 if err != nil { 472 return err 473 } 474 475 if innerPrivateReceipt != nil { 476 if innerPrivateReceipt.Logs == nil { 477 innerPrivateReceipt.Logs = make([]*types.Log, 0) 478 } 479 480 // Store the receipt for the inner private transaction. 481 innerPrivateReceipt.TxHash = innerTx.Hash() 482 vmenv.InnerPrivateReceipt = innerPrivateReceipt 483 } 484 485 return nil 486 } 487 488 // Quorum 489 func prepareStates(tx *types.Transaction, stateDB *state.StateDB, privateStateDB *state.StateDB, txIndex int) { 490 stateDB.Prepare(tx.Hash(), stateDB.BlockHash(), txIndex) 491 privateStateDB.Prepare(tx.Hash(), privateStateDB.BlockHash(), txIndex) 492 }