script (⏱,💰)

script (⏱,💰)

NG#6 - 存储布局

ng6 start

这个任务是关于合约状态变量存储的。需要用类似 Solidty 中的 sstore 直接向智能合约的存储中的指定位置写入特定值。

下载任务后,可以看到需要用 storage_write_syscall 按要求在特定 slot 位置写 felt252,2 个参数都是 felt252 类型。

fn write(ref self: ContractState, slot_index: felt252, value: felt252) {
  let storageAddress = slot_index.try_into().unwrap();
  storage_write_syscall(0, storageAddress, value);
}

StarkNet 官方文档Cairo book 都有相关内容的解释。未指明的是哪个函数等同于 sn_keccak,在任务的 workthrough 里也有指引。

在 Cairo 中 slot 地址是变量名计算得到的,不像 Solidity 中是连续的,更方便合约逻辑升级,只需要保证属性命名相同,slot 地址就是相同的。

我采取的方式是本地写测试,通过测试后,print 出 slot 的地址和值,用 starkli invoke write [slot_index] [value] 来解答。这样直接调用合约函数更为简单,就不单独写 hack 合约了。

测试逻辑我使用的是 starknet-foundry,然后用如下代码就可以模拟合约部署,很方便。

use src::contracts::catacombs::{ICatacombsDispatcher, ICatacombsDispatcherTrait};
use snforge_std::{declare, ContractClassTrait};

#[test]
fn test_1() {
    let contract = declare('Catacombs');
    let contract_address = contract.deploy(@ArrayTrait::new()).unwrap();
    let dispatcher = ICatacombsDispatcher { contract_address };
...

felt252#

第一个 entry_code 是个字符串表示的 felt252,在 write 里可直接写,唯一问题是需要打印出来显示成 hex 传入 starkli 命令才能发交易。

u256#

第二个子任务是写入值为 10^40 的 u256。涉及如何写乘方和如何存储 u256。

官方核心库里没看到相关操作符,三方库 alexandria 库里有实现。不过最简单的,10 的 40 次方可以直接写 10000000000000000000000000000000000000000_u256。

u256 分为 low 和 high 两部分存储为 1 个 struct。根据官方文档,struct 中的第一个元素存储在 sn_keccak,然后是 sn_keccak+1。所以这题算出 2 个位置后,提交 2 笔交易分别写入 u256.low 和 u256.high 即可。

map#

第三题是写入 LegacyMap::<u256, bool>,类似 Solidity 的 mapping,文档里有详细说明。如果是 Map,slot 的位置计算方式是 h(...h(h(sn_keccak(variable_name),k_1),k_2),...,k_n)

由于 key 是 u256,需要占 2 个位置,所以得 hash 两次。

hash 使用的函数是 core::pedersen::pedersen

传入的 bool 值对应 felt252 就是 0 和 1,找到 3 个索引位置写入 1 即可。

struct#

#[derive(Copy, Drop, Serde, starknet::Store)]
struct Chest {
    is_open: bool,
    owner: starknet::ContractAddress
}

第四题是写入 Chest 结构体。其实第二题已经知道了 struct 是怎么计算位置的了。这题的唯一问题在于如何把一个 StarkNet 合约地址转为 felt252,我在源码中找到了对应方法。

总结#

这题主要知识点是 StarkNet 的存储布局,不算难,弄懂文档很容易就能完成。

ng6 end

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。