(番外篇)Michael.W基于Foundry精读Openzeppelin第22期——内联汇编staticcall

news2024/11/15 11:20:48

(番外篇)Michael.W基于Foundry精读Openzeppelin第22期——内联汇编staticcall

      • 0. 版本
      • 1. 关于内联汇编staticcall
      • 2. foundry代码验证
        • 2.1 目标合约
        • 2.2 返回数据字节长度为32
        • 2.3 返回数据字节长度为64
        • 2.4 返回数据为动态数组

0. 版本

[forge-std]:v1.5.6

1. 关于内联汇编staticcall

内联汇编中,Instruction staticcall的功能及使用方法与Instruction call类似。唯一不同的是在staticcall的过程中不允许发生storage的修改。

staticcall的使用方法如下:

assembly{
	staticcall(g, a, in, insize, out, outsize)
}

其中,各个参数为:

  • g: 本次调用时设置的gas上限;
  • a: call的目标合约地址;
  • in: memory中的staticcall的calldata的起始位置;
  • insize: memory中的staticcall的calldata的长度;
  • out: memory中存放返回数据的起始位置;
  • outsize: memory中存放返回数据的长度。

需要注意的是:Instruction callcallcodedelegatecall在调用时的参数都与上述staticcall一样。其中,out和outsize是在memory中定义的用来存放返回数据的区域。这个区域的改写规则由返回数据的字节长度来决定:

  1. 当返回数据的字节长度>outsize时,只有mem [out…(out+outsize))会存储返回数据,剩下的返回数据需要通过Instruction returndatacopy来获取;
  2. 当返回数据的字节长度<outsize时,mem [out…(out+outsize))中前面会存储全部返回数据,剩余的mem保持不变;
  3. 当返回数据的字节长度=outsize时,mem [out…(out+outsize))中正好存储全部的返回数据。

2. foundry代码验证

全部foundry测试合约:

Github: https://github.com/RevelationOfTuring/foundry-openzeppelin-contracts/blob/master/test/extra/assembly/STATICCALL.t.sol

2.1 目标合约

contract Target {
    uint _n = 1024;
    address _addr = address(1);
    uint[] _arr = [1, 2];

	// 返回数据字节长度为32
    function getN() external view returns (uint){
        return _n;
    }

	// 返回数据字节长度为64
    function getNAndAddr() external view returns (uint, address){
        return (_n, _addr);
    }

	// 返回数据为动态数组
    function getArr() external view returns (uint[] memory){
        return _arr;
    }
}

2.2 返回数据字节长度为32

contract STATICCALLTest is Test {
    Target t = new Target();

    function test_Staticcall_ReturnUint() external {
        bytes memory encodedParams = abi.encodeCall(t.getN, ());
        address targetAddr = address(t);
        bytes32 outPtr;
        // case 1: right outsize for return data (uint)
        uint outSize = 0x20;
        bool success;
        uint returnSize;
        bytes32 originalValueOnOutPtr;
        assembly{
            outPtr := mload(0x40)  // free memory pointer
            success := staticcall(100000, targetAddr, add(encodedParams, 0x20), mload(encodedParams), outPtr, outSize)
            returnSize := returndatasize()
            originalValueOnOutPtr := mload(outPtr)
        }
        assertTrue(success);
        assertEq(returnSize, 0x20);
        uint n = t.getN();
        assertEq(n, uint(originalValueOnOutPtr));

        // case 2: insufficient outsize for return data
        outSize = 0x00;
        bytes32 outValueFromReturnDataCopy;
        assembly{
            outPtr := mload(0x40)  // free memory pointer
            success := staticcall(100000, targetAddr, add(encodedParams, 0x20), mload(encodedParams), outPtr, outSize)
            returnSize := returndatasize()
            originalValueOnOutPtr := mload(outPtr)
        	// copy return data (returnSize bytes) with returndatacopy() to the outPtr
            returndatacopy(outPtr, 0x00, returnSize)
            outValueFromReturnDataCopy := mload(outPtr)
        }
        assertTrue(success);
        assertEq(returnSize, 0x20);
        assertNotEq(n, uint(originalValueOnOutPtr));
        assertEq(n, uint(outValueFromReturnDataCopy));

        // case 3: outsize more than the size of return data
        outSize = 0x40;
        bytes32 restBytesInOutSize;
        assembly{
            outPtr := mload(0x40)  // free memory pointer
            success := staticcall(100000, targetAddr, add(encodedParams, 0x20), mload(encodedParams), outPtr, outSize)
            returnSize := returndatasize()
            originalValueOnOutPtr := mload(outPtr)
        	// outsize中多余出来的未使用的内容
            restBytesInOutSize := mload(add(outPtr, returnSize))
        }

        assertTrue(success);
        assertEq(returnSize, 0x20);
        assertEq(n, uint(originalValueOnOutPtr));
        // untouched
        assertEq(0, restBytesInOutSize);
    }
}

