以太坊黃皮書詳解(二)

三稚新、交易執(zhí)行

交易執(zhí)行是以太坊中最為重要的部分勘伺。

在執(zhí)行交易之前首先需要對(duì)交易進(jìn)行初步校驗(yàn):

  • 交易是RLP格式的,無多余字符
  • 交易的簽名是有效的
  • 交易的nonce是有效的(與發(fā)送者賬戶的nonce值一致)
  • gasLimit的值不小于固有g(shù)as g_0
  • 賬戶余額至少夠支付預(yù)付費(fèi)用v_0
    當(dāng)交易滿足上述條件后褂删,交易才會(huì)被執(zhí)行飞醉。
// preCheck校驗(yàn)的后三條。
//交易校驗(yàn)的前兩條是在其他地方執(zhí)行的屯阀。對(duì)于礦工來說交易簽名在加txpool的時(shí)候會(huì)檢查缅帘,在commitTransactions的時(shí)候也會(huì)檢查。
func (st *StateTransition) preCheck() error {
    // Make sure this transaction's nonce is correct.
    // 檢查nonce值
    if st.msg.CheckNonce() {
        nonce := st.state.GetNonce(st.msg.From())
        if nonce < st.msg.Nonce() {
            return ErrNonceTooHigh
        } else if nonce > st.msg.Nonce() {
            return ErrNonceTooLow
        }
    }
    return st.buyGas()
}
func (st *StateTransition) buyGas() error {
    //gasLimit*gasPrice即為預(yù)付的費(fèi)用v0
    mgval := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice)
    if st.state.GetBalance(st.msg.From()).Cmp(mgval) < 0 {
        return errInsufficientBalanceForGas
    }
    if err := st.gp.SubGas(st.msg.Gas()); err != nil {
        return err
    }
    st.gas += st.msg.Gas()
    //initialGas 
    st.initialGas = st.msg.Gas()
    st.state.SubBalance(st.msg.From(), mgval)
    return nil
}

交易的形式化表示
\begin{equation} \boldsymbol{\sigma}' = \Upsilon(\boldsymbol{\sigma}, T) \tag{51} \end{equation}
公式51难衰,是對(duì)交易的形式化定義钦无。交易的執(zhí)行,相當(dāng)于當(dāng)前狀態(tài)\sigma和交易T,通過交易轉(zhuǎn)變函數(shù)\Upsilon,到達(dá)新的狀態(tài){\sigma}'盖袭。

3.1 子狀態(tài)

在交易執(zhí)行的整個(gè)過程中失暂,以太坊保持跟蹤“子狀態(tài)”。子狀態(tài)是紀(jì)錄交易中生成信息的一種方式鳄虱,當(dāng)交易完成時(shí)會(huì)立即需要這些信息弟塞。
交易的子狀態(tài)包含:

  • 自毀集合(self-destruct set),用A_s表示拙已,指在交易完成之后需要被銷毀的賬戶集合决记。
  • 日志序列 (log series),用A_l表示倍踪,指虛擬機(jī)代碼執(zhí)行的歸檔的和可檢索的檢查點(diǎn)系宫。
  • 賬戶集合(touched accounts),用A_t表示惭适,其中空的賬戶在交易結(jié)束時(shí)將被刪除。
  • 退款余額(refund balance)楼镐,用A_r癞志,指在交易完成之后需要退還給發(fā)送賬戶的總額。

子狀態(tài)的形式化表示
\begin{equation} A \equiv (A_{\mathbf{s}}, A_{\mathbf{l}}, A_{\mathbf{t}}, A_{\mathrm{r}}) \tag{52} \end{equation}

\begin{equation} A^0 \equiv (\varnothing,(), \varnothing, 0) \tag{53} \end{equation}
公式52框产,是交易子狀態(tài)的形式化表示.

公式53凄杯,定義了空的子狀態(tài)A^0.

3.2 執(zhí)行

  • 對(duì)交易進(jìn)行初步檢查错洁,從發(fā)送者賬戶中扣除預(yù)付的交易費(fèi)。預(yù)付交易費(fèi)值如公式57所示戒突,為gasLimit*gasPrice + value屯碴。(代碼中是gasLimit*gasPrice,Value是在Call的過程中判斷和扣除的)
  • 計(jì)算固有g(shù)as消耗g_0,如公式54-56所示膊存。并消耗掉該花費(fèi)导而。
  • 如果是創(chuàng)建合約,則走合約創(chuàng)建流程隔崎。消耗相應(yīng)花費(fèi)今艺。
  • 如果是合約執(zhí)行,則走合約執(zhí)行流程爵卒。消耗相應(yīng)花費(fèi)虚缎。
  • 計(jì)算退款余額,將余額退還到發(fā)送者賬戶钓株。
  • 將交易的交易費(fèi)加到礦工賬戶实牡。
  • 返回當(dāng)前狀態(tài),以及交易的花費(fèi)轴合。
/*
The State Transitioning Model

A state transition is a change made when a transaction is applied to the current world state
The state transitioning model does all all the necessary work to work out a valid new state root.

1) Nonce handling
2) Pre pay gas
3) Create a new state object if the recipient is \0*32
4) Value transfer
== If contract creation ==
  4a) Attempt to run transaction data
  4b) If valid, use result as code for the new state object
== end ==
5) Run Script section
6) Derive new state root
*/
//gp 中一開始有g(shù)asLimit數(shù)量的gas
type StateTransition struct {
    gp         *GasPool
    msg        Message
    gas        uint64
    gasPrice   *big.Int
    initialGas uint64
    value      *big.Int
    data       []byte
    state      vm.StateDB
    evm        *vm.EVM
}
// 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.
func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bool, err error) {
    //交易檢查创坞,檢查正確的話,st.gas為gasPrice*gasLimit值桩,即預(yù)付的交易費(fèi)摆霉。
    if err = st.preCheck(); err != nil {
        return
    }
    msg := st.msg
    sender := vm.AccountRef(msg.From())
    homestead := st.evm.ChainConfig().IsHomestead(st.evm.BlockNumber)
    contractCreation := msg.To() == nil

    // Pay intrinsic gas
    // 固有g(shù)as,也就是g0
    gas, err := IntrinsicGas(st.data, contractCreation, homestead)
    if err != nil {
        return nil, 0, false, err
    }
    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
    )
    //創(chuàng)建合約
    if contractCreation {
        ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value)
    } else {
        // Increment the nonce for the next transaction
        //執(zhí)行合約
        st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)
        ret, st.gas, vmerr = evm.Call(sender, st.to(), 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
        }
    }
    //計(jì)算退款奔坟,并返回到發(fā)送者賬戶
    st.refundGas()
    //付交易費(fèi)給礦工
    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
}

