Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add router management #60

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 115 additions & 2 deletions router/sources/router.move
Original file line number Diff line number Diff line change
@@ -1,9 +1,115 @@
module router::router {
use aptos_framework::object::ExtendRef;
use aptos_framework::object;
use std::error;
use std::option::{Self, Option};
use std::signer::address_of;
use std::string::{String};

// == WRITE ==
// == ROUTER MODE ENUMS ==

// NOTE: New enums must update is_valid_mode(mode: u8)
const MODE_V1: u8 = 0;
const MODE_V1_AND_V2: u8 = 1;
// const MODE_NEXT: u8 = 2;

// == ERROR CODES ==

/// Caller is not the admin
const ENOT_ADMIN: u64 = 0;
/// There is no pending admin
const ENO_PENDING_ADMIN: u64 = 1;
/// Caller is not the pending admin
const ENOT_PENDING_ADMIN: u64 = 2;
/// Provided mode is not supported
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: two spaces :P

const EINVALID_MODE: u64 = 3;
/// Function is not implemented in the current mode
const ENOT_IMPLEMENTED_IN_MODE: u64 = 4;

// == OTHER CONSTANTS ==

const ROUTER_OBJECT_SEED: vector<u8> = b"ANS ROUTER";

// == STRUCTS ==

#[resource_group_member(group = aptos_framework::object::ObjectGroup)]
struct RouterConfig has key {
pending_admin_addr: Option<address>,
admin_addr: address,
mode: u8,
extend_ref: ExtendRef,
}

fun init_module(deployer: &signer) {
let constructor_ref = object::create_named_object(deployer, ROUTER_OBJECT_SEED);
let module_signer = object::generate_signer(&constructor_ref);
move_to(&module_signer, RouterConfig {
pending_admin_addr: option::none(),
admin_addr: address_of(deployer),
mode: MODE_V1,
extend_ref: object::generate_extend_ref(&constructor_ref),
});
}

// == ROUTER MANAGEMENT WRITE FUNCTIONS ==

public entry fun set_pending_admin(
router_admin: &signer,
pending_admin_addr: address,
) acquires RouterConfig {
let router_config = borrow_global_mut<RouterConfig>(router_config_addr());
assert!(router_config.admin_addr == address_of(router_admin), error::permission_denied(ENOT_ADMIN));
router_config.pending_admin_addr = option::some(pending_admin_addr);
}

public entry fun accept_pending_admin(pending_admin: &signer) acquires RouterConfig {
let router_config = borrow_global_mut<RouterConfig>(router_config_addr());
assert!(option::is_some(&router_config.pending_admin_addr), error::invalid_state(ENO_PENDING_ADMIN));
let pending_admin_addr = address_of(pending_admin);
assert!(
option::extract(&mut router_config.pending_admin_addr) == pending_admin_addr,
error::permission_denied(ENOT_PENDING_ADMIN)
);
router_config.admin_addr = pending_admin_addr;
router_config.pending_admin_addr = option::none();
}

public entry fun set_mode(
router_admin: &signer,
mode: u8,
) acquires RouterConfig {
assert!(is_valid_mode(mode), error::invalid_argument(EINVALID_MODE));
let router_config = borrow_global_mut<RouterConfig>(router_config_addr());
assert!(router_config.admin_addr == address_of(router_admin), error::permission_denied(ENOT_ADMIN));
router_config.mode = mode;
}

// == ROUTER MANAGEMENT READ FUNCTIONS ==

inline fun router_config_addr(): address {
object::create_object_address(&@router, ROUTER_OBJECT_SEED)
}

inline fun is_valid_mode(mode: u8): bool {
mode <= MODE_V1_AND_V2
}

public fun get_admin_addr(): address acquires RouterConfig {
let router_config = borrow_global<RouterConfig>(router_config_addr());
router_config.admin_addr
}

public fun get_pending_admin_addr(): Option<address> acquires RouterConfig {
let router_config = borrow_global<RouterConfig>(router_config_addr());
router_config.pending_admin_addr
}

public fun get_mode(): u8 acquires RouterConfig {
let router_config = borrow_global<RouterConfig>(router_config_addr());
router_config.mode
}

// == ROUTER WRITE FUNCTIONS ==

// ==== REGISTRATION ====

Expand Down Expand Up @@ -75,7 +181,7 @@ module router::router {
_subdomain_name: String,
) {}

// ==== READ ====
// == ROUTER READ FUNCTIONS ==

public fun get_target_addr(
_domain_name: String,
Expand Down Expand Up @@ -114,4 +220,11 @@ module router::router {
assert!(true, error::not_implemented(0));
(option::none(), option::none())
}

// == TEST ==

#[test_only]
public fun init_module_for_test(deployer: &signer) {
init_module(deployer);
}
}
204 changes: 204 additions & 0 deletions router/sources/router_tests.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
#[test_only]
module router::router_tests {
use aptos_names_v2::test_helper;
use std::vector;
use router::router;
use std::option;
use std::signer::address_of;

const MAX_MODE: u8 = 1;

#[test(
router = @router,
aptos_names = @aptos_names,
aptos_names_v2 = @aptos_names_v2,
user = @0x077,
aptos = @0x1,
rando = @0x266f,
foundation = @0xf01d
)]
fun test_initialization(
router: &signer,
aptos_names: &signer,
aptos_names_v2: &signer,
user: signer,
aptos: signer,
rando: signer,
foundation: signer
) {
router::init_module_for_test(router);
test_helper::e2e_test_setup(aptos_names, aptos_names_v2, user, &aptos, rando, &foundation);
assert!(router::get_admin_addr() == @router, 0);
assert!(option::is_none(&router::get_pending_admin_addr()), 1);
assert!(router::get_mode() == 0, 2)
}

