script (⏱,💰)

script (⏱,💰)

NG#6 - 儲存佈局

這個任務涉及到合約狀態變量存儲。需要使用類似 Solidity 中的 sstore 直接將特定值寫入智能合約存儲的指定位置。

下載任務後,可以看到需要使用 storage_write_syscall 按要求在特定的 slot 位置寫入 felt252,這兩個參數都是 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 函數中可以直接寫入,唯一的問題是需要打印出來以十六進制形式傳入 starkli 命令才能發送交易。

u256#

第二個子任務是寫入值為 10^40 的 u256。涉及到如何計算乘方和如何存儲 u256。

官方核心庫中沒有看到相關的操作符,三方庫 alexandria 中有實現。不過最簡單的方法是直接寫入 10000000000000000000000000000000000000000_u256。

u256 分為 low 和 high 兩部分存儲,作為一個 struct。根據官方文檔,struct 中的第一個元素存儲在 sn_keccak,然後是 sn_keccak+1。所以,計算出兩個位置後,提交兩筆交易分別寫入 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,需要佔用兩個位置,所以需要對其進行兩次哈希。

哈希使用的函數是 core::pedersen::pedersen

傳入的 bool 值對應 felt252 就是 0 和 1,找到三個索引位置並寫入 1 即可。

struct#

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

第四個任務是寫入 Chest 結構體。其實,第二個任務已經知道了 struct 是如何計算位置的。這個問題的唯一問題在於如何將一個 StarkNet 合約地址轉為 felt252,我在源碼中找到了相應的方法。

總結#

這個問題的主要知識點是 StarkNet 的存儲布局,不算難,只要弄懂文檔就能輕鬆完成。

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