我最近在重新学以太坊opcodes,也写一个“WTF EVM Opcodes极简入门”,供小白们使用。
所有代码和教程开源在github: github.com/WTFAcademy/WTF-Opcodes
这一讲,我们将介绍EVM中用于比较运算的6个指令,包括LT
(小于),GT
(大于),和EQ
(相等)。并且,我们将在用Python写的极简版EVM中添加对他们的支持。
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
指令和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
指令从堆栈中弹出两个元素,如果两个元素相等,那么将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
指令从堆栈中弹出一个元素,如果元素为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]
-
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一样实现
-
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
对应的指令形式,并给出运行后的堆栈状态。