#[test(
router = @router,
aptos_names = @aptos_names,
aptos_names_v2 = @aptos_names_v2,
user = @0x077,
aptos = @0x1,
rando = @0x266f,
foundation = @0xf01d
)]
fun test_accept_admin(
router: &signer,
aptos_names: &signer,
aptos_names_v2: &signer,
user: signer,
aptos: signer,
rando: signer,
foundation: signer
) {
router::init_module_for_test(router);
let users = test_helper::e2e_test_setup(aptos_names, aptos_names_v2, user, &aptos, rando, &foundation);
let user = vector::borrow(&users, 0);
let user_addr = address_of(user);

router::set_pending_admin(router, user_addr);
assert!(router::get_admin_addr() == @router, 0);
assert!(option::extract(&mut router::get_pending_admin_addr()) == user_addr, 1);

router::accept_pending_admin(user);
assert!(router::get_admin_addr() == user_addr, 0);
assert!(option::is_none(&router::get_pending_admin_addr()), 1);
}

#[test(
router = @router,
aptos_names = @aptos_names,
aptos_names_v2 = @aptos_names_v2,
user = @0x077,
aptos = @0x1,
rando = @0x266f,
foundation = @0xf01d
)]
#[expected_failure(abort_code = 327682, location = router)]
fun test_accept_admin_only_pending_admin(
router: &signer,
aptos_names: &signer,
aptos_names_v2: &signer,
user: signer,
aptos: signer,
rando: signer,
foundation: signer
) {
router::init_module_for_test(router);
let users = test_helper::e2e_test_setup(aptos_names, aptos_names_v2, user, &aptos, rando, &foundation);
let user = vector::borrow(&users, 0);
let user_addr = address_of(user);

router::set_pending_admin(router, user_addr);
assert!(router::get_admin_addr() == @router, 0);
assert!(option::extract(&mut router::get_pending_admin_addr()) == user_addr, 1);

router::accept_pending_admin(router);
}

#[test(
router = @router,
aptos_names = @aptos_names,
aptos_names_v2 = @aptos_names_v2,
user = @0x077,
aptos = @0x1,
rando = @0x266f,
foundation = @0xf01d
)]
#[expected_failure(abort_code = 327680, location = router)]
fun test_set_pending_admin_only_admin(
router: &signer,
aptos_names: &signer,
aptos_names_v2: &signer,
user: signer,
aptos: signer,
rando: signer,
foundation: signer
) {
router::init_module_for_test(router);
let users = test_helper::e2e_test_setup(aptos_names, aptos_names_v2, user, &aptos, rando, &foundation);
let user = vector::borrow(&users, 0);

router::set_pending_admin(user, address_of(user));
}

#[test(
router = @router,
aptos_names = @aptos_names,
aptos_names_v2 = @aptos_names_v2,
user = @0x077,
aptos = @0x1,
rando = @0x266f,
foundation = @0xf01d
)]
fun test_set_mode(
router: &signer,
aptos_names: &signer,
aptos_names_v2: &signer,
user: signer,
aptos: signer,
rando: signer,
foundation: signer
) {
router::init_module_for_test(router);
test_helper::e2e_test_setup(aptos_names, aptos_names_v2, user, &aptos, rando, &foundation);

let i = 0;
while (i <= MAX_MODE) {
router::set_mode(router, i);
assert!(router::get_mode() == i, 0);
i = i + 1
}
}

#[test(
router = @router,
aptos_names = @aptos_names,
aptos_names_v2 = @aptos_names_v2,
user = @0x077,
aptos = @0x1,
rando = @0x266f,
foundation = @0xf01d
)]
#[expected_failure(abort_code = 327680, location = router)]
fun test_set_mode_admin_only(
router: &signer,
aptos_names: &signer,
aptos_names_v2: &signer,
user: signer,
aptos: signer,
rando: signer,
foundation: signer
) {
router::init_module_for_test(router);
let users = test_helper::e2e_test_setup(aptos_names, aptos_names_v2, user, &aptos, rando, &foundation);
let user = vector::borrow(&users, 0);

router::set_mode(user, 0);
}

#[test(
router = @router,
aptos_names = @aptos_names,
aptos_names_v2 = @aptos_names_v2,
user = @0x077,
aptos = @0x1,
rando = @0x266f,
foundation = @0xf01d
)]
#[expected_failure(abort_code = 65539, location = router)]
fun test_set_mode_invalid_mode(
router: &signer,
aptos_names: &signer,
aptos_names_v2: &signer,
user: signer,
aptos: signer,
rando: signer,
foundation: signer
) {
router::init_module_for_test(router);
test_helper::e2e_test_setup(aptos_names, aptos_names_v2, user, &aptos, rando, &foundation);

router::set_mode(router, MAX_MODE + 1);
}
}