溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何進(jìn)行以太坊智能合約虛擬機(jī)EVM原理與實(shí)現(xiàn)

發(fā)布時(shí)間:2021-12-18 13:49:11 來源:億速云 閱讀:208 作者:柒染 欄目:互聯(lián)網(wǎng)科技

今天就跟大家聊聊有關(guān)如何進(jìn)行以太坊智能合約虛擬機(jī)EVM原理與實(shí)現(xiàn),可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

以太坊 EVM原理與實(shí)現(xiàn)

通常智能合約的開發(fā)流程是用solidlity編寫邏輯代碼,再通過編譯器編譯元數(shù)據(jù),最后再發(fā)布到以太坊上。以太坊底層通過EVM模塊支持合約的執(zhí)行與調(diào)用,調(diào)用時(shí)根據(jù)合約地址獲取到代碼,生成環(huán)境后載入到EVM中運(yùn)行。

如何進(jìn)行以太坊智能合約虛擬機(jī)EVM原理與實(shí)現(xiàn)

代碼結(jié)構(gòu)

.
├── analysis.go            //跳轉(zhuǎn)目標(biāo)判定
├── common.go
├── contract.go            //合約數(shù)據(jù)結(jié)構(gòu)
├── contracts.go           //預(yù)編譯好的合約
├── errors.go
├── evm.go                 //執(zhí)行器 對外提供一些外部接口   
├── gas.go                 //call gas花費(fèi)計(jì)算 一級指令耗費(fèi)gas級別
├── gas_table.go           //指令耗費(fèi)計(jì)算函數(shù)表
├── gen_structlog.go       
├── instructions.go        //指令操作
├── interface.go           
├── interpreter.go         //解釋器 調(diào)用核心
├── intpool.go             //int值池
├── int_pool_verifier_empty.go
├── int_pool_verifier.go
├── jump_table.go           //指令和指令操作(操作,花費(fèi),驗(yàn)證)對應(yīng)表
├── logger.go               //狀態(tài)日志
├── memory.go               //EVM 內(nèi)存
├── memory_table.go         //EVM 內(nèi)存操作表 主要衡量操作所需內(nèi)存大小
├── noop.go
├── opcodes.go              //Op指令 以及一些對應(yīng)關(guān)系     
├── runtime
│   ├── env.go              //執(zhí)行環(huán)境 
│   ├── fuzz.go
│   └── runtime.go          //運(yùn)行接口 測試使用
├── stack.go                //棧
└── stack_table.go          //棧驗(yàn)證

指令

OpCode

文件opcodes.go中定義了所有的OpCode,該值是一個(gè)byte,合約編譯出來的bytecode中,一個(gè)OpCode就是上面的一位。opcodes按功能分為9組(運(yùn)算相關(guān),塊操作,加密相關(guān)等)。

    //算數(shù)相關(guān)
    const (
        // 0x0 range - arithmetic ops
        STOP OpCode = iota
        ADD
        MUL
        SUB
        DIV
        SDIV
        MOD
        SMOD
        ADDMOD
        MULMOD
        EXP
        SIGNEXTEND
    )

Instruction

文件jump.table.go定義了四種指令集合,每個(gè)集合實(shí)質(zhì)上是個(gè)256長度的數(shù)組,名字翻譯過來是(荒地,農(nóng)莊,拜占庭,君士坦丁堡)估計(jì)是對應(yīng)了EVM的四個(gè)發(fā)展階段。指令集向前兼容。

	frontierInstructionSet       = NewFrontierInstructionSet()
	homesteadInstructionSet      = NewHomesteadInstructionSet()
	byzantiumInstructionSet      = NewByzantiumInstructionSet()
	constantinopleInstructionSet = NewConstantinopleInstructionSet()

具體每條指令結(jié)構(gòu)如下,字段意思見注釋。

type operation struct {
	//對應(yīng)的操作函數(shù)
	execute executionFunc
	// 操作對應(yīng)的gas消耗
	gasCost gasFunc
	// 棧深度驗(yàn)證
	validateStack stackValidationFunc
	// 操作所需空間
	memorySize memorySizeFunc

	halts   bool // 運(yùn)算中止
	jumps   bool // 跳轉(zhuǎn)(for)
	writes  bool // 是否寫入
	valid   bool // 操作是否有效
	reverts bool // 出錯(cuò)回滾
	returns bool // 返回
}

