引言
dent_list是sui-framework实现的一个拒绝名单列表,用于进行对sui核心类型的地址访问控制。它仅能由系统事务创建,是share obj,本文通过介绍deny_list的实现与使用,理解在sui move中实现访问控制的思想
deny_list
首先来看实现deny_list使用了哪些库
use sui::table::{Self, Table};
use sui::bag::{Self, Bag};
use sui::vec_set::{Self, VecSet};
table、bag、vec_set在前面的文章都有分析
结构体
struct DenyList has key {
id: UID,
lists: Bag,
}
struct PerTypeList has key, store {
id: UID,
denied_count: Table<address, u64>,
denied_addresses: Table<vector<u8>, VecSet<address>>,
}
拒绝名单的构成可以抽象为
/ address 1
type 1 ...
/ \ address n
TypeList 1 ...
\ / address 1
type n ...
\ address n
/
DenyList ...
\
/ address 1
type 1 ...
/ \ address n
TypeList n ...
\ / address 1
type n ...
\ address n
DenyList中bag:键为TypeList索引,值为相应的TypeList
TypeList中denied_count:用来快速检索一个地址是否在拒绝列表中
denied_addresses:用来储存拒绝列表 键为限制类型 用VecSet储存限制被限制的地址
添加/删除
public(friend) fun add(
deny_list: &mut DenyList,
per_type_index: u64,
type: vector<u8>,
addr: address,
) {
per_type_list_add(bag::borrow_mut(&mut deny_list.lists, per_type_index), type, addr)
}
fun per_type_list_add(
list: &mut PerTypeList,
type: vector<u8>,
addr: address,
) {
if (!table::contains(&list.denied_addresses, type)) {
table::add(&mut list.denied_addresses, type, vec_set::empty());
};
let denied_addresses = table::borrow_mut(&mut list.denied_addresses, type);
let already_denied = vec_set::contains(denied_addresses, &addr);
if (already_denied) return;
vec_set::insert(denied_addresses, addr);
if (!table::contains(&list.denied_count, addr)) {
table::add(&mut list.denied_count, addr, 0);
};
let denied_count = table::borrow_mut(&mut list.denied_count, addr);
*denied_count = *denied_count + 1;
}
- 先通过对应的per_type_index找到储存在DenyList中的对应TypeList
- 检查denied_addresses table中是否存在type类型的键 如果没有添加一个键为type类型的新的table
- 检查待添加的地址是否已经在拒绝列表中,如果存在直接返回
- 如果待添加地址还不存在 将其插入
- 检查此地址之前是否存在于拒绝列表,如果没有创建在denied_count table中添加相应的字段
- 将其denied_count加1
public(friend) fun remove(
deny_list: &mut DenyList,
per_type_index: u64,
type: vector<u8>,
addr: address,
) {
per_type_list_remove(bag::borrow_mut(&mut deny_list.lists, per_type_index), type, addr)
}
fun per_type_list_remove(
list: &mut PerTypeList,
type: vector<u8>,
addr: address,
) {
let denied_addresses = table::borrow_mut(&mut list.denied_addresses, type);
assert!(vec_set::contains(denied_addresses, &addr), ENotDenied);
vec_set::remove(denied_addresses, &addr);
let denied_count = table::borrow_mut(&mut list.denied_count, addr);
*denied_count = *denied_count - 1;
if (*denied_count == 0) {
table::remove(&mut list.denied_count, addr);
}
}
- 先通过对应的per_type_index找到储存在DenyList中的对应TypeList
- 根据type从denied_addresses table中取出相应的VecSet,检查待移除地址是否在其中
- 将denied_count table中对应地址的denied_count减去1
- 如果对应地址的denied_count成为0,说明他不存在于任任何拒绝名单中了,将其从denied_count table中移除
检验存在
public(friend) fun contains(
deny_list: &DenyList,
per_type_index: u64,
type: vector<u8>,
addr: address,
): bool {
per_type_list_contains(bag::borrow(&deny_list.lists, per_type_index), type, addr)
}
fun per_type_list_contains(
list: &PerTypeList,
type: vector<u8>,
addr: address,
): bool {
if (!table::contains(&list.denied_count, addr)) return false;
let denied_count = table::borrow(&list.denied_count, addr);
if (*denied_count == 0) return false;
if (!table::contains(&list.denied_addresses, type)) return false;
let denied_addresses = table::borrow(&list.denied_addresses, type);
vec_set::contains(denied_addresses, &addr)
}
- 先通过对应的per_type_index找到储存在DenyList中的对应TypeList
- 首先检查addr作为键是否存在于denied_count,如果没有,说明它不存在于任何type的拒绝列表中,返回false
- 再检查type作为键是否存在于denied_addresses table中,返回false
- 检查待检测addr是否存在于对应的VecSet中,存在返回true,否则返回false
总结
在本文中,介绍了sui-framework中的拒绝名单列表(deny_list)的实现与使用方法。该功能用于进行对sui核心类型的地址访问控制。希望有所帮助。
Move语言学习交流QQ群: 79489587
Sui官方中文开发者电报群: https://t.me/sui_dev_cn