1. 程式人生 > >以太坊worker打包commitransactions到insertchain呼叫process的state_processor、state_transition程式碼流程

以太坊worker打包commitransactions到insertchain呼叫process的state_processor、state_transition程式碼流程

commitTransactions函式

worker將txpool.pending的交易傳給commitransactions函式進行驗證、執行、打包。
func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, bc *core.BlockChain, coinbase common.Address) {
	// 由於是打包新的區塊中交易,所以將總 gasPool 初始化為 env.header.GasLimit
	if env.gasPool == nil {
		env.gasPool = new(core.GasPool).AddGas(env.header.GasLimit)
	}

	var coalescedLogs []*types.Log

	for {
		// If we don't have enough gas for any further transactions then we're done
		// 如果當前區塊中所有 Gas 消耗已經使用完,則退出打包交易
		if env.gasPool.Gas() < params.TxGas {
			log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas)
			break
		}
				
		// Retrieve the next transaction and abort if all done
		// 檢索下一筆交易,如果交易集合為空則退出 commit
		tx := txs.Peek()
		if tx == nil {
			break
		}
		// Error may be ignored here. The error has already been checked
		// during transaction acceptance is the transaction pool.
		//
		// We use the eip155 signer regardless of the current hf.
		from, _ := types.Sender(env.signer, tx)
		// Check whether the tx is replay protected. If we're not in the EIP155 hf
		// phase, start ignoring the sender until we do.
		// 請參考 https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md
		// DAO事件發生後,以太坊分裂為ETH和ETC,因為兩個鏈上的東西一摸一樣,所以在ETC
		// 上面發生的交易可以拿到ETH上面進行重放, 反之亦然。 所以Vitalik提出了EIP155來避免這種情況。
		if tx.Protected() && !env.config.IsEIP155(env.header.Number) {
			log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", env.config.EIP155Block)

			txs.Pop()
			continue
		}
		// Start executing the transaction
		env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount)
		// 執行交易
		err, logs := env.commitTransaction(tx, bc, coinbase, gp)
		switch err {
		case core.ErrGasLimitReached:
			// Pop the current out-of-gas transaction without shifting in the next from the account
			// 彈出整個賬戶的所有交易, 不處理使用者的下一個交易。
			log.Trace("Gas limit exceeded for current block", "sender", from)
			txs.Pop()

		case core.ErrNonceTooLow:
			// New head notification data race between the transaction pool and miner, shift
			// 移動到使用者的下一個交易
			log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce())
			txs.Shift()

		case core.ErrNonceTooHigh:
			// Reorg notification data race between the transaction pool and miner, skip account =
			// 跳過這個賬戶
			log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce())
			txs.Pop()

		case nil:
			// Everything ok, collect the logs and shift in the next transaction from the same account
			coalescedLogs = append(coalescedLogs, logs...)
			env.tcount++
			txs.Shift()

		default:
			// Strange error, discard the transaction and get the next in line (note, the
			// nonce-too-high clause will prevent us from executing in vain).
			// 其他奇怪的錯誤,跳過這個交易。
			log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err)
			txs.Shift()
		}
	}

	if len(coalescedLogs) > 0 || env.tcount > 0 {
		// make a copy, the state caches the logs and these logs get "upgraded" from pending to mined
		// logs by filling in the block hash when the block was mined by the local miner. This can
		// cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed.
		// 因為需要把log傳送出去,而這邊在挖礦完成後需要對log進行修改,所以拷貝一份傳送出去,避免爭用。
		cpy := make([]*types.Log, len(coalescedLogs))
		for i, l := range coalescedLogs {
			cpy[i] = new(types.Log)
			*cpy[i] = *l
		}
		go func(logs []*types.Log, tcount int) {
			if len(logs) > 0 {
				mux.Post(core.PendingLogsEvent{Logs: logs})
			}
			if tcount > 0 {
				mux.Post(core.PendingStateEvent{})
			}
		}(cpy, env.tcount)
	}
}

commitTransaction函式

commitTransaction執行ApplyTransaction

