github.com/Unheilbar/quorum@v1.0.0/core/state_prefetcher.go (about)

     1  // Copyright 2019 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  	"sync"
    21  	"sync/atomic"
    22  	"time"
    23  
    24  	"github.com/ethereum/go-ethereum/consensus"
    25  	"github.com/ethereum/go-ethereum/core/state"
    26  	"github.com/ethereum/go-ethereum/core/types"
    27  	"github.com/ethereum/go-ethereum/core/vm"
    28  	"github.com/ethereum/go-ethereum/params"
    29  
    30  	// Quorum
    31  	"github.com/ethereum/go-ethereum/common"
    32  	"github.com/ethereum/go-ethereum/core/mps"
    33  	"github.com/ethereum/go-ethereum/private"
    34  )
    35  
    36  // statePrefetcher is a basic Prefetcher, which blindly executes a block on top
    37  // of an arbitrary state with the goal of prefetching potentially useful state
    38  // data from disk before the main block processor start executing.
    39  type statePrefetcher struct {
    40  	config *params.ChainConfig // Chain configuration options
    41  	bc     *BlockChain         // Canonical block chain
    42  	engine consensus.Engine    // Consensus engine used for block rewards
    43  
    44  	pend sync.WaitGroup // Quorum: wait for MPS prefetching
    45  }
    46  
    47  // newStatePrefetcher initialises a new statePrefetcher.
    48  func newStatePrefetcher(config *params.ChainConfig, bc *BlockChain, engine consensus.Engine) *statePrefetcher {
    49  	return &statePrefetcher{
    50  		config: config,
    51  		bc:     bc,
    52  		engine: engine,
    53  	}
    54  }
    55  
    56  // Prefetch processes the state changes according to the Ethereum rules by running
    57  // the transaction messages using the statedb, but any changes are discarded. The
    58  // only goal is to pre-cache transaction signatures and state trie nodes.
    59  // Quorum: Add privateStateDb argument
    60  func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, privateStateRepo mps.PrivateStateRepository, cfg vm.Config, interrupt *uint32) {
    61  	var (
    62  		header  = block.Header()
    63  		gaspool = new(GasPool).AddGas(block.GasLimit())
    64  	)
    65  	// Iterate over and process the individual transactions
    66  	byzantium := p.config.IsByzantium(block.Number())
    67  	for i, tx := range block.Transactions() {
    68  		// If block precaching was interrupted, abort
    69  		if interrupt != nil && atomic.LoadUint32(interrupt) == 1 {
    70  			return
    71  		}
    72  
    73  		// Quorum
    74  		if tx.IsPrivate() && privateStateRepo.IsMPS() {
    75  			p.prefetchMpsTransaction(block, tx, i, statedb.Copy(), privateStateRepo, cfg, interrupt)
    76  		}
    77  		privateStateDb, _ := privateStateRepo.DefaultState()
    78  		privateStateDb.Prepare(tx.Hash(), block.Hash(), i)
    79  		// End Quorum
    80  
    81  		// Block precaching permitted to continue, execute the transaction
    82  		statedb.Prepare(tx.Hash(), block.Hash(), i)
    83  
    84  		innerApply := createInnerApply(block, tx, i, statedb, privateStateRepo, cfg, interrupt, p, privateStateDb)
    85  
    86  		// Quorum: Add privateStateDb argument
    87  		if err := precacheTransaction(p.config, p.bc, nil, gaspool, statedb, privateStateDb, header, tx, cfg, innerApply); err != nil {
    88  			return // Ugh, something went horribly wrong, bail out
    89  		}
    90  		// If we're pre-byzantium, pre-load trie nodes for the intermediate root
    91  		if !byzantium {
    92  			statedb.IntermediateRoot(true)
    93  		}
    94  	}
    95  	// If were post-byzantium, pre-load trie nodes for the final root hash
    96  	if byzantium {
    97  		statedb.IntermediateRoot(true)
    98  	}
    99  }
   100  
   101  // precacheTransaction attempts to apply a transaction to the given state database
   102  // and uses the input parameters for its environment. The goal is not to execute
   103  // the transaction successfully, rather to warm up touched data slots.
   104  // Quorum: Add privateStateDb and isMPS arguments
   105  func precacheTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gaspool *GasPool, statedb *state.StateDB, privateStateDb *state.StateDB, header *types.Header, tx *types.Transaction, cfg vm.Config, innerApply func(*types.Transaction) error) error {
   106  	// Convert the transaction into an executable message and pre-cache its sender
   107  	msg, err := tx.AsMessage(types.MakeSigner(config, header.Number))
   108  	if err != nil {
   109  		return err
   110  	}
   111  	// Quorum
   112  	// Create the EVM and execute the transaction
   113  	context := NewEVMBlockContext(header, bc, author)
   114  	txContext := NewEVMTxContext(msg)
   115  
   116  	var evm *vm.EVM
   117  	// Quorum: Add privateStateDb argument
   118  	if tx.IsPrivate() {
   119  		evm = vm.NewEVM(context, txContext, statedb, privateStateDb, config, cfg)
   120  	} else {
   121  		evm = vm.NewEVM(context, txContext, statedb, statedb, config, cfg)
   122  	}
   123  	// End Quorum
   124  	evm.SetCurrentTX(tx) // Quorum
   125  	evm.InnerApply = innerApply
   126  	// Add addresses to access list if applicable
   127  	_, err = ApplyMessage(evm, msg, gaspool)
   128  	return err
   129  }
   130  
   131  // Quorum
   132  
   133  func (p *statePrefetcher) prefetchMpsTransaction(block *types.Block, tx *types.Transaction, txIndex int, statedb *state.StateDB, privateStateRepo mps.PrivateStateRepository, cfg vm.Config, interrupt *uint32) {
   134  	byzantium := p.config.IsByzantium(block.Number())
   135  	// Block precaching permitted to continue, execute the transaction
   136  	_, managedParties, _, _, err := private.P.Receive(common.BytesToEncryptedPayloadHash(tx.Data()))
   137  	if err != nil {
   138  		return
   139  	}
   140  	for _, managedParty := range managedParties {
   141  		if interrupt != nil && atomic.LoadUint32(interrupt) == 1 {
   142  			return
   143  		}
   144  		psMetadata, err := p.bc.PrivateStateManager().ResolveForManagedParty(managedParty)
   145  		if err != nil {
   146  			continue
   147  		}
   148  
   149  		privateStateDb, err := privateStateRepo.StatePSI(psMetadata.ID)
   150  		if err != nil {
   151  			continue
   152  		}
   153  		p.pend.Add(1)
   154  
   155  		innerApply := createInnerApply(block, tx, txIndex, statedb, privateStateRepo, cfg, interrupt, p, privateStateDb)
   156  
   157  		go func(start time.Time, followup *types.Block, statedb *state.StateDB, privateStateDb *state.StateDB, tx *types.Transaction, gaspool *GasPool) {
   158  			privateStateDb.Prepare(tx.Hash(), block.Hash(), txIndex)
   159  			if err := precacheTransaction(p.config, p.bc, nil, gaspool, statedb, privateStateDb, followup.Header(), tx, cfg, innerApply); err != nil {
   160  				return
   161  			}
   162  			// If we're pre-byzantium, pre-load trie nodes for the intermediate root
   163  			if !byzantium {
   164  				privateStateDb.IntermediateRoot(true)
   165  			}
   166  			p.pend.Done()
   167  		}(time.Now(), block, statedb, privateStateDb, tx, new(GasPool).AddGas(tx.Gas())) // TODO ricardolyn: which gas: block or Tx?
   168  	}
   169  	p.pend.Wait()
   170  }
   171  
   172  func createInnerApply(block *types.Block, tx *types.Transaction, txIndex int, statedb *state.StateDB, privateStateRepo mps.PrivateStateRepository, cfg vm.Config, interrupt *uint32, p *statePrefetcher, privateStateDb *state.StateDB) func(innerTx *types.Transaction) error {
   173  	return func(innerTx *types.Transaction) error {
   174  		if !tx.IsPrivacyMarker() {
   175  			return nil
   176  		} else if innerTx.IsPrivate() && privateStateRepo.IsMPS() {
   177  			p.prefetchMpsTransaction(block, innerTx, txIndex, statedb.Copy(), privateStateRepo, cfg, interrupt)
   178  			return nil
   179  		} else {
   180  			return precacheTransaction(p.config, p.bc, nil, new(GasPool).AddGas(innerTx.Gas()), statedb, privateStateDb, block.Header(), innerTx, cfg, nil)
   181  		}
   182  	}
   183  }
   184  
   185  // End Quorum