在 Substrate 中,是否有一种方法可以在一个自定义模块中使用另一个自定义模块的存储和函数?

8

我看过 Substrate 教程中有关创建单独 Substrate 运行时模块的教程,链接在这里,以便重复使用功能。但我想知道是否有一种方法让一个自定义模块可以访问另一个自定义模块的存储或函数?

大致如下:

/// In ModuleA

    pub type IndexType = u64;

    decl_storage! {
        trait Store for Module<T: Trait> as ModuleA {
                pub MyIndexCount get(my_index_count): Option<IndexType>;
        }
    }

在ModuleB中,我需要做什么才能使用/包含ModuleA的功能,以及如何调用它?

/// In ModuleB

    decl_module! {
    pub struct Module<T: Trait> for enum Call where origin: T::Origin {
        fn deposit_event<T>() = default;

        pub fn edit_index(origin) -> Result {
            let sender = ensure_signed(origin)?;

            // --->>>> I want to read some storage from ModuleA whilst inside ModuleB
            let c: IndexType = ReadStorageFromModuleA >>> my_index_count().ok_or("Storage Read Error: cannot get index")?;

            // change storage in ModuleA from ModuleB
            WriteToStorageInModuleA <MyIndexCount<T>>::put(&c + 1);

            Ok(())
            }
        }
    }    

这与以下内容相关:https://stackoverflow.com/questions/56599293/what-is-the-purpose-of-pub-in-decl-storage - Shawn Tabrizi
你在问题中标记了 substrate,是否有兴趣参与一个专门针对 Substrate、Polkadot 等的 Stack Exchange 问答网站?请查看 Area51 Substrate Proposal - q9f
@T9b,请您检查并支持 Substrate StackExchange 提案:https://area51.stackexchange.com/proposals/126136 - Shawn Tabrizi
1个回答

10

如果您正在构建一个具有直接依赖关系的模块(module2)到另一个模块(module1),则必须在module2的trait定义中继承module1的trait:

pub trait Trait: module1::Trait {
    ...
}

要从module1中的公共存储空间访问module2中的存储项,需要执行以下操作:
  • 导入相应的存储特征以访问存储API:StorageValue、StorageMap等。
  • 通过module1的存储类型访问公共存储空间。
    • <module1::Something<T>>::get()
    • <module1::Something<T>>::put()
    • 等等...
要在module2中访问module1中的其他公共函数,需要使用Module类型:
<module1::Module<T>>::public_function();

这是两个模块互相作用的简单例子:

module1.rs

请注意,此模块中所有事物都被标记为公共(pub

use support::{decl_module, decl_storage, StorageValue};

pub trait Trait: system::Trait {}

decl_storage! {
    trait Store for Module<T: Trait> as TemplateModule {
        pub Something: u32;
    }
}

decl_module! {
    pub struct Module<T: Trait> for enum Call where origin: T::Origin {
    }
}

impl<T: Trait> Module<T> {
    pub fn get_value() -> u32 {
        <Something<T>>::get()
    }
}

module2.rs

use support::{decl_module, decl_event, StorageValue, dispatch::Result};
use system::ensure_signed;

use crate::module1;

pub trait Trait: module1::Trait {
    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
}

decl_module! {
    /// The module declaration.
    pub struct Module<T: Trait> for enum Call where origin: T::Origin {
        fn deposit_event<T>() = default;

        pub fn get_value_directly(origin) -> Result {
            let who = ensure_signed(origin)?;
            let value = <module1::Something<T>>::get();
            Self::deposit_event(RawEvent::ValueIs(value, who));
            Ok(())
        }

        pub fn set_value_directly(origin, value: u32) -> Result {
            let _ = ensure_signed(origin)?;
            <module1::Something<T>>::put(value);
            Ok(())
        }

        pub fn get_value_public_function(origin) -> Result {
            let who = ensure_signed(origin)?;
            let value = <module1::Module<T>>::get_value();
            Self::deposit_event(RawEvent::ValueIs(value, who));
            Ok(())
        }
    }
}

decl_event!(
    pub enum Event<T> where <T as system::Trait>::AccountId {
        ValueIs(u32, AccountId),
    }
);

如果我理解正确的话,从另一个模块与存储进行交互有两种可能性,一是在原始模块中包装为公共函数,然后调用该函数,二是直接调用,前提是存储也声明为公共。这两种方式中是否有推荐的模式或者两种方式都可以接受? - T9b
1
我认为公开函数绝对是正确的选择。一般来说,我不想公开太多的公共存储项,因为这会让其他模块直接干扰你的存储,并以你不希望的方式进行操作。公共函数作为外部逻辑的API,可以安全地访问和修改你的存储。 - Shawn Tabrizi
@ShawnTabrizi 我不确定为什么,我正在尝试使用您建议的完全相同的方法,但这与使用托盘有什么关系呢? - Afeez Aziz
对不起,我不明白你在这里在问什么。 你想要打开一个新问题吗? - Shawn Tabrizi
这段代码展示了如何使用紧耦合来耦合托盘,但更好的方法是使用松耦合 - Luke Schoen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接