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