2.3 返回数据字节长度为64

contract STATICCALLTest is Test {
    Target t = new Target();

    function test_Staticcall_ReturnUintAndAddress() external {
        bytes memory encodedParams = abi.encodeCall(t.getNAndAddr, ());
        address targetAddr = address(t);
        bytes32 outPtr;
        // case 1: right outsize for return data (uint and address)
        uint outSize = 0x40;
        bool success;
        uint returnSize;
        bytes32 originalValueOnOutPtr;
        bytes32 nextWordOfOutPtr;
        assembly{
            outPtr := mload(0x40)  // free memory pointer
        	// outsize为2个字,即32*2=64个字节(即0x40)
            success := staticcall(100000, targetAddr, add(encodedParams, 0x20), mload(encodedParams), outPtr, outSize)
            returnSize := returndatasize()
        	// outPtr开始,第一个字为第一个uint返回值,第二个字为第二个address返回值
            originalValueOnOutPtr := mload(outPtr)
            nextWordOfOutPtr := mload(add(outPtr, 0x20))
        }
        assertTrue(success);
        assertEq(returnSize, 0x40);
        (uint n, address addr) = t.getNAndAddr();
        assertEq(n, uint(originalValueOnOutPtr));
        assertEq(addr, address(uint160(uint(nextWordOfOutPtr))));

        // case 2: insufficient outsize for return data
        outSize = 0x20;
        bytes32 outValueFromReturnDataCopy;
        assembly{
            outPtr := mload(0x40)  // free memory pointer
        	// outsize为1个字,即32*1=32个字节(即0x20)
            success := staticcall(100000, targetAddr, add(encodedParams, 0x20), mload(encodedParams), outPtr, outSize)
            returnSize := returndatasize()
        	// outPtr开始,第一个字为第一个uint返回值
            originalValueOnOutPtr := mload(outPtr)
        	// 第二个字不再是第二个address返回值,因为outSize不够
            nextWordOfOutPtr := mload(add(outPtr, 0x20))
        	// 从returndata中复制第二个字到outPtr指向的内存中
            returndatacopy(outPtr, 0x20, 0x20)
        	// 从outPtr指向的内存中取出内容(即returndata中的第二个address返回值)
            outValueFromReturnDataCopy := mload(outPtr)
        }
        assertTrue(success);
        assertEq(returnSize, 0x40);
        assertEq(n, uint(originalValueOnOutPtr));
        // 由于outSize设置过小,outPtr开始的第二个字内容不是returndata中的第二个address返回值
        assertNotEq(addr, address(uint160(uint(nextWordOfOutPtr))));
        assertEq(addr, address(uint160(uint(outValueFromReturnDataCopy))));

        // case 3: outsize more than the size of return data
        outSize = 0x60;
        bytes32 restBytesInOutSize;
        assembly{
            outPtr := mload(0x40)  // free memory pointer
        	// outsize为3个字,即32*3=96个字节(即0x60)
            success := staticcall(100000, targetAddr, add(encodedParams, 0x20), mload(encodedParams), outPtr, outSize)
            returnSize := returndatasize()
            originalValueOnOutPtr := mload(outPtr)
            nextWordOfOutPtr := mload(add(outPtr, 0x20))
        	// outsize中多余出来的未使用的内容
            restBytesInOutSize := mload(add(outPtr, returnSize))
        }
        assertTrue(success);
        assertEq(returnSize, 0x40);
        assertEq(n, uint(originalValueOnOutPtr));
        assertEq(addr, address(uint160(uint(nextWordOfOutPtr))));
        // untouched
        assertEq(0, restBytesInOutSize);
    }
}

2.4 返回数据为动态数组

