前序博客有:
Solidity底层通过SLOAD和SSTORE opcode来控制EVM storage。
Storage为每个合约的持久mapping,具有 2 256 − 1 2^{256}-1 2256−1个32 byte words。当在合约中设置某状态变量值时,其会存储在指定的slot中,其将持续在EVM中,除非被相同类型的其它值覆盖。
当首次加载某storage slot时,其是cold的,意味着需要2100 gas,后续再调用该slot时,其是warm的,仅需100 gas。而Memory更便宜,其低至3 gas(当有memory expansion时,将更贵点)。
举例如下,未优化合约:
contract C {
struct S {
uint256 a;
uint256 b;
address c;
}
S public s;
function foo(uint256 input) external {
// `s.b` is loaded from storage once: warming up the storage!
if (input < s.b) revert;
// Second `s.b` SLOAD with warm storage.
if (s.b > 50) revert;
}
其中s,b从storage中加载了2次。可优化为:创建内存变量来存储s.b值,后续使用该内存变量。原因在于MLOAD比SLOAD便宜。即优化为:
function foo(uint256 input) external {
// Initial storage load to store in memory.
uint256 b = s.b;
// Using MLOAD in comparison operations!
if (input < b) revert;
if (b > 50) revert;
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.6;
contract C {
struct S {
uint16 a; // 2 bytes, 2 bytes total
uint24 b; // 3 bytes, 5 bytes total
address c; // 20 bytes, 25 bytes total + end of slot 0x01
address d; // 20 bytes, slot 0x02
}
// I've noted the storage slots each state is located at.
// A single slot is 32 bytes :)
uint256 boring; // 0x00
S s_struct; // 0x01, 0x02
S[] s_array; // 0x03
mapping(uint256 => S) s_map; // 0x04
constructor() {
boring = 0x288;
s_struct = S({
a: 10,
b: 20,
c: 0xdcD49C36E69bF85FA9c5a25dEA9455602C0B289e,
d: 0x4675C7e5BaAFBFFbca748158bEcBA61ef3b0a263
});
s_array.push(s_struct);
s_array.push(s_struct);
}
function view_boring() external view returns (bytes32) {
bytes32 x;
assembly {
x := sload(0x00)
}
return x;
}
function view_slot(uint256 slot) external view returns(bytes32) {
bytes32 x;
assembly {
x := sload(slot)
}
return x;
}
function view_b() external view returns (uint256) {
bytes32 x;
assembly {
// before: 00000000000000 dcd49c36e69bf85fa9c5a25dea9455602c0b289e 000014 000a
// ^
// after: 0000 00000000000000 dcd49c36e69bf85fa9c5a25dea9455602c0b289e 000014
// ^
let v := shr(0x10, sload(0x01))
// If both characters aren't 0, keep the bit (1). Otherwise, set to 0.
// mask: 0000000000000000000000000000000000000000000000000000000000 FFFFFF
// v: 000000000000000000dcd49c36e69bf85fa9c5a25dea9455602c0b289e 000014
// result: 0000000000000000000000000000000000000000000000000000000000 000014
v := and(0xffffff, v)
// Store in memory bc return uses memory.
mstore(0x40, v)
// Return reads left to right.
// Since our value is far right we can just return 32 bytes from the 64th byte in memory.
x := mload(0x40)
}
return uint256(x);
}
// unused bytes c b a
// before: 00000000000000 dcd49c36e69bf85fa9c5a25dea9455602c0b289e 000014 000a
// unused bytes c b a
// after: 00000000000000 dcd49c36e69bf85fa9c5a25dea9455602c0b289e 0001F4 000a
function set_b(uint24 b) external {
assembly {
// Removing the `uint16` from the right.
// before: 00000000000000 dcd49c36e69bf85fa9c5a25dea9455602c0b289e 000014 000a
// ^
// after: 0000 00000000000000 dcd49c36e69bf85fa9c5a25dea9455602c0b289e 000014
// ^
let new_v := shr(0x10, sload(0x01))
// Create our mask.
new_v := and(0xffffff, new_v)
// Input our value into the mask.
new_v := xor(b, new_v)
// Add back the removed `a` value bits.
new_v := shl(0x10, new_v)
// Replace original 32 bytes' `000014` with `0001F4`.
new_v := xor(new_v, sload(0x01))
// Store our new value.
sstore(0x01, new_v)
}
}
function view_d() external view returns (address) {
return s_array[0].d;
}
// keccak256(array_slot) + var_slot
// keccak256(0x03) + 1
// Remember how `s_struct` takes up 2 slots?
// The `+ 1` indicates the second slot allocation in S
// For the bitpacked slot in S we use don't need the add
// The next element's slot would be `+ 2`
function get_element() external view returns(bytes32) {
bytes32 x;
assembly {
// Store array slot in memory.
mstore(0x0, 0x03)
// Keccak does the MLOAD internally so we give the memory location.
let hash := add(keccak256(0x0, 0x20), 1)
// Store the return value.
mstore(0x0, sload(hash))
// Return `d`.
x := mload(0x0)
}
return x;
}
// 返回s_array数组的长度
function s_arrayLength () public view returns (uint r) {
assembly {
r := sload (3)
}
}
// 从storage中取s_array数组的第i个32字节内容。
function s_arrayElement (uint i) public view returns (bytes32 r) {
assembly {
mstore (0, 3)
r := sload (add (keccak256 (0, 32), i))
}
}
}
当想要访问uint256 boring时,访问方式可为:
assembly {
let x := sload(0x00)
}
所谓bitpacked,是指在单个slot(32 bytes)内存储了多个变量。在本例中,slot 0x01中pack了共25字节内容:
uint16 a (2 bytes).uint24 b (3 bytes).address c (20 bytes).对应s_struct的slots为:
// 0x01 0x00000000000000dcd49c36e69bf85fa9c5a25dea9455602c0b289e000014000a
// 0x02 0x0000000000000000000000004675c7e5baafbffbca748158becba61ef3b0a263
Bitpacked结构体的查询和设置,可参看上面合约中的view_b和set_b。
pragma solidity >=0.7.0 <0.9.0;
contract Foo {
uint internal x; // Storage slot #0
mapping (uint => uint) internal y; // Storage slot #1
uint [] internal z; // Storage slot #2
constructor() {
z.push(8);
z.push(9);
}
// 动态数组的长度
function zLength () public view returns (uint r) {
assembly {
r := sload (2)
}
}
// 动态数组中第i个元素的值
function zElement (uint i) public view returns (uint r) {
assembly {
mstore (0, 2)
r := sload (add (keccak256 (0, 32), i))
}
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract Foo {
uint internal x; // Storage slot #0
mapping (uint => address) internal y; // Storage slot #1
uint [] internal z; // Storage slot #2
constructor() {
y[10] = 0xdcD49C36E69bF85FA9c5a25dEA9455602C0B289e;
y[3] = 0x4675C7e5BaAFBFFbca748158bEcBA61ef3b0a263;
}
// 本例中,参数取(10, 1)或(3, 1)可分别获得y[10]以及y[3]的值
function getStorageValue(uint num, uint slot) public view returns (address result) {
assembly {
// Store num in memory scratch space (note: lookup "free memory pointer" if you need to allocate space)
mstore(0, num)
// Store slot number in scratch space after num
mstore(32, slot)
// Create hash from previously stored num and slot
let hash := keccak256(0, 64)
// Load mapping value using the just calculated hash
result := sload(hash)
}
}
}
bytes和string中的元素在storage中均以ASCII码值表示。
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract Foo {
uint internal x; // Storage slot #0
mapping (uint => address) internal y; // Storage slot #1
uint [] internal z; // Storage slot #2
bytes internal b; // Storage slot #3
string internal s; // Storage slot #4
constructor() {
b = "0123456789012345678901234567890123456789";
s = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
}
// bytes和string中的元素在storage中均以ASCII码值表示。
// bytes长度,本例中,返回值为81
function bLength () public view returns (uint r) {
assembly {
r := sload (3)
}
}
// 返回bytes中第i个32字节值
//i=0,对应返回:0x3031323334353637383930313233343536373839303132333435363738393031
//i=1,对应返回:0x3233343536373839000000000000000000000000000000000000000000000000
function bElement (uint i) public view returns (bytes32 r) {
assembly {
mstore (0, 3)
r := sload (add (keccak256 (0, 32), i))
}
}
// string长度,本例中,返回值为105
function sLength () public view returns (uint r) {
assembly {
r := sload (4)
}
}
// 返回string中的第i个32字节值
//i=0,对应返回:0x6162636465666768696a6b6c6d6e6f707172737475767778797a414243444546
//i=1,对应返回:0x4748494a4b4c4d4e4f505152535455565758595a000000000000000000000000
function sElement (uint i) public view returns (bytes32 r) {
assembly {
mstore (0, 4)
r := sload (add (keccak256 (0, 32), i))
}
}
}
[1] A Low-Level Guide To Solidity’s Storage Management
[2] Layout of State Variables in Storage
[3] How to get access to the storage array through the solidity assembler?
[4] How to get access to the storage mapping through the solidity assembler?
[5] Storage and memory layout of strings
我正在使用i18n从头开始构建一个多语言网络应用程序,虽然我自己可以处理一大堆yml文件,但我说的语言(非常)有限,最终我想寻求外部帮助帮助。我想知道这里是否有人在使用UI插件/gem(与django上的django-rosetta不同)来处理多个翻译器,其中一些翻译器不愿意或无法处理存储库中的100多个文件,处理语言数据。谢谢&问候,安德拉斯(如果您已经在rubyonrails-talk上遇到了这个问题,我们深表歉意) 最佳答案 有一个rails3branchofthetolkgem在github上。您可以通过在Gemfi
我安装了ruby版本管理器,并将RVM安装的ruby实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby。有没有办法让emacs像shell一样尊重ruby的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el
是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s
我想用这两种语言中的任何一种(最好是ruby)制作一个窗口管理器。老实说,除了我需要加载某种X模块外,我不知道从哪里开始。因此,如果有人有线索,如果您能指出正确的方向,那就太好了。谢谢 最佳答案 XCB,X的下一代API使用XML格式定义X协议(protocol),并使用脚本生成特定语言绑定(bind)。它在概念上与SWIG类似,只是它描述的不是CAPI,而是X协议(protocol)。目前,C和Python存在绑定(bind)。理论上,Ruby端口只是编写一个从XML协议(protocol)定义语言到Ruby的翻译器的问题。生
这是我在ActiveAdmin中的自定义页面ActiveAdmin.register_page"Settings"doaction_itemdolink_to('Importprojects','settings/importprojects')endcontentdopara"Text"endcontrollerdodefimportprojectssystem"rakedataspider:import_projects_ninja"para"OK"endendend我想做的是,当我单击“导入项目”按钮时,我想在Controller中执行rake任务。但是我无法访问该方法。可能是什
我正在寻找用于Rails的优质管理插件。似乎大多数现有的插件/gem(例如“restful_authentication”、“acts_as_authenticated”)都围绕着self注册等展开。但是,我正在寻找一种功能齐全的基于管理/管理角色的解决方案——但不是简单地附加到另一个非基于角色的解决方案。如果我找不到,我想我会自己动手......只是不想重新发明轮子。 最佳答案 RyanBates最近做了两个关于授权的railscast(注意身份验证和授权之间的区别;身份验证检查用户是否如她所说的那样,授权检查用户是否有权访问资源
文章目录一基础定义二创建逻辑卷2-1准备物理设备2-2创建物理卷2-3创建卷组2-4创建逻辑卷2-5创建文件系统并挂载文件三扩展卷组和缩减卷组3-1准备物理设备3-2创建物理卷3-3扩展卷组3-4查看卷组的详细信息以验证3-5缩减卷组四扩展逻辑卷4-1检查卷组是否有可用的空间4-2扩展逻辑卷4-3扩展文件系统五删除逻辑卷5-1备份数据5-2卸载文件系统5-3删除逻辑卷5-4删除卷组5-5删除物理卷六LVM逻辑卷缩容6-1缩容注意事项6-2标准缩容步骤一基础定义LVM,LogicalVolumeManger,逻辑卷管理,Linux磁盘分区管理的一种机制,建立在硬盘和分区上的一个逻辑层,提高磁盘分
我为Devise用户和管理员提供了不同的模型。我也在使用Basecamp风格的子域。除了我需要能够以用户或管理员身份进行身份验证的一些Controller和操作外,一切都运行良好。目前我有authenticate_user!在我的application_controller.rb中设置,对于那些只有管理员才能访问的Controller和操作,我使用skip_before_filter跳过它。不幸的是,我不能简单地指定每个Controller的身份验证要求,因为我仍然需要一些Controller和操作才能被用户或管理员访问。我尝试了一些方法都无济于事。看来,如果我移动authentica
我正在根据Rails指南的建议开发Rails应用程序,以创建包含翻译的文件夹树和文件。我的文件夹树与此类似:|-defaults|---es.rb|---en.rb|-models|---book|-----es.rb|-----en.rb|-views|---defaults|-----es.rb|-----en.rb|---books|-----es.rb|-----en.rb|---users|-----es.rb|-----en.rb|---navigation|-----es.rb|-----en.rbconfig/locales/views/books/en.yml中的内容
我在ruby(2.0.0p39474)中执行非常快速的文件访问,并不断收到异常Toomanyopenfiles看过thisthread,here,以及各种其他来源,我很清楚操作系统限制(在我的系统上设置为1024)。我执行此文件访问的代码部分是互斥的,并采用以下形式:File.open(filename,'w'){|f|Marshal.dump(value,f)}其中filename会根据调用该部分的线程快速变化。据我了解,此表单在block后放弃其文件句柄。我可以使用ObjectSpace.each_object(File)验证打开的File对象的数量.这报告最多有100个常驻内