3.2.1 每一步的形式化表示

計(jì)算固有g(shù)as消耗的形式化表示

\begin{align} g_0 \equiv {} & \sum_{i \in T_{\mathbf{i}}, T_{\mathbfigoe86m}} \begin{cases} {G_{\mathrm{txdatazero}}} & \text{if} \quad i = 0 \\ {G_{\mathrm{txdatanonzero}}} & \text{otherwise} \end{cases} \tag{54}\\ {} & + \begin{cases} {G_{\mathrm{txcreate}}} & \text{if} \quad T_{\mathrm{t}} = \varnothing \\ 0 & \text{otherwise} \end{cases} \tag{55}\\ {} & + {G_{\mathrm{transaction}}} \tag{56} \end{align}
公式54-56為固有g(shù)as消耗g_0的計(jì)算方式携栋。

  • 統(tǒng)計(jì)T_i,T_d,即交易的init和data字段咳秉,0和非0分開計(jì)算婉支。為0的字節(jié)數(shù)TxDataZeroGas,非0字節(jié)數(shù)TxDataNonZeroGas澜建,并求和向挖。
  • 如果是創(chuàng)建合約,則加上創(chuàng)建合約的固定消耗炕舵。
  • 如果是執(zhí)行合約何之,加上合約執(zhí)行的固定消耗。
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
// 計(jì)算g0
func IntrinsicGas(data []byte, contractCreation, homestead bool) (uint64, error) {
    // Set the starting gas for the raw transaction
    var gas uint64
    //公式55和56
    if contractCreation && homestead {
        gas = params.TxGasContractCreation
    } else {
        gas = params.TxGas
    }
    //公式54咽筋,根據(jù)non-zero和zero data進(jìn)行計(jì)算
    // Bump the required gas by the amount of transactional data
    if len(data) > 0 {
        // Zero and non-zero bytes are priced differently
        var nz uint64
        for _, byt := range data {
            if byt != 0 {
                nz++
            }
        }
        // Make sure we don't exceed uint64 for all data combinations
        if (math.MaxUint64-gas)/params.TxDataNonZeroGas < nz {
            return 0, vm.ErrOutOfGas
        }
        gas += nz * params.TxDataNonZeroGas

        z := uint64(len(data)) - nz
        if (math.MaxUint64-gas)/params.TxDataZeroGas < z {
            return 0, vm.ErrOutOfGas
        }
        gas += z * params.TxDataZeroGas
    }
    return gas, nil
}

計(jì)算預(yù)付交易費(fèi)的形式化表示
\begin{equation} v_0 \equiv {T_{\mathrm{g}}} {T_{\mathrm{p}}} + {T_{\mathrm{v}}} \tag{57} \end{equation}
公式57表示預(yù)付費(fèi)用v_0的計(jì)算溶推。表示預(yù)付費(fèi)用為gasLimit*gasPrice+value。實(shí)際代碼中如上文buyGas.

交易初步校驗(yàn)的形式化表示
\begin{equation} \begin{array}[t]{rcl} S(T) & \neq & \varnothing \quad \wedge \\ \boldsymbol{\sigma}[S(T)] & \neq & \varnothing \quad \wedge \\ {}T_{\mathrm{n}} & = & \boldsymbol{\sigma}[S(T)]_{\mathrm{n}} \quad \wedge \\ g_0 & \leqslant & T_{\mathrm{g}} \quad \wedge \\ v_0 & \leqslant & \boldsymbol{\sigma}[S(T)]_{\mathrm} \quad \wedge \\ T_{\mathrm{g}} & \leqslant & {B_{H}}_{\mathrm{l}} - {\ell}(B_{\mathbf{R}})_{\mathrm{u}} \end{array} \tag{58} \end{equation}
公式58為交易的初步驗(yàn)證的形式化表示蒜危。第一和第二行表示發(fā)送者賬戶不為空虱痕,且存在。第三行表示交易的nonce值為發(fā)送者的當(dāng)前nonce值辐赞。第四行表示固定消耗g_0小于等于gasLimit(否則連固定消耗都不夠)部翘。第五行表示當(dāng)前賬戶余額必須足夠支付預(yù)付費(fèi)用。第六行表示當(dāng)前區(qū)塊的gasLimit-已經(jīng)消耗掉的gas值大于等于交易的gasLimit响委。

交易初始狀態(tài)(虛擬機(jī)執(zhí)行之前)的形式化表示
\begin{equation} \boldsymbol{\sigma}_0 \equiv \boldsymbol{\sigma} \quad \text{except:} \tag{59} \end{equation}

\begin{equation} \boldsymbol{\sigma}_0[S(T)]_{\mathrm新思} \equiv \boldsymbol{\sigma}[S(T)]_{\mathrm} - T_{\mathrm{g}} T_{\mathrm{p}} \tag{60} \end{equation}

\begin{equation} \boldsymbol{\sigma}_0[S(T)]_{\mathrm{n}} \equiv \boldsymbol{\sigma}[S(T)]_{\mathrm{n}} + 1 \tag{61} \end{equation}
公式59-61表示交易執(zhí)行時(shí)的初始狀態(tài)晃酒,該處也是一個(gè)檢查點(diǎn)表牢。用于后續(xù)操作中出錯(cuò)時(shí)的回滾。
交易開始執(zhí)行時(shí)贝次,會(huì)首先將發(fā)送者賬戶的balance減去預(yù)付gas(gasLimit*gasPrice)崔兴,并將該賬戶的nonce加一。其他與原狀態(tài)相同蛔翅。