按下面的ADD指令為例

定義
    ADD: {
        execute:       opAdd,
        gasCost:       constGasFunc(GasFastestStep),
        validateStack: makeStackFunc(2, 1),
        valid:         true,
    },
操作

不同的操作有所不同,操作對象根據(jù)指令不同可能影響棧,內(nèi)存,statedb。

    func opAdd(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
        //彈出一個(gè)值,取出一個(gè)值(這個(gè)值依舊保存在棧上面,運(yùn)算結(jié)束后這個(gè)值就改變成結(jié)果值)
        x, y := stack.pop(), stack.peek()
        //加運(yùn)算
        math.U256(y.Add(x, y))
        //數(shù)值緩存
        evm.interpreter.intPool.put(x)
        return nil, nil
    }
gas花費(fèi)

不同的運(yùn)算有不同的初始值和對應(yīng)的運(yùn)算方法,具體的方法都定義在gas_table里面。 按加法的為例,一次加操作固定耗費(fèi)為3。

    //固定耗費(fèi)
    func constGasFunc(gas uint64) gasFunc {
        return func(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
            return gas, nil
        }
    }

除此之外還有兩個(gè)定義會(huì)影響gas的計(jì)算,通常作為量化的一個(gè)單位。

    //file go-ethereum/core/vm/gas.go
    const (
        GasQuickStep   uint64 = 2
        GasFastestStep uint64 = 3
        GasFastStep    uint64 = 5
        GasMidStep     uint64 = 8
        GasSlowStep    uint64 = 10
        GasExtStep     uint64 = 20

        GasReturn       uint64 = 0
        GasStop         uint64 = 0
        GasContractByte uint64 = 200
    )

    //file go-ethereum/params/gas_table.go
    type GasTable struct {
        ExtcodeSize uint64
        ExtcodeCopy uint64
        Balance     uint64
        SLoad       uint64
        Calls       uint64
        Suicide     uint64

        ExpByte uint64

        // CreateBySuicide occurs when the
        // refunded account is one that does
        // not exist. This logic is similar
        // to call. May be left nil. Nil means
        // not charged.
        CreateBySuicide uint64
    }
memorySize

因?yàn)榧硬僮鞑恍枰暾垉?nèi)存因而memorySize為默認(rèn)值0。

棧驗(yàn)證

先驗(yàn)證棧上的操作數(shù)夠不夠,再驗(yàn)證棧是否超出最大限制,加法在這里僅需驗(yàn)證其參數(shù)夠不夠,運(yùn)算之后棧是要減一的。

    func makeStackFunc(pop, push int) stackValidationFunc {
        return func(stack *Stack) error {
            //深度驗(yàn)證
            if err := stack.require(pop); err != nil {
                return err
            }
            //最大值驗(yàn)證
            //StackLimit       uint64 = 1024 
            if stack.len()+push-pop > int(params.StackLimit) {
                return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit)
            }
            return nil
        }
    }

智能合約

合約是EVM智能合約的存儲(chǔ)單位也是解釋器執(zhí)行的基本單位,包含了代碼,調(diào)用人,所有人,gas相關(guān)的信息.

    type Contract struct {
        // CallerAddress is the result of the caller which initialised this
        // contract. However when the "call method" is delegated this value
        // needs to be initialised to that of the caller's caller.
        CallerAddress common.Address
        caller        ContractRef
        self          ContractRef

        jumpdests destinations // result of JUMPDEST analysis.

        Code     []byte
        CodeHash common.Hash
        CodeAddr *common.Address
        Input    []byte

        Gas   uint64
        value *big.Int

        Args []byte

        DelegateCall bool
    }

EVM原生預(yù)編譯了一批合約,定義在contracts.go里面。主要用于加密操作。

// PrecompiledContractsByzantium contains the default set of pre-compiled Ethereum
// contracts used in the Byzantium release.
var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{
	common.BytesToAddress([]byte{1}): &ecrecover{},
	common.BytesToAddress([]byte{2}): &sha256hash{},
	common.BytesToAddress([]byte{3}): &ripemd160hash{},
	common.BytesToAddress([]byte{4}): &dataCopy{},
	common.BytesToAddress([]byte{5}): &bigModExp{},
	common.BytesToAddress([]byte{6}): &bn256Add{},
	common.BytesToAddress([]byte{7}): &bn256ScalarMul{},
	common.BytesToAddress([]byte{8}): &bn256Pairing{},
}

