以太坊源码解析-虚拟机&智能合约篇

   本文将从代码层级深入分析以太坊的虚拟机的设计原理和运行机制,以及智能合约运行的相关机制。
   一、虚拟机堆栈和内存数据结构
   虚拟机的底层数据机构是一个堆栈,包括一个stack和一个memory。
   1)我们先来看一下stack的数据结构:

// Stack is an object for basic stack operations. Items popped to the stack are
// expected to be changed and modified. stack does not take care of adding newly
// initialised objects.
type Stack struct {
data []big.Int //big.int是一个结构体,32个字节的切片
}
func newstack() Stack {
return &Stack{data: make([]
big.Int, 0, 1024)} //指定深度1024
}

以及push/pop/dup(复制栈顶元素)/peek(查看栈顶元素)/Back/swap(交换栈顶和指定元素)/require(保证栈顶元素的数量大于等于n)

2)intpool
可以重复利用的big int pool,大小为256。
type intPool struct {
pool *Stack
}

以及get/put函数,取出或设置默认值,

3)intPoolPool
intPool的管理池,默认的容量是25
type intPoolPool struct {
pools []*intPool
lock sync.Mutex
}

get/put,取出或者加入intPool,使用同步锁来控制。

4)memory
一个简单的内存模型,包含最近gas花费记录,why?
type Memory struct {
store []byte
lastGasCost uint64
}

func NewMemory() *Memory {
return &Memory{}
}

首先使用Resize分配空间
// Resize resizes the memory to size
func (m *Memory) Resize(size uint64) {
if uint64(m.Len()) < size {
m.store = append(m.store, make([]byte, size-uint64(m.Len()))...)
}
}

再使用set来设置值
// 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)
}
}

以及包含Get/Getpro/Len/Data/Print等函数,其中Getpro函数中可能存在切片访问越界的问题。

5)一些工具类函数,比如判定某stack是否可以执行dup或者swap操作:
func makeDupStackFunc/makeSwapStackFunc(n int) stackValudationFunc

2.虚拟机指令,跳转表和解释器
operation标识一条操作指令所需要的函数和变量,jumptable是一个[256]operation的数据结构。
type operation struct {
// execute is the operation function
execute executionFunc //执行函数
// gasCost is the gas function and returns the gas required for execution
gasCost gasFunc //消耗函数
// validateStack validates the stack (size) for the operation
validateStack stackValidationFunc //验证stack的大小
// memorySize returns the memory size required for the operation
memorySize memorySizeFunc //内存大小
halts bool // indicates whether the operation shoult halt further execution 表示操作是否停止进一步执行
jumps bool // indicates whether the program counter should not increment 指示程序计数器是否不增加
writes bool // determines whether this a state modifying operation 确定这是否是一个状态修改操作
valid bool // indication whether the retrieved operation is valid and known 指示检索到的操作是否有效并且已知
reverts bool // determines whether the operation reverts state (implicitly halts)确定操作是否恢复状态(隐式停止)
returns bool // determines whether the opertions sets the return data content 确定操作是否设置了返回数据内容
}

然后分别设置三个指令集,后者在前者的基础上生成。:
newHomesteadInstructionSet
newByzantiumInstructionSet
newConstantinopleInstructionSet

instruction.go中列举了很多具体的指令,such as:
func opPc(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack Stack) ([]byte, error) {
stack.push(interpreter.intPool.get().SetUint64(
pc))
return nil, nil
}
func opMsize(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
stack.push(interpreter.intPool.get().SetInt64(int64(memory.Len())))
return nil, nil
}

gas_table.go 返回了各种指令消耗的gas的函数,基本上只有errGasUintOverflow的整数溢出错误。
比如说
func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
if newMemSize == 0 {
return 0, nil
}
// The maximum that will fit in a uint64 is max_word_count - 1
// anything above that will result in an overflow.
// Additionally, a newMemSize which results in a
// newMemSizeWords larger than 0x7ffffffff will cause the square operation
// to overflow.
// The constant 0xffffffffe0 is the highest number that can be used without
// overflowing the gas calculation
if newMemSize > 0xffffffffe0 {
return 0, errGasUintOverflow
}
newMemSizeWords := toWordSize(newMemSize)
newMemSize = newMemSizeWords * 32
if newMemSize > uint64(mem.Len()) {
square := newMemSizeWords * newMemSizeWords
linCoef := newMemSizeWords * params.MemoryGas
quadCoef := square / params.QuadCoeffDiv
newTotalFee := linCoef + quadCoef

    fee := newTotalFee - mem.lastGasCost
    mem.lastGasCost = newTotalFee

    return fee, nil
}
return 0, nil

}