虛擬機(jī)執(zhí)行的形式化表示
\begin{equation} (\boldsymbol{\sigma}_{P}, g', A, z) \equiv \begin{cases} \Lambda_{4}(\boldsymbol{\sigma}_0, S(T), T_{\mathrm{o}}, g, T_{\mathrm{p}}, T_{\mathrm{v}}, T_{\mathbf{i}}) & \text{if} \quad T_{\mathrm{t}} = \varnothing \\ \Theta_{4}(\boldsymbol{\sigma}_0, S(T), T_{\mathrm{o}}, T_{\mathrm{t}}, g, T_{\mathrm{p}}, T_{\mathrm{v}}, T_{\mathbfu66m8is}) & \text{otherwise} \end{cases} \tag{62} \end{equation}

\begin{equation} g \equiv T_{\mathrm{g}} - g_0 \tag{63} \end{equation}
公式62表示

  • 如果交易的to為空敲茄,則是合約創(chuàng)建交易,狀態(tài)轉(zhuǎn)變函數(shù)為\Lambda_{4}山析。
  • 否則為合約執(zhí)行堰燎,狀態(tài)轉(zhuǎn)變函數(shù)為\Theta_{4}
  • 兩個(gè)轉(zhuǎn)變的共同參數(shù)有:初始狀態(tài){\sigma}_{0}笋轨,發(fā)送者賬戶地址S(T),可用gas值g秆剪,gasPriceT_p,交易valueT_v,原始調(diào)用者T_{\mathrm{o}},0,和\top.其中可用gas值g為gasLimit-g_0爵政,如公式63所示仅讽。原始調(diào)用者T_{\mathrm{o}},根據(jù)交易是創(chuàng)建合約還是執(zhí)行合約會(huì)有所不同,不由交易控制钾挟,由虛擬機(jī)來控制洁灵。
  • 接收賬戶T_t
    經(jīng)過虛擬機(jī)執(zhí)行后狀態(tài)從{\sigma}_{0}轉(zhuǎn)變?yōu)?img class="math-inline" src="https://math.jianshu.com/math?formula=%7B%5Csigma%7D_%7BP%7D" alt="{\sigma}_{P}" mathimg="1">,剩余的gas為g',交易子狀態(tài)為A掺出,交易的狀態(tài)碼為z徽千。

計(jì)算退款的形式化表示
\begin{equation} A'_{r} \equiv A_{r} + \sum_{i \in A_{s}} R_{selfdestruct} \tag{64} \end{equation}