執(zhí)行機(jī)

EVM中棧用于保存操作數(shù),每個(gè)操作數(shù)的類型是big.int,這就是網(wǎng)上很多人說EVM是256位虛擬機(jī)的原因。執(zhí)行opcode的時(shí)候,從上往下彈出操作數(shù),作為操作的參數(shù)。

type Stack struct {
	data []*big.Int
}

func (st *Stack) push(d *big.Int) {
	// NOTE push limit (1024) is checked in baseCheck
	//stackItem := new(big.Int).Set(d)
	//st.data = append(st.data, stackItem)
	st.data = append(st.data, d)
}

func (st *Stack) peek() *big.Int {
	return st.data[st.len()-1]
}

func (st *Stack) pop() (ret *big.Int) {
	ret = st.data[len(st.data)-1]
	st.data = st.data[:len(st.data)-1]
	return
}

內(nèi)存

內(nèi)存用于一些內(nèi)存操作(MLOAD,MSTORE,MSTORE8)及合約調(diào)用的參數(shù)拷貝(CALL,CALLCODE)。

內(nèi)存數(shù)據(jù)結(jié)構(gòu),維護(hù)了一個(gè)byte數(shù)組,MLOAD,MSTORE讀取存入的時(shí)候都要指定位置及長度才能準(zhǔn)確的讀寫。

    type Memory struct {
        store       []byte
        lastGasCost uint64
    }

    // Set sets offset + size to value
    func (m *Memory) Set(offset, size uint64, value []byte) {
        // length of store may never be less than offset + size.
        // The store should be resized PRIOR to setting the memory
        if size > uint64(len(m.store)) {
            panic("INVALID memory: store empty")
        }

        // It's possible the offset is greater than 0 and size equals 0. This is because
        // the calcMemSize (common.go) could potentially return 0 when size is zero (NO-OP)
        if size > 0 {
            copy(m.store[offset:offset+size], value)
        }
    }

    func (self *Memory) Get(offset, size int64) (cpy []byte) {
        if size == 0 {
            return nil
        }

        if len(self.store) > int(offset) {
            cpy = make([]byte, size)
            copy(cpy, self.store[offset:offset+size])

            return
        }

        return
    }

內(nèi)存操作

    func opMload(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
        offset := stack.pop()
        val := evm.interpreter.intPool.get().SetBytes(memory.Get(offset.Int64(), 32))
        stack.push(val)

        evm.interpreter.intPool.put(offset)
        return nil, nil
    }

    func opMstore(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
        // pop value of the stack
        mStart, val := stack.pop(), stack.pop()
        memory.Set(mStart.Uint64(), 32, math.PaddedBigBytes(val, 32))

        evm.interpreter.intPool.put(mStart, val)
        return nil, nil
    }

    func opMstore8(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
        off, val := stack.pop().Int64(), stack.pop().Int64()
        memory.store[off] = byte(val & 0xff)

        return nil, nil
    }

stateDb

合約本身不保存數(shù)據(jù),那么合約的數(shù)據(jù)是保存在哪里呢?合約及其調(diào)用類似于數(shù)據(jù)庫的日志,保存了合約定義以及對他的一系列操作,只要將這些操作執(zhí)行一遍就能獲取當(dāng)前的結(jié)果,但是如果每次都要去執(zhí)行就太慢了,因而這部分?jǐn)?shù)據(jù)是會(huì)持久化到stateDb里面的。code中定義了兩條指令SSTORE SLOAD用于從db中讀寫合約當(dāng)前的狀態(tài)。

    func opSload(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
        loc := common.BigToHash(stack.pop())
        val := evm.StateDB.GetState(contract.Address(), loc).Big()
        stack.push(val)
        return nil, nil
    }

    func opSstore(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
        loc := common.BigToHash(stack.pop())
        val := stack.pop()
        evm.StateDB.SetState(contract.Address(), loc, common.BigToHash(val))

        evm.interpreter.intPool.put(val)
        return nil, nil
    }

執(zhí)行過程

