Solidity中通过this强制递归调用触发EVM消息调用是否正确?
递归EVM消息调用实现方案确认
你通过this发起自身合约调用的写法是可靠、符合规范的,可以稳定实现每一次递归都触发独立EVM消息的需求,不存在正确性问题。
核心原理
- 第一版普通递归的内部调用:直接写函数名调用同合约方法时,Solidity编译器会将其编译为EVM内部的JUMP跳转指令,不会创建新的调用栈帧,不会触发EVM消息调用,全程共享同一个执行上下文,因此Gas消耗极低。
- 第二版的外部自调用:只要是通过合约地址实例发起的函数调用(包括传入
this即当前合约自身地址的场景),编译器都会生成对应的EVM调用操作码:- 调用
view/pure函数时生成STATICCALL - 调用普通状态修改函数时生成
CALL
哪怕调用目标是当前合约本身,每一次调用都会生成独立的EVM消息、压入新的调用栈帧,走完完整的外部调用流程(参数ABI编码、上下文切换、返回值ABI解码等)。你测试观察到的Gas显著升高,正是这些外部调用固定开销带来的正常现象。
- 调用
- 该行为是Solidity语言规范明确定义的,不属于未定义行为,可稳定复现。
测试场景下的严谨性增强建议
如果要彻底避免编译器优化带来的干扰,100%确保所有递归走外部EVM消息,可以做两个小调整:
- 将递归函数的可见性标记为
external,从语法层面禁止内部直接调用,从根源上避免被编译器优化成内部跳转:
contract Fibonacci { function fibonacciIndirect(uint32 n) external view returns(uint32) { if (n == 0 || n == 1) { return n; } uint32 n1 = Fibonacci(address(this)).fibonacciIndirect(n - 1); uint32 n2 = Fibonacci(address(this)).fibonacciIndirect(n - 2); return n1 + n2; } }
- 可通过
msg.sender的值做校验:外部自调用场景下,每一层子调用的msg.sender都会是当前合约自身地址,和最外层用户调用的msg.sender(用户地址)有明确区别,可以很方便地验证每一层调用确实是独立的EVM消息。
测试注意事项
- EVM有固定的调用栈深度限制(目前为1024层),斐波那契的二叉递归调用量随n值指数增长,很容易触发栈深度限制导致调用失败,测试时需要控制n的取值范围。
- 如果你后续测试场景需要在递归调用中修改合约状态,只需要去掉函数的
view标记,调用就会自动切换为普通CALL操作码,依然保持每一层独立EVM消息的特性。
内容的提问来源于stack exchange,提问作者Stylishcoder
相关产品推荐
相关产品推荐