contract STATICCALLTest is Test {
    Target t = new Target();

    function test_Staticcall_ReturnUintArr() external {
        bytes memory encodedParams = abi.encodeCall(t.getArr, ());
        address targetAddr = address(t);
        bytes32 outPtr;
        // case 1: right outsize for return data
        // (uint[] with 2 members —— 1(offset to start the array) + 1(length of array) + 2(2 words for 3 members) = 4)
        uint outSize = 0x80;
        bool success;
        uint returnSize;
        bytes32 originalValueOnOutPtr;
        bytes32 secondWordOfOutPtr;
        bytes32 thirdWordOfOutPtr;
        bytes32 forthWordOfOutPtr;
        bytes32 fifthWordOfOutPtr;
        assembly{
            outPtr := mload(0x40)  // free memory pointer
        	// outsize为4个字,即32*4=128个字节(即0x80)
            success := staticcall(100000, targetAddr, add(encodedParams, 0x20), mload(encodedParams), outPtr, outSize)
            returnSize := returndatasize()
        	// outPtr开始,第一个字为动态数组真实数据的偏移值
            originalValueOnOutPtr := mload(outPtr)
        	// 第二个字为动态数组长度
            secondWordOfOutPtr := mload(add(outPtr, 0x20))
        	// 第三个字为第一个元素值
            thirdWordOfOutPtr := mload(add(outPtr, 0x40))
        	// 第四个字为第二个元素值
            forthWordOfOutPtr := mload(add(outPtr, 0x60))
        }
        assertTrue(success);
        assertEq(0x80, returnSize);
        uint[] memory arr = t.getArr();
        // offset to start the uint array is 0x20
        assertEq(0x20, uint(originalValueOnOutPtr));
        // 动态数组长度
        assertEq(arr.length, uint(secondWordOfOutPtr));
        // 依次为2个元素的值
        assertEq(arr[0], uint(thirdWordOfOutPtr));
        assertEq(arr[1], uint(forthWordOfOutPtr));


        // case 2: insufficient outsize for return data
        // (uint[] with 2 members —— 1(offset to start the array) + 1(length of array) + 2(2 words for 3 members) = 4)
        outSize = 0x20;
        assembly{
            outPtr := mload(0x40)  // free memory pointer
        	// outsize为1个字,即32*1=32个字节(即0x20)
            success := staticcall(100000, targetAddr, add(encodedParams, 0x20), mload(encodedParams), outPtr, outSize)
            returnSize := returndatasize()
        	// outPtr开始,第一个字为动态数组真实数据的偏移值
            originalValueOnOutPtr := mload(outPtr)
        	// 第二个字不再是动态数组的长度,因为outSize不够
            secondWordOfOutPtr := mload(add(outPtr, 0x20))
        }
        assertTrue(success);
        assertEq(0x80, returnSize);
        // offset to start the uint array is 0x20
        assertEq(0x20, uint(originalValueOnOutPtr));
        // returndatacopy()之前,outPtr后的第二个字内容不是数组长度
        assertNotEq(arr.length, uint(secondWordOfOutPtr));

        assembly{
        	// 从returndata中复制第二个至第四个字到outPtr+0x20开始的内存中
            returndatacopy(add(outPtr, 0x20), 0x20, sub(returnSize, 0x20))
        	// outPtr后的第二个字为数组长度
            secondWordOfOutPtr := mload(add(outPtr, 0x20))
        	// 第三个字为第一个元素值
            thirdWordOfOutPtr := mload(add(outPtr, 0x40))
        	// 第四个字为第二个元素值
            forthWordOfOutPtr := mload(add(outPtr, 0x60))
        }
        // returndatacopy()之后,outPtr后的第二个字内容为数组长度
        assertEq(arr.length, uint(secondWordOfOutPtr));
        // returndatacopy()之后,outPtr后的第三个至第五个字内容依次为数组元素值
        assertEq(arr[0], uint(thirdWordOfOutPtr));
        assertEq(arr[1], uint(forthWordOfOutPtr));

        // case 3: outsize more than the size of return data
        // (uint[] with 2 members —— 1(offset to start the array) + 1(length of array) + 2(2 words for 3 members) = 4)
        outSize = (4 + 1) * 0x20;
        assembly{
            outPtr := mload(0x40)  // free memory pointer
        	// outsize为5个字,即32*5=160个字节(即0xa0)
            success := staticcall(100000, targetAddr, add(encodedParams, 0x20), mload(encodedParams), outPtr, outSize)
            returnSize := returndatasize()
        	// outPtr开始,第一个字为动态数组真实数据的偏移值
            originalValueOnOutPtr := mload(outPtr)
        	// 第二个字为动态数组长度
            secondWordOfOutPtr := mload(add(outPtr, 0x20))
        	// 第三个字为第一个元素值
            thirdWordOfOutPtr := mload(add(outPtr, 0x40))
        	// 第四个字为第二个元素值
            forthWordOfOutPtr := mload(add(outPtr, 0x60))
        	// 第五个字为outsize中未使用的空间
            fifthWordOfOutPtr := mload(add(outPtr, 0x80))
        }
        assertTrue(success);
        assertEq(0x80, returnSize);
        // offset to start the uint array is 0x20
        assertEq(0x20, uint(originalValueOnOutPtr));
        // 动态数组长度
        assertEq(arr.length, uint(secondWordOfOutPtr));
        // 依次为2个元素的值
        assertEq(arr[0], uint(thirdWordOfOutPtr));
        assertEq(arr[1], uint(forthWordOfOutPtr));
        // 第五个字为untouched
        assertEq(0, fifthWordOfOutPtr);
    }
}