这个函数计算内存扩张的费用2,只针对扩展内存。nMS2 + nMS*3 - 内存的最近一次花费。
其中有很多各种指令的gas定义函数。

interpreter.go 解释器
// Config are the configuration options for the Interpreter
type Config struct {
// Debug enabled debugging Interpreter options
Debug bool
// Tracer is the op code logger
Tracer Tracer
// NoRecursion disabled Interpreter call, callcode,
// delegate call and create.
NoRecursion bool
// Enable recording of SHA3/keccak preimages
EnablePreimageRecording bool
// JumpTable contains the EVM instruction table. This
// may be left uninitialised and will be set to the default
// table.
JumpTable [256]operation
}

// Interpreter is used to run Ethereum based contracts and will utilise the
// passed environment to query external sources for state information.
// The Interpreter will run the byte code VM based on the passed
// configuration.
type Interpreter interface {
// Run loops and evaluates the contract's code with the given input data and returns
// the return byte-slice and an error if one occurred.
Run(contract *Contract, input []byte) ([]byte, error)
// CanRun tells if the contract, passed as an argument, can be
// run by the current interpreter. This is meant so that the
// caller can do something like:
//
// golang // for _, interpreter := range interpreters { // if interpreter.CanRun(contract.code) { // interpreter.Run(contract.code, input) // } // } //
CanRun([]byte) bool
// IsReadOnly reports if the interpreter is in read only mode.
IsReadOnly() bool
// SetReadOnly sets (or unsets) read only mode in the interpreter.
SetReadOnly(bool)
}

//EVMInterpreter represents an EVM interpreter
type EVMInterpreter struct {
evm *EVM
cfg Config
gasTable params.GasTable // 标识了很多操作的Gas价格
intPool *intPool
readOnly bool // Whether to throw on stateful modifications
returnData []byte // Last CALL's return data for subsequent reuse 最后一个函数的返回值
}

// NewInterpreter returns a new instance of the Interpreter.
func NewEVMInterpreter(evm *EVM, cfg Config) *Interpreter {
// We use the STOP instruction whether to see
// the jump table was initialised. If it was not
// we'll set the default jump table.
// 用一个STOP指令测试JumpTable是否已经被初始化了, 如果没有被初始化,那么设置为默认值
if !cfg.JumpTable[STOP].valid {
switch {
case evm.ChainConfig().IsConstantinople(evm.BlockNumber):
cfg.JumpTable = constantinopleInstructionSet
case evm.ChainConfig().IsByzantium(evm.BlockNumber):
cfg.JumpTable = byzantiumInstructionSet
case evm.ChainConfig().IsHomestead(evm.BlockNumber):
cfg.JumpTable = homesteadInstructionSet
default:
cfg.JumpTable = frontierInstructionSet
}
}
return &Interpreter{
evm: evm,
cfg: cfg,
gasTable: evm.ChainConfig().GasTable(evm.BlockNumber),
intPool: newIntPool(),
}
}

func (in *EVMInterpreter) enforceRestrictions(op OpCode, operation operation, stack *Stack) error {
if in.evm.chainRules.IsByzantium {
if in.readOnly {
// If the interpreter is operating in readonly mode, make sure no
// state-modifying operation is performed. The 3rd stack item
// for a call operation is the value. Transferring value from one
// account to the others means the state is modified and should also
// return with an error.
if operation.writes || (op == CALL && stack.Back(2).BitLen() > 0) {
return errWriteProtection
}
}
}
return nil
}

另外一个重要的函数就是run,用给定的入参循环执行合约的代码,并返回return的字节片段,如果发生错误则返回错误。解释器返回任何错误除了errExecutionReverted之外都视为消耗完所有gas。
func (in *EVMInterpreter) Run(contract *Contract, input []byte) (ret []byte, err error) {
if in.intPool == nil {
in.intPool = poolOfIntPools.get()
defer func() {
poolOfIntPools.put(in.intPool)
in.intPool = nil
}()
}

// Increment the call depth which is restricted to 1024
in.evm.depth++
defer func() { in.evm.depth-- }()

// Reset the previous call's return data. It's unimportant to preserve the old buffer
// as every returning call will return new data anyway.
in.returnData = nil

// Don't bother with the execution if there's no code.
if len(contract.Code) == 0 {
    return nil, nil
}

var (
    op    OpCode        // current opcode
    mem   = NewMemory() // bound memory
    stack = newstack()  // local stack
    // For optimisation reason we're using uint64 as the program counter.
    // It's theoretically possible to go above 2^64. The YP defines the PC
    // to be uint256. Practically much less so feasible.
    pc   = uint64(0) // program counter
    cost uint64
    // copies used by tracer
    pcCopy  uint64 // needed for the deferred Tracer
    gasCopy uint64 // for Tracer to log gas remaining before execution
    logged  bool   // deferred Tracer should ignore already logged steps
)
contract.Input = input

// Reclaim the stack as an int pool when the execution stops
defer func() { in.intPool.put(stack.data...) }()
    //查看是否是debug状态
if in.cfg.Debug {
    defer func() {
        if err != nil {
            if !logged {
                in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
            } else {
                in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
            }
        }
    }()
}
    for atomic.LoadInt32(&in.evm.abort) == 0 {
    if in.cfg.Debug {
        // Capture pre-execution values for tracing.
        logged, pcCopy, gasCopy = false, pc, contract.Gas
    }
    //得到下一个需要执行的指令
    op = contract.GetOp(pc)
    operation := in.cfg.JumpTable[op]
    if !operation.valid {
    return nil, fmt.Errorf("invalid opcode 0x%x", int(op))
}
    //检查是否有足够的堆栈空间
if err := operation.validateStack(stack); err != nil {
    return nil, err
}
    // If the operation is valid, enforce and write restrictions
    if err := in.enforceRestrictions(op, operation, stack); err != nil {
        return nil, err
    }

    var memorySize uint64
    // calculate the new memory size and expand the memory to fit
    // the operation
    if operation.memorySize != nil {
        memSize, overflow := bigUint64(operation.memorySize(stack))
        if overflow {
            return nil, errGasUintOverflow
        }
        // memory is expanded in words of 32 bytes. Gas
        // is also calculated in words.
        if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
            return nil, errGasUintOverflow
        }
    }
      //计算gas的cost并使用,如果不够则out of gas。
      cost, err = operation.gasCost(in.gasTable, in.evm, contract, stack, mem, memorySize)
    if err != nil || !contract.UseGas(cost) {
        return nil, ErrOutOfGas
    }
    if memorySize > 0 {
        mem.Resize(memorySize)
    }

    if in.cfg.Debug {
        in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
        logged = true
    }

    // execute the operation
    res, err := operation.execute(&pc, in, contract, mem, stack)
    // verifyPool is a build flag. Pool verification makes sure the integrity
    // of the integer pool by comparing values to a default value.
    if verifyPool {
        verifyIntegerPool(in.intPool)
    }
    // if the operation clears the return data (e.g. it has returning data)
    // set the last return to the result of the operation.
    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

}

二、智能合约contract.go
type ContractRef interface{Address() common.Address} 这是一个合约背后支持对象的引用。
AccountRef 实现了上述接口。
type Contract struct {
// CallerAddress是初始化合约的用户account,如果是合约调用初始化则设置为合约的调用者
CallerAddress common.Address
caller ContractRef
self ContractRef
jumpdests destinations // JUMPDEST 指令分析.
Code []byte //代码
CodeHash common.Hash //代码hash
CodeAddr *common.Address //代码地址
Input []byte //入参
Gas uint64 //合约剩余gas
value *big.Int //合约剩余的eth
Args []byte //参数
DelegateCall bool
}

构造函数
func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uint64) Contract {
c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object, Args: nil}
如果caller是一个合约,则jumpodests设置为caller的jumpdests.
if parent, ok := caller.(
Contract); ok {
// Reuse JUMPDEST analysis from parent context if available.
c.jumpdests = parent.jumpdests
} else {
c.jumpdests = make(destinations)
}
// Gas should be a pointer so it can safely be reduced through the run
// This pointer will be off the state transition
c.Gas = gas
// ensures a value is set
c.value = value
return c
}
//为了链式调用,当调用者是合约时,设置本合约的CallerAddress、value为caller相应的值
func (c Contract) AsDelegate() Contract {
c.DelegateCall = true
// NOTE: caller must, at all times be a contract. It should never happen
// that caller is something other than a Contract.
parent := c.caller.(
Contract)
c.CallerAddress = parent.CallerAddress
c.value = parent.value
return c
}
//GetOP用来获取下一跳指令,
推测合约code是不是已经拆成了指令合集,然后在input或者Args中获取。
func (c *Contract) GetOp(n uint64) OpCode

//接下来的两个:SetCode和SetCallCode,*合约一旦部署则无法修改,应当是部署时的设置
func (c *Contract) SetCode(hash common.Hash, code []byte) {
c.Code = code
c.CodeHash = hash
}

evm.go
首先是Context为EVM提供辅助信息,一旦提供,则无法修改
// 上下文为EVM提供辅助信息。 一旦提供,不应该修改。
type Context struct {
// CanTransfer returns whether the account contains
// sufficient ether to transfer the value
// CanTransfer 函数返回账户是否有足够的ether用来转账
CanTransfer CanTransferFunc
// Transfer transfers ether from one account to the other
// Transfer 用来从一个账户给另一个账户转账
Transfer TransferFunc
// GetHash returns the hash corresponding to n
// GetHash用来返回入参n对应的hash值
GetHash GetHashFunc

// Message information
// 用来提供Origin的信息 sender的地址
Origin   common.Address // Provides information for ORIGIN
// 用来提供GasPrice信息
GasPrice *big.Int       // Provides information for GASPRICE

// Block information
Coinbase    common.Address // Provides information for COINBASE
GasLimit    *big.Int       // Provides information for GASLIMIT
BlockNumber *big.Int       // Provides information for NUMBER
Time        *big.Int       // Provides information for TIME
Difficulty  *big.Int       // Provides information for DIFFICULTY

}

// EVM是以太坊虚拟机基础对象,并提供必要的工具,以使用提供的上下文运行给定状态的合约。
// 应该指出的是,任何调用产生的任何错误都应该被认为是一种回滚修改状态和消耗所有GAS操作,
// 不应该执行对具体错误的检查。 解释器确保生成的任何错误都被认为是错误的代码。
// The EVM should never be reused and is not thread safe.
type EVM struct {
// Context provides auxiliary blockchain related information
Context
// StateDB gives access to the underlying state
StateDB StateDB
// Depth is the current call stack
// 当前的调用堆栈
depth int
// chainConfig contains information about the current chain
// 包含了当前的区块链的信息
chainConfig *params.ChainConfig
// chain rules contains the chain rules for the current epoch
chainRules params.Rules
// virtual machine configuration options used to initialise the
// evm.
vmConfig Config
// global (to this context) ethereum virtual machine
// used throughout the execution of the tx.
interpreter *Interpreter
// abort is used to abort the EVM calling operations
// NOTE: must be set atomically
abort int32
}

func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM {
evm := &EVM{
Context: ctx,
StateDB: statedb,
vmConfig: vmConfig,
chainConfig: chainConfig,
chainRules: chainConfig.Rules(ctx.BlockNumber),
}

evm.interpreter = NewInterpreter(evm, vmConfig)
return evm

}

// Cancel cancels any running EVM operation. This may be called concurrently and
// it's safe to be called multiple times.
func (evm *EVM) Cancel() {
atomic.StoreInt32(&evm.abort, 1)
}

create函数创造一个contract.
// create creates a new contract using code as deployment code.
func (evm *EVM) create(caller ContractRef, code []byte, gas uint64, value *big.Int, address common.Address) ([]byte, common.Address, uint64, 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
}
nonce := evm.StateDB.GetNonce(caller.Address())
evm.StateDB.SetNonce(caller.Address(), nonce+1)

