以太坊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