Substrate 区块链-存储结构体

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

自定义结构体

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

#[derive(Encode, Decode, Default, Clone, PartialEq)]
#[cfg_attr(feature = "std", derive(Debug))]
pub struct MyStruct {
    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 as Example {
        MyItem: map T::AccountId => MyStruct;
    }
}

我们还注意到在定义结构体的前面有#[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 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,
            };

            >::insert(sender, new_struct);
            Ok(())
        }
    }
}

可以看到,在这个函数create_struct中,我们创建一个new_struct的结构体实例,并将其中的some_numbersome_generic,和some_other_generic进行了赋值。然后使用>::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中增加一个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 {
    subject: u32,
    when: Timestamp,
    by: AccountId
}

decl_storage! {
    trait Store for Module as VerifiableCreds {
        SubjectCount: u32;
        Subjects: map u32 => T::AccountId;
        Credentials get(credentials): map (T::AccountId, u32) => Credential;
    }
}


decl_module! {
    pub struct Module for enum Call where origin: T::Origin {

        fn create_subject(origin) -> Result {
            let sender = ensure_signed(origin)?;
            let subject = >::get();

            >::put(subject + 1);
            >::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: >::get(),
                by: sender,
            };

            >::insert((to, subject), new_cred);

            Ok(())
        }
    }
}

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

fn issue_credential(origin, to: T::AccountId, subject: u32) -> Result {
            let sender = ensure_signed(origin)?;

            let new_cred = Credential {
                subject: subject,
                when: >::get(),
                by: sender,
            };

            >::insert((to, subject), new_cred);

            Ok(())
        }

其中,我们使用>::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"
    }
}
Substrate 区块链-存储结构体_第1张图片
image.png

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

Substrate 区块链-存储结构体_第2张图片
Issuing a Credential

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

Substrate 区块链-存储结构体_第3张图片
Viewing a Credential

你可能感兴趣的:(Substrate 区块链-存储结构体)