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

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。