區塊鏈DAPP挖礦模式專案系統開發技術程式碼講解

a1271916008發表於2023-04-19

在本教程的上一節中,我們奠定了旨在管理小貓的所有權的基礎 - 即使它們還不存在!在這一部分中,我們將透過使用我們宣告的儲存專案使我們的託盤能夠建立Kitty來使用這些基礎。稍微分解一下,我們將寫:

  • create_kitty:一個可排程或可公開呼叫的函式,允許帳戶鑄造Kitty。
  • mint():一個輔助功能,用於更新託的儲存專案並執行錯誤檢查,由 呼叫。 create_kitty
  • pallet Events: 使用 FRAME的 #[pallet::event] 屬性。

在本部分結束時,我們將檢查所有內容是否編譯無誤,並使用外部PolkadotJS 應用程式 UI 呼叫我們的 create_kitty 。

公共和私人功能

在我們深入研究之前,瞭解我們將圍繞 Kitty pallet的鑄幣和所有權管理功能進行編碼的pallet設計決策是非常重要。

作為開發人員,我們希望確保我們編寫的程式碼高效且優雅。 通常,針對一個進行最佳化會針對另一個進行最佳化。 我們將設定pallet以最佳化兩者的方式是將“繁重”邏輯分解為私有輔助函式。 這也提高了程式碼的可讀性和可重用性。 正如我們將看到的,我們可以建立可以由多個可排程函式呼叫的私有函式,而不會影響安全性。 事實上,以這種方式構建可以被認為是一種附加的安全功能。 檢視這個關於編寫和使用輔助函式的操作指南以瞭解更多資訊。

在開始實施這種方法之前,讓我們首先描繪一下組合可排程和輔助函式的樣子。

create_kitty是一個可排程的功能或外在功能::

  • 檢查源是否已簽名
  • 使用簽名帳戶生成隨機雜湊
  • 使用隨機雜湊建立新的 Kitty 物件
  • 呼叫私有函式 mint()

mint是一個私有助手函式,它:

  • 檢查小貓咪是否不存在
  • 使用新的 Kitty ID 更新儲存(適用於所有 Kitty 和所有者的帳戶)
  • 更新儲存和新所有者帳戶的新小貓總數
  • 存入一個事件,以指示已成功建立小貓

編寫可排程 create_kitty

FRAME 中的可排程始終遵循相同的結構。 所有的pallet dispatchable 都存在於#[pallet::call] 宏下,該宏需要使用impl<T: Config> Pallet<T> {} 宣告dispatchables 部分。 閱讀有關這些 FRAME 宏的檔案以瞭解它們的工作原理。 這裡我們需要知道的是,它們是 FRAME 的一個有用功能,可以最大限度地減少為將pallet正確整合到 Substrate 鏈的runtime所需編寫的程式碼。

權重

根據其檔案中描述的#[pallet::call] 的要求,每個可排程函式都必須具有關聯的權重。 權重是使用 Substrate 開發的重要部分,因為它們提供了圍繞計算量的安全防護,以在執行時適合塊。

Substrate 的加權系統迫使開發人員在呼叫每個外部函式之前考慮其計算複雜性。 這允許節點考慮最壞情況的執行時間,避免因外部因素導致網路滯後,這些外部因素可能需要比指定的塊時間更長的時間。 權重也與任何已簽名外在的收費系統密切相關。

由於這只是一個教程,我們將預設所有權重為 100 以保持簡單。

假設您現在已經用本節的幫助檔案替換了pallets/kitties/src/lib.rs 的內容,找到ACTION #1 並使用以下行完成函式的開頭:

#[pallet::weight(100)]pub fn create_kitty(origin: OriginFor<T>) -> DispatchResult {
    let sender = ensure_signed(origin)?; // <- add this line
    let kitty_id = Self::mint(&sender, None, None)?; // <- add this line
    // Logging to the console
    log::info!("A kitty is born with ID: {:?}.", kitty_id); // <- add this line
    // ACTION #4: Deposit `Created` event
    Ok(())}

我們不會進行除錯,但登入到控制檯是一個有用的提示,可確保您的託盤按預期執行。 為了使用 log::info,將其新增到您的託盤的 Cargo.toml 檔案中。