// Ensure there's no existing contract already at the designated address
contractHash := evm.StateDB.GetCodeHash(address)
if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) {
    return nil, common.Address{}, 0, ErrContractAddressCollision
}
// Create a new account on the state
snapshot := evm.StateDB.Snapshot()
evm.StateDB.CreateAccount(address)
if evm.ChainConfig().IsEIP158(evm.BlockNumber) {
    evm.StateDB.SetNonce(address, 1)
}
evm.Transfer(evm.StateDB, caller.Address(), 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.
contract := NewContract(caller, AccountRef(address), value, gas)
contract.SetCallCode(&address, crypto.Keccak256Hash(code), code)

if evm.vmConfig.NoRecursion && evm.depth > 0 {
    return nil, address, gas, nil
}

if evm.vmConfig.Debug && evm.depth == 0 {
    evm.vmConfig.Tracer.CaptureStart(caller.Address(), address, true, code, gas, value)
}
start := time.Now()

ret, err := run(evm, contract, nil)

// check whether the max code size has been exceeded
maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize
// if the contract creation ran successfully and no errors were returned
// calculate the gas required to store the code. If the code could not
// be stored due to not enough gas set an error and let it be handled
// by the error checking condition below.
if err == nil && !maxCodeSizeExceeded {
    createDataGas := uint64(len(ret)) * params.CreateDataGas
    if contract.UseGas(createDataGas) {
        evm.StateDB.SetCode(address, 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.
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, address, contract.Gas, err

}

//call执行地址关联的合约,通过给定的inputs,它同样处理有必要的转账,采取必要步骤在创建账户,恢复英文执行错误或者失败的value转账。
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
}
    
    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()
)
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 anything, 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)
}
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.
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

}

Callcode、DelegateCall、StaticCall不由外部调用,通过Opcode来执行:
Callcode--to = AccountRef(caller.Address()) //这里是最不同的地方 to的地址被修改为caller的地址了 而且没有转账的行为
DelegateCall -- // 标识为AsDelete()
contract := NewContract(caller, to, nil, gas).AsDelegate()
StaticCall -- IsReadOnly设置为true。

合约执行分析
// c1.sol
pragma solidity ^0.4.11;
contract C {
uint256 a;
function C() {
a = 1;
}
}

======= c1.sol:C =======
EVM assembly:
/* "c1.sol":26:94 contract C {... /
mstore(0x40, 0x60)
/
"c1.sol":59:92 function C() {... /
jumpi(tag_1, iszero(callvalue))
0x0
dup1
revert
tag_1:
tag_2:
/
"c1.sol":84:85 1 /
0x1
/
"c1.sol":80:81 a /
0x0
/
"c1.sol":80:85 a = 1 /
dup2
swap1
sstore
pop
/
"c1.sol":59:92 function C() {... /
tag_3:
/
"c1.sol":26:94 contract C {... /
tag_4:
dataSize(sub_0)
dup1
dataOffset(sub_0)
0x0
codecopy
0x0
return
stop
sub_0: assembly {
/
"c1.sol":26:94 contract C {... */
mstore(0x40, 0x60)
tag_1:
0x0
dup1
revert
auxdata: 0xa165627a7a72305820af3193f6fd31031a0e0d2de1ad2c27352b1ce081b4f3c92b5650ca4dd542bb770029
}
Binary:
60606040523415600e57600080fd5b5b[6001600081905550赋值语句:a=1]5b5b60368060266000396000f30060606040525b600080fd00a165627a7a72305820af3193f6fd31031a0e0d2de1ad2c27352b1ce081b4f3c92b5650ca4dd542bb770029
[6001600081905550赋值语句:a=1]
60 01 = push(0x1) stack: [0x1]
60 00 stack: [0x0, 0x1]
81 dup2 stack: [0x1, 0x0, 0x1]
90 swap1 stack: [0x0, 0x1, 0x1]
55 sstore stack: [0x1] store: {0x0 => 0x1}
50 pop stack :[] store: {0x0 => 0x1}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容

  • 本系列是对Howard的Diving Into The Ethereum VM系列文章进行简单翻译和笔记 Soli...
    187J3X1阅读 892评论 0 1
  • 西安今年的第一场雪 除了减少了雾霾 便也再无欣赏的兴致。 它银装素裹就纯洁了吗? 阴暗被覆盖上棉被 丑恶被包裹上遮...
    诗译生活阅读 447评论 0 2
  • 夏太康碑。 这四个字,是藏在松柏香与墨香里的古老密码,苦寻了四十天,几多夜读搜罗的目力。 其实,它们只是一扇窗,打...
    西州路西阅读 401评论 1 1
  • 目标:希望2018.12.31前能和儿子关系圆融,能看到儿子用超强的意志力去减肥成功(减到150斤)。 种子实践 ...
    叶耘蔓阅读 43评论 0 0
  • 一对六旬老人,独生子意外身亡,于是将所有的精神寄托都放在了不满2岁的孙子身上。然而,由于儿子去世后与儿媳积怨较深,...
    小好阅读 285评论 1 2