\begin{equation} g^* \equiv g' + \min \left\{ \Big\lfloor \dfrac{T_{\mathrm{g}} - g'}{2} \Big\rfloor, {A'_{\mathrm{r}}} \right\} \tag{65} \end{equation}

公式64表示交易執(zhí)行之后的子狀態(tài)的退款額A'為原先子狀態(tài)退款額與此次需要銷毀的自毀集合返回的退款的和。

公式65時(shí)交易執(zhí)行后的退款額的計(jì)算汤锨,退款額為交易執(zhí)行后剩余gas值g'加上双抽,子狀態(tài)退款額A'和用掉的gas(T_g-g')的一半中較小的那個(gè)。

func (st *StateTransition) refundGas() {
    // Apply refund counter, capped to half of the used gas.
    // 用掉gas的一半闲礼,與子狀態(tài)退款額比較
    refund := st.gasUsed() / 2
    if refund > st.state.GetRefund() {
        refund = st.state.GetRefund()
    }
    //剩余的總gas值
    st.gas += refund


    // Return ETH for remaining gas, exchanged at the original rate.
    remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice)
    st.state.AddBalance(st.msg.From(), remaining)

    // Also return remaining gas to the block gas counter so it is
    // available for the next transaction.
    st.gp.AddGas(st.gas)
}

交易之后狀態(tài)的形式化表示
\begin{eqnarray} \boldsymbol{\sigma}^* & \equiv & \boldsymbol{\sigma}_{P} \quad \text{except} \tag{66}\\ \boldsymbol{\sigma}^*[S(T)]_{\mathrm牍汹} & \equiv & \boldsymbol{\sigma}_{P}[S(T)]_{\mathrm琅翻} + g^* T_{\mathrm{p}} \tag{67}\\ \boldsymbol{\sigma}^*[m]_{\mathrm} & \equiv & \boldsymbol{\sigma}_{P}[m]_{\mathrm柑贞} + (T_{\mathrm{g}} - g^*) T_{\mathrm{p}} \tag{68}\\ m & \equiv & {B_{H}}_{\mathrm{c}} \tag{69} \end{eqnarray}
公式66-69定義了交易之后的預(yù)結(jié)束狀態(tài)(之所以說是預(yù)結(jié)束狀態(tài)是這個(gè)時(shí)候一些需要銷毀的狀態(tài)尚未銷毀),其和虛擬機(jī)執(zhí)行后的狀態(tài){\sigma}_{P}的區(qū)別為:

  • 將退款額返回給交易發(fā)送者賬戶聂抢。公式67钧嘶。
  • 支付交易費(fèi)給礦工。公式68琳疏。其中m為礦工的地址有决。公式69。

\begin{eqnarray} \boldsymbol{\sigma}' & \equiv & \boldsymbol{\sigma}^* \quad \text{except} \tag{70}\\ {}\forall i \in A_{\mathbf{s}}: \boldsymbol{\sigma}'[i] & = & \varnothing \tag{71}\\ {}\forall i \in A_{\mathbf{t}}: \boldsymbol{\sigma}'[i] & = & \varnothing \quad\text{if}\quad \mathtt{\tiny DEAD}(\boldsymbol{\sigma}^*\kern -2pt, i) \tag{72} \end{eqnarray}
公式70-72定義了空盼,交易的結(jié)束狀態(tài)书幕。結(jié)束狀態(tài){\sigma}'與預(yù)結(jié)束狀態(tài)的區(qū)別為,將自毀集合中的賬戶置為空揽趾,公式71台汇,將可控的賬戶集合中的死掉的賬戶,置為空篱瞎。公式72.

\begin{eqnarray} \Upsilon^{\mathrm{g}}(\boldsymbol{\sigma}, T) & \equiv & T_{\mathrm{g}} - g^* \tag{73}\\ \Upsilon^{\mathbf{l}}(\boldsymbol{\sigma}, T) & \equiv & {A_{\mathbf{l}}} \tag{74}\\ \Upsilon^{\mathrm{z}}(\boldsymbol{\sigma}, T) & \equiv & z \tag{75} \end{eqnarray}
公式73-75苟呐,定義了交易的其他幾個(gè)相關(guān)字段。其中公式73俐筋,定義了交易的花費(fèi)為牵素,gasLimit-退款。公式74定義了交易的日志序列即為交易子狀態(tài)中的日志序列澄者。公式75定義了交易的最終狀態(tài)即為虛擬機(jī)執(zhí)行后的狀態(tài)碼笆呆。

四、合約創(chuàng)建

第三部分于合約創(chuàng)建和合約執(zhí)行的具體過程未詳細(xì)介紹粱挡。該部分對(duì)合約創(chuàng)建的過程進(jìn)行詳細(xì)的解釋赠幕。

以太坊中有兩類賬戶,一類為外部擁有賬戶抱怔,即通常意義上的用戶賬戶劣坊。一類為合約賬戶。當(dāng)一個(gè)交易是合約創(chuàng)建屈留,是指該交易的目的是創(chuàng)建一個(gè)新的合約賬戶局冰。合約賬戶創(chuàng)建的過程如下:

  • 根據(jù)規(guī)則生成合約賬戶地址。公式77.
  • 設(shè)置合約賬戶nonce值為1灌危,其balance設(shè)為捐獻(xiàn)值康二,storageRoot設(shè)為空,codeHash為空的Hash勇蝙。公式78-79.
  • 當(dāng)前賬戶余額中減去捐獻(xiàn)值沫勿。公式80-81.
  • 運(yùn)行合約,進(jìn)行合約的初始化工作。如果運(yùn)行過程中g(shù)as不足产雹,則所有狀態(tài)回滾诫惭,消耗掉所有g(shù)as。也就是被創(chuàng)建的合約賬戶也會(huì)被回滾掉蔓挖,捐獻(xiàn)值回滾到原賬戶夕土。
  • 如果合約初始化運(yùn)行成功,則計(jì)算存儲(chǔ)code的花費(fèi)瘟判,若成功怨绣,則設(shè)置賬戶的code。
  • 如果不足以支付存儲(chǔ)費(fèi)用拷获,回滾狀態(tài)篮撑。(這個(gè)地方根據(jù)配置不同菠镇,homestead 和 byzantium會(huì)有所不同)
// 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.
    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())
    evm.StateDB.SetNonce(caller.Address(), nonce+1)
    //生成合約賬戶地址
    contractAddr = crypto.CreateAddress(caller.Address(), nonce)
    //若之前該合約賬戶對(duì)應(yīng)的地址不空吼虎,則返回地址沖突的錯(cuò)誤
    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
    snapshot := evm.StateDB.Snapshot()
    //創(chuàng)建合約賬戶
    evm.StateDB.CreateAccount(contractAddr)
    if evm.ChainConfig().IsEIP158(evm.BlockNumber) {
        evm.StateDB.SetNonce(contractAddr, 1)
    }
    //將捐獻(xiàn)值轉(zhuǎn)移給合約賬戶
    evm.Transfer(evm.StateDB, caller.Address(), contractAddr, 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.
    contract := NewContract(caller, AccountRef(contractAddr), value, gas)
    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()

    //運(yùn)行合約汹忠,進(jìn)行合約的初始化社付,錯(cuò)誤交由最后處理
    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.
    //如果合約創(chuàng)建成功奸鸯,無錯(cuò)誤返回祠丝,則計(jì)算合約存儲(chǔ)代碼的花費(fèi)嘶伟。成功的話九昧,設(shè)置合約賬戶的code铸鹰。如果存儲(chǔ)時(shí)gas不足皂岔,err交由下面處理躁垛。
    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.
    // 如果不是ErrCodeStoreOutOfGas的話剖毯,revert當(dāng)前狀態(tài),消耗gas教馆。說明ErrCodeStoreOutOfGas賬戶會(huì)創(chuàng)建成功逊谋。
    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
}

4.1 形式化表示

合約創(chuàng)建流程的形式化表示
\begin{equation} (\boldsymbol{\sigma}', g', A, z, \mathbf{o}) \equiv \Lambda(\boldsymbol{\sigma}, s, o, g, p, v, \mathbf{i}, e, w) \tag{76} \end{equation}
公式76表示合約創(chuàng)建的形式化表示。

合約創(chuàng)建需要的參數(shù)有:系統(tǒng)狀態(tài)\sigma,發(fā)送者(s)土铺,原始調(diào)用者(o)胶滋,可用gas值(g)板鬓,gas價(jià)格(p),捐獻(xiàn)值(v)究恤,虛擬機(jī)的初始化代碼其實(shí)際為一段任意長度的字節(jié)數(shù)組(i),當(dāng)前虛擬機(jī)調(diào)用的棧深度(e)俭令,以及權(quán)限控制列表(w)。

虛擬機(jī)執(zhí)行合約創(chuàng)建的結(jié)果為新的中間過程狀態(tài)集合{\sigma}'部宿,剩余的gas值g',交易子狀態(tài)A唤蔗,交易狀態(tài)碼z,合約的body code \mathbf{o}.

新建合約賬戶地址的形式化表示
\begin{equation} a \equiv \mathcal{B}_{96..255}\Big(\mathtt{\tiny KEC}\Big(\mathtt{\tiny RLP}\big(\;(s, \boldsymbol{\sigma}[s]_{\mathrm{n}} - 1)\;\big)\Big)\Big) \tag{77} \end{equation}
公式77給出了合約賬戶的地址的計(jì)算方法窟赏,可以看出是跟發(fā)送者賬戶地址以及發(fā)送者的nonce值有關(guān)。

