Skip to content

Commit

Permalink
feat: add version check, improve executable check
Browse files Browse the repository at this point in the history
  • Loading branch information
thounyy committed Oct 16, 2024
1 parent a979307 commit de3ebee
Show file tree
Hide file tree
Showing 21 changed files with 637 additions and 480 deletions.
39 changes: 22 additions & 17 deletions packages/actions/sources/access_control.move
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/// Developers can restrict access to functions in their own package with an Access.
/// Access structs are issued using a Cap type that can be locked at any time.
/// Developers can restrict access to functions in their own package with a Caps that can be locked into
///
/// The Access has copy and drop abilities to be used multiple times within a single PTB
/// It has a generic type which acts as a proof of cap.
Expand All @@ -16,15 +15,16 @@ module account_actions::access_control;
// === Imports ===

use std::{
type_name,
type_name::{Self, TypeName},
string::String
};
use account_protocol::{
account::Account,
proposals::{Proposal, Expired},
executable::Executable,
auth::Auth
auth::Auth,
};
use account_actions::version;

// === Errors ===

Expand All @@ -43,7 +43,7 @@ public struct AccessLock<Cap: store> has store {
}

/// [PROPOSAL] issues an Access<Cap>, Cap being the type of a Cap held by the Account
public struct AccessProposal() has drop;
public struct AccessProposal() has copy, drop;

/// [ACTION] mint new coins
public struct AccessAction<phantom Cap> has store {}
Expand All @@ -62,7 +62,7 @@ public fun lock_cap<Config, Outcome, Cap: store>(
cap: Cap,
) {
auth.verify(account.addr());
account.add_managed_asset(AccessKey<Cap> {}, AccessLock { cap });
account.add_managed_asset(AccessKey<Cap> {}, AccessLock { cap }, version::current());
}

