Skip to content

Latest commit

 

History

History

06_BitwiseOp

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

WTF Opcodes 极简入门: 6. 位级指令

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

推特:@0xAA_Science

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

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


这一讲,我们将介绍 EVM 中用于位级运算的 8 个指令,包括AND(与),OR(或),和XOR(异或)。并且,我们将在用 Python 写的极简版 EVM 中添加对他们的支持。

AND (与)

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 (或)

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 (异或)

XOR指令与ANDOR指令类似,但执行的是异或运算。操作码是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

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

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

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)

其他位级指令

  1. BYTE: BYTE指令从堆栈中弹出两个元素(ab),将第二个元素(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)
  2. 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对应的指令形式,并给出运行后的堆栈状态。