From 6719ace59a69dc7ee51e94e2f58845f9e933144b Mon Sep 17 00:00:00 2001 From: Manolis Liolios Date: Mon, 1 Jul 2024 14:16:31 +0300 Subject: [PATCH] Removes managed names (#146) --- packages/managed_names/Move.lock | 36 -- packages/managed_names/Move.toml | 11 - packages/managed_names/sources/managed.move | 218 ------------ .../managed_names/tests/managed_tests.move | 330 ------------------ 4 files changed, 595 deletions(-) delete mode 100644 packages/managed_names/Move.lock delete mode 100644 packages/managed_names/Move.toml delete mode 100644 packages/managed_names/sources/managed.move delete mode 100644 packages/managed_names/tests/managed_tests.move diff --git a/packages/managed_names/Move.lock b/packages/managed_names/Move.lock deleted file mode 100644 index 76fd3001..00000000 --- a/packages/managed_names/Move.lock +++ /dev/null @@ -1,36 +0,0 @@ -# @generated by Move, please check-in and do not edit manually. - -[move] -version = 0 -manifest_digest = "647A8DC32832639A4EE349194470C0ABB724BD48910C44488CA7F5201FE50EE0" -deps_digest = "3C4103934B1E040BB6B23F1D610B4EF9F2F1166A50A104EADCF77467C004C600" - -dependencies = [ - { name = "Sui" }, - { name = "suins" }, -] - -[[move.package]] -name = "MoveStdlib" -source = { git = "https://github.com/MystenLabs/sui.git", rev = "framework/mainnet", subdir = "crates/sui-framework/packages/move-stdlib" } - -[[move.package]] -name = "Sui" -source = { git = "https://github.com/MystenLabs/sui.git", rev = "framework/mainnet", subdir = "crates/sui-framework/packages/sui-framework" } - -dependencies = [ - { name = "MoveStdlib" }, -] - -[[move.package]] -name = "suins" -source = { local = "../suins" } - -dependencies = [ - { name = "Sui" }, -] - -[move.toolchain-version] -compiler-version = "1.23.1" -edition = "2024.beta" -flavor = "sui" diff --git a/packages/managed_names/Move.toml b/packages/managed_names/Move.toml deleted file mode 100644 index 29624359..00000000 --- a/packages/managed_names/Move.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "managed_names" -version = "0.0.1" -edition = "2024.beta" - -[dependencies] -Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/mainnet", override=true } -suins = { local = "../suins" } - -[addresses] -managed_names = "0x0" diff --git a/packages/managed_names/sources/managed.move b/packages/managed_names/sources/managed.move deleted file mode 100644 index 520d09a1..00000000 --- a/packages/managed_names/sources/managed.move +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - - -/// A simple wrapper to allow having a `third-party` managed `SuinsRegistration` object -/// without the danger of losing it. -/// -/// It's particularly useful for `Enoki<->SuiNS`, where the `SuinsRegistration` object -/// will be owned by a multi-sig address (for any company), and Enoki addresses will be able to -/// borrow it for creating subdomains. -/// -/// Also, instead of going the `cap` way, we go with the `address` way to remove any chance of -/// equivocation, both for Enoki Backends + owner actions on the domain. -/// -/// Since `SuiNS` is required as a parameter to create subdomains anyways, -/// we're also using it to store the managed names (to avoid using separate shared objects). -/// -module managed_names::managed { - use std::string::{String}; - - use sui::{table::{Self, Table}, tx_context::sender, clock::Clock}; - - use suins::{domain::{Self, Domain}, suins_registration::SuinsRegistration, suins::{Self, SuiNS, AdminCap}}; - - /// Tries to add an NFT that has expired. - const EExpiredNFT: u64 = 1; - /// Tries to add a name that already exists (that's impossible but protecting anyways). - const EAlreadyExists: u64 = 2; - /// Tries to borrow a name that doesn't exist in the managed registry - const ENameNotExists: u64 = 3; - /// Tries to do an unauthorized action on a name. - const ENotAuthorized: u64 = 4; - /// Tries to return an NFT that doesn't match the promise. - const EInvalidReturnedNFT: u64 = 5; - - /// Authorizes the `ManagedNames` to add a `registry` under the main SuiNS object. - public struct ManagedNamesApp has drop {} - - - /// The `registry` that holds the managed names per domain. - /// To simplify, we can only hold a single managed name per domain. - /// If a valid NFT is passed, the previous name is returned to the owner (who can burn it, as it's an expired one). - public struct ManagedNames has store { - names: Table - } - - /// A managed name. - /// `owner`: the only address that can get the `NFT` back - /// `allowlist`: A list of allowed addresses (that can borrow + return the `NFT`) - /// `nft`: The `SuinsRegistration` object that can be borrowed. - public struct ManagedName has store { - owner: address, - allowed_addresses: vector
, - nft: Option - } - - /// A hot-potato promise that the NFT will be returned upon borrowing. - public struct ReturnPromise { - id: ID - } - - // Create the store that will hold the managed names as an admin. - public fun setup(self: &mut SuiNS, cap: &AdminCap, ctx: &mut TxContext) { - suins::add_registry(cap, self, ManagedNames { - names: table::new(ctx) - }); - } - - - /// Attaches a `SuinsRegistration` object for usability from third-party addresses. - public fun attach_managed_name( - suins: &mut SuiNS, - nft: SuinsRegistration, - clock: &Clock, - allowed_addresses: vector
, - ctx: &mut TxContext - ) { - assert!(!nft.has_expired(clock), EExpiredNFT); - - let managed_names = managed_names_mut(suins); - - let domain = nft.domain(); - - // if the name exists. We check if it's expired, and return it to the owner. - if(table::contains(&managed_names.names, domain)) { - let existing = table::remove(&mut managed_names.names, domain); - - let ManagedName { nft, allowed_addresses: _, owner } = existing; - - let existing_nft = option::destroy_some(nft); - - assert!(existing_nft.has_expired(clock), EAlreadyExists); - // transfer it back to the owner. - transfer::public_transfer(existing_nft, owner); - }; - - // add the name to the managed names list. - managed_names.names.add(domain, ManagedName { - owner: sender(ctx), - allowed_addresses, - nft: option::some(nft) - }); - } - - /// Allows the `owner` to remove a name from the managed system. - public fun remove_attached_name( - suins: &mut SuiNS, - name: String, - ctx: &mut TxContext - ): SuinsRegistration { - let managed_names = managed_names_mut(suins); - let domain = domain::new(name); - - assert!(table::contains(&managed_names.names, domain), ENameNotExists); - let existing = managed_names.names.remove(domain); - - assert!(is_owner(&existing, sender(ctx)), ENotAuthorized); - - let ManagedName { nft, allowed_addresses: _, owner: _ } = existing; - - option::destroy_some(nft) - } - - - /// Allow a list of addresses to borrow the `SuinsRegistration` object. - public fun allow_addresses( - suins: &mut SuiNS, - name: String, - mut addresses: vector
, - ctx: &mut TxContext - ) { - let existing = internal_get_managed_name(managed_names_mut(suins), domain::new(name)); - assert!(is_owner(existing, sender(ctx)), ENotAuthorized); - - while(addresses.length() > 0) { - let addr = addresses.pop_back(); - - if(!existing.allowed_addresses.contains(&addr)) { - existing.allowed_addresses.push_back(addr); - } - } - } - - // Removes a list of addresses from the allow-list. - public fun revoke_addresses( - suins: &mut SuiNS, - name: String, - mut addresses: vector
, - ctx: &mut TxContext - ) { - let existing = internal_get_managed_name(managed_names_mut(suins), domain::new(name)); - assert!(is_owner(existing, sender(ctx)), ENotAuthorized); - - while(addresses.length() > 0) { - let addr = addresses.pop_back(); - - let (has_address, index) = existing.allowed_addresses.index_of(&addr); - - if (has_address) { - existing.allowed_addresses.remove(index); - } - } - } - - /// Borrows the `SuinsRegistration` object. - public fun borrow_val( - suins: &mut SuiNS, - name: String, - ctx: &mut TxContext - ): (SuinsRegistration, ReturnPromise) { - let existing = internal_get_managed_name(managed_names_mut(suins), domain::new(name)); - - assert!(is_authorized_address(existing, sender(ctx)), ENotAuthorized); - - let nft = option::extract(&mut existing.nft); - let id = object::id(&nft); - - (nft, ReturnPromise { - id - }) - } - - /// Returns the `SuinsRegistration` object back with the promise. - public fun return_val( - suins: &mut SuiNS, - nft: SuinsRegistration, - promise: ReturnPromise - ) { - let ReturnPromise { id } = promise; - assert!(object::id(&nft) == id, EInvalidReturnedNFT); - - let existing = internal_get_managed_name(managed_names_mut(suins), nft.domain()); - - // return the NFT back. - option::fill(&mut existing.nft, nft) - } - - - fun internal_get_managed_name(managed_names: &mut ManagedNames, domain: Domain): &mut ManagedName { - assert!(managed_names.names.contains(domain), ENameNotExists); - - &mut managed_names.names[domain] - } - - - fun is_owner(self: &ManagedName, addr: address): bool { - self.owner == addr - } - /// Check if an address is authorized for borrowing. - fun is_authorized_address(self: &ManagedName, addr: address): bool { - self.owner == addr || self.allowed_addresses.contains(&addr) - } - - /// a mutable reference to the registry - fun managed_names_mut(self: &mut SuiNS): &mut ManagedNames { - suins::app_registry_mut(ManagedNamesApp {}, self) - } -} diff --git a/packages/managed_names/tests/managed_tests.move b/packages/managed_names/tests/managed_tests.move deleted file mode 100644 index ca8487d4..00000000 --- a/packages/managed_names/tests/managed_tests.move +++ /dev/null @@ -1,330 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -module managed_names::managed_tests { - use std::string::{String, utf8}; - - use sui::{test_scenario::{Self as ts, Scenario, ctx}, clock::{Self, Clock}}; - - use suins::{suins_registration::{Self, new_for_testing, SuinsRegistration}, suins::{Self, SuiNS}, domain}; - - use managed_names::managed::{Self, ManagedNamesApp, ReturnPromise}; - - const USER: address = @0x1; - const USER_TWO: address = @0x2; - const USER_THREE: address = @0x3; - - #[test] - fun e2e() { - let mut scenario_val = test_init(); - let scenario = &mut scenario_val; - - let domain_name = utf8(b"example.sui"); - - attach_name(get_nft(domain_name, USER, scenario), vector[], USER, scenario); - - add_or_remove_addresses(domain_name, vector[USER_TWO], true, USER, scenario); - - simulate_borrowing(domain_name, USER_TWO, scenario); - simulate_borrowing(domain_name, USER, scenario); - - let nft = remove_attached_name(domain_name, USER, scenario); - transfer::public_transfer(nft, USER); - - scenario_val.end(); - } - - #[test] - fun deattach_expired_to_attach_non_expired() { - let mut scenario_val = test_init(); - let scenario = &mut scenario_val; - - let domain_name = utf8(b"example.sui"); - - let first_nft = get_nft(domain_name, USER, scenario); - let random_nft = get_nft(utf8(b"random.sui"), USER, scenario); - - let id = object::id(&first_nft); - - attach_name(first_nft, vector[USER_TWO], USER, scenario); - simulate_borrowing(domain_name, USER_TWO, scenario); - - // advance clock so that its expired. - advance_clock_post_expiration_of_nft(&random_nft, scenario); - - let re_registered_nft = get_nft(domain_name, USER_THREE, scenario); - attach_name(re_registered_nft, vector[], USER_THREE, scenario); - - // Since we attached the re-registered version for the name, - // the original `owner` should have received back the expired NFT. - { - scenario.next_tx(USER); - let mut nft_transferred_back = ts::most_recent_id_for_address(USER); - - assert!(option::is_some(&nft_transferred_back), 0); - assert!(option::extract(&mut nft_transferred_back) == id, 0); - - }; - - suins_registration::burn_for_testing(random_nft); - - scenario_val.end(); - } - - #[test, expected_failure(abort_code=managed_names::managed::EExpiredNFT)] - fun attach_expired_failure() { - let mut scenario_val = test_init(); - let scenario = &mut scenario_val; - - let domain_name = utf8(b"example.sui"); - let nft = get_nft(domain_name, USER, scenario); - - advance_clock_post_expiration_of_nft(&nft, scenario); - - attach_name(nft, vector[], USER, scenario); - - abort 1337 - } - - #[test, expected_failure(abort_code=managed_names::managed::ENameNotExists)] - fun borrow_non_existing_name_failure() { - let mut scenario_val = test_init(); - let scenario = &mut scenario_val; - let domain_name = utf8(b"example.sui"); - - simulate_borrowing(domain_name, USER, scenario); - abort 1337 - } - - #[test, expected_failure(abort_code=managed_names::managed::EInvalidReturnedNFT)] - fun borrow_and_return_different_nft() { - let mut scenario_val = test_init(); - let scenario = &mut scenario_val; - - let domain_name = utf8(b"example.sui"); - let domain_name_two = utf8(b"test.example.sui"); - - let nft1 = get_nft(domain_name, USER, scenario); - let nft2 = get_nft(domain_name_two, USER, scenario); - - attach_name(nft1, vector[], USER, scenario); - let (_nft, promise) = simulate_borrow(domain_name, USER, scenario); - simulate_return(nft2, promise, scenario); - - abort 1337 - } - - #[test, expected_failure(abort_code=managed_names::managed::ENotAuthorized)] - fun try_to_borrow_as_unauthorized_user() { - let mut scenario_val = test_init(); - let scenario = &mut scenario_val; - - let domain_name = utf8(b"example.sui"); - - let nft1 = get_nft(domain_name, USER, scenario); - attach_name(nft1, vector[], USER, scenario); - let (_nft, _promise) = simulate_borrow(domain_name, USER_THREE, scenario); - - abort 1337 - } - - #[test, expected_failure(abort_code=managed_names::managed::ENotAuthorized)] - fun try_to_remove_not_being_owner_but_being_authorized() { - let mut scenario_val = test_init(); - let scenario = &mut scenario_val; - - let domain_name = utf8(b"example.sui"); - - let nft = get_nft(domain_name, USER, scenario); - attach_name(nft, vector[USER_TWO], USER, scenario); - - let _nft = remove_attached_name(domain_name, USER_TWO, scenario); - - abort 1337 - } - - #[test, expected_failure(abort_code=managed_names::managed::ENotAuthorized)] - fun try_to_remove_not_being_owner_not_authorized() { - let mut scenario_val = test_init(); - let scenario = &mut scenario_val; - - let domain_name = utf8(b"example.sui"); - - let nft = get_nft(domain_name, USER, scenario); - attach_name(nft, vector[], USER, scenario); - - let _nft = remove_attached_name(domain_name, USER_TWO, scenario); - - abort 1337 - } - - #[test, expected_failure(abort_code=managed_names::managed::ENotAuthorized)] - fun remove_from_authorized_and_fail_to_borrow() { - let mut scenario_val = test_init(); - let scenario = &mut scenario_val; - - let domain_name = utf8(b"example.sui"); - let nft = get_nft(domain_name, USER, scenario); - - // authorizes and allows user two. - attach_name(nft, vector[USER_TWO], USER, scenario); - // removes User Two from authorized list. - add_or_remove_addresses(domain_name, vector[USER_TWO], false, USER, scenario); - - // tries to borrow as user Two. - simulate_borrowing(domain_name, USER_TWO, scenario); - - abort 1337 - } - - #[test, expected_failure(abort_code=managed_names::managed::ENotAuthorized)] - fun revoke_addresses_as_non_owner() { - let mut scenario_val = test_init(); - let scenario = &mut scenario_val; - - let domain_name = utf8(b"example.sui"); - let nft = get_nft(domain_name, USER, scenario); - - attach_name(nft, vector[USER_TWO], USER, scenario); - add_or_remove_addresses(domain_name, vector[USER_TWO], false, USER_TWO, scenario); - - abort 1337 - } - - #[test, expected_failure(abort_code=managed_names::managed::ENotAuthorized)] - fun add_addresses_as_non_owner() { - let mut scenario_val = test_init(); - let scenario = &mut scenario_val; - - let domain_name = utf8(b"example.sui"); - let nft = get_nft(domain_name, USER, scenario); - - attach_name(nft, vector[USER_TWO], USER, scenario); - add_or_remove_addresses(domain_name, vector[USER_THREE], true, USER_TWO, scenario); - - abort 1337 - } - - #[test, expected_failure(abort_code=managed_names::managed::ENameNotExists)] - fun remove_name_that_does_not_exist() { - let mut scenario_val = test_init(); - let scenario = &mut scenario_val; - - let domain_name = utf8(b"example.sui"); - let _nft = remove_attached_name(domain_name, USER, scenario); - - abort 1337 - } - - /// == Helpers == - /// - public fun test_init(): (Scenario) { - let mut scenario = ts::begin(USER); - { - scenario.next_tx(USER); - - let clock = clock::create_for_testing(scenario.ctx()); - clock::share_for_testing(clock); - - let (mut suins, cap) = suins::new_for_testing(scenario.ctx()); - - suins.authorize_app_for_testing(); - - managed::setup(&mut suins, &cap, ctx(&mut scenario)); - suins.share_for_testing(); - - suins::burn_admin_cap_for_testing(cap); - }; - - scenario - } - - public fun attach_name(nft: SuinsRegistration, addresses: vector
, addr: address, scenario: &mut Scenario) { - scenario.next_tx(addr); - let mut suins = scenario.take_shared(); - let clock = scenario.take_shared(); - - managed::attach_managed_name(&mut suins, nft, &clock, addresses, scenario.ctx()); - - ts::return_shared(clock); - ts::return_shared(suins); - } - - public fun remove_attached_name(domain_name: String, addr: address, scenario: &mut Scenario): SuinsRegistration { - scenario.next_tx(addr); - let mut suins = scenario.take_shared(); - - let nft = managed::remove_attached_name(&mut suins, domain_name, scenario.ctx()); - - ts::return_shared(suins); - nft - } - - public fun add_or_remove_addresses(name: String, addresses: vector
, add: bool, addr: address, scenario: &mut Scenario) { - scenario.next_tx(addr); - let mut suins = scenario.take_shared(); - let clock = scenario.take_shared(); - - if(add){ - managed::allow_addresses(&mut suins, name, addresses, scenario.ctx()); - }else { - managed::revoke_addresses(&mut suins, name, addresses, scenario.ctx()); - }; - - ts::return_shared(clock); - ts::return_shared(suins); - } - - public fun simulate_borrow(domain_name: String, addr: address, scenario: &mut Scenario): (SuinsRegistration, ReturnPromise) { - scenario.next_tx(addr); - let mut suins = scenario.take_shared(); - - let (name, promise) = managed::borrow_val(&mut suins, domain_name, scenario.ctx()); - - assert!(name.domain() == domain::new(domain_name), 0); - - ts::return_shared(suins); - - (name, promise) - } - - public fun simulate_return(nft: SuinsRegistration, promise: ReturnPromise, scenario: &mut Scenario) { - scenario.next_tx(USER); - let mut suins = scenario.take_shared(); - - managed::return_val(&mut suins, nft, promise); - - ts::return_shared(suins); - } - - public fun simulate_borrowing(domain_name: String, addr: address, scenario: &mut Scenario) { - - let (name, promise) = simulate_borrow(domain_name, addr, scenario); - - simulate_return(name, promise, scenario); - } - - public fun advance_clock_post_expiration_of_nft(nft: &SuinsRegistration, scenario: &mut Scenario) { - scenario.next_tx(USER); - let mut clock = scenario.take_shared(); - // expire name - clock.increment_for_testing(nft.expiration_timestamp_ms() + 1); - ts::return_shared(clock); - } - - // generates a SuinsRegistration NFT for testing - public fun get_nft(name: String, addr: address, scenario: &mut Scenario): SuinsRegistration { - scenario.next_tx(addr); - let suins = scenario.take_shared(); - let clock = scenario.take_shared(); - let nft = new_for_testing( - domain::new(name), - 1, - &clock, - ctx(scenario) - ); - ts::return_shared(clock); - ts::return_shared(suins); - nft - } -}