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

news/2024/4/20 23:52:40/

(番外篇)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];// 返回数据字节长度为32function getN() external view returns (uint){return _n;}// 返回数据字节长度为64function 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 pointersuccess := 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 dataoutSize = 0x00;bytes32 outValueFromReturnDataCopy;assembly{outPtr := mload(0x40)  // free memory pointersuccess := staticcall(100000, targetAddr, add(encodedParams, 0x20), mload(encodedParams), outPtr, outSize)returnSize := returndatasize()originalValueOnOutPtr := mload(outPtr)// copy return data (returnSize bytes) with returndatacopy() to the outPtrreturndatacopy(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 dataoutSize = 0x40;bytes32 restBytesInOutSize;assembly{outPtr := mload(0x40)  // free memory pointersuccess := 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));// untouchedassertEq(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 dataoutSize = 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 dataoutSize = 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))));// untouchedassertEq(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 0x20assertEq(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 0x20assertEq(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 0x20assertEq(0x20, uint(originalValueOnOutPtr));// 动态数组长度assertEq(arr.length, uint(secondWordOfOutPtr));// 依次为2个元素的值assertEq(arr[0], uint(thirdWordOfOutPtr));assertEq(arr[1], uint(forthWordOfOutPtr));// 第五个字为untouchedassertEq(0, fifthWordOfOutPtr);}
}

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

在这里插入图片描述

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


http://www.ppmy.cn/news/1017838.html

相关文章

运维作业5

一.基于 CS 7 构建 LVS-DR 群集。 1.lvs安装ipvsadm [rootnode ~]# yum install -y ipvsadm 2.配置lvs虚拟ip&#xff08;vip&#xff09; [rootnode ~]# ifconfig ens32:200 192.168.72.200 netmask 255.255.255.0 up 客户端测试&#xff1a; 3.在两台rs上安装httpd 4.两台rs建…

使用Spring Boot整合Mybatis-Plus

博主主页&#xff1a;一季春秋博主简介&#xff1a;专注Java技术领域和毕业设计项目实战、Java、微信小程序、安卓等技术开发&#xff0c;远程调试部署、代码讲解、文档指导、ppt制作等技术指导。主要内容&#xff1a;毕业设计(Java项目、小程序等)、简历模板、学习资料、面试题…

摄像机折线裁剪算法参考

摄像机折线裁剪算法参考 需要说明的是两点&#xff1a; 一、Plane.Raycast这个方法在参数Ray与Plane是平行状态的时候不会产生交点&#xff0c;返回值为false&#xff0c;同时参数enter赋值为0。如果参数Ray与Plane不是平行状态&#xff0c;那么就一定会产生交点&#xff0c;…

《Python入门到精通》函数详解

「作者主页」&#xff1a;士别三日wyx 「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;小白零基础《Python入门到精通》 函数 1、函数的调用2、函数的参数2.1、变量的就近原则2.2、传递参数2.3、形参和实…

SNAT与DNAT原理

SNAT和DNAT &#xff08;源地址转换和目标地址转换&#xff09; SNAT&#xff1a;源地址转换。内网到外网转换的是源地址。 DNAT&#xff1a;目标地址转换&#xff1a;外网到内网转换的是目的地址 &#xff08;把内部服务器的ip地址转换成一个所有人都可以访问的地址&#xff0…

.netcore grpc一元方法详解

一、grpc服务端搭建 打开visual studio--》新建项目--》创建ASP.NET Core gRPC服务。 这里我是用的.NET 6.0做为底层框架&#xff0c;使用该框架支持grpc的功能更全面。令注使用nuget包Grpc.AspNetCore这里我使用的是2.40.0版本。 // 创建dollar.proto文件syntax "prot…

Linux(进程地址空间)

进程地址空间 程序地址空间进程地址空间 程序地址空间 在Linux环境下&#xff0c;我们可以对上述程序空间地址进行验证&#xff1a; 运行程序&#xff0c;可以看到&#xff0c;我们就可以很好看出程序的地址空间的排布了&#xff1a; 进程地址空间 严格来说&#xff0c;我们…

React实现关键字高亮

先看效果&#xff1a; 实现很简单通过以下这个函数&#xff1a; highLight (text, keyword ) > {return text.split(keyword).flatMap(str > [<span style{{ color: red, fontWeight: bold }}>{keyword}</span>, str]).slice(1);}展示某段文本时调用该函数…

【Docker】AUFS、BTRFS、ZFS、储存池详解

