Evm7种重要指令的实现原理

Evm7种重要指令的实现原理:

Evm的所有指令定义都在core/vm/jump_table.go里实现的,而每个指令对应的操作函数都是在core/vm/instructions.go里实现的。

如果一个节点并发调用智能合约,那么对memory的操作是否有线程安全问题。不会,因为每执行一个交易,都会创建一个新的evm对象。只有最终写入statedb的数据会有线程安全问题。

基本原理:一个指令占一个字节,也就是8为,16进制从0x01到0xff,10进制从1到255。最多255个指令,如果加上0的话,就256个指令。evm执行指令的主要载体是栈。

栈里的dup函数实现把栈上的某个值存入intpool中。

1.Mload和Mstore指令( 这里的m是memory的意思)

   mload:花费32gas*数据大小,用途:取出栈顶的元素作为key,从memory中取出该key对应的value,存入initpoll中最新元素,并且把该值压入栈中。

  Mstore:取出栈上的最新的两个数据,一个作为key,一个作为value,写入memory,并且存入initpoll中。initpoll也是一个栈的结构


 

func opMload(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {

    offset := stack.pop()

    val := interpreter.intPool.get().SetBytes(memory.Get(offset.Int64(), 32))

    stack.push(val)

    interpreter.intPool.put(offset)

    return nil, nil

}



func opMstore(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {

    // pop value of the stack

    mStart, val := stack.pop(), stack.pop()

    memory.Set32(mStart.Uint64(), val)

    interpreter.intPool.put(mStart, val)

    return nil, nil

}



// intPool is a pool of big integers that

// can be reused for all big.Int operations.

type intPool struct {

    pool *Stack

}

2.sload和sstore指令( 这里的s是statedb的意思)

  sload:从statedb中取出合约地址下面的某个key对应的value值,存入栈的最新元素里。sload固定是200gas

  Sstore:从栈中取中两个值作为key和value,然后在statedb中存入刚才取出的key和value,并且在initpool中放入该value。


 

func opSload(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {

    loc := stack.peek()

    val := interpreter.evm.StateDB.GetState(contract.Address(), common.BigToHash(loc))

    loc.SetBytes(val.Bytes())

    return nil, nil

}



func opSstore(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {

    loc := common.BigToHash(stack.pop())

    val := stack.pop()

    interpreter.evm.StateDB.SetState(contract.Address(), loc, common.BigToHash(val))

    interpreter.intPool.put(val)

    return nil, nil

}

 

3.push1,push2… push32指令集,对应调用的是makePush(1,1)…makePush(32,32) 。gas费用都是3

  实现的功能是:从字节指令代码数据中取出从pc计数器到x个指令出来,压入栈中。x为1到32。

// make push instruction function

func makePush(size uint64, pushByteSize int) executionFunc {

    return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {

        codeLen := len(contract.Code)

        startMin := codeLen

        if int(*pc+1) < startMin {

            startMin = int(*pc + 1)

        }

        endMin := codeLen

        if startMin+pushByteSize < endMin {

            endMin = startMin + pushByteSize

        }

        integer := interpreter.intPool.get()

        stack.push(integer.SetBytes(common.RightPadBytes(contract.Code[startMin:endMin], pushByteSize)))

        *pc += size

        return nil, nil

    }

}

4.dup1,dup2… dup16指令,对应调用的是makeDup(1)…makeDup(16) 。gas费用都是3

  dump是转存的意思,主要实现的是把栈中的某个元素压入栈顶

  具体实现的功能是:从栈顶开始算起,把栈上第x个元素存入intpool的栈顶,并且把该元素也存入栈的栈顶。


 

// make dup instruction function

func makeDup(size int64) executionFunc {

    return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {

        stack.dup(interpreter.intPool, int(size))

        return nil, nil

    }

}

func (st *Stack) dup(pool *intPool, n int) {

    st.push(pool.get().Set(st.data[st.len()-n]))

}

5.swap1,swap2… swap16指令,对应调用的是makeSwap(1)…makeSwap(16) 。gas费用都是3

 实现的功能是:把栈上的第x个元素和栈顶元素进行交换

  

// make swap instruction function

func makeSwap(size int64) executionFunc {

    // switch n + 1 otherwise n would be swapped with n

    size++

    return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {

        stack.swap(int(size))

        return nil, nil

    }

}



func (st *Stack) swap(n int) {

    st.data[st.len()-n], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-n]

}

 

6.log1,log2… log4指令,对应调用的是makeLog(1)…makeLog(4) 。gas费用是 8gas* 字节大小

  实现的功能是:根据栈的前两个元素作为key和size,从内存里取出相应的数据,存入statedb的journal(日志)里。1,2,3,4代表的是日志的主题,一次最多可以存入4个主题。

  如果有多个主题,取数据的时候从data里切分出不同主题的数据。 

 

// make log instruction function

func makeLog(size int) executionFunc {

    return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {

        topics := make([]common.Hash, size)

        mStart, mSize := stack.pop(), stack.pop()

        for i := 0; i < size; i++ {

            topics[i] = common.BigToHash(stack.pop())

        }

        d := memory.Get(mStart.Int64(), mSize.Int64())

        interpreter.evm.StateDB.AddLog(&types.Log{

            Address: contract.Address(),

            Topics:  topics,

            Data:    d,

            // This is a non-consensus field, but assigned here because

            // core/state doesn't know the current block number.

            BlockNumber: interpreter.evm.BlockNumber.Uint64(),

        })

        interpreter.intPool.put(mStart, mSize)

        return nil, nil

    }

}



func (self *StateDB) AddLog(log *types.Log) {

    self.journal.append(addLogChange{txhash: self.thash})

    log.TxHash = self.thash

    log.BlockHash = self.bhash

    log.TxIndex = uint(self.txIndex)

    log.Index = self.logSize

    self.logs[self.thash] = append(self.logs[self.thash], log)

    self.logSize++

}

 

7.create,call,callcode,return 

  (1) Create指令实现的是创建合约,将会调用

res, addr, returnGas, suberr := interpreter.evm.Create(contract, input, gas, value)

  (2)call指令实现的是调用合约,将会调用

 ret, returnGas, err := interpreter.evm.Call(contract, toAddr, args, gas, value)

 (3)callcode指令实现的是一个合约调用其他合约,最终将会调用callcode方法,和call方法最大不同的是执行合约的上下文是调用者,而不是将要执行的合约。

ret, returnGas, err := interpreter.evm.CallCode(contract, toAddr, args, gas, value)

  callcode一般发生在定义了多个合约,其中一个合约调用了其他合约的方法。

 (4)return指令实现的是:从内存中,以栈顶的前两个元素作为偏移量和size,取出相应的数据放入intpool中,并返回数据  

 

 func opReturn(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {

    offset, size := stack.pop(), stack.pop()

    ret := memory.GetPtr(offset.Int64(), size.Int64())

    interpreter.intPool.put(offset, size)

    return ret, nil

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值