Tightly- 和 Loosely-Coupled Pallets
check-membership crate包含了两个pallet去用稍微不同的方式去解决同一个问题。两个pallet都实现了一个单独的只能由访问控制列表(以下简称为ACL)里的caller成员执行的可调度函数。维护ACL的工作被抽象包装为另一个pallet,这个pallet和membership-managing pallet可以以两种方式来耦合,下面我们用tight和loose这两个变体pallet来演示这两种耦合方式。
孪生Pallets
在观察pallet代码之前,我们先来讲讲在pallets/check-membership
目录下crate中的结构。这个目录文件夹是用来独立存放两个pallet的crate。这两个pallet被分别存放在pallets/check-membership/tight
和pallets/check-membership/loose
目录下。在这个crate的主要文件lib.rs
,我们简单地引入了各个变体pallet。
pub mod loose;
pub mod tight;
这一步骤让我们能够演示这两种耦合技术,同时把相关联的工程放在同一个crate。
控制访问
虽然使用这些孪生pallet主要目的是用来学习它们如何在membership-managing pallets中耦合,但是它们也体现了一个ACL的概念,这是我们先要关注学习的。
我们经常把一些函数指定为已授权的(permissioned),因此,这些函数只能被一个已被定义授权的用户组执行。在这个pallet,我们检查函数的caller是否符合已授权集合的成员。
loosely变体看上去像是这样:
/// Checks whether the caller is a member of the set of Account Ids provided by the
/// MembershipSource type. Emits an event if they are, and errors if not.
/// 翻译:检查这个caller是不是通过MembershipSource type所提供的账户身份集合的其中一个成员。如果是的话发送事件,否则就报错。
fn check_membership(origin) -> DispatchResult {
let caller = ensure_signed(origin)?;
// Get the members from the vec-set pallet
// 翻译:从vec-set pallet中得到成员组
let members = T::MembershipSource::accounts();
// Check whether the caller is a member
// 翻译:检查caller是不是一个成员
ensure!(members.contains(&caller), Error::<T>::NotAMember);
// If the previous call didn't error, then the caller is a member, so emit the event
// 翻译:如果先前的呼叫(call)没有发生错误,那么这个caller是个成员,因此发送事件
Self::deposit_event(RawEvent::IsAMember(caller));
Ok(())
}
耦合Pallets
事实上,每个check-membership
pallet都包含了非常少的逻辑,它并不存储自己的数据,而由一个单独的外在(extrinsic)来检查会员数据。所有繁重的工作都被抽象包装为pallet。存在着两种不同的让一个pallet与另一个pallet耦合的方式,下面的部分将会研究这两种方式。
紧耦合(Tight Coupling)
紧耦合比松耦合更容易。当你为了把一些其他pallet作为依赖来进行紧耦合而去写pallet的时候,你明确指定你所要依赖的pallet的名字来作为你正在编写的pallet的配置trait所绑定的trait。现在将展示在紧耦合下的变体check-membership
。
pub trait Config: frame_system::Config + vec_set::Trait {
// --snip--
}
这个pallet以及所有的pallet都与
frame_system
紧耦合
提供这个trait绑定意味着check-membership
的紧耦合只可以安装在runtime中,而runtime中也安装着vec-set
pallet。我们也可以在pallet的Cargo.toml
文件中发现紧耦合,其中指定了vec-sec的名称。
vec-set = { path = '../vec-set', default-features = false }
为了真正得到集合下的成员,我们需要定义getter函数这样一种途径。
// Get the members from the vec-set pallet
// 翻译:从vec-set pallet中得到成员组
let members = vec_set::Module::<T>::members();
虽然紧耦合pallets在概念上很简单,但是它存在着缺点,即它由一个特定的实现而不是一个抽象接口来决定。这使得随着时间的推移,代码变得更难以维护,这是我们所不能接受的。check-membership
的紧耦合版本完全由vec-set
pallet来决定而不是由例如管理一系列账户等行为来决定。
松耦合(Loose Coupling)
松耦合解决了耦合特定实现的问题。当向其他pallet松耦合时,你需要在pallet的配置trait增加一个关联类型,并且确保你所提供的类型通过指定了一个trait绑定而实现了必要的行为。
pub trait Config: frame_system::Config {
// --snip--
/// A type that will supply a set of members to check access control against
/// 翻译:一个能够提供一组成员来再次检查访问控制的类型
type MembershipSource: AccountSet<AccountId = Self::AccountId>;
}
整个生态中的许多pallet都通过
Currency
trait而耦合在一起
拥有了这个关联类型意味着变体check-membership
pallet的松耦合可以安装在任何runtime,这个变体可以提供一组账户去当作ACL来使用。AccountSet
trait的代码存放在traits/account-set/src/lib.rs
目录下,而且非常短。
pub trait AccountSet {
type AccountId;
fn accounts() -> BTreeSet<Self::AccountId>;
}
我们也可以看到松耦合在pallet的Cargo.toml
文件下,其中还列出了account-set
。
account-set = { path = '../../traits/account-set', default-features = false }
为了真正得到一组成员,我们需要调用trait提供的accounts
方法。
// Get the members from the vec-set pallet
// 翻译:从vec-set pallet中得到成员组
let members = T::MembershipSource::accounts();