// CreateAddress creates an ethereum address given the bytes and the nonce
func CreateAddress(b common.Address, nonce uint64) common.Address {
    data, _ := rlp.EncodeToBytes([]interface{}{b, nonce})
    return common.BytesToAddress(Keccak256(data)[12:])
}

合約賬戶初始化的形式化表示
\begin{equation} \boldsymbol{\sigma}^* \equiv \boldsymbol{\sigma} \quad \text{except:} \tag{78} \end{equation}

\begin{eqnarray} \boldsymbol{\sigma}^*[a] &=& \big( 1, v + v', \mathtt{\tiny {TRIE}}(\varnothing), \mathtt{\tiny KEC}\big(()\big) \big) \tag{79}\\ \boldsymbol{\sigma}^*[s] &=& \begin{cases} \varnothing & \text{if}\ \boldsymbol{\sigma}[s] = \varnothing \ \wedge\ v = 0 \\ \mathbf{a}^* & \text{otherwise} \end{cases} \tag{80}\\ \mathbf{a}^* &\equiv& (\boldsymbol{\sigma}[s]_{\mathrm{n}}, \boldsymbol{\sigma}[s]_{\mathrm箱季} - v, \boldsymbol{\sigma}[s]_{\mathbf{s}}, \boldsymbol{\sigma}[s]_{\mathrm{c}}) \tag{81} \end{eqnarray}

\begin{equation} v' \equiv \begin{cases} 0 & \text{if} \quad \boldsymbol{\sigma}[a] = \varnothing\\ \boldsymbol{\sigma}[a]_{\mathrm拷况} & \text{otherwise} \end{cases} \tag{82} \end{equation}

生成了合約賬戶的地址后,需要對(duì)賬戶進(jìn)行相應(yīng)的初始化。公式78-82給出了創(chuàng)建合約之后的相關(guān)狀態(tài)揽咕。

公式79表示新建的合約賬戶,其nonce值為1蛹头,balance值為捐獻(xiàn)值+原有值(如果原合約賬戶不為空,公式82),storage為空斑胜,codeHash為空的hash掺炭。

公式80-81表示如果調(diào)用賬戶若為空,則仍為空者冤。若不為空,則相應(yīng)的balance值減去捐獻(xiàn)值v。

虛擬機(jī)執(zhí)行的形式化表示
\begin{equation} (\boldsymbol{\sigma}^{**}, g^{**}, A, \mathbf{o}) \equiv \Xi(\boldsymbol{\sigma}^*, g, I, \{s, a\}) \tag{83} \end{equation}

\begin{eqnarray} I_{\mathrm{a}} & \equiv & a \tag{84}\\ I_{\mathrm{o}} & \equiv & o \tag{85}\\ I_{\mathrm{p}} & \equiv & p \tag{86}\\ I_{\mathbfeg88mis} & \equiv & () \tag{87}\\ I_{\mathrm{s}} & \equiv & s \tag{88}\\ {I_{\mathrm{v}}} & \equiv & v \tag{89}\\ I_{\mathbf} & \equiv & \mathbf{i} \tag{90}\\ I_{\mathrm{e}} & \equiv & e \tag{91}\\ I_{\mathrm{w}} & \equiv & w \tag{92} \end{eqnarray}

合約賬戶地址生成且初始化后吗跋,需要虛擬機(jī)執(zhí)行合約的相應(yīng)初始化代碼。公式83給出了虛擬機(jī)執(zhí)行的過程。

公式83表示入问,虛擬機(jī)在\sigma^*基礎(chǔ)上執(zhí)行,可用gas值為g棱烂,運(yùn)行環(huán)境參數(shù)為I哩治,s為合約創(chuàng)建的調(diào)用者地址,a為新生成的合約賬戶地址。 虛擬機(jī)執(zhí)行之后生成新的臨時(shí)狀態(tài)\sigma^**,剩余的gas值g^**,子狀態(tài)A台谢,以及狀態(tài)碼z。

其中I中的項(xiàng)如公式84-92所示怀读。

  • I_a為新生成的合約賬戶地址苍糠,即a;
  • I_o為原始調(diào)用者,即o姚炕;
  • I_p為gas價(jià)格,即p掸刊;
  • I_d為虛擬機(jī)調(diào)用的input data,因?yàn)槭呛霞s創(chuàng)建尼斧,所以data段為空;
  • I_s為發(fā)送者烛恤,或者說調(diào)用者,即s;
  • I_v為捐獻(xiàn)值杀餐,即v;
  • I_b為初始化代碼段琼讽,即i;
  • I_e為當(dāng)前調(diào)用棧深度脉让,即e;
  • I_w為權(quán)限管理列表滚澜,即w借浊。

虛擬機(jī)執(zhí)行過程中如果出現(xiàn)了gas值不足的情況,則會(huì)回滾所有的狀態(tài)曙蒸。狀態(tài)回到調(diào)用合約創(chuàng)建開始時(shí)的狀態(tài),也就是調(diào)用過程中消耗掉了所有的gas值,但是合約賬戶被混滾掉审孽,變成沒有被創(chuàng)建的狀態(tài)。如果有捐獻(xiàn)值搓萧,捐獻(xiàn)值回滾至原賬戶揍移。

如果虛擬機(jī)執(zhí)行初始化合約的操作成功踏施,則需要存儲(chǔ)相應(yīng)的合約代碼到新創(chuàng)建的合約賬戶诉探。公式93時(shí)計(jì)算存儲(chǔ)合約代碼所需的花費(fèi)竖席,其值與合約的代碼長度有關(guān)束析。
\begin{equation} c \equiv G_{codedeposit} \times |\mathbf{o}| \tag{93} \end{equation}