編寫函式 mint()

正如我們在上一節中編寫 create_kitty時所看到的,我們需要建立 mint(),以便將我們新的唯Kitty物件寫入本教程第二部分中宣告的各種儲存項。

讓我們直接開始吧。我們的 mint()函式將採用以下引數:

  • owner: &T::AccountId
  • dna:   Option<[u8; 16]>
  • genderOption<Gender>

它將返回 Result<T::Hash, Error<T>>

貼上以下程式碼片段以編寫 mint函式,取代工作程式碼庫中的ACTION #2:

// Helper to mint a Kitty.pub fn mint(
    owner: &T::AccountId,
    dna: Option<[u8; 16]>,
    gender: Option<Gender>,) -> Result<T::Hash, Error<T>> {
    let kitty = Kitty::<T> {
        dna: dna.unwrap_or_else(Self::gen_dna),
        price: None,
        gender: gender.unwrap_or_else(Self::gen_gender),
        owner: owner.clone(),
    };
    let kitty_id = T::Hashing::hash_of(&kitty);
    // Performs this operation first as it may fail
    let new_cnt = Self::count_for_kitties().checked_add(1)
        .ok_or(<Error<T>>::CountForKittiesOverflow)?;
    // Check if the kitty does not already exist in our storage map
    ensure!(Self::kitties(&kitty_id) == None, <Error<T>>::KittyExists);
    // Performs this operation first because as it may fail
    <KittiesOwned<T>>::try_mutate(&owner, |kitty_vec| {
        kitty_vec.try_push(kitty_id)
    }).map_err(|_| <Error<T>>::ExceedMaxKittyOwned)?;
    <Kitties<T>>::insert(kitty_id, kitty);
    <CountForKitties<T>>::put(new_cnt);
    Ok(kitty_id)}

讓我們來看看上面的程式碼在做什麼。

我們正在做的第一件事就是建立一個新的Kitty物件。然後,我們使用基於kitty當前屬性的雜湊函式建立一個一的 kitty_id

接下來,我們使用儲存獲取器函式 Self::count_for_kitties()增加 CountForKitties。我們還在檢查 check_add()函式的溢位。

我們最後的驗證是確保 kitty_id 不存在於我們的 Kitties StorageMap 中。 這是為了避免任何可能的雜湊鍵重複插入。

一旦我們的支票透過,我們就會透過以下方式更新我們的儲存專案:

  1. 利用 更新小貓的主人vector。
  2. 使用 Substrate的StorageMap API提供的方法,用於儲存實際的Kitty物件並將其與其 kitty_id相關聯。
  3. 使用 由StorageValue API提供,用於儲存最新的Kitty計數。

快速回顧我們的儲存專案

  • <Kitties<T>>:透過儲存Kitty物件並將其與其Kitty ID相關聯,儲存Kitty的獨特特徵和價格。
  • <KittyOwned<T>>:跟蹤哪些帳戶擁有哪些Kitties。
  • <CountForKitties<T>>:所有現存小貓的計數。

實施pallet Events

我們的託盤還可以在功能結束時發出 。這不僅報告了函式執行的成功,還告訴“鏈外世界”發生了一些特定的狀態轉換。

FRAME幫助我們使用 屬性。使用FRAME宏,事件只是一個像這樣宣告的列舉:

#[pallet::event]#[pallet::generate_deposit(pub(super) fn deposit_event)]pub enum Event<T: Config>{
    /// A function succeeded. [time, day]
    Success(T::Time, T::Day),}

正如您在上面的程式碼段中看到的,我們使用屬性宏:

#[pallet::generate_deposit(pub(super) fn deposit_event)]

這使我們能夠使用以下模式來存放特定事件:

Self::deposit_event(Event::Success(var_time, var_day));

為了在託盤中使用事件,我們需要在託盤的配置特徵 Config中新增一個新的關聯型別 Event。此外,就像在將任何型別新增到託盤的 Config特徵時一樣,我們還需要在執行時 runtime/src/lib.rs中定義它。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69983064/viewspace-2946763/,如需轉載,請註明出處,否則將追究法律責任。

相關文章