From 18e50b5b7387413001512818fed4f236c2c5b0c3 Mon Sep 17 00:00:00 2001 From: karim-en <karim@aurora.dev> Date: Tue, 10 Sep 2024 23:50:12 +0100 Subject: [PATCH 1/5] Make view method pausable --- .../tests/contracts/pausable/src/lib.rs | 1 + near-plugins-derive/tests/pausable.rs | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/near-plugins-derive/tests/contracts/pausable/src/lib.rs b/near-plugins-derive/tests/contracts/pausable/src/lib.rs index 9d7fec9..8c66f4a 100644 --- a/near-plugins-derive/tests/contracts/pausable/src/lib.rs +++ b/near-plugins-derive/tests/contracts/pausable/src/lib.rs @@ -57,6 +57,7 @@ impl Counter { } /// Returns the value of the counter. + #[pause] pub fn get_counter(&self) -> u64 { self.counter } diff --git a/near-plugins-derive/tests/pausable.rs b/near-plugins-derive/tests/pausable.rs index 6f5604a..446285f 100644 --- a/near-plugins-derive/tests/pausable.rs +++ b/near-plugins-derive/tests/pausable.rs @@ -341,6 +341,18 @@ async fn test_not_paused_with_different_key() -> anyhow::Result<()> { Ok(()) } +#[tokio::test] +async fn test_view_call_of_pausable_method() -> anyhow::Result<()> { + let setup = Setup::new().await?; + let res = setup + .call_counter_modifier(&setup.unauth_account, "increase_1") + .await?; + assert_success_with_unit_return(res); + assert_eq!(setup.get_counter().await?, 1); + + Ok(()) +} + #[tokio::test] async fn test_work_after_unpause() -> anyhow::Result<()> { let setup = Setup::new().await?; From 2849dddd28ee118499569d0ef78ce97ebab96801 Mon Sep 17 00:00:00 2001 From: karim-en <karim@aurora.dev> Date: Wed, 11 Sep 2024 00:34:54 +0100 Subject: [PATCH 2/5] Fix near-sdk version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e5a4c57..1717d53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ keywords = ["near", "smart contract", "plugin"] [workspace.dependencies] bitflags = "1.3" -near-sdk = "5.2" +near-sdk = "=5.2" near-plugins = { path = "near-plugins" } near-plugins-derive = { path = "near-plugins-derive" } serde = "1" From c1d074b04e113b56cb1924dd390b7d3a3c744ad6 Mon Sep 17 00:00:00 2001 From: karim-en <karim@aurora.dev> Date: Wed, 11 Sep 2024 01:51:56 +0100 Subject: [PATCH 3/5] Skip except roles verification if it is empty --- near-plugins-derive/src/pausable.rs | 28 +++++++++++++---------- near-plugins-derive/tests/common/utils.rs | 10 ++++++++ near-plugins-derive/tests/pausable.rs | 15 +++++++++++- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/near-plugins-derive/src/pausable.rs b/near-plugins-derive/src/pausable.rs index bda149b..0429ac0 100644 --- a/near-plugins-derive/src/pausable.rs +++ b/near-plugins-derive/src/pausable.rs @@ -201,16 +201,20 @@ pub fn if_paused(attrs: TokenStream, item: TokenStream) -> TokenStream { } fn get_bypass_condition(args: &ExceptSubArgs) -> proc_macro2::TokenStream { - let except_roles = args.roles.clone(); - quote!( - let __except_roles: Vec<&str> = vec![#(#except_roles.into()),*]; - let __except_roles: Vec<String> = __except_roles.iter().map(|&x| x.into()).collect(); - let may_bypass = self.acl_has_any_role( - __except_roles, - ::near_sdk::env::predecessor_account_id() - ); - if may_bypass { - __check_paused = false; - } - ) + if args.roles.len() > 0 { + let except_roles = args.roles.clone(); + quote!( + let __except_roles: Vec<&str> = vec![#(#except_roles.into()),*]; + let __except_roles: Vec<String> = __except_roles.iter().map(|&x| x.into()).collect(); + let may_bypass = self.acl_has_any_role( + __except_roles, + ::near_sdk::env::predecessor_account_id() + ); + if may_bypass { + __check_paused = false; + } + ) + } else { + quote!() + } } diff --git a/near-plugins-derive/tests/common/utils.rs b/near-plugins-derive/tests/common/utils.rs index 2fb95a9..bbacb85 100644 --- a/near-plugins-derive/tests/common/utils.rs +++ b/near-plugins-derive/tests/common/utils.rs @@ -110,6 +110,16 @@ pub fn assert_method_is_paused(res: ExecutionFinalResult) { ); } +pub fn assert_view_method_is_paused(err: anyhow::Error) { + let err = format!("{:?}", err); + let must_contain = "Pausable: Method is paused"; + assert!( + err.contains(must_contain), + "Expected method to be paused, instead it failed with: {}", + err + ); +} + pub fn assert_pausable_escape_hatch_is_closed(res: ExecutionFinalResult, feature: &str) { let must_contain = format!("Pausable: {feature} must be paused to use this function"); assert_failure_with(res, &must_contain); diff --git a/near-plugins-derive/tests/pausable.rs b/near-plugins-derive/tests/pausable.rs index 446285f..d01bb71 100644 --- a/near-plugins-derive/tests/pausable.rs +++ b/near-plugins-derive/tests/pausable.rs @@ -7,6 +7,7 @@ use common::pausable_contract::PausableContract; use common::utils::{ assert_failure_with, assert_insufficient_acl_permissions, assert_method_is_paused, assert_pausable_escape_hatch_is_closed, assert_success_with, assert_success_with_unit_return, + assert_view_method_is_paused, }; use near_sdk::serde_json::json; use near_workspaces::network::Sandbox; @@ -319,6 +320,11 @@ async fn test_pause_with_all_allows_except() -> anyhow::Result<()> { .call_counter_modifier(&exempted_account, "increase_4") .await?; assert_success_with_unit_return(res); + let res = setup + .pausable_contract + .pa_unpause_feature(&setup.pause_manager, "ALL") + .await?; + assert_success_with(res, true); assert_eq!(setup.get_counter().await?, 4); Ok(()) } @@ -342,7 +348,7 @@ async fn test_not_paused_with_different_key() -> anyhow::Result<()> { } #[tokio::test] -async fn test_view_call_of_pausable_method() -> anyhow::Result<()> { +async fn test_pause_view_method() -> anyhow::Result<()> { let setup = Setup::new().await?; let res = setup .call_counter_modifier(&setup.unauth_account, "increase_1") @@ -350,6 +356,13 @@ async fn test_view_call_of_pausable_method() -> anyhow::Result<()> { assert_success_with_unit_return(res); assert_eq!(setup.get_counter().await?, 1); + let res = setup + .pausable_contract + .pa_pause_feature(&setup.pause_manager, "get_counter") + .await?; + assert_success_with(res, true); + assert_view_method_is_paused(setup.get_counter().await.unwrap_err()); + Ok(()) } From a9bc76f04ab8879671a6fc152a00735866eff29f Mon Sep 17 00:00:00 2001 From: karim-en <karim@aurora.dev> Date: Wed, 11 Sep 2024 02:22:43 +0100 Subject: [PATCH 4/5] Improve `near-sdk` version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 1717d53..747d1cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ keywords = ["near", "smart contract", "plugin"] [workspace.dependencies] bitflags = "1.3" -near-sdk = "=5.2" +near-sdk = ">=5.2, <5.4" near-plugins = { path = "near-plugins" } near-plugins-derive = { path = "near-plugins-derive" } serde = "1" From 61a9c369f14f3df3c15b87e4940a119ef499efc9 Mon Sep 17 00:00:00 2001 From: karim-en <karim@aurora.dev> Date: Wed, 11 Sep 2024 02:42:10 +0100 Subject: [PATCH 5/5] Replace `if/else` with early return --- near-plugins-derive/src/pausable.rs | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/near-plugins-derive/src/pausable.rs b/near-plugins-derive/src/pausable.rs index 0429ac0..3c109e4 100644 --- a/near-plugins-derive/src/pausable.rs +++ b/near-plugins-derive/src/pausable.rs @@ -201,20 +201,20 @@ pub fn if_paused(attrs: TokenStream, item: TokenStream) -> TokenStream { } fn get_bypass_condition(args: &ExceptSubArgs) -> proc_macro2::TokenStream { - if args.roles.len() > 0 { - let except_roles = args.roles.clone(); - quote!( - let __except_roles: Vec<&str> = vec![#(#except_roles.into()),*]; - let __except_roles: Vec<String> = __except_roles.iter().map(|&x| x.into()).collect(); - let may_bypass = self.acl_has_any_role( - __except_roles, - ::near_sdk::env::predecessor_account_id() - ); - if may_bypass { - __check_paused = false; - } - ) - } else { - quote!() + if args.roles.len() == 0 { + return quote!(); } + + let except_roles = args.roles.clone(); + quote!( + let __except_roles: Vec<&str> = vec![#(#except_roles.into()),*]; + let __except_roles: Vec<String> = __except_roles.iter().map(|&x| x.into()).collect(); + let may_bypass = self.acl_has_any_role( + __except_roles, + ::near_sdk::env::predecessor_account_id() + ); + if may_bypass { + __check_paused = false; + } + ) }