\begin{align} \quad g' &\equiv \begin{cases} 0 & \text{if} \quad F \\ g^{**} - c & \text{otherwise} \\ \end{cases} \tag{94}\\ \quad \boldsymbol{\sigma}' &\equiv \begin{cases} \boldsymbol{\sigma} & \text{if} \quad F \\ \boldsymbol{\sigma}^{**} \quad \text{except:} & \\ \quad\boldsymbol{\sigma}'[a] = \varnothing & \text{if} \quad \mathtt{\tiny DEAD}(\boldsymbol{\sigma}^{**}, a) \\ \boldsymbol{\sigma}^{**} \quad \text{except:} & \\ \quad\boldsymbol{\sigma}'[a]_{\mathrm{c}} = {\small KEC}(\mathbf{o}) & \text{otherwise} \end{cases} \tag{95}\\ \quad z &\equiv \begin{cases} 0 & \text{if} \quad \boldsymbol{\sigma}^{**} = \varnothing \lor g^{**} < c \\ 1 & \text{otherwise} \end{cases} \tag{96}\\ F &\equiv \big((\boldsymbol{\sigma}^{**} = \varnothing \ \wedge\ \mathbf{o} = \varnothing) \vee\ g^{**} < c \ \vee\ |\mathbf{o}| > 24576\big) \tag{97} \end{align}

虛擬機(jī)執(zhí)行之后根據(jù)執(zhí)行的情況

  • gas值虽填,如果沒有出錯(cuò)牲览。當(dāng)前的臨時(shí)gas值為g^{**} - c兔港。即在虛擬機(jī)執(zhí)行完成后飒赃,再消耗掉存儲(chǔ)code的gas。如果出錯(cuò)蔫慧,則剩余gas值為0.公式94.
  • 狀態(tài)集合盟蚣,如果出錯(cuò)阐枣,則回滾到虛擬機(jī)執(zhí)行前的狀態(tài)。若成功,則狀態(tài)轉(zhuǎn)變?yōu)榕R時(shí)狀態(tài)集\sigma^{**}彬祖,然后進(jìn)行一些后續(xù)處理,如果合約賬戶是個(gè)死賬戶突倍,則將該賬戶置為空淡喜,否則對(duì)合約賬戶的code進(jìn)行設(shè)置澎嚣。
  • 狀態(tài)碼模狭,如果臨時(shí)狀態(tài)集為空(虛擬機(jī)運(yùn)行初始化的時(shí)候就出錯(cuò)了)驱富,或者gas不足以支付存儲(chǔ)code的費(fèi)用线脚,則狀態(tài)碼為0.沒有出錯(cuò)則為1.
  • 出錯(cuò)的情況有,1. 生成的臨時(shí)狀態(tài)集為空且code為空 2. gas不足以支付存儲(chǔ)code的費(fèi)用 3. code長度>24576。

4.2 特別提示

當(dāng)初始化代碼交由虛擬機(jī)執(zhí)行的時(shí)候伶选,新創(chuàng)建的合約賬戶地址是存在的史飞,只不過是沒有body code。因此任何調(diào)用該合約的代碼都會(huì)因?yàn)闆]有可執(zhí)行的代碼而返回錯(cuò)誤仰税。如果初始化代碼中是以自毀操作為結(jié)束的构资,那么合約賬戶在交易完成之前就會(huì)被刪除掉,目前該問題還有爭(zhēng)議陨簇。而對(duì)于正常的STOP代碼葵姥,或者是初始化執(zhí)行返回的代碼為空,虛擬機(jī)執(zhí)行完成后瞻惋,判定到該合約賬戶的code也還是空,那么這個(gè)合約賬戶就會(huì)變成一個(gè)僵尸賬戶悄晃,而其余額也會(huì)被永久的凍結(jié)在里面。

五枫疆、合約調(diào)用

合約調(diào)用的流程如下:

  • 如果to賬戶地址不存在愿险,則新建.
  • 從sender中轉(zhuǎn)賬value值到to賬戶.
  • 從合約賬戶中獲取合約代碼,進(jìn)行設(shè)置恍箭,供虛擬機(jī)執(zhí)行.
  • 虛擬機(jī)執(zhí)行合約代碼。
  • 如果合約執(zhí)行出錯(cuò)呛凶,則回滾到合約執(zhí)行之前的狀態(tài)拣播。
// 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) {
    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
    }

    var (
        to       = AccountRef(addr)
        snapshot = evm.StateDB.Snapshot()
    )
    //to賬戶不存在讹剔,則新建
    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 {
            // Calling a non existing account, don't do antything, but ping the tracer
            if evm.vmConfig.Debug && evm.depth == 0 {
                evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
                evm.vmConfig.Tracer.CaptureEnd(ret, 0, 0, nil)
            }
            return nil, gas, nil
        }
        evm.StateDB.CreateAccount(addr)
    }
    //轉(zhuǎn)賬
    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.
    //設(shè)置要執(zhí)行的代碼
    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)
        }()
    }
    //執(zhí)行代碼
    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.
    if err != nil {
        evm.StateDB.RevertToSnapshot(snapshot)
        if err != errExecutionReverted {
            contract.UseGas(contract.Gas)
        }
    }
    return ret, contract.Gas, err
}


5.1 合約調(diào)用的形式化表示

\begin{equation} (\boldsymbol{\sigma}', g', A, z, \mathbf{o}) \equiv \Theta(\boldsymbol{\sigma}, s, o, r, c, g, p, v, \tilde{v}, \mathbf6aqg0m8, e, w) \tag{98} \end{equation}
公式98為合約調(diào)用的形式化表示蹬音。

合約調(diào)用需要的參數(shù)有:系統(tǒng)狀態(tài)\sigma,發(fā)送者(s)衫画,原始調(diào)用者(o)辛孵,收款人(r),合約賬戶地址(c)斜做,可用gas值(g),gas價(jià)格(p)搜贤,轉(zhuǎn)賬額(v)锌介,捐獻(xiàn)值(\tilde{v}),虛擬機(jī)的執(zhí)行代碼的input data其實(shí)際為一段任意長度的字節(jié)數(shù)組(d),當(dāng)前虛擬機(jī)調(diào)用的棧深度(e),以及權(quán)限控制列表(w)。