ps:
本人热爱图灵,热爱中本聪,热爱V神。
以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。
同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下!
如果需要转发,麻烦注明作者。十分感谢!

在这里插入图片描述

公众号名称:后现代泼痞浪漫主义奠基人

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/854416.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

腾讯云COS的快速接入

背景 最近在研究一个剪贴板粘贴工具&#xff0c;实现粘贴图片&#xff0c;返回可访问的地址&#xff0c;这个在我的哔哩哔哩上有出一期视频&#x1f92d;。但是&#xff0c;我发现部分博客平台不能正常的转载我的图片链接&#xff0c;于是研究了一下腾讯云的COS&#xff08;阿…

MySQL数据库面试题:如何优化呢?

文章目录 优化字段类型的选择优化索引的使用优化SQL语句事务与隔离级别并发事务的问题与解决undo log和redo log的区别事务的隔离性与MVCCMySQL主从同步原理分库分表的经验水平分库的应用 在数据库开发中&#xff0c;创建表是一个至关重要的步骤&#xff0c;优化设计可以显著提…

【非欧几里得域信号的信号处理】使用经典信号处理和图信号处理在一维和二维欧几里得域信号上应用低通滤波器研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

遍历集合List的五种方法以及如何在遍历集合过程中安全移除元素

一、遍历集合List的五种方法 测试数据 List<String> list new ArrayList<>(); list.add("A");list.add("B");list.add("C");1. 普通for循环 普通for循环&#xff0c;通过索引遍历 for (int i 0; i < list.size(); i) {Syst…

《UNUX环境高级编程》(14)高级I/O

1、引言 2、 非阻塞I/O 系统调用分为两类&#xff1a;低速系统调用和其他系统调用。低速系统调用是可能会使进程永远阻塞的一类系统调用&#xff0c;包括&#xff1a; 如果某些文件类型&#xff08;如读管道、终端设备和网络设备&#xff09;的数据并不存在&#xff0c;读操作…

国产低功耗蓝牙HS6621CxC/6621Px系列支持Find My网络功能方案芯片

目录 什么是“Find My“&#xff1f;HS6621系列简介 什么是“Find My“&#xff1f; “Find My”是苹果公司于19年前推出的针对失物追踪&#xff0c;Find My iPhone&#xff08;查找我的iPhone&#xff09;和Find My Friends&#xff08;查找朋友&#xff09;的结合体应用。为…

【CSS3】CSS3 动画 ④ ( 使用动画制作地图热点图 )

文章目录 一、需求说明二、动画代码分析1、地图背景设置2、热点动画位置测量3、热点动画布局分析4、动画定义5、小圆点实现6、波纹效果盒子实现7、延迟动画设置 三、代码示例 一、需求说明 实现如下效果 , 在一张地图上 , 以某个位置为中心点 , 向四周发散 ; 核心 是实现 向四周…

go-zero 是如何做路由管理的?

原文链接&#xff1a; go-zero 是如何做路由管理的&#xff1f; go-zero 是一个微服务框架&#xff0c;包含了 web 和 rpc 两大部分。 而对于 web 框架来说&#xff0c;路由管理是必不可少的一部分&#xff0c;那么本文就来探讨一下 go-zero 的路由管理是怎么做的&#xff0c…

速卖通,国际站店铺想要增加曝光,提升销量,测评补单有效果吗?

作为一个卖家&#xff0c;成功运营速卖通店铺需要一系列的策略和技巧 1.借助平台的力量对于成长期的店铺来说&#xff0c;平台本身是最大的流量来源。如何从平台那儿获取更多的支持则成为这个阶段最重要的难题。以速卖通为例&#xff0c;经过反复测试&#xff0c;平台给普通卖家…