執(zhí)行入口定義在evm.go中,功能就是組裝執(zhí)行環(huán)境(代碼,執(zhí)行人關(guān)系,參數(shù)等)。

    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
        }

        // 合約調(diào)用深度檢查
        if evm.depth > int(params.CallCreateDepth) {
            return nil, gas, ErrDepth
        }
        // balance 檢查
        if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
            return nil, gas, ErrInsufficientBalance
        }

        var (
            to       = AccountRef(addr)
            //保存當(dāng)前狀態(tài),如果出錯(cuò),就回滾到這個(gè)狀態(tài)
            snapshot = evm.StateDB.Snapshot()
        )
        if !evm.StateDB.Exist(addr) {
            //創(chuàng)建調(diào)用對象的stateObject
            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)
        }
        //調(diào)用別人合約可能需要花錢
        evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)

        //創(chuàng)建合約環(huán)境
        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 {
            //錯(cuò)誤回滾
            evm.StateDB.RevertToSnapshot(snapshot)
            if err != errExecutionReverted {
                contract.UseGas(contract.Gas)
            }
        }
        return ret, contract.Gas, err
    }

類似的函數(shù)有四個(gè)。詳細(xì)區(qū)別見最后的參考。

  • Call A->B A,B的環(huán)境獨(dú)立

  • CallCode、 和Call類似 區(qū)別在于storage位置不一樣

  • DelegateCall、 和CallCode類似,區(qū)別在于msg.send不一樣

  • StaticCall 和call相似 只是不能修改狀態(tài)

Contract和參數(shù)構(gòu)造完成后調(diào)用執(zhí)行函數(shù),執(zhí)行函數(shù)會(huì)檢查調(diào)用的是否會(huì)之前編譯好的原生合約,如果是原生合約則調(diào)用原生合約,否則調(diào)用解釋器執(zhí)行函數(shù)運(yùn)算合約。

    // run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter.
    func run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
        if contract.CodeAddr != nil {
            precompiles := PrecompiledContractsHomestead
            if evm.ChainConfig().IsByzantium(evm.BlockNumber) {
                precompiles = PrecompiledContractsByzantium
            }
            if p := precompiles[*contract.CodeAddr]; p != nil {
                return RunPrecompiledContract(p, input, contract)
            }
        }
        return evm.interpreter.Run(contract, input)
    }

解釋器

    func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err error) {

        //返回?cái)?shù)據(jù)
        in.returnData = nil

        var (
            op    OpCode        // 當(dāng)前指令
            mem   = NewMemory() // 內(nèi)存
            stack = newstack()  // 棧
            pc   = uint64(0)    // 指令位置
            cost uint64         // gas花費(fèi)
            pcCopy  uint64      // debug使用
            gasCopy uint64      // debug使用
            logged  bool        // debug使用
        )
        contract.Input = input  //函數(shù)入?yún)?

        //*****省略******

        for atomic.LoadInt32(&in.evm.abort) == 0 {
            //獲取一條指令及指令對應(yīng)的操作
            op = contract.GetOp(pc)
            operation := in.cfg.JumpTable[op]
            //valid校驗(yàn)
            if !operation.valid {
                return nil, fmt.Errorf("invalid opcode 0x%x", int(op))
            }
            //棧校驗(yàn)
            if err := operation.validateStack(stack); err != nil {
                return nil, err
            }
            //修改檢查
            if err := in.enforceRestrictions(op, operation, stack); err != nil {
                return nil, err
            }

            var memorySize uint64
            //計(jì)算內(nèi)存 按操作所需要的操作數(shù)來算
            if operation.memorySize != nil {
                memSize, overflow := bigUint64(operation.memorySize(stack))
                if overflow {
                    return nil, errGasUintOverflow
                }
                // 
                if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
                    return nil, errGasUintOverflow
                }
            }
            // 校驗(yàn)cost 調(diào)用前面提到的costfunc 計(jì)算本次操作cost消耗
            cost, err = operation.gasCost(in.gasTable, in.evm, contract, stack, mem, memorySize)
            if err != nil || !contract.UseGas(cost) {
                return nil, ErrOutOfGas  //超出掛掉
            }
            if memorySize > 0 {
                //如果本次操作需要消耗memory ,擴(kuò)展memory 
                mem.Resize(memorySize)  
            }

            // 執(zhí)行操作
            res, err := operation.execute(&pc, in.evm, contract, mem, stack)

            if verifyPool {
                verifyIntegerPool(in.intPool)
            }
            // 如果遇到return 設(shè)置返回值
            if operation.returns {
                in.returnData = res
            }

            switch {
            case err != nil:
                return nil, err       //報(bào)錯(cuò)
            case operation.reverts:   //出錯(cuò)回滾
                return res, errExecutionReverted
            case operation.halts:
                return res, nil       //停止
            case !operation.jumps:    //跳轉(zhuǎn)
                pc++
            }
        }
        return nil, nil
    }

