WTFOpcodes极简入门9.控制流指令


文档摘要

WTF Opcodes极简入门: 9. 控制流指令 我最近在重新学以太坊opcodes,也写一个“WTF EVM Opcodes极简入门”,供小白们使用。 推特:@0xAAScience 社区:Discord|微信群|官网 wtf.academy 所有代码和教程开源在github: github.com/WTFAcademy/WTF-Opcodes 在这一讲,我们将介绍EVM中用于控制流的5个指令,包括 , , , ,和 。我们将在用Python写的极简版EVM中添加对这些操作的支持。 EVM中的控制流 我们在第三讲介绍了程序计数器(Program Counter,PC),而EVM的控制流是由跳转指令( , , )控制PC指向新的指令位置而实现的,这允许合约进行条件执行和循环执行。

WTF Opcodes极简入门: 9. 控制流指令

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

推特:@0xAA_Science

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

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

在这一讲,我们将介绍EVM中用于控制流的5个指令,包括STOPJUMPJUMPIJUMPDEST,和PC。我们将在用Python写的极简版EVM中添加对这些操作的支持。

EVM中的控制流

我们在第三讲介绍了程序计数器(Program Counter,PC),而EVM的控制流是由跳转指令(JUMPJUMPIJUMPDEST)控制PC指向新的指令位置而实现的,这允许合约进行条件执行和循环执行。

STOP(停止)

STOP是EVM的停止指令,它的作用是停止当前上下文的执行,并成功退出。它的操作码是0x00,gas消耗为0。

STOP操作作码设为0x00有一个好处:当一个调用被执行到一个没有代码的地址(EOA),并且EVM尝试读取代码数据时,系统会返回一个默认值0,这个默认值对应的就是STOP指令,程序就会停止执行。

下面,让我们在run()函数中添加对STOP指令的处理:

def run(self): while self.pc < len(self.code): op = self.next_instruction() # ... 其他指令的处理 ... elif op == STOP: # 处理STOP指令 print('Program has been stopped') break # 停止执行

现在,我们可以尝试运行一个包含STOP指令的字节码:

# STOP code = b"\x00" evm = EVM(code) evm.run() # output: Program has been stopped

JUMPDEST(跳转目标)

JUMPDEST指令标记一个有效的跳转目标位置,不然无法使用JUMPJUMPI进行跳转。它的操作码是0x5b,gas消耗为1。

但是0x5b有时会作为PUSH的参数(详情可看黄皮书中的9.4.3. Jump Destination Validity),所以需要在运行代码前,筛选字节码中有效的JUMPDEST指令,使用ValidJumpDest 来存储有效的JUMPDEST指令所在位置。

def findValidJumpDestinations(self): pc = 0 while pc < len(self.code): op = self.code[pc] if op == JUMPDEST: self.validJumpDest[pc] = True elif op >= PUSH1 and op <= PUSH32: pc += op - PUSH1 + 1 pc += 1
def jumpdest(self): pass

JUMP(跳转)

JUMP指令用于无条件跳转到一个新的程序计数器位置。它从堆栈中弹出一个元素,将这个元素设定为新的程序计数器(pc)的值。操作码是0x56,gas消耗为8。

def jump(self): if len(self.stack) < 1: raise Exception('Stack underflow') destination = self.stack.pop() if destination not in self.validJumpDest: raise Exception('Invalid jump destination') else: self.pc = destination

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

elif op == JUMP: self.jump() elif op == JUMPDEST: self.jumpdest()

现在,我们可以尝试运行一个包含JUMPJUMPDEST指令的字节码:0x600456005B(PUSH1 4 JUMP STOP JUMPDEST)。这段字节码将4推入堆栈,然后进行JUMP,跳转到pc = 4的位置,该位置正好是JUMPDEST指令,跳转成功,程序没有被STOP指令中断。

# JUMP code = b"\x60\x04\x56\x00\x5b" evm = EVM(code) evm.run() print(evm.pc) # output: 5

JUMPI(条件跳转)

JUMPI指令用于条件跳转,它从堆栈中弹出两个元素,如果第二个元素(条件,condition)不为0,那么将第一个元素(目标,destination)设定为新的pc的值。操作码是0x57,gas消耗为10。

def jumpi(self): if len(self.stack) < 2: raise Exception('Stack underflow') destination = self.stack.pop() condition = self.stack.pop() if condition != 0: if destination not in self.validJumpDest: raise Exception('Invalid jump destination') else: self.pc = destination

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

elif op == JUMPI: self.jumpi()

现在,我们可以尝试运行一个包含JUMPIJUMPDEST指令的字节码:0x6001600657005B(PUSH1 01 PUSH1 6 JUMPI STOP JUMPDEST)。这个字节码将16推入堆栈,然后进行JUMPI,由于条件不为0,执行跳转到pc = 6的位置,该位置正好是JUMPDEST指令,跳转成功,程序没有被STOP指令中断。

# JUMPI code = b"\x60\x01\x60\x06\x57\x00\x5b" evm = EVM(code) evm.run() print(evm.pc) # output: 7 程序没有被中断

PC(程序计数器)

PC指令将当前的程序计数器(pc)的值压入堆栈。操作码为0x58,gas消耗为2。

def pc(self): self.stack.append(self.pc)

总结

这一讲,我们介绍了EVM中的控制流程指令,并在极简版EVM中添加了对它们的支持。这些操作为合约提供了控制流程的能力,为编写更复杂的合约逻辑提供了可能。

课后习题: 写出0x5F600557005B对应的指令形式,并给出运行后的堆栈和程序计数器状态。


作者与出处
原作者: WTFAcademy
来源:WTFAcademy
许可证:MIT
整理: 灏天文库整理
由灏天文库结构化整理,提供目录导航、全文检索与在线阅读,便于系统化学习
发布者: 作者: WTFAcademy 转发
评论区 (0)
U