虛擬機(jī)執(zhí)行合約調(diào)用的結(jié)果為新的中間過程狀態(tài)集合{\sigma}'世分,剩余的gas值g',交易子狀態(tài)A,交易狀態(tài)碼z臀玄,合約的調(diào)用結(jié)果\mathbf{o}.

合約調(diào)用前的狀態(tài)
\begin{equation} \boldsymbol{\sigma}_1[r]_{\mathrm瓢阴} \equiv \boldsymbol{\sigma}[r]_{\mathrm} + v \quad\wedge\quad \boldsymbol{\sigma}_1[s]_{\mathrm健无} \equiv \boldsymbol{\sigma}[s]_{\mathrm荣恐} - v \tag{99} \end{equation}

\begin{equation} \boldsymbol{\sigma}_1 \equiv \boldsymbol{\sigma}_1' \quad \text{except:} \tag{100} \end{equation}

\begin{equation} \boldsymbol{\sigma}_1[s] \equiv \begin{cases} \varnothing & \text{if}\ \boldsymbol{\sigma}_1'[s] = \varnothing \ \wedge\ v = 0 \\ \mathbf{a}_1 &\text{otherwise} \end{cases} \tag{101} \end{equation}

\begin{equation} \mathbf{a}_1 \equiv \left(\boldsymbol{\sigma}_1'[s]_{\mathrm{n}}, \boldsymbol{\sigma}_1'[s]_{\mathrm} - v, \boldsymbol{\sigma}_1'[s]_{\mathbf{s}}, \boldsymbol{\sigma}_1'[s]_{\mathrm{c}}\right) \tag{102} \end{equation}

\begin{equation} \text{and}\quad \boldsymbol{\sigma}_1' \equiv \boldsymbol{\sigma} \quad \text{except:} \tag{103} \end{equation}

\begin{equation} \begin{cases} \boldsymbol{\sigma}_1'[r] \equiv (0, v, \mathtt{\tiny TRIE}(\varnothing), \mathtt{\tiny KEC}(())) & \text{if} \quad \boldsymbol{\sigma}[r] = \varnothing \wedge v \neq 0 \\ \boldsymbol{\sigma}_1'[r] \equiv \varnothing & \text{if}\quad \boldsymbol{\sigma}[r] = \varnothing \wedge v = 0 \\ \boldsymbol{\sigma}_1'[r] \equiv \mathbf{a}_1' & \text{otherwise} \end{cases} \tag{104} \end{equation}

\begin{equation} \mathbf{a}_1' \equiv (\boldsymbol{\sigma}[r]_{\mathrm{n}}, \boldsymbol{\sigma}[r]_{\mathrm睬涧} + v, \boldsymbol{\sigma}[r]_{\mathbf{s}}, \boldsymbol{\sigma}[r]_{\mathrm{c}}) \tag{105} \end{equation}
公式99-105為虛擬機(jī)執(zhí)行合約代碼之前的一些臨時(shí)狀態(tài)募胃。

在虛擬機(jī)執(zhí)行合約代碼之前旗唁,首先進(jìn)行轉(zhuǎn)賬。(除非發(fā)送者和接收者相同)公式99.

由于調(diào)用者有可能是未定義的痹束。所以更嚴(yán)謹(jǐn)?shù)亩x如公式100-105.

  • 調(diào)用者如果之前為空且value為0检疫,則調(diào)用者依然為空。否則祷嘶,調(diào)用者的balance減去轉(zhuǎn)賬值屎媳。公式100-102.
  • 如果接收者賬戶不存在,且轉(zhuǎn)賬值不為0论巍,則新建接收者烛谊,將其nonce值0,余額為轉(zhuǎn)賬額v嘉汰,storageRoot為空的TRIE丹禀,codeHash為空的hash。
  • 如果接收者賬戶不存在鞋怀,且轉(zhuǎn)賬值為0.則不處理双泪。
  • 如果接收者賬戶存在,則其余額為原先的余額加上轉(zhuǎn)賬值密似。

虛擬機(jī)執(zhí)行合約調(diào)用的形式化表示

\begin{eqnarray} \boldsymbol{\sigma}' & \equiv & \begin{cases} \boldsymbol{\sigma} & \text{if} \quad \boldsymbol{\sigma}^{**} = \varnothing \\ \boldsymbol{\sigma}^{**} & \text{otherwise} \end{cases} \tag{106}\\ g' & \equiv & \begin{cases} 0 & \text{if} \quad \boldsymbol{\sigma}^{**} = \varnothing \ \wedge \mathbf{o} = \varnothing \\ g^{**} & \text{otherwise} \end{cases} \tag{107}\\ z & \equiv & \begin{cases} 0 & \text{if} \quad \boldsymbol{\sigma}^{**} = \varnothing \\ 1 & \text{otherwise} \end{cases} \tag{108}\\ (\boldsymbol{\sigma}^{**}, g^{**},A, \mathbf{o}) & \equiv & \Xi \tag{109}\\ I_{\mathrm{a}} & \equiv & r \tag{110}\\ I_{\mathrm{o}} & \equiv & o \tag{111}\\ I_{\mathrm{p}} & \equiv & p \tag{112}\\ I_{\mathbf6mkkigq} & \equiv & \mathbfyccus8k \tag{113}\\ I_{\mathrm{s}} & \equiv & s \tag{114}\\ I_{\mathrm{v}} & \equiv & \tilde{v} \tag{115}\\ I_{\mathrm{e}} & \equiv & e \tag{116}\\ I_{\mathrm{w}} & \equiv & w \tag{117}\\ \mathbf{t} & \equiv & \{s, r\} \tag{118}\\ \end{eqnarray}

