WTF Opcodes 极简入门: 6. 位级指令 我最近在重新学以太坊 opcodes,也写一个“WTF EVM Opcodes 极简入门”,供小白们使用。 推特:@0xAAScience 社区:Discord|微信群|官网 wtf.academy 所有代码和教程开源在 github: github.com/WTFAcademy/WTF-Opcodes 这一讲,我们将介绍 EVM 中用于位级运算的 8 个指令,包括 (与), (或),和 (异或)。并且,我们将在用 Python 写的极简版 EVM 中添加对他们的支持。 AND (与) 指令从堆栈中弹出两个元素,对它们进行位与运算,并将结果推入堆栈。操作码是 ,gas 消耗为 。
我最近在重新学以太坊 opcodes,也写一个“WTF EVM Opcodes 极简入门”,供小白们使用。
所有代码和教程开源在 github: github.com/WTFAcademy/WTF-Opcodes
这一讲,我们将介绍 EVM 中用于位级运算的 8 个指令,包括AND(与),OR(或),和XOR(异或)。并且,我们将在用 Python 写的极简版 EVM 中添加对他们的支持。
AND指令从堆栈中弹出两个元素,对它们进行位与运算,并将结果推入堆栈。操作码是0x16,gas 消耗为3。
我们将AND指令的实现添加到我们的 EVM 模拟器中:
def and_op(self): if len(self.stack) < 2: raise Exception('Stack underflow') a = self.stack.pop() b = self.stack.pop() self.stack.append(a & b)
我们在run()函数中添加对AND指令的处理:
def run(self): while self.pc < len(self.code): op = self.next_instruction() # ... 其他指令的处理 ... elif op == AND: # 处理AND指令 self.and_op()
现在,我们可以尝试运行一个包含AND指令的字节码:0x6002600316(PUSH1 2 PUSH1 3 AND)。这个字节码将2(0000 0010)和3(0000 0011)推入堆栈,然后进行位级与运算,结果应该为2(0000 0010)。
code = b"\x60\x02\x60\x03\x16" evm = EVM(code) evm.run() print(evm.stack) # output: [2]
OR指令与AND指令类似,但执行的是位或运算。操作码是0x17,gas 消耗为3。
我们将OR指令的实现添加到 EVM 模拟器:
def or_op(self): if len(self.stack) < 2: raise Exception('Stack underflow') a = self.stack.pop() b = self.stack.pop() self.stack.append(a | b)
我们在run()函数中添加对OR指令的处理:
def run(self): while self.pc < len(self.code): op = self.next_instruction() # ... 其他指令的处理 ... elif op == OR: # 处理OR指令 self.or_op()
现在,我们可以尝试运行一个包含OR指令的字节码:0x6002600317(PUSH1 2 PUSH1 3 OR)。这个字节码将2(0000 0010)和3(0000 0011)推入堆栈,然后进行位级与运算,结果应该为3(0000 0011)。
code = b"\x60\x02\x60\x03\x17" evm = EVM(code) evm.run() print(evm.stack) # output: [3]
XOR指令与AND和OR指令类似,但执行的是异或运算。操作码是0x18,gas 消耗为3。
我们将XOR指令的实现添加到 EVM 模拟器:
def xor_op(self): if len(self.stack) < 2: raise Exception('Stack underflow') a = self.stack.pop() b = self.stack.pop() self.stack.append(a ^ b)
我们在run()函数中添加对XOR指令的处理:
def run(self): while self.pc < len(self.code): op = self.next_instruction() # ... 其他指令的处理 ... elif op == XOR: # 处理XOR指令 self.xor_op()
现在,我们可以尝试运行一个包含XOR指令的字节码:0x6002600318(PUSH1 2 PUSH1 3 XOR)。这个字节码将2(0000 0010)和3(0000 0011)推入堆栈,然后进行位级与运算,结果应该为1(0000 0001)。
code = b"\x60\x02\x60\x03\x18" evm = EVM(code) evm.run() print(evm.stack) # output: [1]
NOT 指令执行按位非操作,取栈顶元素的补码,然后将结果推回栈顶。它的操作码是0x19,gas 消耗为3。
我们将NOT指令的实现添加到 EVM 模拟器:
def not_op(self): if len(self.stack) < 1: raise Exception('Stack underflow') a = self.stack.pop() self.stack.append(~a % (2**256)) # 按位非操作的结果需要模2^256,防止溢出
在run()函数中添加对NOT指令的处理:
elif op == NOT: # 处理NOT指令 self.not_op()
现在,我们可以尝试运行一个包含NOT指令的字节码:0x600219(PUSH1 2 NOT)。这个字节码将2(0000 0010)推入堆栈,然后进行位级非运算,结果应该为很大的数(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd)。
# NOT code = b"\x60\x02\x19" evm = EVM(code) evm.run() print(evm.stack) # output: [很大的数] (fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd)
SHL指令执行左移位操作,从堆栈中弹出两个元素,将第二个元素左移第一个元素位数,然后将结果推回栈顶。它的操作码是0x1B,gas 消耗为3。
我们将SHL指令的实现添加到 EVM 模拟器:
def shl(self): if len(self.stack) < 2: raise Exception('Stack underflow') a = self.stack.pop() b = self.stack.pop() self.stack.append((b << a) % (2**256)) # 左移位操作的结果需要模2^256
在run()函数中添加对SHL指令的处理:
elif op == SHL: # 处理SHL指令 self.shl()
现在,我们可以尝试运行一个包含XOR指令的字节码:0x600260031B(PUSH1 2 PUSH1 3 SHL)。这个字节码将2(0000 0010)和3(0000 0011)推入堆栈,然后将2左移3位,结果应该为16(0001 0000)。
code = b"\x60\x02\x60\x03\x1B" evm = EVM(code) evm.run() print(evm.stack) # output: [16] (0x000000010 << 3 => 0x00010000)
SHR指令执行右移位操作,从堆栈中弹出两个元素,将第二个元素右移第一个元素位数,然后将结果推回栈顶。它的操作码是0x1C,gas 消耗为3。
我们将SHR指令的实现添加到 EVM 模拟器:
def shr(self): if len(self.stack) < 2: raise Exception('Stack underflow') a = self.stack.pop() b = self.stack.pop() self.stack.append(b >> a) # 右移位操作
在run()函数中添加对SHR指令的处理:
elif op == SHR: # 处理SHR指令 self.shr()
现在,我们可以尝试运行一个包含XOR指令的字节码:0x601060031C(PUSH1 16 PUSH1 3 SHL)。这个字节码将16(0001 0000)和3(0000 0011)推入堆栈,然后将16右移3位,结果应该为2(0000 0010)。
code = b"\x60\x10\x60\x03\x1C" evm = EVM(code) evm.run() print(evm.stack) # output: [2] (0x00010000 >> 3 => 0x000000010)
BYTE: BYTE指令从堆栈中弹出两个元素(a和b),将第二个元素(b)看作一个32字节的数组,不够位数补 0,并返回该字节数组中从高位开始的第a个索引的字节,即(b[31-a]),并压入堆栈。如果索引a大于或等于 32,返回0,否则返回b[31-a]。 操作码是0x1a,gas 消耗为3。
def byte_op(self): if len(self.stack) < 2: raise Exception('Stack underflow') position = self.stack.pop() value = self.stack.pop() if position >= 32: res = 0 else: res = (value // pow(256, 31 - position)) & 0xFF self.stack.append(res)
SAR: SAR指令执行算术右移位操作,与SHR类似,但考虑符号位:如果我们对一个负数进行算术右移,那么在右移的过程中,最左侧(符号位)会被填充F以保持数字的负值。它从堆栈中弹出两个元素,将第二个元素以符号位填充的方式右移第一个元素位数,然后将结果推回栈顶。它的操作码是0x1D,gas 消耗为3。由于 Python 的>>操作符已经是算术右移,我们可以直接复用shr函数的代码。
def sar(self): if len(self.stack) < 2: raise Exception('Stack underflow') a = self.stack.pop() b = self.stack.pop() self.stack.append(b >> a) # 右移位操作
这一讲,我们介绍了 EVM 中的 8 个位级指令,并在极简版 EVM 中添加了对他们的支持。课后习题: 写出0x6002600160011B1B对应的指令形式,并给出运行后的堆栈状态。