Solidity案例

和其他語言類似,有了字節(jié)碼運(yùn)行機(jī),就可以在字節(jié)碼上面再組織其他高級語言,而solidlity語言就是實(shí)現(xiàn)了這樣的語言編譯器,方便了合約編寫,有利于推廣以太坊dapp開發(fā)。

pragma solidity ^0.4.17;

contract simple {
      uint num = 0;
    function simple(){
        num = 123;
    }
    
  
    function add(uint i) public returns(uint){
        uint m = 111;
        num =num * i+m;
        return num;
    } 

}

生成的Opcodes碼

JUMPDEST 函數(shù)入口

PUSH + JUMPI/JUMP 類似于調(diào)用函數(shù)

CALLDATASIZE + CALLDATALOAD 大約是獲取函數(shù)參數(shù)

.code
  PUSH 80			contract simple {\n      uint ...
  PUSH 40			contract simple {\n      uint ...
  MSTORE 			contract simple {\n      uint ...
  PUSH 0			0  //成員變量初始值
  DUP1 			uint num = 0
  //從下面這條指令可以看出,初始化的時(shí)候成員變量就會(huì)存到statedb里面去
  SSTORE 			uint num = 0
  CALLVALUE 			function simple(){\n        nu...
  DUP1 			olidity ^
  ISZERO 			a 
  PUSH [tag] 1			a 
  JUMPI 			a 
  PUSH 0			r
  DUP1 			o
  REVERT 			.17;\n
contra
tag 1			a 
  //下面部分是構(gòu)造函數(shù)執(zhí)行的部分
  JUMPDEST 			a 
  POP 			function simple(){\n        nu...
  PUSH 7B			123
  PUSH 0			num  
  DUP2 			num = 123
  SWAP1 			num = 123
  //改變成員變量最后都會(huì)寫入到statedb里面去
  SSTORE 			num = 123
  POP 			num = 123
  PUSH #[$] 0000000000000000000000000000000000000000000000000000000000000000			contract simple {\n      uint ...
  DUP1 			contract simple {\n      uint ...
  PUSH [$] 0000000000000000000000000000000000000000000000000000000000000000			contract simple {\n      uint ...
  PUSH 0			contract simple {\n      uint ...
  CODECOPY 			contract simple {\n      uint ...
  PUSH 0			contract simple {\n      uint ...
  RETURN 			contract simple {\n      uint ...
  //上面部分做完初始化之后并不會(huì)進(jìn)入到runtime階段
.data
  0:
    .code
      //下面這段代碼大約是處理參數(shù)的
      PUSH 80			contract simple {\n      uint ...
      PUSH 40			contract simple {\n      uint ...
      MSTORE 			contract simple {\n      uint ...
      PUSH 4			contract simple {\n      uint ...
      CALLDATASIZE 			contract simple {\n      uint ...
      LT 			contract simple {\n      uint ...
      PUSH [tag] 1			contract simple {\n      uint ...
      JUMPI 			contract simple {\n      uint ...
      PUSH 0			contract simple {\n      uint ...
      CALLDATALOAD 			contract simple {\n      uint ...
      PUSH 100000000000000000000000000000000000000000000000000000000			contract simple {\n      uint ...
      SWAP1 			contract simple {\n      uint ...
      DIV 			contract simple {\n      uint ...
      PUSH FFFFFFFF			contract simple {\n      uint ...
      AND 			contract simple {\n      uint ...
      DUP1 			contract simple {\n      uint ...
      PUSH 1003E2D2			contract simple {\n      uint ...
      EQ 			contract simple {\n      uint ...
      PUSH [tag] 2			contract simple {\n      uint ...
      JUMPI 			contract simple {\n      uint ...
    tag 1			contract simple {\n      uint ...
      JUMPDEST 			contract simple {\n      uint ...
      PUSH 0			contract simple {\n      uint ...
      DUP1 			contract simple {\n      uint ...
      REVERT 			contract simple {\n      uint ...
    tag 2			function add(uint i) public re...
      JUMPDEST 			function add(uint i) public re...
      CALLVALUE 			function add(uint i) public re...
      DUP1 			olidity ^
      ISZERO 			a 
      PUSH [tag] 3			a 
      JUMPI 			a 
      PUSH 0			r
      DUP1 			o
      REVERT 			.17;\n
contra
    tag 3			a 
      JUMPDEST 			a 
      POP 			function add(uint i) public re...
      PUSH [tag] 4			function add(uint i) public re...
      PUSH 4			function add(uint i) public re...
      DUP1 			function add(uint i) public re...
      CALLDATASIZE 			function add(uint i) public re...
      SUB 			function add(uint i) public re...
      DUP2 			function add(uint i) public re...
      ADD 			function add(uint i) public re...
      SWAP1 			function add(uint i) public re...
      DUP1 			function add(uint i) public re...
      DUP1 			function add(uint i) public re...
      CALLDATALOAD 			function add(uint i) public re...
      SWAP1 			function add(uint i) public re...
      PUSH 20			function add(uint i) public re...
      ADD 			function add(uint i) public re...
      SWAP1 			function add(uint i) public re...
      SWAP3 			function add(uint i) public re...
      SWAP2 			function add(uint i) public re...
      SWAP1 			function add(uint i) public re...
      POP 			function add(uint i) public re...
      POP 			function add(uint i) public re...
      POP 			function add(uint i) public re...
      PUSH [tag] 5			function add(uint i) public re...
      JUMP 			function add(uint i) public re...
    tag 4			function add(uint i) public re...
      JUMPDEST 			function add(uint i) public re...
      PUSH 40			function add(uint i) public re...
      MLOAD 			function add(uint i) public re...
      DUP1 			function add(uint i) public re...
      DUP3 			function add(uint i) public re...
      DUP2 			function add(uint i) public re...
      MSTORE 			function add(uint i) public re...
      PUSH 20			function add(uint i) public re...
      ADD 			function add(uint i) public re...
      SWAP2 			function add(uint i) public re...
      POP 			function add(uint i) public re...
      POP 			function add(uint i) public re...
      PUSH 40			function add(uint i) public re...
      MLOAD 			function add(uint i) public re...
      DUP1 			function add(uint i) public re...
      SWAP2 			function add(uint i) public re...
      SUB 			function add(uint i) public re...
      SWAP1 			function add(uint i) public re...
      RETURN 			function add(uint i) public re...
    tag 5			function add(uint i) public re...
      //函數(shù)內(nèi)容
JUMPDEST 			function add(uint i) public re...
      //這下面就是函數(shù)的代碼了
      PUSH 0			uint //局部變量在棧里面
      DUP1 			uint m
      PUSH 6F			111
      SWAP1 			uint m = 111
      POP 			uint m = 111 //從push0到這里實(shí)現(xiàn)了定義局部變量并賦值
      DUP1 			m
      DUP4 			i            //獲取參數(shù)
      PUSH 0			num
      SLOAD 			num      //上面那句和這句實(shí)現(xiàn)了讀取成員變量
      MUL 			num * i      //乘
      ADD 			num * i+m    //加
      PUSH 0			num
      DUP2 			num =num * i+m
      SWAP1 			num =num * i+m   //這三句賦值
      SSTORE 			num =num * i+m   //成員變量存儲(chǔ)
      POP 			num =num * i+m
      //下面幾句實(shí)現(xiàn)return
      PUSH 0			num
      SLOAD 			num
      SWAP2 			return num    
      POP 			return num
      POP 			function add(uint i) public re...
      SWAP2 			function add(uint i) public re...
      SWAP1 			function add(uint i) public re...
      POP 			function add(uint i) public re...
      JUMP [out]			function add(uint i) public re...
    .data

如何進(jìn)行以太坊智能合約虛擬機(jī)EVM原理與實(shí)現(xiàn)

看完上述內(nèi)容,你們對如何進(jìn)行以太坊智能合約虛擬機(jī)EVM原理與實(shí)現(xiàn)有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI