以太坊智能合约虚拟机(EVM)原理与实现

EVM原理及其功能扩展 EVM原理及其功能扩展EVM运行机制概述EVM即以太坊虚拟机,用于执行智能合约。智能合约可用高级开发语言Solidity进行开发,合约源代码经过编译得到可在EVM中运行的字节码。在部署合约、与合约交互的时候,字节码都是以16进制字符串形式传递和展现。EVM运行过程中,其本身并不是一个独立的协程、线程更不是进程,它只是交易处理的一部分,在交易处理过程中以函数方式被调用。调用路径为:StateProc... 阅读详情

以太坊 EVM原理与实现

以太坊底层通过EVM模块支持合约的执行与调用,调用时根据合约地址获取到代码,生成环境后载入到EVM中运行。通常智能合约的开发流程是用solidlity编写逻辑代码,再通过编译器编译元数据,最后再发布到以太坊上。

输入图片说明

代码结构

.
├── analysis.go            //跳转目标判定
├── common.go
├── contract.go            //合约数据结构
├── contracts.go           //预编译好的合约
├── errors.go
├── evm.go                 //执行器 对外提供一些外部接口   
├── gas.go                 //call gas花费计算 一级指令耗费gas级别
├── gas_table.go           //指令耗费计算函数表
├── gen_structlog.go       
├── instructions.go        //指令操作
├── interface.go           
├── interpreter.go         //解释器 调用核心
├── intpool.go             //int值池
├── int_pool_verifier_empty.go
├── int_pool_verifier.go
├── jump_table.go           //指令和指令操作(操作,花费,验证)对应表
├── logger.go               //状态日志
├── memory.go               //EVM 内存
├── memory_table.go         //EVM 内存操作表 主要衡量操作所需内存大小
├── noop.go
├── opcodes.go              //Op指令 以及一些对应关系     
├── runtime
│   ├── env.go              //执行环境 
│   ├── fuzz.go
│   └── runtime.go          //运行接口 测试使用
├── stack.go                //栈
└── stack_table.go          //栈验证

指令

OpCode

文件opcodes.go中定义了所有的OpCode,该值是一个byte,合约编译出来的bytecode中,一个OpCode就是上面的一位。opcodes按功能分为9组(运算相关,块操作,加密相关等)。

    //算数相关
    const (
        // 0x0 range - arithmetic ops
        STOP OpCode = iota
        ADD
        MUL
        SUB
        DIV
        SDIV
        MOD
        SMOD
        ADDMOD
        MULMOD
        EXP
        SIGNEXTEND
    )

Instruction

文件jump.table.go定义了四种指令集合,每个集合实质上是个256长度的数组,名字翻译过来是(荒地,农庄,拜占庭,君士坦丁堡)估计是对应了EVM的四个发展阶段。指令集向前兼容。

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

具体每条指令结构如下,字段意思见注释。

type operation struct {
	//对应的操作函数
	execute executionFunc
	// 操作对应的gas消耗
	gasCost gasFunc
	// 栈深度验证
	validateStack stackValidationFunc
	// 操作所需空间
	memorySize memorySizeFunc

	halts   bool // 运算中止
	jumps   bool // 跳转(for)
	writes  bool // 是否写入
	valid   bool // 操作是否有效
	reverts bool // 出错回滚
	returns bool // 返回
}

按下面的ADD指令为例

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

不同的操作有所不同,操作对象根据指令不同可能影响栈,内存,statedb。

    func opAdd(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
        //弹出一个值,取出一个值(这个值依旧保存在栈上面,运算结束后这个值就改变成结果值)
        x, y := stack.pop(), stack.peek()
        //加运算
        math.U256(y.Add(x, y))
        //数值缓存
        evm.interpreter.intPool.put(x)
        return nil, nil
    }
gas花费

不同的运算有不同的初始值和对应的运算方法,具体的方法都定义在gas_table里面。 按加法的为例,一次加操作固定耗费为3。

    //固定耗费
    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
        }
    }

除此之外还有两个定义会影响gas的计算,通常作为量化的一个单位。

    //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

因为加操作不需要申请内存因而memorySize为默认值0。

栈验证

先验证栈上的操作数够不够,再验证栈是否超出最大限制,加法在这里仅需验证其参数够不够,运算之后栈是要减一的。

    func makeStackFunc(pop, push int) stackValidationFunc {
        return func(stack *Stack) error {
            //深度验证
            if err := stack.require(pop); err != nil {
                return err
            }
            //最大值验证
            //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智能合约的存储单位也是解释器执行的基本单位,包含了代码,调用人,所有人,gas相关的信息.

    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原生预编译了一批合约,定义在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{},
}

执行机

栈

EVM中栈用于保存操作数,每个操作数的类型是big.int,这就是网上很多人说EVM是256位虚拟机的原因。执行opcode的时候,从上往下弹出操作数,作为操作的参数。

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
}

内存

内存用于一些内存操作(MLOAD,MSTORE,MSTORE8)及合约调用的参数拷贝(CALL,CALLCODE)。

内存数据结构,维护了一个byte数组,MLOAD,MSTORE读取存入的时候都要指定位置及长度才能准确的读写。

    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
    }

内存操作

    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

合约本身不保存数据,那么合约的数据是保存在哪里呢?合约及其调用类似于数据库的日志,保存了合约定义以及对他的一系列操作,只要将这些操作执行一遍就能获取当前的结果,但是如果每次都要去执行就太慢了,因而这部分数据是会持久化到stateDb里面的。code中定义了两条指令SSTORE SLOAD用于从db中读写合约当前的状态。

    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
    }

执行过程

执行入口定义在evm.go中,功能就是组装执行环境(代码,执行人关系,参数等)。

    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
        }

        // 合约调用深度检查
        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)
            //保存当前状态,如果出错,就回滚到这个状态
            snapshot = evm.StateDB.Snapshot()
        )
        if !evm.StateDB.Exist(addr) {
            //创建调用对象的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)
        }
        //调用别人合约可能需要花钱
        evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)

        //创建合约环境
        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.
        if err != nil {
            //错误回滚
            evm.StateDB.RevertToSnapshot(snapshot)
            if err != errExecutionReverted {
                contract.UseGas(contract.Gas)
            }
        }
        return ret, contract.Gas, err
    }

类似的函数有四个。详细区别见最后的参考。

  • Call A->B A,B的环境独立

  • CallCode、 和Call类似 区别在于storage位置不一样

  • DelegateCall、 和CallCode类似,区别在于msg.send不一样

  • StaticCall 和call相似 只是不能修改状态

Contract和参数构造完成后调用执行函数,执行函数会检查调用的是否会之前编译好的原生合约,如果是原生合约则调用原生合约,否则调用解释器执行函数运算合约。

    // 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) {

        //返回数据
        in.returnData = nil

        var (
            op    OpCode        // 当前指令
            mem   = NewMemory() // 内存
            stack = newstack()  // 栈
            pc   = uint64(0)    // 指令位置
            cost uint64         // gas花费
            pcCopy  uint64      // debug使用
            gasCopy uint64      // debug使用
            logged  bool        // debug使用
        )
        contract.Input = input  //函数入参

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

        for atomic.LoadInt32(&in.evm.abort) == 0 {
            //获取一条指令及指令对应的操作
            op = contract.GetOp(pc)
            operation := in.cfg.JumpTable[op]
            //valid校验
            if !operation.valid {
                return nil, fmt.Errorf("invalid opcode 0x%x", int(op))
            }
            //栈校验
            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
            //计算内存 按操作所需要的操作数来算
            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
                }
            }
            // 校验cost 调用前面提到的costfunc 计算本次操作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 ,扩展memory 
                mem.Resize(memorySize)  
            }

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

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

            switch {
            case err != nil:
                return nil, err       //报错
            case operation.reverts:   //出错回滚
                return res, errExecutionReverted
            case operation.halts:
                return res, nil       //停止
            case !operation.jumps:    //跳转
                pc++
            }
        }
        return nil, nil
    }

Solidity案例

和其他语言类似,有了字节码运行机,就可以在字节码上面再组织其他高级语言,而solidlity语言就是实现了这样的语言编译器,方便了合约编写,有利于推广以太坊dapp开发。

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 函数入口

PUSH + JUMPI/JUMP 类似于调用函数

CALLDATASIZE + CALLDATALOAD 大约是获取函数参数

.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
  //从下面这条指令可以看出,初始化的时候成员变量就会存到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 
  //下面部分是构造函数执行的部分
  JUMPDEST 			a 
  POP 			function simple(){\n        nu...
  PUSH 7B			123
  PUSH 0			num  
  DUP2 			num = 123
  SWAP1 			num = 123
  //改变成员变量最后都会写入到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 ...
  //上面部分做完初始化之后并不会进入到runtime阶段
.data
  0:
    .code
      //下面这段代码大约是处理参数的
      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...
      //函数内容
JUMPDEST 			function add(uint i) public re...
      //这下面就是函数的代码了
      PUSH 0			uint //局部变量在栈里面
      DUP1 			uint m
      PUSH 6F			111
      SWAP1 			uint m = 111
      POP 			uint m = 111 //从push0到这里实现了定义局部变量并赋值
      DUP1 			m
      DUP4 			i            //获取参数
      PUSH 0			num
      SLOAD 			num      //上面那句和这句实现了读取成员变量
      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   //成员变量存储
      POP 			num =num * i+m
      //下面几句实现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

输入图片说明

参考

Call、CallCode、DelegateCall:https://ethereum.stackexchange.com/questions/3667/difference-between-call-callcode-and-delegatecall

solidity结构:https://solidity.readthedocs.io/en/develop/structure-of-a-contract.html#

runtime bytecode和bytecode :https://ethereum.stackexchange.com/questions/13086/solc-bin-vs-bin-runtime/13087#13087

remix: https://remix.ethereum.org/

转自:(魂祭心)https://my.oschina.net/hunjixin/blog/1805306。


如果你希望高效的学习以太坊DApp开发,可以访问汇智网提供的最热门在线互动教程:

1.适合区块链新手的以太坊DApp实战入门教程
2.区块链+IPFS+Node.js+MongoDB+Express去中心化以太坊电商应用开发实战

其他更多内容也可以访问这个以太坊博客

以太坊虚拟机 EVM 的原理 作者:禅与计算机程序设计艺术 1.背景介绍 什么是EVM? 以太坊虚拟机(Ethereum Virtual Machine,简称EVM)是一个运行在以太坊区块链网络上执行智能合约、存储数据的软件。它负责部署、验证、执行智能合约的代码,并维护所有账户信息。用 阅读详情

相关推荐

eth入门之以太机虚拟机 (EVM)

文档:以太机虚拟机 (EVM) | ethereum.org EVM 的物理实例是真实存在并由数以千计运行以太坊客户端的计算机共同维护的一个实体。以太坊协议本身的存在仅仅是为了保持这种特殊状态机的连续、不间断和永久的运行。这是所有以太坊帐户和智能合约赖以存在的环境。在链中任何给定的区块上,以太坊只有一个“规范”状态EVM 定义了从一个区块计算产生下一个区块新的有效状态的规则。通常使用“分布式账簿”类比像比特币这样的区块链,它使用密码学工具来实现去中心化的货币。加密货币类似于“正常”货币,因为规则支配着可以做

蛐蛐的博客 1960

EVM 是什么? 怎么测?

EVM(Error Vector Magnitude)是在频谱分析仪和通信中经常听到的一个词,第一次见到这个词,莫名就觉得它充满故事性。今天就来探究一下它的背景。 在WIFI网络中,为了保证信号传输的质量,我们需要验证发射器以及接收机的性能。发射器的性能评判指标是多样化的,但EVM一个指标,便可将它们“一波带走”。在了解EVM之前,我们先来了解一下什么是QAM调制。 QAM 调制 ...

bymyl的博客 3万+

8、以太坊深度解析:从基础到核心原理

本文深入解析了以太坊的核心架构与技术原理,涵盖账户管理、P2P网络特性、共识机制(PoW与PoS)、以太坊虚拟机(EVM)、数据存储结构(Merkle Patricia树)、加密算法、智能合约语言Solidity以及RPC远程调用接口。全面介绍了以太坊从基础操作到高级特性的完整体系,帮助开发者和用户深入理解其去中心化、安全性和可扩展性,并展望了未来在性能、跨链和隐私保护方面的发展方向。

j2k3l4的博客 63

以太坊虚拟机EVM的工作原理是怎样的

在底层,EVM 使用一组指令(称为操作码)来执行特定任务。

lucasma的博客 1811

An AI compute of cities based on Distributed-Platform and Distributed-Databases(天池地铁流量预测)

摘要 在日益城市化的今天,城市居民出行问题逐渐成为衡量一个城市现代化能力的标准;城市出行方式多元化的同时,也对各交通系统的调度运营能力提出了更高的要求:如何提高运营效率,如何运用有限的安保力量进行有效的安检部署等问题一直在阻挠着城市交通的发展。 An AI compute of cities based on Distributed-Platform and Distributed-Databas...

Clearlove灬Star 5162

智能优化算法-北方苍鹰优化算法(NGO)(附源码)

北方苍鹰优化算法 (Northern Goshawk Optimizer, NGO) 是一种基于群体智能的元启发式优化算法,它模拟了北方苍鹰(Northern Goshawk)的捕食行为、领地行为以及社交互动,用于解决复杂的优化问题。总之,NGO作为一种新颖的优化算法,在处理复杂优化问题方面展现出了潜力。随着进一步的研究和应用,NGO有望成为解决实际问题的有效工具。

w971656545的博客 1360

EVM、Wasm虚拟机原理和设计思路

目前我们的形势和任务 ——任正非在1995年总结大会上的讲话 1995年12月26日 【1987年到1995年,华为用了不到10年时间,员工人数从6人至1750人,销售收入从0到15亿元。任正非在这次讲话中首次提出末位淘汰的思想,每年保持5%的自然淘汰率和10%的合理流动率,以激发员工的积极性。】 一、历史回顾 十五年前,我们打开国门,实行开放改革。那时十亿中国人的心中装满的是世界上2/3...

跨链技术践行者 1万+

C#创建COM组件,QT调用COM组件简单测试(VS2008+QT4.6.4)

一. C#创建组件: 1. VS2008新建一个项目,选择Visual C#, Class library 修改Properties下面的AssemblyInfo.cs文件,ComVisible属性设置为True,即:[assembly: ComVisible(true)] 2. 右击项目,选择“属性”, 选择“生成”选项卡,把“为COM互操作注册”勾选上 选择“签名”选项卡,把“

3517

以太坊开发

  https://www.cnblogs.com/helloworld2018/p/8998926.html 以太坊 EVM原理与实现 以太坊底层通过EVM模块支持合约的执行与调用,调用时根据合约地址获取到代码,生成环境后载入到EVM中运行。通常智能合约的开发流程是用solidlity编写逻辑代码,再通过编译器编译元数据,最后再发布到以太坊上。 代码结构 . ├── analysi...

嘿嘿 1445

USB之ch376s数据手册

         最近师兄交给我一个项目,虽说刚学嵌入式起步,但上手一个项目怎说都是最好的快速学习方式。因为刚了解ch376s管理U盘的数据,所以先是看了ch376s的数据手册。那我就把数据手册直接放上来吧。。。。      1、概述 CH376 是文件管理控制芯片,用于单片机系统读写 U 盘或者 SD 卡中的文件。 CH376 支持 USB 设备方式和 USB 主机方式,并且内置了 USB 通...

iBGG的博客 6002

OpenPCDet安装及其3D检测算法实现

OpenPCDet及pointpillar复现

weixin_58207716的博客 1万+

以太坊虚拟机实现分析

以太坊虚拟机实现分析 1、 原理 a)     以太坊虚拟机(EVM)是以太坊中智能合约的运行环境。它不仅被沙箱封装起来,事实上它被完全隔离,也就是说运行在EVM内部的代码不能接触到网络、文件系统或者其它进程。甚至智能合约与其它智能合约只有有限的接触。 b)     编程语言支持:为了兼容尚未实现的应用程序,虚拟机应该支持编程语言,而不是特定的应用程序,应用程序的

2009

精确时钟同步协议ptp/IEEE-1588v2协议-------(1)简介

ptp, 1588V2, IEEE-1588, 精确时钟同步协议

meihualing的专栏 5352

区块链和智能合约的关系

链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载。 尽管比特币(Bitcoin)和以太坊(Ethereum)是经常被一起提及的两个词,但实际上,比特币与以太坊有着很大的区别。它们唯一的共同点是,以太坊也是一个在区块链之上运行的加密资产。 与比特币仅仅是一个加密货币不同,以太坊还有很多其他特性。正是这些特性,才使得以太坊成为了一个去中心化的超级电脑。 在理解 以太坊 之...

weixin_44172023的博客 381

外设测试 - ATAES132A 加解密芯片测试

原理 使用芯片官方提供的读写操作库,进行先写后读并校验,确认数据一致。 示例代码 int main(void) { uint8_t ret; aes132p_enable_interface(); ret = write_memory_test(); if (ret != AES132_FUNCTION_RETCODE_SUCCESS) { aes132p_disable_interface(); printf

一个软件工程师的技术笔记 853

基于 YOLOv26 的电路焊点虚焊检测系统:AI 质检实战

本文介绍了一种基于YOLOv26的电路焊点虚焊检测系统,利用深度学习技术实现自动化质检。系统能识别虚焊、桥接、少锡等8种常见焊点缺陷,具有高精度、实时性和强鲁棒性等特点。文章详细阐述了系统架构,包括数据采集、预处理、YOLOv26检测模型和后处理模块,并提供了代码实现的关键部分,如数据预处理增强和模型训练配置。该系统可显著提高电子制造业的质检效率和准确性。

一键难忘的博客 690

以太坊初学笔记01

PY比赛做不动了,来学学区块链~ 区块链类似于一个记账本 传统账单写满先后顺序装订,区块链以时间为序,以太坊14s生成区块,基于端对端网络,去中心化 区块链模型演示:https://anders.com/ 以太坊: 区块链2.0代表平台 以太坊是运⾏在⼀个计算机⽹络中的软件,它确保数据以及称为智能合约的⼩程序可以在没有中⼼协调者的情况下被所有⽹络中的计算机复制和处理。以太坊的愿景是创建⼀个⽆法停⽌,抗屏蔽(审查)和⾃我维持的去中⼼化世界计算机。 官⽅⽹址:https://www.ethereu

ciudadcaa的博客 269

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

区块链/以太坊/读书笔记/精通以太坊思维导图

第一章~第三章 学习笔记 思维导图 附:文本结构 精通以太坊-基础1~3章 概念 基于区块链技术打造的平台 智能合约 以太坊计算基础框架上执行的程序 DApp 狭义 基于智能合约开发的用户界面 至少包含一个智能合约 广义 开放的去中心化的网络应用 以太币 以太坊官方货币 由挖矿成功得以产生 成功打包区块的奖励 可购买gas 运行智能合约需消耗gas 特点 任何人都可以在平台上发布dapp,区块链应用

帅泽泽的博客 1786

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

智能合约全栈介绍 - 钟文斌 | Jeth 第三期

编者按:本文系 Qtum 的 钟文斌讲师,在由掘金技术社区主办,以太坊社区基金会、以太坊爱好者与 ConsenSys 协办的 《开发者的以太坊入门指南 | Jeth 第三期 - 上海场》 活动上的分享整理。Jeth 是围绕以太坊技术开发主题的系列线下活动。每期 Jeth 会邀请以太坊开发领域的优秀技术团队和工程师在线下分享技术干货。旨在为开发者提供线下技术交流互动机会,帮助开发者成长。 欢...

weixin_34365417的博客 317

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

【转载】智能合约简介

一个简单的智能合约 先从一个非常基础的例子开始,不用担心你现在还一点都不了解,我们将逐步了解到更多的细节。存储contract SimpleStorage { uint storedData; function set(

斌斌(Java) 2512

深入解析 EVM 运行机制:从字节码到状态变更

本文系统解析了以太坊虚拟机(EVM)的核心机制,将其比作"世界计算机的CPU"。EVM是基于栈的256位虚拟机,包含执行栈、易失性内存、持久化存储等核心组件,通过Gas机制实现资源定价和网络安全。文章详细阐述了交易执行流程、存储布局与状态根的关系,以及预编译合约的优化作用,并介绍了EVM的最新演进方向。理解EVM的运作原理是开发高效安全智能合约的基础,其确定性执行和沙盒环境设计构成了以太坊生态的核心技术支柱。

u011175571的博客 589

区块链学习总结

区块链学习总结 区块链 区块链(Blockchain)是指通过去中心化和去信任的方式集体维护一个可靠数据库的技术方案。该技术方案让参与系统中的任意多个节点,把一段时间系统内全部事务通过密码学算法计算并记录到一个数据块(block),生成该数据块的hash用于链接下个数据块,系统所有参与节点来共同检验记录是否为真,并且每个区块的内容都由后续子链上的区块来保证其内容不可被篡改。 各个参与节点可以在新区块产生确认及奖励分配上达成共识,从而逐渐形成的一个庞大、去中心化的公开账本。 作者:诠科教 链接:https:/

qq_42249942的博客 3846
上一篇: 用go语言创建区块链
下一篇: 以太坊RPC原理及实现
落叶无声9
博客等级 码龄12年 443粉丝 343原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值