func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, coinbase common.Address, gp *core.GasPool) (error, []*types.Log) {
	snap := env.state.Snapshot()

	receipt, _, err := core.ApplyTransaction(env.config, bc, &coinbase, gp, env.state, env.header, tx, env.header.GasUsed, vm.Config{})
	if err != nil {
		env.state.RevertToSnapshot(snap)
		return err, nil
	}
	env.txs = append(env.txs, tx)
	env.receipts = append(env.receipts, receipt)

	return nil, receipt.Logs
}

ApplyTransaction函式

// ApplyTransaction attempts to apply a transaction to the given state database
// and uses the input parameters for its environment. It returns the receipt
// for the transaction, gas used and an error if the transaction failed,
// indicating the block was invalid.
//1.ApplyTransaction嘗試將交易應用於給定的狀態資料庫,並使用輸入引數作為其環境。
//2.它返回交易的收據,使用的Gas和錯誤,如果交易失敗,表明塊是無效的。
func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, uint64, error) {
	//將types.Transaction結構變數轉為core.Message物件;這過程中會對傳送者做簽名驗證,並獲得傳送者的地址快取起來;
	msg, err := tx.AsMessage(types.MakeSigner(config, header.Number))
	if err != nil {
		return nil, 0, err
	}
	// Create a new context to be used in the EVM environment
	//建立新的上下文(Context),此上下文將在EVM 環境(EVM environment)中使用;上下文中包含msg,區塊頭、區塊指標、作者(挖礦者、獲益者)
	context := NewEVMContext(msg, header, bc, author)
	// Create a new environment which holds all relevant information
	// about the transaction and calling mechanisms.
	//建立新的EVM environment,其中包括了交易相關的所有資訊以及呼叫機制;
	vmenv := vm.NewEVM(context, statedb, config, cfg)
	// Apply the transaction to the current state (included in the env)
	//ApplyMessage, 將交易應用於當前的狀態中,也就是執行狀態轉換,新的狀態包含在環境物件中;得到執行結果以及花費的gas;
	_, gas, failed, err := ApplyMessage(vmenv, msg, gp)
	if err != nil {
		return nil, 0, err
	}
	// Update the state with pending changes
	var root []byte
	if config.IsByzantium(header.Number) {
		statedb.Finalise(true)
	} else {
		//建立一箇中間狀態的root收據
		root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
	}
	//區塊的usedGas累加當前交易的gas
	*usedGas += gas

	// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
	// based on the eip phase, we're passing wether the root touch-delete accounts.
	//建立一個收據, 用來儲存中間狀態的root, 以及交易使用的gas;
	receipt := types.NewReceipt(root, failed, *usedGas)
	receipt.TxHash = tx.Hash()
	receipt.GasUsed = gas
	// if the transaction created a contract, store the creation address in the receipt.
	//如果是建立合約的交易,那麼我們把建立地址儲存到收據裡面
	if msg.To() == nil {
		receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
	}
	// Set the receipt logs and create a bloom for filtering
	//設定所有的日誌並建立日誌的布隆過濾器;返回
	receipt.Logs = statedb.GetLogs(tx.Hash())
	receipt.Bloom = types.CreateBloom(types.Receipts{receipt})

	return receipt, gas, err
}

ApplyMessage

// ApplyMessage computes the new state by applying the given message
// against the old state within the environment.
//
// ApplyMessage returns the bytes returned by any EVM execution (if it took place),
// the gas used (which includes gas refunds) and an error if it failed. An error always
// indicates a core error meaning that the message would always fail for that particular
// state and would never be accepted within a block.
func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, uint64, bool, error) {
	return NewStateTransition(evm, msg, gp).TransitionDb()
}

ApplyMessage將交易應用於當前的狀態中,程式碼裡就是建立了一個StateTransition然後呼叫其TransitionDb()方法。

ApplyMessage返回由任何EVM執行(如果發生)返回的位元組(但這個返回值在ApplyTransaction中被忽略了),

使用的Gas(包括Gas退款),如果失敗則返回錯誤。 一個錯誤總是表示一個核心錯誤,

意味著這個訊息對於這個特定的狀態將總是失敗,並且永遠不會在一個塊中被接受。

StateTransition.TransitionDb()

// TransitionDb will transition the state by applying the current message and
// returning the result including the the used gas. It returns an error if it
// failed. An error indicates a consensus issue.
// TransitionDb將通過應用當前訊息和//返回結果(包括使用的gas)來轉換狀態。如果//失敗,它將返回一個錯誤。錯誤表示共識問題。
func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bool, err error) {
	// 預檢查,出錯則函式返回;
	//    a. 檢查交易的Nonce值是否合規;
	//    b. buyGas:根據傳送者定的gaslimit和GasPrice,從傳送者餘額中扣除以太幣;從區塊gas池中減掉本次gas;並對執行環境做好更新;
	if err = st.preCheck(); err != nil {
		return
	}
	msg := st.msg
	sender := st.from() // err checked in preCheck

	homestead := st.evm.ChainConfig().IsHomestead(st.evm.BlockNumber)
	contractCreation := msg.To() == nil

	// Pay intrinsic gas
	//支付固定費用 intrinsic gas。contractCreation為true即為合約建立,固定費用為53000;為false,則為普通交易固定費用為21000
	gas, err := IntrinsicGas(st.data, contractCreation, homestead)
	if err != nil {
		return nil, 0, false, err
	}
	//支付的gas不能大於交易剩餘的gas
	if err = st.useGas(gas); err != nil {
		return nil, 0, false, err
	}

	var (
		evm = st.evm
		// vm errors do not effect consensus and are therefor
		// not assigned to err, except for insufficient balance
		// error.
		vmerr error
	)
	//如果是合約建立, 那麼呼叫evm的Create方法建立新的合約,使用交易的data作為新合約的部署程式碼
	if contractCreation {
		ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value)
	} else {//否則不是合約建立,增加發送者的Nonce值,然後呼叫evm.Call執行交易
		// Increment the nonce for the next transaction
		st.state.SetNonce(sender.Address(), st.state.GetNonce(sender.Address())+1)
		ret, st.gas, vmerr = evm.Call(sender, st.to().Address(), st.data, st.gas, st.value)
	}
	if vmerr != nil {
		log.Debug("VM returned with error", "err", vmerr)
		// The only possible consensus-error would be if there wasn't
		// sufficient balance to make the transfer happen. The first
		// balance transfer may never fail.
		if vmerr == vm.ErrInsufficientBalance {
			return nil, 0, false, vmerr
		}
	}
	//計算並執行退款,將退回的gas對應的以太幣退回給交易傳送者
	st.refundGas()
	//最終結算給coinbase應得的部分
	st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))

	return ret, st.gasUsed(), vmerr != nil, err
}

1. 預檢查,出錯則函式返回;

    a. 檢查交易的Nonce值是否合規;

    b. buyGas:根據傳送者定的gaslimit和GasPrice,從傳送者餘額中扣除以太幣;從區塊gas池中減掉本次gas;並對執行環境做好更新;

2. 支付固定費用 intrinsic gas;

3. 如果是合約建立, 那麼呼叫evm的Create方法建立新的合約,使用交易的data作為新合約的部署程式碼;

4. 否則不是合約建立,增加發送者的Nonce值,然後呼叫evm.Call執行交易;

5. 計算並執行退款,將退回的gas對應的以太幣退回給交易傳送者。

evm.Create:建立新的合約

// Create creates a new contract using code as deployment code.
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {

	// Depth check execution. Fail if we're trying to execute above the
	// limit.
	// 檢查執行深度,若超過params.CallCreateDepth(即1024)就出錯返回;剛開始的執行深度為0,肯定繼續往下執行
	if evm.depth > int(params.CallCreateDepth) {
		return nil, common.Address{}, gas, ErrDepth
	}
	//檢查是否可執行轉賬,即檢查賬戶餘額是否≥要轉賬的數額
	if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
		return nil, common.Address{}, gas, ErrInsufficientBalance
	}
	// Ensure there's no existing contract already at the designated address
	//確保指定的地址沒有,現有合約存在
	nonce := evm.StateDB.GetNonce(caller.Address())
	//傳送者Nonce加1
	evm.StateDB.SetNonce(caller.Address(), nonce+1)
    //建立合約地址並獲取hash,若該合約地址已存在,或不合法(空),則出錯返回
	contractAddr = crypto.CreateAddress(caller.Address(), nonce)
	contractHash := evm.StateDB.GetCodeHash(contractAddr)

	if evm.StateDB.GetNonce(contractAddr) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) {
		return nil, common.Address{}, 0, ErrContractAddressCollision
	}

	// Create a new account on the state
	//儲存statedb快照,然後根據合約地址建立賬戶
	snapshot := evm.StateDB.Snapshot()
	evm.StateDB.CreateAccount(contractAddr)
	if evm.ChainConfig().IsEIP158(evm.BlockNumber) {
		evm.StateDB.SetNonce(contractAddr, 1)
	}
	//執行轉賬evm.Transfer(在statedb中,將value所代表的以太幣從傳送者賬戶轉到新合約賬戶)
	evm.Transfer(evm.StateDB, caller.Address(), contractAddr, value)

	// initialise a new contract and set the code that is to be used by the
	// E The contract is a scoped evmironment for this execution context
	// only.
	//根據傳送者、前面建立的合約賬戶,轉賬的錢,已用的gas建立並初始化合約
	contract := NewContract(caller, AccountRef(contractAddr), value, gas)
	//將交易的data作為合約的程式碼
	contract.SetCallCode(&contractAddr, crypto.Keccak256Hash(code), code)

	if evm.vmConfig.NoRecursion && evm.depth > 0 {
		return nil, contractAddr, gas, nil
	}

	if evm.vmConfig.Debug && evm.depth == 0 {
		evm.vmConfig.Tracer.CaptureStart(caller.Address(), contractAddr, true, code, gas, value)
	}

	start := time.Now()
    //執行前一步建立的合約
	ret, err = run(evm, contract, nil)

	// check whether the max code size has been exceeded
	maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize
	// if the contract creation ran successfully and no errors were returned
	// calculate the gas required to store the code. If the code could not
	// be stored due to not enough gas set an error and let it be handled
	// by the error checking condition below.
	//判斷執行結果是否有錯誤。如果合約成功執行並且沒有錯誤返回,則計算儲存返回資料所需的GAS
	// 如果由於沒有足夠的GAS而導致返回值不能被儲存則設定錯誤,並通過下面的錯誤檢查條件來處理。
	if err == nil && !maxCodeSizeExceeded {
		createDataGas := uint64(len(ret)) * params.CreateDataGas
		if contract.UseGas(createDataGas) {
			evm.StateDB.SetCode(contractAddr, ret)
		} else {
			err = ErrCodeStoreOutOfGas
		}
	}

	// When an error was returned by the EVM or when setting the creation code
	// above we revert to the snapshot and consume any gas remaining. Additionally
	// when we're in homestead this also counts for code storage gas errors.
	//若EVM返回錯誤或上述儲存返回值出現錯誤,則回滾到快照的狀態,並且消耗完剩下的所有gas
	if maxCodeSizeExceeded || (err != nil && (evm.ChainConfig().IsHomestead(evm.BlockNumber) || err != ErrCodeStoreOutOfGas)) {
		evm.StateDB.RevertToSnapshot(snapshot)
		if err != errExecutionReverted {
			contract.UseGas(contract.Gas)
		}
	}
	// Assign err if contract code size exceeds the max while the err is still empty.
	if maxCodeSizeExceeded && err == nil {
		err = errMaxCodeSizeExceeded
	}
	if evm.vmConfig.Debug && evm.depth == 0 {
		evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
	}
	return ret, contractAddr, contract.Gas, err
}

1. 檢查執行深度,若超過params.CallCreateDepth(即1024)就出錯返回;剛開始的執行深度為0,肯定繼續往下執行;

2. 檢查是否可執行轉賬,即檢查賬戶餘額是否≥要轉賬的數額;

3. 傳送者Nonce加1;

4. 建立合約地址並獲取hash,若該合約地址已存在,或不合法(空),則出錯返回;

5. 儲存statedb快照,然後根據合約地址建立賬戶;

6. 執行轉賬evm.Transfer(在statedb中,將value所代表的以太幣從傳送者賬戶轉到新合約賬戶);

7. 根據傳送者、前面建立的合約賬戶,轉賬的錢,已用的gas建立並初始化合約;將交易的data作為合約的程式碼;

8. 執行前一步建立的合約

9. 判斷執行結果是否有錯誤。如果合約成功執行並且沒有錯誤返回,則計算儲存返回資料所需的GAS。 如果由於沒有足夠的GAS而導致返回值不能被儲存則設定錯誤,並通過下面的錯誤檢查條件來處理。

10. 若EVM返回錯誤或上述儲存返回值出現錯誤,則回滾到快照的狀態,並且消耗完剩下的所有gas。

 evm.Call執行交易

Call方法, 無論我們轉賬或者是執行合約程式碼都會呼叫到這裡, 同時合約裡面的call指令也會執行到這裡。

Call方法和evm.Create的邏輯類似,但少了一些步驟。

// Call executes the contract associated with the addr with the given input as
// parameters. It also handles any necessary value transfer required and takes
// the necessary steps to create accounts and reverses the state in case of an
// execution error or failed value transfer.
func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
    //檢查是否允許遞迴執行以及執行深度,若深度超過params.CallCreateDepth(即1024)就出錯返回;
	if evm.vmConfig.NoRecursion && evm.depth > 0 {
		return nil, gas, nil
	}
	// Fail if we're trying to execute above the call depth limit
	if evm.depth > int(params.CallCreateDepth) {
		return nil, gas, ErrDepth
	}
	// Fail if we're trying to transfer more than the available balance
	// 檢查是否可執行轉賬,即檢查賬戶餘額是否≥要轉賬的數額
	if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
		return nil, gas, ErrInsufficientBalance
	}
	//儲存statedb快照,建立接收者賬戶
	var (
		to       = AccountRef(addr)
		snapshot = evm.StateDB.Snapshot()
	)
	//如果接收者在statedb中尚不存在,則執行precompiles預編譯,與編譯結果為nil時出錯返回;無錯誤則在statedb中建立接收者賬戶;
	if !evm.StateDB.Exist(addr) {
		precompiles := PrecompiledContractsHomestead
		if evm.ChainConfig().IsByzantium(evm.BlockNumber) {
			precompiles = PrecompiledContractsByzantium
		}
		if precompiles[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.Sign() == 0 {
			return nil, gas, nil
		}

		evm.StateDB.CreateAccount(addr)
	}
    //執行轉賬
	evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)

	// Initialise a new contract and set the code that is to be used by the EVM.
	// The contract is a scoped environment for this execution context only.
	//根據傳送者、接收者,轉賬的錢,已用的gas建立並初始化合約;將交易的data作為合約的程式碼
	contract := NewContract(caller, to, value, gas)
	contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))

	start := time.Now()

	// Capture the tracer start/end events in debug mode
	if evm.vmConfig.Debug && evm.depth == 0 {
		evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)

		defer func() { // Lazy evaluation of the parameters
			evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
		}()
	}
	//執行前一步建立的合約
	ret, err = run(evm, contract, input)

	// When an error was returned by the EVM or when setting the creation code
	// above we revert to the snapshot and consume any gas remaining. Additionally
	// when we're in homestead this also counts for code storage gas errors.
	//若EVM返回錯誤,則回滾到快照的狀態,並且消耗完剩下的所有gas
	if err != nil {
		evm.StateDB.RevertToSnapshot(snapshot)
		if err != errExecutionReverted {
			contract.UseGas(contract.Gas)
		}
	}
	return ret, contract.Gas, err
}

1. 檢查是否允許遞迴執行以及執行深度,若深度超過params.CallCreateDepth(即1024)就出錯返回;

2. 檢查是否可執行轉賬,即檢查賬戶餘額是否≥要轉賬的數額;

3. 儲存statedb快照,建立接收者賬戶;

4. 如果接收者在statedb中尚不存在,則執行precompiles預編譯,與編譯結果為nil時出錯返回;無錯誤則在statedb中建立接收者賬戶;

5. 執行轉賬;

6. 根據傳送者、接收者,轉賬的錢,已用的gas建立並初始化合約;將交易的data作為合約的程式碼;

7. 執行前一步建立的合約

8. 若EVM返回錯誤,則回滾到快照的狀態,並且消耗完剩下的所有gas。

Process函式

  • Process 根據以太坊規則執行交易資訊來對statedb進行狀態改變,以及獎勵挖礦者或者是其他的叔父節點。
  • Process返回執行過程中累計的收據和日誌,並返回過程中使用的Gas。 如果由於Gas不足而導致任何交易執行失敗,將返回錯誤。
// Process processes the state changes according to the Ethereum rules by running
// the transaction messages using the statedb and applying any rewards to both
// the processor (coinbase) and any included uncles.
//
// Process returns the receipts and logs accumulated during the process and
// returns the amount of gas that was used in the process. If any of the
// transactions failed to execute due to insufficient gas it will return an error.
//1.Process根據以太坊規則執行交易資訊來對statedb進行狀態改變,以及獎勵挖礦者或者是其他的叔父節點。
//2.Process返回執行過程中累計的收據和日誌,並返回過程中使用的Gas。 如果由於Gas不足而導致任何交易執行失敗,將返回錯誤。
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) {
	// 定義及初始化收據、耗費的gas、區塊頭、日誌、gas池等變數
	var (
		receipts types.Receipts
		usedGas  = new(uint64)
		header   = block.Header()
		allLogs  []*types.Log
		gp       = new(GasPool).AddGas(block.GasLimit())
	)

	// Mutate the the block and state according to any hard-fork specs
	if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
		misc.ApplyDAOHardFork(statedb)
	}
	// Iterate over and process the individual transactions
	//對區塊中的每一個交易,進行迭代處理;處理邏輯
	for i, tx := range block.Transactions() {
		//對當前交易做預處理,設定交易的hash、索引、區塊hash,供EVM釋出新的狀態日誌使用
		statedb.Prepare(tx.Hash(), block.Hash(), i)
		// 執行ApplyTransaction,獲取收據
		receipt, _, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, usedGas, cfg)
		if err != nil {
			return nil, nil, 0, err
		}
		//正常,累積記錄收據及日誌。迴圈進入下一個交易的處理
		receipts = append(receipts, receipt)
		allLogs = append(allLogs, receipt.Logs...)
	}
    //呼叫共識模組做Finalize處理
	//Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
	p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles(), receipts)
    //返回所有的收據、日誌、總共使用的gas
	return receipts, allLogs, *usedGas, nil
}

1. 定義及初始化收據、耗費的gas、區塊頭、日誌、gas池等變數;

2. 如果是DAO事件硬分叉相關的處理,則呼叫misc.ApplyDAOHardFork(statedb)執行處理;

3. 對區塊中的每一個交易,進行迭代處理;處理邏輯:

   a. 對當前交易做預處理,設定交易的hash、索引、區塊hash,供EVM釋出新的狀態日誌使用;

   b. 執行ApplyTransaction,獲取收據;

   c. 若上一步出錯,中斷整個Process,返回錯誤;

  d. 若正常,累積記錄收據及日誌。迴圈進入下一個交易的處理。

4. 呼叫共識模組做Finalize處理;

5. 返回所有的收據、日誌、總共使用的gas。

梳理下流程:

miner/worker.go:CommitTransactions

->Core/state_processor.go:ApplyTransactions

->Core/state_transition:TransitionDb

->Core/block_validator.go:process

CommitTransactions :1.for迴圈抵用commitrannsaction. 2.直到所有交易gas總量接近區塊gasLimit的極限值。

StateProcessor:1.呼叫ApplyTransactions.

                             2.呼叫ApplyMessage 

                             3. 呼叫StateTransition

TransitionDb:

                           1. 驗證(執行)Transaction;

                           2. 扣除transaction.data.payload計算資料所需要消耗的gas;

                           3. 在vm中執行code(生成contract or 執行contract);vm執 行過程中,其gas會被自動消耗。如果gas不足,vm                                 會自選退出;

                           4. 將多餘的gas退回到sender.balance中;

                           5. 將消耗的gas換成balance加到當前env.Coinbase()中;

Process:

                         1. 驗證UsedGas

                         2. 驗證Bloom

                         3. 驗證receiptSha

                         4. 驗證stateDB.IntermediateRoot