LeetCode练习习题集【4月 - 7 月】

LEETCODE习题集【4月-7月总结】 简单 数组部分 1.重复数 题目&#xff1a; 在一个长度u为 n 的数组 nums 里的所有数字都在 0&#xff5e;n-1 的范围内。数组中某些数字是重复的&#xff0c;但不知道有几个数字重复了&#xff0c;也不知道每个数字重复了几次。请找出数组中…

蛋糕小程序商店制作攻略教程分享

想要开发一个蛋糕小程序商店&#xff0c;可以通过以下步骤进行操作&#xff1a; 1.首先&#xff0c;我们需要注册登录账号&#xff0c;进入操作后台。找到并点击【商城】中的【去管理】进入商城的后台管理页面。然后再点击【小程序商城】模块中的【去装修】进入小程序商城的制作…

【torch.nn.PixelShuffle】和 【torch.nn.UnpixelShuffle】

文章目录 torch.nn.PixelShuffle直观解释官方文档 torch.nn.PixelUnshuffle直观解释官方文档 torch.nn.PixelShuffle 直观解释 PixelShuffle是一种上采样方法&#xff0c;它将形状为 ( ∗ , C r 2 , H , W ) (∗, C\times r^2, H, W) (∗,Cr2,H,W)的张量重新排列转换为形状为…

网络安全 Day28-运维安全项目-加密隧道

运维安全项目-加密隧道 1. 加密隧道服务概述2. openVPN应用场景3. 虚拟机环境准备3.0 准备知识3.1 添加网卡![请添加图片描述](https://img-blog.csdnimg.cn/f155ca2804d84118b89a69da3688911e.png)3.2 配置内网&#xff08;LAN区段)3.3 虚拟机选择LAN区段3.4 书写eth1网卡配置…

力扣hot100刷题记录

二刷hot100&#xff0c;坚持每天打卡&#xff01;&#xff01;&#xff01; 1. 两数之和 // 先求差&#xff0c;再查哈希表 public int[] twoSum(int[] nums, int target) {Map<Integer,Integer> map new HashMap<>();for(int i 0;i<nums.length;i){int key …

UE Mesh Generation and Editing at Runtime

UE Mesh Generation and Editing at Runtime 虚幻运行时和编辑器下生成和编辑 网格体。 UE Mesh 虚幻中常用的三种网格体 UProceduralMeshComponent 程序化网格体 UStaticMeshComponent 静态网格体 USimpleDynamicMeshComponent 动态网格体 借用他人总结的&#xff0c;UE4…

设计一个“完美“的测试用例,用户登录模块实例...

前言 好的测试用例一定是一个完备的集合&#xff0c;它能够覆盖所有等价类以及各种边界值&#xff0c;而跟能否发现缺陷无关 好的测试用例必须具备哪些特征 整体完备性&#xff1a;一定是一个完备的整体&#xff0c;是有效测试用例组成的集合&#xff0c;能够完全覆盖测试需…

在vue中Antv G2 折线图如何添加点击事件获取折线上点的值

在项目中有个需求是点击折线图的点&#xff0c;获取当前点的信息&#xff0c;其它图形都可以参考相关的API获取到&#xff0c;但area做的折线图怎么都获取不到点击的信息&#xff0c;只能获取全部的信息&#xff0c;最终解决如下&#xff1a; 实现思路 用户的鼠标在折线图上移…

高中教师能去美国做访问学者吗?

美国作为世界上高等教育水平较高的国家之一&#xff0c;吸引了众多学者前往交流学习。那么高中教师是否能够成为美国访问学者&#xff0c;这是当然的&#xff0c;高中老师是可以出国访学的&#xff0c;但是出国做访问学者会涉及到多方面的因素。 首先&#xff0c;教师个人的学术…

带你Debug SpringApplication.run(MainApp.class, args) 看看SpringBoot 如何启动Tomcat

&#x1f600;前言 本篇博文是关于SpringBoot 如何启动Tomcat的笔记&#xff0c;希望能够让你到SpringBoot印象深刻&#x1f60a; &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到…

2023深圳杯A题完整代码模型

已更新深圳杯A题全部版本&#xff0c;文末获取&#xff01; 摘要 现代社会&#xff0c;随着生活方式的变化和工作压力的增大&#xff0c;慢性非传染性疾病日益成为威胁公众健康的主要问题。心脑血管疾病、糖尿病、恶性肿瘤及慢性阻塞性肺病等慢性病的发病率呈现出上升趋势。为…