美文网首页
Substrate 区块链-存储结构体

Substrate 区块链-存储结构体

作者: SeanC52111 | 来源:发表于2019-11-17 14:58 被阅读0次

    上一篇blog我们介绍了如何在Substrate区块链中储存和读取u32类型的数和map类型的映射。接下来我们尝试如何在Substrate中定义结构体,并读取和储存结构体。

    自定义结构体

    在Substrate中自定义结构体可以用以下代码:

    #[derive(Encode, Decode, Default, Clone, PartialEq)]
    #[cfg_attr(feature = "std", derive(Debug))]
    pub struct MyStruct<A, B> {
        some_number: u32,
        some_generic: A,
        some_other_generic: B,
    }
    

    注意,这里的derive中出现了Encode和Decode traits (traits可以看作在rust语言中类似于Java的Interface), 所以我们需要将parity_codec 导入到文件中:

    use parity_codec::{Encode, Decode};
    

    在以上定义结构体的代码中,我们使用了Generic(泛型),泛型可以让我们抽象出类似的操作而不用考虑具体类型。这里,AB即为在结构体中需要使用的泛型。如果我们想在some_generic中使用Moment类型,在 some_other_generic中使用Hash类型,那么哦我们可以用以下定义来实现:

    decl_storage! {
        trait Store for Module<T: Trait> as Example {
            MyItem: map T::AccountId => MyStruct<T::Moment, T::Hash>;
        }
    }
    

    我们还注意到在定义结构体的前面有#[derive(...)]语句。这是Rust编译器提供的特性,它可以引入一些基础traits的实现。同时,#[cfg_attr(feature = "std", derive(Debug))]是对于Debug trait的基础实现。详情可以阅读derive

    在Module函数中使用结构体

    我们已经在decl_storage!中声明了一个map结构,key为AccountId,value是自定义的MyStruct,下面就可以为其进行赋值和更新。
    以下代码为创建一个结构体并赋值的一个模块函数:

    decl_module! {
        pub struct Module<T: Trait> for enum Call where origin: T::Origin {
            fn create_struct(origin, value: u32, moment: T::Moment, hash: T::Hash) -> Result {
                let sender = ensure_signed(origin)?;
    
                let new_struct = MyStruct {
                    some_number: value,
                    some_generic: moment,
                    some_other_generic: hash,
                };
    
                <MyItem<T>>::insert(sender, new_struct);
                Ok(())
            }
        }
    }
    

    可以看到,在这个函数create_struct中,我们创建一个new_struct的结构体实例,并将其中的some_numbersome_generic,和some_other_generic进行了赋值。然后使用<MyItem<T>>::insert(sender, new_struct);为mapMyItem增加新的键值对:sender, new_struct

    例子

    承接上一个blog https://www.jianshu.com/p/42657bc89df4 的例子,下面我们依照上述方法,对每个(account, subject)增加一个Credential结构体。
    一个Credential中有以下属性:

    • subject : u32
    • when : Timestamp
    • by : AccountId

    Module<T: Trait>中增加一个issue_credential()函数,在此函数中使用Credential类型创建new_cred ,并将其键入到runtime存储中。

    use support::{decl_storage, decl_module, StorageValue, StorageMap, dispatch::Result};
    use system::ensure_signed;
    use runtime_primitives::traits::{As, Hash};
    use parity_codec::{Encode, Decode};
    
    pub trait Trait: system::Trait + timestamp::Trait {}
    
    #[derive(Encode, Decode, Default, Clone, PartialEq)]
    #[cfg_attr(feature = "std", derive(Debug))]
    pub struct Credential<Timestamp, AccountId> {
        subject: u32,
        when: Timestamp,
        by: AccountId
    }
    
    decl_storage! {
        trait Store for Module<T: Trait> as VerifiableCreds {
            SubjectCount: u32;
            Subjects: map u32 => T::AccountId;
            Credentials get(credentials): map (T::AccountId, u32) => Credential<T::Moment, T::AccountId>;
        }
    }
    
    
    decl_module! {
        pub struct Module<T: Trait> for enum Call where origin: T::Origin {
    
            fn create_subject(origin) -> Result {
                let sender = ensure_signed(origin)?;
                let subject = <SubjectCount<T>>::get();
    
                <SubjectCount<T>>::put(subject + 1);
                <Subjects<T>>::insert(subject, sender);
    
                Ok(())
            }
            // NOTE: We added a new function
            fn issue_credential(origin, to: T::AccountId, subject: u32) -> Result {
                let sender = ensure_signed(origin)?;
    
                let new_cred = Credential {
                    subject: subject,
                    when: <timestamp::Module<T>>::get(),
                    by: sender,
                };
    
                <Credentials<T>>::insert((to, subject), new_cred);
    
                Ok(())
            }
        }
    }
    

    在上述代码例子中,可以看到Credential结构体被声明为pub类型,在decl_storage!macro中添加了Credentials get(credentials): map (T::AccountId, u32) => Credential<T::Moment, T::AccountId>;map映射。在Module<T: Trait>中,添加了新的函数issue_credential:

    fn issue_credential(origin, to: T::AccountId, subject: u32) -> Result {
                let sender = ensure_signed(origin)?;
    
                let new_cred = Credential {
                    subject: subject,
                    when: <timestamp::Module<T>>::get(),
                    by: sender,
                };
    
                <Credentials<T>>::insert((to, subject), new_cred);
    
                Ok(())
            }
    

    其中,我们使用<timestamp::Module<T>>::get()来获取当前的timestamp。最后将new_cred添加到map中。

    与区块链交互并查看储存的结构体

    我们已经将新的结构体与其对应的交互函数添加到了Substrate runtime当中,下面我们需要与区块链进行交互并查看程序逻辑是否正确。

    在启动节点前,我们仍然需要首先编译,并清除之前的区块链数据:

    ./scripts/build.sh
    cargo build --release
    ./target/release/substrate-verifiable-credentials purge-chain --dev
    ./target/release/substrate-verifiable-credentials --dev
    

    因为我们在区块链中引入了新的结构体,Polkadots-JS UI可以根据用户自定义JSON文件来反序列化结构体数据。

    在Polkadots中登记定制的结构体

    Polkadots UI提供简单的方式来引入新的结构体,这样页面就可以反序列化结构体数据以便呈现给终端用户。

    进入Setting, 选择Developer tab,将以下JSON文件提交:

    {
        "Credential": {
            "subject": "u32",
            "when": "Moment",
            "by": "AccountId"
        }
    }
    
    image.png

    然后,我们可以调用issueCredentials函数。进入Extrinsic,选择VerifiableCredts>issueCredentials(AccountId, u32)。这里会要求用户输入给subject发放Credential对应的AccountId,和与之对应的subject id。

    Issuing a Credential

    最后,我们需要在Chain State中查看我们之前存储的credential对象。首先进入Chain State,然后选择VerifiableCreds > credentials((AccountId, u32)): Credential,之后可以选择对应持有Credential的账户:Bob,和对应subject:0。点击蓝色+号,即可看到我们存储的Credential的值。可以看到,发放给Bob的Credential对应的对象为0,时间是1573982250,发放者为ALICE的地址。

    Viewing a Credential

    相关文章

      网友评论

          本文标题:Substrate 区块链-存储结构体

          本文链接:https://www.haomeiwen.com/subject/wappictx.html