\begin{equation} \Xi \equiv \begin{cases} \Xi_{\mathtt{ECREC}}(\boldsymbol{\sigma}_1, g, I, \mathbf{t}) & \text{if} \quad r = 1 \\ \Xi_{\mathtt{SHA256}}(\boldsymbol{\sigma}_1, g, I, \mathbf{t}) & \text{if} \quad r = 2 \\ \Xi_{\mathtt{RIP160}}(\boldsymbol{\sigma}_1, g, I, \mathbf{t}) & \text{if} \quad r = 3 \\ \Xi_{\mathtt{ID}}(\boldsymbol{\sigma}_1, g, I, \mathbf{t}) & \text{if} \quad r = 4 \\ \Xi_{\mathtt{EXPMOD}}(\boldsymbol{\sigma}_1, g, I, \mathbf{t}) & \text{if} \quad r = 5 \\ \Xi_{\mathtt{BN\_ADD}}(\boldsymbol{\sigma}_1, g, I, \mathbf{t}) & \text{if} \quad r = 6 \\ \Xi_{\mathtt{BN\_MUL}}(\boldsymbol{\sigma}_1, g, I, \mathbf{t}) & \text{if} \quad r = 7 \\ \Xi_{\mathtt{SNARKV}}(\boldsymbol{\sigma}_1, g, I, \mathbf{t}) & \text{if} \quad r = 8 \\ \Xi(\boldsymbol{\sigma}_1, g, I, \mathbf{t}) & \text{otherwise} \end{cases} \tag{119} \end{equation}

\begin{equation} \text{Let} \; \mathtt{\tiny KEC}(I_{\mathbf焙矛}) = \boldsymbol{\sigma}[c]_{\mathrm{c}} \tag{120} \end{equation}

虛擬機(jī)執(zhí)行的參數(shù)為,公式109-118:

  • I_a為接收者賬戶残腌,即r村斟,(在合約創(chuàng)建的時(shí)候,該處為新創(chuàng)建的合約賬戶);
  • I_o為原始調(diào)用者抛猫,即o;
  • I_p為gasPric蟆盹,即p;
  • I_d為輸入數(shù)據(jù)(input data),即d;
  • I_s為調(diào)用者賬戶邑滨,即s;
  • I_v為捐獻(xiàn)值(注意這里并不是轉(zhuǎn)賬值)日缨,即\tilde{v};
  • I_e當(dāng)前調(diào)用棧深度,即e;
  • I_w權(quán)限管理掖看,即w;
  • t可控的賬戶(touched accounts)為調(diào)用者賬戶和接收者賬戶;

虛擬機(jī)執(zhí)行的合約為公式119-120匣距,其中公式119的前8種為預(yù)編譯好的合約,主要完成一些基本的加密和運(yùn)算等操作哎壳,最后一種即為調(diào)用用戶的合約毅待。

合約執(zhí)行的結(jié)果如公式106-118所示。

  • 如果合約執(zhí)行失敗归榕,則狀態(tài)回滾到之前的狀態(tài)尸红。如果執(zhí)行成功,則狀態(tài)轉(zhuǎn)變?yōu)閳?zhí)行后狀態(tài)。公式106.
  • 如果合約執(zhí)行失敗外里,則消耗掉所有的gas怎爵,gas剩余值為0.成功,則消耗掉執(zhí)行過程中的gas盅蝗。公式107.
  • 如果合約執(zhí)行失敗鳖链,則返回0,成功返回1.
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末墩莫,一起剝皮案震驚了整個(gè)濱河市芙委,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌狂秦,老刑警劉巖灌侣,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異裂问,居然都是意外死亡侧啼,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門愕秫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來慨菱,“玉大人焰络,你說我怎么就攤上這事戴甩。” “怎么了闪彼?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵甜孤,是天一觀的道長。 經(jīng)常有香客問我畏腕,道長缴川,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任描馅,我火速辦了婚禮把夸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘铭污。我一直安慰自己恋日,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布嘹狞。 她就那樣靜靜地躺著岂膳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪磅网。 梳的紋絲不亂的頭發(fā)上谈截,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼簸喂。 笑死毙死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的喻鳄。 我是一名探鬼主播规哲,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼诽表!你這毒婦竟也來了唉锌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤竿奏,失蹤者是張志新(化名)和其女友劉穎袄简,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泛啸,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡绿语,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了候址。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吕粹。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖岗仑,靈堂內(nèi)的尸體忽然破棺而出匹耕,到底是詐尸還是另有隱情,我是刑警寧澤荠雕,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布稳其,位于F島的核電站,受9級(jí)特大地震影響炸卑,放射性物質(zhì)發(fā)生泄漏既鞠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一盖文、第九天 我趴在偏房一處隱蔽的房頂上張望嘱蛋。 院中可真熱鬧,春花似錦五续、人聲如沸洒敏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽桐玻。三九已至,卻和暖如春荆萤,著一層夾襖步出監(jiān)牢的瞬間镊靴,已是汗流浹背铣卡。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留偏竟,地道東北人煮落。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像踊谋,于是被迫代替她去往敵國和親蝉仇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 簡(jiǎn)介 不管你們知不知道以太坊(Ethereum blockchain)是什么殖蚕,但是你們大概都聽說過以太坊轿衔。最近在新...
    Lilymoana閱讀 3,887評(píng)論 1 22
  • 寫在篇頭 本文是對(duì)以太坊的黃皮書的解析,并參照go-ethereum中的實(shí)現(xiàn)睦疫,將相應(yīng)的代碼也列了出來害驹。黃皮書中使用...
    yuan1028閱讀 7,515評(píng)論 4 17
  • 原文:Smart contracts 正如我們?cè)赱intro]中看到的那樣,以太坊中有兩種不同類型的帳戶:外部擁有...
    Jisen閱讀 4,914評(píng)論 1 7
  • 作為交換蛤育,我也可以給他寫傳記宛官。但,也就只能是想想而已瓦糕。 就算自己的一生過得像歷險(xiǎn)記底洗,也很難以寫出來,讓別人感受到這...
    子瞳閱讀 223評(píng)論 0 0
  • 子陌鳥咕娄, 忘微草亥揖, 世人看不穿卻還嘆橋邊紅藥。 物有情谭胚, 人共老徐块, 盼得清流水竹蕭蕭萬劍妖嬈。 青玉案灾而, 婉美暗消...
    墨子龍閱讀 242評(píng)論 1 3