洁洁的个人主页 我就问你有没有发挥&#xff01; 知行合一&#xff0c;志存高远。 前言 Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux或Windows操作系统的机器上,也可以实现虚拟化,容器是…

实战指南:使用OpenCV 4.0+Python进行机器学习与计算机视觉

&#x1f482; 个人网站:【办公神器】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 目录 1.背景2. 安装和配…

JVM:运行时数据区域(白话文)

最近有时间在看一本<深入了解Java虚拟机>的书籍&#xff0c;这本书是一个中国人&#xff0c;名叫周志明的人写的。相比于其他翻译过来的技术书籍&#xff0c;这本书还是挺通俗易懂的。先前有和彬哥在聊&#xff0c;他说如果是自己一个人看的话会很枯燥&#xff0c;很难坚…

〖Python网络爬虫实战㉝〗- aiohttp 的基本使用

订阅:新手可以订阅我的其他专栏。免费阶段订阅量1000+python项目实战 Python编程基础教程系列(零基础小白搬砖逆袭) 说明:本专栏持续更新中,订阅本专栏前必读关于专栏〖Python网络爬虫实战〗转为付费专栏的订阅说明作者:爱吃饼干的小白鼠。Python领域优质创作者,2022年度…

Oracle-创建PDB

Oracle-创建PDB 创建PDB的方式 从PDB$SEED新建PDB克隆已存在的PDB 本地PDB克隆到同一个CDB中将远程PDB克隆到CDB中将非CDB插入或克隆到CDB中通过插拔的方式创建PDB sql 命令语法 条件 CDB必须open并且read write模式连接CDB$ROOT 用户并且具有CREATEPLUGGABLEDATABASE系统权…

人物启示-张一鸣与陆奇

在科技行业中&#xff0c;张一鸣与陆奇可谓是两位颇具影响力的人物。张一鸣和陆奇分别是字节跳动&#xff08;TikTok 的母公司&#xff09;的创始人和百度前总裁。张一鸣作为字节跳动的创始人&#xff0c;成功打造了今日头条、抖音等知名产品&#xff0c;而陆奇则曾任微软副总裁…

2023/08/05【网络课程总结】

1. 查看git拉取记录 git reflog --dateiso|grep pull2. TCP/IP和OSI七层参考模型 3. DNS域名解析 4. 预检请求OPTIONS 5. 渲染进程的回流(reflow)和重绘(repaint) 6. V8解析JavaScript 7. CDN负载均衡的简单理解 8. 重学Ajax 重学Ajax满神 9. 对于XML的理解 大白话叙述XML是…

Spring Boot中整合Redis

博主主页&#xff1a;一季春秋博主简介&#xff1a;专注Java技术领域和毕业设计项目实战、Java、微信小程序、安卓等技术开发&#xff0c;远程调试部署、代码讲解、文档指导、ppt制作等技术指导。主要内容&#xff1a;毕业设计(Java项目、小程序等)、简历模板、学习资料、面试题…

【手动配置ip地址后,电脑仍自动分配ip的问题】

现象 手动给电脑分配了一个ipv4地址&#xff0c;但是电脑会自动分配一个169开头的ipv4&#xff0c;导致虽然可以上网&#xff0c;但访问不了局域网内其他的设备&#xff08;我配置的另一个网关&#xff0c;所以可以上网&#xff09; 原因 ip地址冲突了&#xff0c;把电脑的i…

【深度学习】【风格迁移】Visual Concept Translator,一般图像到图像的翻译与一次性图像引导,论文

General Image-to-Image Translation with One-Shot Image Guidance 论文&#xff1a;https://arxiv.org/abs/2307.14352 代码&#xff1a;https://github.com/crystalneuro/visual-concept-translator 文章目录 Abstract1. Introduction2. 相关工作2.1 图像到图像转换2.2. Di…

JAVA Android 正则表达式

正则表达式 正则表达式是对字符串执行模式匹配的技术。 正则表达式匹配流程 private void RegTheory() {// 正则表达式String content "1998年12月8日&#xff0c;第二代Java平台的企业版J2EE发布。1999年6月&#xff0c;Sun公司发布了第二代Java平台(简称为Java2) &qu…

python的条件判断中的not、is、is not、is not None、is None

目录 1.not A 2.is和is not 3.is not None和is None 4.实例 1.not A 是判断A是否为0、False、空字符串、空列表、空字典、空元组以及None&#xff0c;满足任一条件即返回True 2.is和is not 是不是某种对象 3.is not None和is None None&#xff1a;在Python中是一个单…