github.com/fff-chain/go-fff@v0.0.0-20220726032732-1c84420b8a99/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  	"bytes"
    21  	"errors"
    22  	"fmt"
    23  	"math/big"
    24  	"math/rand"
    25  	"sync"
    26  	"time"
    27  
    28  	"github.com/fff-chain/go-fff/common"
    29  	"github.com/fff-chain/go-fff/common/gopool"
    30  	"github.com/fff-chain/go-fff/consensus"
    31  	"github.com/fff-chain/go-fff/consensus/misc"
    32  	"github.com/fff-chain/go-fff/core/rawdb"
    33  	"github.com/fff-chain/go-fff/core/state"
    34  	"github.com/fff-chain/go-fff/core/state/snapshot"
    35  	"github.com/fff-chain/go-fff/core/systemcontracts"
    36  	"github.com/fff-chain/go-fff/core/types"
    37  	"github.com/fff-chain/go-fff/core/vm"
    38  	"github.com/fff-chain/go-fff/crypto"
    39  	"github.com/fff-chain/go-fff/log"
    40  	"github.com/fff-chain/go-fff/params"
    41  	"github.com/fff-chain/go-fff/rlp"
    42  )
    43  
    44  const (
    45  	fullProcessCheck       = 21 // On diff sync mode, will do full process every fullProcessCheck randomly
    46  	recentTime             = 1024 * 3
    47  	recentDiffLayerTimeout = 5
    48  	farDiffLayerTimeout    = 2
    49  )
    50  
    51  // StateProcessor is a basic Processor, which takes care of transitioning
    52  // state from one point to another.
    53  //
    54  // StateProcessor implements Processor.
    55  type StateProcessor struct {
    56  	config *params.ChainConfig // Chain configuration options
    57  	bc     *BlockChain         // Canonical block chain
    58  	engine consensus.Engine    // Consensus engine used for block rewards
    59  }
    60  
    61  // NewStateProcessor initialises a new StateProcessor.
    62  func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consensus.Engine) *StateProcessor {
    63  	return &StateProcessor{
    64  		config: config,
    65  		bc:     bc,
    66  		engine: engine,
    67  	}
    68  }
    69  
    70  type LightStateProcessor struct {
    71  	check int64
    72  	StateProcessor
    73  }
    74  
    75  func NewLightStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consensus.Engine) *LightStateProcessor {
    76  	randomGenerator := rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
    77  	check := randomGenerator.Int63n(fullProcessCheck)
    78  	return &LightStateProcessor{
    79  		check:          check,
    80  		StateProcessor: *NewStateProcessor(config, bc, engine),
    81  	}
    82  }
    83  
    84  func (p *LightStateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*state.StateDB, types.Receipts, []*types.Log, uint64, error) {
    85  	allowLightProcess := true
    86  	if posa, ok := p.engine.(consensus.PoSA); ok {
    87  		allowLightProcess = posa.AllowLightProcess(p.bc, block.Header())
    88  	}
    89  	// random fallback to full process
    90  	if allowLightProcess && block.NumberU64()%fullProcessCheck != uint64(p.check) && len(block.Transactions()) != 0 {
    91  		var pid string
    92  		if peer, ok := block.ReceivedFrom.(PeerIDer); ok {
    93  			pid = peer.ID()
    94  		}
    95  		var diffLayer *types.DiffLayer
    96  		var diffLayerTimeout = recentDiffLayerTimeout
    97  		if time.Now().Unix()-int64(block.Time()) > recentTime {
    98  			diffLayerTimeout = farDiffLayerTimeout
    99  		}
   100  		for tried := 0; tried < diffLayerTimeout; tried++ {
   101  			// wait a bit for the diff layer
   102  			diffLayer = p.bc.GetUnTrustedDiffLayer(block.Hash(), pid)
   103  			if diffLayer != nil {
   104  				break
   105  			}
   106  			time.Sleep(time.Millisecond)
   107  		}
   108  		if diffLayer != nil {
   109  			if err := diffLayer.Receipts.DeriveFields(p.bc.chainConfig, block.Hash(), block.NumberU64(), block.Transactions()); err != nil {
   110  				log.Error("Failed to derive block receipts fields", "hash", block.Hash(), "number", block.NumberU64(), "err", err)
   111  				// fallback to full process
   112  				return p.StateProcessor.Process(block, statedb, cfg)
   113  			}
   114  
   115  			receipts, logs, gasUsed, err := p.LightProcess(diffLayer, block, statedb)
   116  			if err == nil {
   117  				log.Info("do light process success at block", "num", block.NumberU64())
   118  				return statedb, receipts, logs, gasUsed, nil
   119  			}
   120  			log.Error("do light process err at block", "num", block.NumberU64(), "err", err)
   121  			p.bc.removeDiffLayers(diffLayer.DiffHash)
   122  			// prepare new statedb
   123  			statedb.StopPrefetcher()
   124  			parent := p.bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
   125  			statedb, err = state.New(parent.Root, p.bc.stateCache, p.bc.snaps)
   126  			statedb.SetExpectedStateRoot(block.Root())
   127  			if p.bc.pipeCommit {
   128  				statedb.EnablePipeCommit()
   129  			}
   130  			if err != nil {
   131  				return statedb, nil, nil, 0, err
   132  			}
   133  			// Enable prefetching to pull in trie node paths while processing transactions
   134  			statedb.StartPrefetcher("chain")
   135  		}
   136  	}
   137  	// fallback to full process
   138  	return p.StateProcessor.Process(block, statedb, cfg)
   139  }
   140  
   141  func (p *LightStateProcessor) LightProcess(diffLayer *types.DiffLayer, block *types.Block, statedb *state.StateDB) (types.Receipts, []*types.Log, uint64, error) {
   142  	statedb.MarkLightProcessed()
   143  	fullDiffCode := make(map[common.Hash][]byte, len(diffLayer.Codes))
   144  	diffTries := make(map[common.Address]state.Trie)
   145  	diffCode := make(map[common.Hash][]byte)
   146  
   147  	snapDestructs, snapAccounts, snapStorage, err := statedb.DiffLayerToSnap(diffLayer)
   148  	if err != nil {
   149  		return nil, nil, 0, err
   150  	}
   151  
   152  	for _, c := range diffLayer.Codes {
   153  		fullDiffCode[c.Hash] = c.Code
   154  	}
   155  	stateTrie, err := statedb.Trie()
   156  	if err != nil {
   157  		return nil, nil, 0, err
   158  	}
   159  	for des := range snapDestructs {
   160  		stateTrie.TryDelete(des[:])
   161  	}
   162  	threads := gopool.Threads(len(snapAccounts))
   163  
   164  	iteAccounts := make([]common.Address, 0, len(snapAccounts))
   165  	for diffAccount := range snapAccounts {
   166  		iteAccounts = append(iteAccounts, diffAccount)
   167  	}
   168  
   169  	errChan := make(chan error, threads)
   170  	exitChan := make(chan struct{})
   171  	var snapMux sync.RWMutex
   172  	var stateMux, diffMux sync.Mutex
   173  	for i := 0; i < threads; i++ {
   174  		start := i * len(iteAccounts) / threads
   175  		end := (i + 1) * len(iteAccounts) / threads
   176  		if i+1 == threads {
   177  			end = len(iteAccounts)
   178  		}
   179  		go func(start, end int) {
   180  			for index := start; index < end; index++ {
   181  				select {
   182  				// fast fail
   183  				case <-exitChan:
   184  					return
   185  				default:
   186  				}
   187  				diffAccount := iteAccounts[index]
   188  				snapMux.RLock()
   189  				blob := snapAccounts[diffAccount]
   190  				snapMux.RUnlock()
   191  				addrHash := crypto.Keccak256Hash(diffAccount[:])
   192  				latestAccount, err := snapshot.FullAccount(blob)
   193  				if err != nil {
   194  					errChan <- err
   195  					return
   196  				}
   197  
   198  				// fetch previous state
   199  				var previousAccount state.Account
   200  				stateMux.Lock()
   201  				enc, err := stateTrie.TryGet(diffAccount[:])
   202  				stateMux.Unlock()
   203  				if err != nil {
   204  					errChan <- err
   205  					return
   206  				}
   207  				if len(enc) != 0 {
   208  					if err := rlp.DecodeBytes(enc, &previousAccount); err != nil {
   209  						errChan <- err
   210  						return
   211  					}
   212  				}
   213  				if latestAccount.Balance == nil {
   214  					latestAccount.Balance = new(big.Int)
   215  				}
   216  				if previousAccount.Balance == nil {
   217  					previousAccount.Balance = new(big.Int)
   218  				}
   219  				if previousAccount.Root == (common.Hash{}) {
   220  					previousAccount.Root = types.EmptyRootHash
   221  				}
   222  				if len(previousAccount.CodeHash) == 0 {
   223  					previousAccount.CodeHash = types.EmptyCodeHash
   224  				}
   225  
   226  				// skip no change account
   227  				if previousAccount.Nonce == latestAccount.Nonce &&
   228  					bytes.Equal(previousAccount.CodeHash, latestAccount.CodeHash) &&
   229  					previousAccount.Balance.Cmp(latestAccount.Balance) == 0 &&
   230  					previousAccount.Root == common.BytesToHash(latestAccount.Root) {
   231  					// It is normal to receive redundant message since the collected message is redundant.
   232  					log.Debug("receive redundant account change in diff layer", "account", diffAccount, "num", block.NumberU64())
   233  					snapMux.Lock()
   234  					delete(snapAccounts, diffAccount)
   235  					delete(snapStorage, diffAccount)
   236  					snapMux.Unlock()
   237  					continue
   238  				}
   239  
   240  				// update code
   241  				codeHash := common.BytesToHash(latestAccount.CodeHash)
   242  				if !bytes.Equal(latestAccount.CodeHash, previousAccount.CodeHash) &&
   243  					!bytes.Equal(latestAccount.CodeHash, types.EmptyCodeHash) {
   244  					if code, exist := fullDiffCode[codeHash]; exist {
   245  						if crypto.Keccak256Hash(code) != codeHash {
   246  							errChan <- fmt.Errorf("code and code hash mismatch, account %s", diffAccount.String())
   247  							return
   248  						}
   249  						diffMux.Lock()
   250  						diffCode[codeHash] = code
   251  						diffMux.Unlock()
   252  					} else {
   253  						rawCode := rawdb.ReadCode(p.bc.db, codeHash)
   254  						if len(rawCode) == 0 {
   255  							errChan <- fmt.Errorf("missing code, account %s", diffAccount.String())
   256  							return
   257  						}
   258  					}
   259  				}
   260  
   261  				//update storage
   262  				latestRoot := common.BytesToHash(latestAccount.Root)
   263  				if latestRoot != previousAccount.Root {
   264  					accountTrie, err := statedb.Database().OpenStorageTrie(addrHash, previousAccount.Root)
   265  					if err != nil {
   266  						errChan <- err
   267  						return
   268  					}
   269  					snapMux.RLock()
   270  					storageChange, exist := snapStorage[diffAccount]
   271  					snapMux.RUnlock()
   272  
   273  					if !exist {
   274  						errChan <- errors.New("missing storage change in difflayer")
   275  						return
   276  					}
   277  					for k, v := range storageChange {
   278  						if len(v) != 0 {
   279  							accountTrie.TryUpdate([]byte(k), v)
   280  						} else {
   281  							accountTrie.TryDelete([]byte(k))
   282  						}
   283  					}
   284  
   285  					// check storage root
   286  					accountRootHash := accountTrie.Hash()
   287  					if latestRoot != accountRootHash {
   288  						errChan <- errors.New("account storage root mismatch")
   289  						return
   290  					}
   291  					diffMux.Lock()
   292  					diffTries[diffAccount] = accountTrie
   293  					diffMux.Unlock()
   294  				} else {
   295  					snapMux.Lock()
   296  					delete(snapStorage, diffAccount)
   297  					snapMux.Unlock()
   298  				}
   299  
   300  				// can't trust the blob, need encode by our-self.
   301  				latestStateAccount := state.Account{
   302  					Nonce:    latestAccount.Nonce,
   303  					Balance:  latestAccount.Balance,
   304  					Root:     common.BytesToHash(latestAccount.Root),
   305  					CodeHash: latestAccount.CodeHash,
   306  				}
   307  				bz, err := rlp.EncodeToBytes(&latestStateAccount)
   308  				if err != nil {
   309  					errChan <- err
   310  					return
   311  				}
   312  				stateMux.Lock()
   313  				err = stateTrie.TryUpdate(diffAccount[:], bz)
   314  				stateMux.Unlock()
   315  				if err != nil {
   316  					errChan <- err
   317  					return
   318  				}
   319  			}
   320  			errChan <- nil
   321  		}(start, end)
   322  	}
   323  
   324  	for i := 0; i < threads; i++ {
   325  		err := <-errChan
   326  		if err != nil {
   327  			close(exitChan)
   328  			return nil, nil, 0, err
   329  		}
   330  	}
   331  
   332  	var allLogs []*types.Log
   333  	var gasUsed uint64
   334  	for _, receipt := range diffLayer.Receipts {
   335  		allLogs = append(allLogs, receipt.Logs...)
   336  		gasUsed += receipt.GasUsed
   337  	}
   338  
   339  	// Do validate in advance so that we can fall back to full process
   340  	if err := p.bc.validator.ValidateState(block, statedb, diffLayer.Receipts, gasUsed, false); err != nil {
   341  		log.Error("validate state failed during diff sync", "error", err)
   342  		return nil, nil, 0, err
   343  	}
   344  
   345  	// remove redundant storage change
   346  	for account := range snapStorage {
   347  		if _, exist := snapAccounts[account]; !exist {
   348  			log.Warn("receive redundant storage change in diff layer")
   349  			delete(snapStorage, account)
   350  		}
   351  	}
   352  
   353  	// remove redundant code
   354  	if len(fullDiffCode) != len(diffLayer.Codes) {
   355  		diffLayer.Codes = make([]types.DiffCode, 0, len(diffCode))
   356  		for hash, code := range diffCode {
   357  			diffLayer.Codes = append(diffLayer.Codes, types.DiffCode{
   358  				Hash: hash,
   359  				Code: code,
   360  			})
   361  		}
   362  	}
   363  
   364  	statedb.SetSnapData(snapDestructs, snapAccounts, snapStorage)
   365  	if len(snapAccounts) != len(diffLayer.Accounts) || len(snapStorage) != len(diffLayer.Storages) {
   366  		diffLayer.Destructs, diffLayer.Accounts, diffLayer.Storages = statedb.SnapToDiffLayer()
   367  	}
   368  	statedb.SetDiff(diffLayer, diffTries, diffCode)
   369  
   370  	return diffLayer.Receipts, allLogs, gasUsed, nil
   371  }
   372  
   373  // Process processes the state changes according to the Ethereum rules by running
   374  // the transaction messages using the statedb and applying any rewards to both
   375  // the processor (coinbase) and any included uncles.
   376  //
   377  // Process returns the receipts and logs accumulated during the process and
   378  // returns the amount of gas that was used in the process. If any of the
   379  // transactions failed to execute due to insufficient gas it will return an error.
   380  func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*state.StateDB, types.Receipts, []*types.Log, uint64, error) {
   381  	var (
   382  		usedGas = new(uint64)
   383  		header  = block.Header()
   384  		allLogs []*types.Log
   385  		gp      = new(GasPool).AddGas(block.GasLimit())
   386  	)
   387  	signer := types.MakeSigner(p.bc.chainConfig, block.Number())
   388  	statedb.TryPreload(block, signer)
   389  	var receipts = make([]*types.Receipt, 0)
   390  	// Mutate the block and state according to any hard-fork specs
   391  	if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
   392  		misc.ApplyDAOHardFork(statedb)
   393  	}
   394  	// Handle upgrade build-in system contract code
   395  	systemcontracts.UpgradeBuildInSystemContract(p.config, block.Number(), statedb)
   396  
   397  	blockContext := NewEVMBlockContext(header, p.bc, nil)
   398  	vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg)
   399  
   400  	txNum := len(block.Transactions())
   401  	// Iterate over and process the individual transactions
   402  	posa, isPoSA := p.engine.(consensus.PoSA)
   403  	commonTxs := make([]*types.Transaction, 0, txNum)
   404  
   405  	// initilise bloom processors
   406  	bloomProcessors := NewAsyncReceiptBloomGenerator(txNum)
   407  	statedb.MarkFullProcessed()
   408  
   409  	// usually do have two tx, one for validator set contract, another for system reward contract.
   410  	systemTxs := make([]*types.Transaction, 0, 2)
   411  	for i, tx := range block.Transactions() {
   412  		if isPoSA {
   413  			if isSystemTx, err := posa.IsSystemTransaction(tx, block.Header()); err != nil {
   414  				return statedb, nil, nil, 0, err
   415  			} else if isSystemTx {
   416  				systemTxs = append(systemTxs, tx)
   417  				continue
   418  			}
   419  		}
   420  
   421  		msg, err := tx.AsMessage(signer)
   422  		if err != nil {
   423  			return statedb, nil, nil, 0, err
   424  		}
   425  		statedb.Prepare(tx.Hash(), block.Hash(), i)
   426  		receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, header, tx, usedGas, vmenv, bloomProcessors)
   427  		if err != nil {
   428  			return statedb, nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
   429  		}
   430  
   431  		commonTxs = append(commonTxs, tx)
   432  		receipts = append(receipts, receipt)
   433  	}
   434  	bloomProcessors.Close()
   435  
   436  	// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
   437  	err := p.engine.Finalize(p.bc, header, statedb, &commonTxs, block.Uncles(), &receipts, &systemTxs, usedGas)
   438  	if err != nil {
   439  		return statedb, receipts, allLogs, *usedGas, err
   440  	}
   441  	for _, receipt := range receipts {
   442  		allLogs = append(allLogs, receipt.Logs...)
   443  	}
   444  
   445  	return statedb, receipts, allLogs, *usedGas, nil
   446  }
   447  
   448  func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, evm *vm.EVM, receiptProcessors ...ReceiptProcessor) (*types.Receipt, error) {
   449  	// Create a new context to be used in the EVM environment.
   450  	txContext := NewEVMTxContext(msg)
   451  	evm.Reset(txContext, statedb)
   452  
   453  	// Apply the transaction to the current state (included in the env).
   454  	result, err := ApplyMessage(evm, msg, gp)
   455  	if err != nil {
   456  		return nil, err
   457  	}
   458  
   459  	// Update the state with pending changes.
   460  	var root []byte
   461  	if config.IsByzantium(header.Number) {
   462  		statedb.Finalise(true)
   463  	} else {
   464  		root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
   465  	}
   466  	*usedGas += result.UsedGas
   467  
   468  	// Create a new receipt for the transaction, storing the intermediate root and gas used
   469  	// by the tx.
   470  	receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: *usedGas}
   471  	if result.Failed() {
   472  		receipt.Status = types.ReceiptStatusFailed
   473  	} else {
   474  		receipt.Status = types.ReceiptStatusSuccessful
   475  	}
   476  	receipt.TxHash = tx.Hash()
   477  	receipt.GasUsed = result.UsedGas
   478  
   479  	// If the transaction created a contract, store the creation address in the receipt.
   480  	if msg.To() == nil {
   481  		receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce())
   482  	}
   483  
   484  	// Set the receipt logs and create the bloom filter.
   485  	receipt.Logs = statedb.GetLogs(tx.Hash())
   486  	receipt.BlockHash = statedb.BlockHash()
   487  	receipt.BlockNumber = header.Number
   488  	receipt.TransactionIndex = uint(statedb.TxIndex())
   489  	for _, receiptProcessor := range receiptProcessors {
   490  		receiptProcessor.Apply(receipt)
   491  	}
   492  	return receipt, err
   493  }
   494  
   495  // ApplyTransaction attempts to apply a transaction to the given state database
   496  // and uses the input parameters for its environment. It returns the receipt
   497  // for the transaction, gas used and an error if the transaction failed,
   498  // indicating the block was invalid.
   499  func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config, receiptProcessors ...ReceiptProcessor) (*types.Receipt, error) {
   500  	msg, err := tx.AsMessage(types.MakeSigner(config, header.Number))
   501  	if err != nil {
   502  		return nil, err
   503  	}
   504  	// Create a new context to be used in the EVM environment
   505  	blockContext := NewEVMBlockContext(header, bc, author)
   506  	vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg)
   507  	defer func() {
   508  		ite := vmenv.Interpreter()
   509  		vm.EVMInterpreterPool.Put(ite)
   510  		vm.EvmPool.Put(vmenv)
   511  	}()
   512  	return applyTransaction(msg, config, bc, author, gp, statedb, header, tx, usedGas, vmenv, receiptProcessors...)
   513  }