以太坊原始碼分析(23)core-state-process原始碼分析
## StateTransition
狀態轉換模型
/*
The State Transitioning Model
狀態轉換模型
A state transition is a change made when a transaction is applied to the current world state
狀態轉換 是指用當前的world state來執行交易,並改變當前的world state
The state transitioning model does all all the necessary work to work out a valid new state root.
狀態轉換做了所有所需的工作來產生一個新的有效的state root
1) Nonce handling Nonce 處理
2) Pre pay gas 預先支付Gas
3) Create a new state object if the recipient is \0*32 如果接收人是空,那麼建立一個新的state object
4) Value transfer 轉賬
== If contract creation ==
4a) Attempt to run transaction data 嘗試執行輸入的資料
4b) If valid, use result as code for the new state object 如果有效,那麼用執行的結果作為新的state object的code
== end ==
5) Run Script section 執行指令碼部分
6) Derive new state root 匯出新的state root
*/
type StateTransition struct {
gp *GasPool //用來追蹤區塊內部的Gas的使用情況
msg Message // Message Call
gas uint64
gasPrice *big.Int // gas的價格
initialGas *big.Int // 最開始的gas
value *big.Int // 轉賬的值
data []byte // 輸入資料
state vm.StateDB // StateDB
evm *vm.EVM // 虛擬機器
}
// Message represents a message sent to a contract.
type Message interface {
From() common.Address
//FromFrontier() (common.Address, error)
To() *common.Address //
GasPrice() *big.Int // Message 的 GasPrice
Gas() *big.Int //message 的 GasLimit
Value() *big.Int
Nonce() uint64
CheckNonce() bool
Data() []byte
}
構造
// NewStateTransition initialises and returns a new state transition object.
func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition {
return &StateTransition{
gp: gp,
evm: evm,
msg: msg,
gasPrice: msg.GasPrice(),
initialGas: new(big.Int),
value: msg.Value(),
data: msg.Data(),
state: evm.StateDB,
}
}
執行Message
// ApplyMessage computes the new state by applying the given message
// against the old state within the environment.
// ApplyMessage 通過應用給定的Message 和狀態來生成新的狀態
// 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.
// ApplyMessage返回由任何EVM執行(如果發生)返回的位元組,
// 使用的Gas(包括Gas退款),如果失敗則返回錯誤。 一個錯誤總是表示一個核心錯誤,
// 意味著這個訊息對於這個特定的狀態將總是失敗,並且永遠不會在一個塊中被接受。
func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, bool, error) {
st := NewStateTransition(evm, msg, gp)
ret, _, gasUsed, failed, err := st.TransitionDb()
return ret, gasUsed, failed, err
}
TransitionDb
// TransitionDb will transition the state by applying the current message and returning the result
// including the required gas for the operation as well as the used gas. It returns an error if it
// failed. An error indicates a consensus issue.
// TransitionDb
func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big.Int, failed bool, err error) {
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 // 如果msg.To是nil 那麼認為是一個合約建立
// Pay intrinsic gas
// TODO convert to uint64
// 計算最開始的Gas g0
intrinsicGas := IntrinsicGas(st.data, contractCreation, homestead)
if intrinsicGas.BitLen() > 64 {
return nil, nil, nil, false, vm.ErrOutOfGas
}
if err = st.useGas(intrinsicGas.Uint64()); err != nil {
return nil, nil, nil, 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
)
if contractCreation { //如果是合約建立, 那麼呼叫evm的Create方法
ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value)
} else {
// Increment the nonce for the next transaction
// 如果是方法呼叫。那麼首先設定sender的nonce。
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, nil, nil, false, vmerr
}
}
requiredGas = new(big.Int).Set(st.gasUsed()) // 計算被使用的Gas數量
st.refundGas() //計算Gas的退費 會增加到 st.gas上面。 所以礦工拿到的是退稅後的
st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(st.gasUsed(), st.gasPrice)) // 給礦工增加收入。
// requiredGas和gasUsed的區別一個是沒有退稅的, 一個是退稅了的。
// 看上面的呼叫 ApplyMessage直接丟棄了requiredGas, 說明返回的是退稅了的。
return ret, requiredGas, st.gasUsed(), vmerr != nil, err
}
關於g0的計算,在黃皮書上由詳細的介紹
和黃皮書有一定出入的部分在於if contractCreation && homestead {igas.SetUint64(params.TxGasContractCreation) 這是因為 Gtxcreate+Gtransaction = TxGasContractCreation
func IntrinsicGas(data []byte, contractCreation, homestead bool) *big.Int {
igas := new(big.Int)
if contractCreation && homestead {
igas.SetUint64(params.TxGasContractCreation)
} else {
igas.SetUint64(params.TxGas)
}
if len(data) > 0 {
var nz int64
for _, byt := range data {
if byt != 0 {
nz++
}
}
m := big.NewInt(nz)
m.Mul(m, new(big.Int).SetUint64(params.TxDataNonZeroGas))
igas.Add(igas, m)
m.SetInt64(int64(len(data)) - nz)
m.Mul(m, new(big.Int).SetUint64(params.TxDataZeroGas))
igas.Add(igas, m)
}
return igas
}
執行前的檢查
func (st *StateTransition) preCheck() error {
msg := st.msg
sender := st.from()
// Make sure this transaction's nonce is correct
if msg.CheckNonce() {
nonce := st.state.GetNonce(sender.Address())
// 當前本地的nonce 需要和 msg的Nonce一樣 不然就是狀態不同步了。
if nonce < msg.Nonce() {
return ErrNonceTooHigh
} else if nonce > msg.Nonce() {
return ErrNonceTooLow
}
}
return st.buyGas()
}
buyGas, 實現Gas的預扣費, 首先就扣除你的GasLimit * GasPrice的錢。 然後根據計算完的狀態在退還一部分。
func (st *StateTransition) buyGas() error {
mgas := st.msg.Gas()
if mgas.BitLen() > 64 {
return vm.ErrOutOfGas
}
mgval := new(big.Int).Mul(mgas, st.gasPrice)
var (
state = st.state
sender = st.from()
)
if state.GetBalance(sender.Address()).Cmp(mgval) < 0 {
return errInsufficientBalanceForGas
}
if err := st.gp.SubGas(mgas); err != nil { // 從區塊的gaspool裡面減去, 因為區塊是由GasLimit限制整個區塊的Gas使用的。
return err
}
st.gas += mgas.Uint64()
st.initialGas.Set(mgas)
state.SubBalance(sender.Address(), mgval)
// 從賬號裡面減去 GasLimit * GasPrice
return nil
}
退稅,退稅是為了獎勵大家執行一些能夠減輕區塊鏈負擔的指令, 比如清空賬戶的storage. 或者是執行suicide命令來清空賬號。
func (st *StateTransition) refundGas() {
// Return eth for remaining gas to the sender account,
// exchanged at the original rate.
sender := st.from() // err already checked
remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice)
// 首先把使用者還剩下的Gas還回去。
st.state.AddBalance(sender.Address(), remaining)
// Apply refund counter, capped to half of the used gas.
// 然後退稅的總金額不會超過使用者Gas總使用的1/2。
uhalf := remaining.Div(st.gasUsed(), common.Big2)
refund := math.BigMin(uhalf, st.state.GetRefund())
st.gas += refund.Uint64()
// 把退稅的金額加到使用者賬戶上。
st.state.AddBalance(sender.Address(), refund.Mul(refund, st.gasPrice))
// Also return remaining gas to the block gas counter so it is
// available for the next transaction.
// 同時也把退稅的錢還給gaspool給下個交易騰點Gas空間。
st.gp.AddGas(new(big.Int).SetUint64(st.gas))
}
## StateProcessor
StateTransition是用來處理一個一個的交易的。那麼StateProcessor就是用來處理區塊級別的交易的。
結構和構造
// StateProcessor is a basic Processor, which takes care of transitioning
// state from one point to another.
//
// StateProcessor implements Processor.
type StateProcessor struct {
config *params.ChainConfig // Chain configuration options
bc *BlockChain // Canonical block chain
engine consensus.Engine // Consensus engine used for block rewards
}
// NewStateProcessor initialises a new StateProcessor.
func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consensus.Engine) *StateProcessor {
return &StateProcessor{
config: config,
bc: bc,
engine: engine,
}
}
Process,這個方法會被blockchain呼叫。
// 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 根據以太坊規則執行交易資訊來對statedb進行狀態改變,以及獎勵挖礦者或者是其他的叔父節點。
// 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.
// Process返回執行過程中累計的收據和日誌,並返回過程中使用的Gas。 如果由於Gas不足而導致任何交易執行失敗,將返回錯誤。
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, *big.Int, error) {
var (
receipts types.Receipts
totalUsedGas = big.NewInt(0)
header = block.Header()
allLogs []*types.Log
gp = new(GasPool).AddGas(block.GasLimit())
)
// Mutate the the block and state according to any hard-fork specs
// DAO 事件的硬分叉處理
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() {
statedb.Prepare(tx.Hash(), block.Hash(), i)
receipt, _, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, totalUsedGas, cfg)
if err != nil {
return nil, nil, nil, err
}
receipts = append(receipts, receipt)
allLogs = append(allLogs, receipt.Logs...)
}
// 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使用量和nil
return receipts, allLogs, totalUsedGas, nil
}
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.
ApplyTransaction嘗試將事務應用於給定的狀態資料庫,並使用其環境的輸入引數。
//它返回事務的收據,使用的Gas和錯誤,如果交易失敗,表明塊是無效的。
func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, *big.Int, error) {
// 把交易轉換成Message
// 這裡如何驗證訊息確實是Sender傳送的。 TODO
msg, err := tx.AsMessage(types.MakeSigner(config, header.Number))
if err != nil {
return nil, nil, err
}
// Create a new context to be used in the EVM environment
// 每一個交易都建立了新的虛擬機器環境。
context := NewEVMContext(msg, header, bc, author)
// Create a new environment which holds all relevant information
// about the transaction and calling mechanisms.
vmenv := vm.NewEVM(context, statedb, config, cfg)
// Apply the transaction to the current state (included in the env)
_, gas, failed, err := ApplyMessage(vmenv, msg, gp)
if err != nil {
return nil, nil, err
}
// Update the state with pending changes
// 求得中間狀態
var root []byte
if config.IsByzantium(header.Number) {
statedb.Finalise(true)
} else {
root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
}
usedGas.Add(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 = new(big.Int).Set(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
}
網址:http://www.qukuailianxueyuan.io/
欲領取造幣技術與全套虛擬機器資料
區塊鏈技術交流QQ群:756146052 備註:CSDN
尹成學院微信:備註:CSDN
相關文章
- 以太坊原始碼分析(36)ethdb原始碼分析原始碼
- 以太坊原始碼分析(38)event原始碼分析原始碼
- 以太坊原始碼分析(41)hashimoto原始碼分析原始碼
- 以太坊原始碼分析(43)node原始碼分析原始碼
- 以太坊原始碼分析(51)rpc原始碼分析原始碼RPC
- 以太坊原始碼分析(52)trie原始碼分析原始碼
- 以太坊原始碼分析(13)RPC分析原始碼RPC
- 以太坊原始碼分析(35)eth-fetcher原始碼分析原始碼
- 以太坊原始碼分析(20)core-bloombits原始碼分析原始碼OOM
- 以太坊原始碼分析(24)core-state原始碼分析原始碼
- 以太坊原始碼分析(29)core-vm原始碼分析原始碼
- 以太坊原始碼分析(34)eth-downloader原始碼分析原始碼
- 以太坊原始碼分析(37)eth以太坊協議分析原始碼協議
- 以太坊原始碼分析(18)以太坊交易執行分析原始碼
- 以太坊原始碼分析(5)accounts程式碼分析原始碼
- 以太坊交易池原始碼分析原始碼
- 以太坊原始碼分析(31)eth-downloader-peer原始碼分析原始碼
- 以太坊原始碼分析(32)eth-downloader-peer原始碼分析原始碼
- 以太坊原始碼分析(33)eth-downloader-statesync原始碼分析原始碼
- 以太坊原始碼分析(8)區塊分析原始碼
- 以太坊原始碼分析(9)cmd包分析原始碼
- 以太坊原始碼分析(16)挖礦分析原始碼
- 以太坊原始碼分析(26)core-txpool交易池原始碼分析原始碼
- 以太坊原始碼分析(27)core-vm-jumptable-instruction原始碼分析原始碼Struct
- 以太坊原始碼分析(28)core-vm-stack-memory原始碼分析原始碼
- 以太坊原始碼分析(30)eth-bloombits和filter原始碼分析原始碼OOMFilter
- 以太坊原始碼分析(10)CMD深入分析原始碼
- 以太坊原始碼分析(12)交易資料分析原始碼
- 以太坊原始碼分析(19)core-blockchain分析原始碼Blockchain
- 以太坊原始碼分析(44)p2p-database.go原始碼分析原始碼DatabaseGo
- 以太坊原始碼分析(45)p2p-dial.go原始碼分析原始碼Go
- 以太坊原始碼分析(46)p2p-peer.go原始碼分析原始碼Go
- 以太坊原始碼分析(48)p2p-server.go原始碼分析原始碼ServerGo
- 以太坊原始碼分析(49)p2p-table.go原始碼分析原始碼Go
- 以太坊原始碼分析(50)p2p-udp.go原始碼分析原始碼UDPGo
- 以太坊原始碼分析(52)以太坊fast sync演算法原始碼AST演算法
- 以太坊原始碼分析(39)geth啟動流程分析原始碼
- 以太坊原始碼分析(6)accounts賬戶管理分析原始碼