diff --git a/README.md b/README.md index b086a83..d594d59 100644 --- a/README.md +++ b/README.md @@ -220,6 +220,24 @@ impl Counter { The derived implementation of `AccessControllable` provides more methods that are documented in the [definition of the trait](/near-plugins/src/access_controllable.rs). More usage patterns are explained in [examples](/examples/access-controllable-examples/) and in [integration tests](/near-plugins/tests/access_controllable.rs). +## Internal Architecture + +Each plugin's functionality is described by a trait defined in `near-plugins/src/.rs`. The trait's methods will be available on contracts that use the corresponding plugin, whereas the implementation of the trait is provided by procedural macros. + +The code that is generated for a trait implementation is based on `near-plugins-derive/src/`. To inspect the code generated for your particular smart contract, [`cargo-expand`](https://github.com/dtolnay/cargo-expand) can be helpful. + +## Testing + +Tests should verify that once the macros provided by this crate are expanded, the contract they are used in has the intended functionality. Integration tests are utilized for that purpose: + +- A contract using the plugin is contained in `near-plugins/tests/contracts//`. +- This contract is used in `near-plugins/tests/.rs` which: + - Compiles and deploys the contract on chain via [NEAR `workspaces`](https://docs.rs/workspaces/0.7.0/workspaces/). + - Sends transactions to the deployed contract to verify plugin functionality. + +> **Note** +> Currently some plugins are still tested by unit tests in `near-plugins/src/.rs`, not by integration tests. Migrating these unit tests to integration tests as described above is WIP. + ## Contributors Notes Traits doesn't contain any implementation, even though some interfaces are self-contained enough to have it. diff --git a/near-plugins-derive/src/access_control_role.rs b/near-plugins-derive/src/access_control_role.rs index 10dbc10..1999076 100644 --- a/near-plugins-derive/src/access_control_role.rs +++ b/near-plugins-derive/src/access_control_role.rs @@ -50,6 +50,7 @@ const DEFAULT_SUPER_ADMIN_NAME: &str = "__SUPER_ADMIN"; const DEFAULT_BITFLAGS_TYPE_NAME: &str = "RoleFlags"; const DEFAULT_BOUNDCHECKER_TYPE_NAME: &str = "__AclBoundchecker"; +/// Generates the token stream that implements `AccessControlRole`. pub fn derive_access_control_role(input: TokenStream) -> TokenStream { // This derive doesn't take attributes, so no need to use `darling`. let cratename = cratename(); @@ -190,6 +191,7 @@ pub fn derive_access_control_role(input: TokenStream) -> TokenStream { output.into() } +/// Generates and identifier for the bitflag type that represents permissions. pub fn new_bitflags_type_ident(span: Span) -> Ident { Ident::new(DEFAULT_BITFLAGS_TYPE_NAME, span) } diff --git a/near-plugins-derive/src/access_controllable.rs b/near-plugins-derive/src/access_controllable.rs index 90bc407..22857ae 100644 --- a/near-plugins-derive/src/access_controllable.rs +++ b/near-plugins-derive/src/access_controllable.rs @@ -8,6 +8,7 @@ use quote::quote; use syn::parse::Parser; use syn::{parse_macro_input, AttributeArgs, ItemFn, ItemStruct}; +/// Defines attributes for the `access_controllable` macro. #[derive(Debug, FromMeta)] pub struct MacroArgs { #[darling(default)] @@ -22,6 +23,7 @@ const DEFAULT_ACL_TYPE_NAME: &str = "__Acl"; const ERR_PARSE_BITFLAG: &str = "Value does not correspond to a permission"; const ERR_PARSE_ROLE: &str = "Value does not correspond to a role"; +/// Generates the token stream that implements `AccessControllable`. pub fn access_controllable(attrs: TokenStream, item: TokenStream) -> TokenStream { let cratename = cratename(); let attr_args = parse_macro_input!(attrs as AttributeArgs); @@ -539,11 +541,13 @@ fn inject_acl_field( Ok(()) } +/// Defines attributes for the `access_control_any` macro. #[derive(Debug, FromMeta)] pub struct MacroArgsAny { roles: darling::util::PathList, } +/// Generates the token stream for the `access_control_any` macro. pub fn access_control_any(attrs: TokenStream, item: TokenStream) -> TokenStream { let attr_args = parse_macro_input!(attrs as AttributeArgs); let cloned_item = item.clone(); diff --git a/near-plugins-derive/src/full_access_key_fallback.rs b/near-plugins-derive/src/full_access_key_fallback.rs index 1f22dd2..e7ec512 100644 --- a/near-plugins-derive/src/full_access_key_fallback.rs +++ b/near-plugins-derive/src/full_access_key_fallback.rs @@ -3,6 +3,7 @@ use proc_macro::{self, TokenStream}; use quote::quote; use syn::{parse_macro_input, DeriveInput}; +/// Generates the token stream that implements `FullAccessKeyFallback`. pub fn derive_fak_fallback(input: TokenStream) -> TokenStream { let cratename = cratename(); diff --git a/near-plugins-derive/src/lib.rs b/near-plugins-derive/src/lib.rs index 691ee64..31b3c9e 100644 --- a/near-plugins-derive/src/lib.rs +++ b/near-plugins-derive/src/lib.rs @@ -8,51 +8,61 @@ mod pausable; mod upgradable; mod utils; +/// Defines the derive macro for `Ownable`. #[proc_macro_derive(Ownable, attributes(ownable))] pub fn derive_ownable(input: TokenStream) -> TokenStream { ownable::derive_ownable(input) } +/// Defines attribute macro `only`. #[proc_macro_attribute] pub fn only(attrs: TokenStream, item: TokenStream) -> TokenStream { ownable::only(attrs, item) } +/// Defines the derive macro for `Upgradable`. #[proc_macro_derive(Upgradable, attributes(upgradable))] pub fn derive_upgradable(input: TokenStream) -> TokenStream { upgradable::derive_upgradable(input) } +/// Defines the derive macro for `FullAccessKeyFallback`. #[proc_macro_derive(FullAccessKeyFallback)] pub fn derive_fak_fallback(input: TokenStream) -> TokenStream { full_access_key_fallback::derive_fak_fallback(input) } +/// Defines the derive macro for `Pausable`. #[proc_macro_derive(Pausable, attributes(pausable))] pub fn derive_pausable(input: TokenStream) -> TokenStream { pausable::derive_pausable(input) } +/// Defines the attribute macro `pause`. #[proc_macro_attribute] pub fn pause(attrs: TokenStream, item: TokenStream) -> TokenStream { pausable::pause(attrs, item) } +/// Defines the attribute macro `if_paused`. #[proc_macro_attribute] pub fn if_paused(attrs: TokenStream, item: TokenStream) -> TokenStream { pausable::if_paused(attrs, item) } +/// Defines the derive macro for `AccessControlRole`. #[proc_macro_derive(AccessControlRole)] pub fn derive_access_control_role(input: TokenStream) -> TokenStream { access_control_role::derive_access_control_role(input) } +/// Defines the attribute macro `access_control`. #[proc_macro_attribute] pub fn access_control(attrs: TokenStream, item: TokenStream) -> TokenStream { access_controllable::access_controllable(attrs, item) } +/// Defines the attribute macro `access_control_any`. #[proc_macro_attribute] pub fn access_control_any(attrs: TokenStream, item: TokenStream) -> TokenStream { access_controllable::access_control_any(attrs, item) diff --git a/near-plugins-derive/src/ownable.rs b/near-plugins-derive/src/ownable.rs index 08abcde..7a0af76 100644 --- a/near-plugins-derive/src/ownable.rs +++ b/near-plugins-derive/src/ownable.rs @@ -11,6 +11,7 @@ struct Opts { owner_storage_key: Option, } +/// Generates the token stream that implements `Ownable`. pub fn derive_ownable(input: TokenStream) -> TokenStream { let cratename = cratename(); @@ -83,6 +84,7 @@ pub fn derive_ownable(input: TokenStream) -> TokenStream { output.into() } +/// Generates the token stream for the `only` macro. pub fn only(attrs: TokenStream, item: TokenStream) -> TokenStream { let input = parse::(item.clone()).unwrap(); if is_near_bindgen_wrapped_or_marshall(&input) { diff --git a/near-plugins-derive/src/pausable.rs b/near-plugins-derive/src/pausable.rs index 658fdf7..6217df6 100644 --- a/near-plugins-derive/src/pausable.rs +++ b/near-plugins-derive/src/pausable.rs @@ -16,6 +16,7 @@ struct Opts { manager_roles: PathList, } +/// Generates the token stream that implements `Pausable`. pub fn derive_pausable(input: TokenStream) -> TokenStream { let cratename = cratename(); @@ -103,6 +104,7 @@ pub fn derive_pausable(input: TokenStream) -> TokenStream { output.into() } +/// Defines sub-attributes for the `except` attribute. #[derive(Default, FromMeta, Debug)] #[darling(default)] pub struct ExceptSubArgs { @@ -110,6 +112,7 @@ pub struct ExceptSubArgs { roles: PathList, } +/// Defines attributes for the `pause` macro. #[derive(Debug, FromMeta)] pub struct PauseArgs { #[darling(default)] @@ -118,6 +121,7 @@ pub struct PauseArgs { except: ExceptSubArgs, } +/// Generates the token stream for the `pause` macro. pub fn pause(attrs: TokenStream, item: TokenStream) -> TokenStream { let input = parse::(item.clone()).unwrap(); @@ -143,6 +147,7 @@ pub fn pause(attrs: TokenStream, item: TokenStream) -> TokenStream { utils::add_extra_code_to_fn(&input, check_pause) } +/// Defines attributes for the `if_paused` macro. #[derive(Debug, FromMeta)] pub struct IfPausedArgs { name: String, @@ -150,6 +155,7 @@ pub struct IfPausedArgs { except: ExceptSubArgs, } +/// Generates the token stream for the `if_paused` macro. pub fn if_paused(attrs: TokenStream, item: TokenStream) -> TokenStream { let input = parse::(item.clone()).unwrap(); diff --git a/near-plugins-derive/src/upgradable.rs b/near-plugins-derive/src/upgradable.rs index 504b01f..b80838e 100644 --- a/near-plugins-derive/src/upgradable.rs +++ b/near-plugins-derive/src/upgradable.rs @@ -10,6 +10,7 @@ struct Opts { code_storage_key: Option, } +/// Generates the token stream for the `Upgradable` macro. pub fn derive_upgradable(input: TokenStream) -> TokenStream { let cratename = cratename(); diff --git a/near-plugins-derive/src/utils.rs b/near-plugins-derive/src/utils.rs index 288b4ff..b9f14cc 100644 --- a/near-plugins-derive/src/utils.rs +++ b/near-plugins-derive/src/utils.rs @@ -16,6 +16,7 @@ pub(crate) fn is_near_bindgen_wrapped_or_marshall(item: &ItemFn) -> bool { }) } +/// Returns an identifier for the name of the crate which is imported by plugin users. pub(crate) fn cratename() -> Ident { Ident::new( &crate_name("near-plugins").unwrap_or_else(|_| "near_plugins".to_string()), @@ -23,6 +24,7 @@ pub(crate) fn cratename() -> Ident { ) } +/// Injects extra code into a function. pub(crate) fn add_extra_code_to_fn( fn_code: &ItemFn, extra_code: proc_macro2::TokenStream, diff --git a/near-plugins/src/access_control_role.rs b/near-plugins/src/access_control_role.rs index e049c07..7b1053c 100644 --- a/near-plugins/src/access_control_role.rs +++ b/near-plugins/src/access_control_role.rs @@ -1,3 +1,4 @@ +/// Represents permissions for the [`AccessControllable`](crate::AccessControllable) plugin. pub trait AccessControlRole { /// Returns the bitflag corresponding to the super admin permission. fn acl_super_admin_permission() -> u128; diff --git a/near-plugins/src/events.rs b/near-plugins/src/events.rs index cb8d2f3..6905334 100644 --- a/near-plugins/src/events.rs +++ b/near-plugins/src/events.rs @@ -18,9 +18,12 @@ pub struct EventMetadata { pub data: Option, } +/// Trait to generate and emit NEAR events. pub trait AsEvent { + /// Returns the metadata that makes up the event. fn metadata(&self) -> EventMetadata; + /// Returns the string representation of the event. fn event(&self) -> String { format!( "EVENT_JSON:{}", @@ -28,6 +31,7 @@ pub trait AsEvent { ) } + /// Emits the event on chain. fn emit(&self) { near_sdk::log!(self.event()); } diff --git a/near-plugins/src/full_access_key_fallback.rs b/near-plugins/src/full_access_key_fallback.rs index 7d1d201..479031e 100644 --- a/near-plugins/src/full_access_key_fallback.rs +++ b/near-plugins/src/full_access_key_fallback.rs @@ -16,6 +16,7 @@ use crate::events::{AsEvent, EventMetadata}; use near_sdk::{AccountId, PublicKey}; use serde::Serialize; +/// Trait describing the functionality of the _Full Access Key Fallback_ plugin. pub trait FullAccessKeyFallback { /// Attach a new full access to the current contract. fn attach_full_access_key(&mut self, public_key: PublicKey) -> near_sdk::Promise; @@ -25,7 +26,9 @@ pub trait FullAccessKeyFallback { /// Event emitted every time a new FullAccessKey is added #[derive(Serialize, Clone)] pub struct FullAccessKeyAdded { + /// The account that added the full access key. pub by: AccountId, + /// The public key that was added. pub public_key: PublicKey, } diff --git a/near-plugins/src/ownable.rs b/near-plugins/src/ownable.rs index 5a8aa35..cc4899c 100644 --- a/near-plugins/src/ownable.rs +++ b/near-plugins/src/ownable.rs @@ -15,6 +15,7 @@ use crate::events::{AsEvent, EventMetadata}; use near_sdk::AccountId; use serde::Serialize; +/// Trait describing the functionality of the _Ownable_ plugin. pub trait Ownable { /// Key of storage slot to save the current owner. /// By default b"__OWNER__" is used. @@ -40,7 +41,9 @@ pub trait Ownable { /// Event emitted when ownership is changed. #[derive(Serialize, Clone)] pub struct OwnershipTransferred { + /// The previous owner, if any. pub previous_owner: Option, + /// The new owner, if any. pub new_owner: Option, } diff --git a/near-plugins/src/pausable.rs b/near-plugins/src/pausable.rs index 3258679..3506408 100644 --- a/near-plugins/src/pausable.rs +++ b/near-plugins/src/pausable.rs @@ -29,6 +29,7 @@ use near_sdk::AccountId; use serde::Serialize; use std::collections::HashSet; +/// Trait describing the functionality of the `Pausable` plugin. pub trait Pausable { /// Returns the key of the storage slot which contains the list of features that are paused. By /// default `b"__PAUSED__"` is used. diff --git a/near-plugins/src/upgradable.rs b/near-plugins/src/upgradable.rs index e52e83c..dfc4c8b 100644 --- a/near-plugins/src/upgradable.rs +++ b/near-plugins/src/upgradable.rs @@ -23,6 +23,7 @@ use crate::events::{AsEvent, EventMetadata}; use near_sdk::{AccountId, CryptoHash, Promise}; use serde::Serialize; +/// Trait describing the functionality of the _Upgradable_ plugin. pub trait Upgradable { /// Key of storage slot to save the staged code. /// By default b"__CODE__" is used. @@ -45,7 +46,9 @@ pub trait Upgradable { /// Event emitted when the code is staged #[derive(Serialize, Clone)] struct StageCode { + /// The account which staged the code. by: AccountId, + /// The hash of the code that was staged. code_hash: CryptoHash, } @@ -63,7 +66,9 @@ impl AsEvent for StageCode { /// Event emitted when the code is deployed #[derive(Serialize, Clone)] struct DeployCode { + /// The account that deployed the code. by: AccountId, + /// The hash of the code that was deployed. code_hash: CryptoHash, }