EVM Opcodes 101

1. Hello Opcodes
2. Opcodes分类
3. 堆栈指令
4. 算数指令
5. 比较指令
6. 位级指令
7. 内存指令
8. 存储指令
9. 控制流指令
10. 区块信息指令
11. 堆栈指令2
12. SHA3指令
13. 账户指令
14. 交易指令
15. Log指令
5. 比较指令
比较指令

我最近在重新学以太坊opcodes,也写一个“WTF EVM Opcodes极简入门”,供小白们使用。

推特:@0xAA_Science

社区:Discord|微信群|官网 wtf.academy

所有代码和教程开源在github: github.com/WTFAcademy/WTF-Opcodes


这一讲,我们将介绍EVM中用于比较运算的6个指令,包括LT(小于),GT(大于),和EQ(相等)。并且,我们将在用Python写的极简版EVM中添加对他们的支持。

LT (小于)

LT指令从堆栈中弹出两个元素,比较第二个元素是否小于第一个元素。如果是,那么将0推入堆栈,否则将1推入堆栈。如果堆栈元素不足两个,那么会抛出异常。这个指令的操作码是0x10,gas消耗为3。

我们可以将LT指令的实现添加到我们的极简EVM中:

def lt(self):
    if len(self.stack) < 2:
        raise Exception('Stack underflow')
    a = self.stack.pop()
    b = self.stack.pop()
    self.stack.append(int(b < a)) # 注意这里的比较顺序

我们在run()函数中添加对LT指令的处理:

def run(self):
    while self.pc < len(self.code):
        op = self.next_instruction()

        # ... 其他指令的处理 ...

        elif op == LT: # 处理LT指令
            self.lt()

现在,我们可以尝试运行一个包含LT指令的字节码:0x6002600310(PUSH1 2 PUSH1 3 LT)。这个字节码将2和3推入堆栈,然后比较2是否小于3。

code = b"\x60\x02\x60\x03\x10"
evm = EVM(code)
evm.run()
print(evm.stack)
# output: [0]

GT (大于)

GT指令和LT指令非常类似,不过它比较的是第二个元素是否大于第一个元素。如果是,那么将0推入堆栈,否则将1推入堆栈。如果堆栈元素不足两个,那么会抛出异常。这个指令的操作码是0x10,gas消耗为3。操作码是0x11,gas消耗为3。

我们将GT指令的实现添加到极简EVM:

def gt(self):
    if len(self.stack) < 2:
        raise Exception('Stack underflow')
    a = self.stack.pop()
    b = self.stack.pop()
    self.stack.append(int(b > a)) # 注意这里的比较顺序

我们在run()函数中添加对GT指令的处理:

def run(self):
    while self.pc < len(self.code):
        op = self.next_instruction()

        # ... 其他指令的处理 ...

        elif op == GT: # 处理GT指令
            self.gt()

现在,我们可以运行一个包含GT指令的字节码:0x6002600311(PUSH1 2 PUSH1 3 GT)。这个字节码将2和3推入堆栈,然后比较2是否大于3。

code = b"\x60\x02\x60\x03\x11"
evm = EVM(code)
evm.run()
print(evm.stack)
# output: [1]

EQ (等于)

EQ指令从堆栈中弹出两个元素,如果两个元素相等,那么将1推入堆栈,否则将0推入堆栈。该指令的操作码是0x14,gas消耗为3。

我们将EQ指令的实现添加到极简EVM:

def eq(self):
    if len(self.stack) < 2:
        raise Exception('Stack underflow')
    a = self.stack.pop()
    b = self.stack.pop()
    self.stack.append(int(a == b))

我们在run()函数中添加对EQ指令的处理:

elif op == EQ: 
    self.eq()

现在,我们可以运行一个包含EQ指令的字节码:0x6002600314(PUSH1 2 PUSH1 3 EQ)。这个字节码将2和3推入堆栈,然后比较两者是否相等。

code = b"\x60\x02\x60\x03\x14"
evm = EVM(code)
evm.run()
print(evm.stack)
# output: [0]

ISZERO (是否为零)

ISZERO指令从堆栈中弹出一个元素,如果元素为0,那么将1推入堆栈,否则将0推入堆栈。该指令的操作码是0x15,gas消耗为3。

我们将ISZERO指令的实现添加到极简EVM:

def iszero(self):
    if len(self.stack) < 1:
        raise Exception('Stack underflow')
    a = self.stack.pop()
    self.stack.append(int(a == 0))

我们在run()函数中添加对ISZERO指令的处理:

elif op == ISZERO: 
    self.iszero()

现在,我们可以运行一个包含ISZERO指令的字节码:0x600015(PUSH1 0 ISZERO)。这个字节码将0推入堆栈,然后检查其是否为0。

code = b"\x60\x00\x15"
evm = EVM(code)
evm.run()
print(evm.stack)
# output: [1]

其他比较指令

  1. SLT (有符号小于) : 这个指令会从堆栈中弹出两个元素,然后比较第二个元素是否小于第一个元素,结果以有符号整数形式返回。如果第二个元素小于第一个元素,将0推入堆栈,否则将1推入堆栈。它的操作码是0x12,gas消耗为3。

    def slt(self):
        if len(self.stack) < 2:
            raise Exception('Stack underflow')
        a = self.stack.pop()
        b = self.stack.pop()
        self.stack.append(int(b < a)) # 极简evm stack中的值已经是以有符号整数存储了,所以和lt一样实现
  2. SGT (有符号大于) : 这个指令会从堆栈中弹出两个元素,然后比较第二个元素是否大于第一个元素,结果以有符号整数形式返回。如果第二个元素大于第一个元素,将0推入堆栈,否则将1推入堆栈。它的操作码是0x13,gas消耗为3。

    def sgt(self):
        if len(self.stack) < 2:
            raise Exception('Stack underflow')
        a = self.stack.pop()
        b = self.stack.pop()
        self.stack.append(int(b > a)) # 极简evm stack中的值已经是以有符号整数存储了,所以和gt一样实现

总结

这一讲,我们介绍了EVM中的6个比较指令,并在极简版EVM中添加了对他们的支持。课后习题: 写出0x6003600414对应的指令形式,并给出运行后的堆栈状态。

PreviousNext