public fun has_lock<Config, Outcome, Cap>(
Expand All @@ -89,6 +89,7 @@ public fun propose_access<Config, Outcome, Cap>(
let mut proposal = account.create_proposal(
auth,
outcome,
version::current(),
AccessProposal(),
type_to_name<Cap>(), // the cap type is the witness role name
key,
Expand All @@ -99,7 +100,7 @@ public fun propose_access<Config, Outcome, Cap>(
);

new_access<Outcome, Cap, AccessProposal>(&mut proposal, AccessProposal());
account.add_proposal(proposal, AccessProposal());
account.add_proposal(proposal, version::current(), AccessProposal());
}

// step 2: multiple members have to approve the proposal (account::approve_proposal)
Expand All @@ -110,7 +111,7 @@ public fun execute_access<Config, Outcome, Cap: store>(
executable: &mut Executable,
account: &mut Account<Config, Outcome>,
): (Borrow<Cap>, Cap) {
access_cap<Config, Outcome, Cap, AccessProposal>(executable, account, AccessProposal())
access_cap(executable, account, version::current(), AccessProposal())
}

// step 5: return the cap to destroy Borrow, the action and executable
Expand All @@ -120,9 +121,9 @@ public fun complete_access<Config, Outcome, Cap: store>(
borrow: Borrow<Cap>,
cap: Cap
) {
return_cap(account, borrow, cap);
destroy_access<Cap, AccessProposal>(&mut executable, AccessProposal());
executable.destroy(AccessProposal());
return_cap(account, borrow, cap, version::current());
destroy_access<Cap, AccessProposal>(&mut executable, version::current(), AccessProposal());
executable.terminate(version::current(), AccessProposal());
}

// === [ACTION] Public functions ===
Expand All @@ -134,15 +135,18 @@ public fun new_access<Outcome, Cap, W: drop>(
proposal.add_action(AccessAction<Cap> {}, witness);
}

public fun access_cap<Config, Outcome, Cap: store, W: drop>(
public fun access_cap<Config, Outcome, Cap: store, W: copy + drop>(
executable: &mut Executable,
account: &mut Account<Config, Outcome>,
version: TypeName,
witness: W,
): (Borrow<Cap>, Cap) {
assert!(has_lock<Config, Outcome, Cap>(account), ENoLock);
// check to be sure this cap type has been approved
let _access_mut: &mut AccessAction<Cap> = executable.action_mut(account.addr(), witness);
let AccessLock<Cap> { cap } = account.remove_managed_asset(AccessKey<Cap> {});
let _access_action = executable.load<AccessAction<Cap>, W>(account.addr(), version, witness);
let AccessLock<Cap> { cap } = account.remove_managed_asset(AccessKey<Cap> {}, version);

executable.process<AccessAction<Cap>, W>(version, witness);

(Borrow<Cap> { account_addr: account.addr() }, cap)
}
Expand All @@ -151,15 +155,16 @@ public fun return_cap<Config, Outcome, Cap: store>(
account: &mut Account<Config, Outcome>,
borrow: Borrow<Cap>,
cap: Cap,
version: TypeName,
) {
let Borrow<Cap> { account_addr } = borrow;
assert!(account_addr == account.addr(), EWrongAccount);

account.add_managed_asset(AccessKey<Cap> {}, AccessLock { cap });
account.add_managed_asset(AccessKey<Cap> {}, AccessLock { cap }, version);
}

public fun destroy_access<Cap, W: drop>(executable: &mut Executable, witness: W) {
let AccessAction<Cap> {} = executable.remove_action(witness);
public fun destroy_access<Cap, W: drop>(executable: &mut Executable, version: TypeName, witness: W) {
let AccessAction<Cap> {} = executable.cleanup(version, witness);
}

public fun delete_access_action<Outcome, Cap>(expired: &mut Expired<Outcome>) {
Expand Down
65 changes: 50 additions & 15 deletions packages/actions/sources/config.move
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ module account_actions::config;

// === Imports ===

use std::string::String;
use std::{
type_name::TypeName,
string::String,
};
use account_protocol::{
account::Account,
proposals::{Proposal, Expired},
Expand All @@ -26,13 +29,16 @@ use account_protocol::{
auth::Auth,
};
use account_extensions::extensions::Extensions;
use account_actions::version;

// === Errors ===

#[error]
const EMetadataNotSameLength: vector<u8> = b"The keys and values are not the same length";
#[error]
const EMetadataNameMissing: vector<u8> = b"New metadata must set a name for the Account";
#[error]
const ENameCannotBeEmpty: vector<u8> = b"Name cannot be empty";

// === Structs ===

Expand All @@ -45,9 +51,9 @@ const EMetadataNameMissing: vector<u8> = b"New metadata must set a name for the
/// proof of core dependency
public struct CoreDep() has drop;
/// [PROPOSAL] modifies the name of the account
public struct ConfigMetadataProposal() has drop;
public struct ConfigMetadataProposal() has copy, drop;
/// [PROPOSAL] modifies the dependencies of the account
public struct ConfigDepsProposal() has drop;
public struct ConfigDepsProposal() has copy, drop;

/// [ACTION] wraps the metadata account field into an action
public struct ConfigMetadataAction has store {
Expand Down Expand Up @@ -76,6 +82,7 @@ public fun propose_config_metadata<Config, Outcome>(
let mut proposal = account.create_proposal(
auth,
outcome,
version::current(),
ConfigMetadataProposal(),
b"".to_string(),
key,
Expand All @@ -84,8 +91,9 @@ public fun propose_config_metadata<Config, Outcome>(
expiration_epoch,
ctx
);

new_config_metadata(&mut proposal, keys, values, ConfigMetadataProposal());
account.add_proposal(proposal, ConfigMetadataProposal());
account.add_proposal(proposal, version::current(), ConfigMetadataProposal());
}

// step 2: multiple members have to approve the proposal (account::approve_proposal)
Expand All @@ -96,8 +104,9 @@ public fun execute_config_metadata<Config, Outcome>(
mut executable: Executable,
account: &mut Account<Config, Outcome>,
) {
config_metadata(&mut executable, account, ConfigMetadataProposal());
executable.destroy(ConfigMetadataProposal());
config_metadata(&mut executable, account, version::current(), ConfigMetadataProposal());
destroy_config_metadata(&mut executable, version::current(), ConfigMetadataProposal());
executable.terminate(version::current(), ConfigMetadataProposal());
}

// step 1: propose to update the dependencies
Expand All @@ -118,6 +127,7 @@ public fun propose_config_deps<Config, Outcome>(
let mut proposal = account.create_proposal(
auth,
outcome,
version::current(),
ConfigDepsProposal(),
b"".to_string(),
key,
Expand All @@ -126,8 +136,9 @@ public fun propose_config_deps<Config, Outcome>(
expiration_epoch,
ctx
);

new_config_deps(&mut proposal, extensions, names, packages, versions, ConfigDepsProposal());
account.add_proposal(proposal, ConfigDepsProposal());
account.add_proposal(proposal, version::current(), ConfigDepsProposal());
}

// step 2: multiple members have to approve the proposal (account::approve_proposal)
Expand All @@ -138,8 +149,9 @@ public fun execute_config_deps<Config, Outcome>(
mut executable: Executable,
account: &mut Account<Config, Outcome>,
) {
config_deps(&mut executable, account, ConfigDepsProposal());
executable.destroy(ConfigDepsProposal());
config_deps(&mut executable, account, version::current(), ConfigDepsProposal());
destroy_config_deps(&mut executable, version::current(), ConfigDepsProposal());
executable.terminate(version::current(), ConfigDepsProposal());
}

// === [ACTION] Public functions ===
Expand All @@ -152,20 +164,32 @@ public fun new_config_metadata<Outcome, W: drop>(
) {
assert!(keys.length() == values.length(), EMetadataNotSameLength);
assert!(keys[0] == b"name".to_string(), EMetadataNameMissing);
assert!(values[0] != b"".to_string(), ENameCannotBeEmpty);

proposal.add_action(
ConfigMetadataAction { metadata: metadata::from_keys_values(keys, values) },
witness
);
}

public fun config_metadata<Config, Outcome, W: drop>(
public fun config_metadata<Config, Outcome, W: copy + drop>(
executable: &mut Executable,
account: &mut Account<Config, Outcome>,
version: TypeName,
witness: W,
) {
let ConfigMetadataAction { metadata } = executable.remove_action(witness);
*account.metadata_mut(CoreDep()) = metadata;
let config_metadata_action = executable.load<ConfigMetadataAction, W>(account.addr(), version, witness);
*account.metadata_mut(version) = config_metadata_action.metadata;

executable.process<ConfigMetadataAction, W>(version, witness);
}

public fun destroy_config_metadata<W: drop>(
executable: &mut Executable,
version: TypeName,
witness: W,
) {
let ConfigMetadataAction { .. } = executable.cleanup(version, witness);
}

public fun delete_config_metadata_action<Outcome>(expired: &mut Expired<Outcome>) {
Expand All @@ -188,13 +212,24 @@ public fun new_config_deps<Outcome, W: drop>(
proposal.add_action(ConfigDepsAction { deps }, witness);
}

public fun config_deps<Config, Outcome, W: drop>(
public fun config_deps<Config, Outcome, W: copy + drop>(
executable: &mut Executable,
account: &mut Account<Config, Outcome>,
version: TypeName,
witness: W,
) {
let config_deps_action = executable.load<ConfigDepsAction, W>(account.addr(), version, witness);
*account.deps_mut(version) = config_deps_action.deps;

executable.process<ConfigDepsAction, W>(version, witness);
}

public fun destroy_config_deps<W: drop>(
executable: &mut Executable,
version: TypeName,
witness: W,
) {
let ConfigDepsAction { deps } = executable.remove_action(witness);
*account.deps_mut(CoreDep()) = deps;
let ConfigDepsAction { .. } = executable.cleanup(version, witness);
}

public fun delete_config_deps_action<Outcome>(expired: &mut Expired<Outcome>) {
Expand Down
Loading

0 comments on commit de3ebee

Please sign in to comment.