diff --git a/.gitignore b/.gitignore index 59fb25e..5a86e8a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ /target Cargo.lock near-plugins-derive/tests/contracts/*/target -examples/target # Ignore IDE data .vscode/ diff --git a/README.md b/README.md index 0219382..f9b9991 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,7 @@ using near-sdk-rs and `#[near_bindgen]` macro. Documentation and implementation details of each plugin can be found in the source code, primarily in the [traits](/near-plugins/src/) which define plugin behavior. Events emitted by each plugin are also described in the [source code](/near-plugins-derive/src/) of each macro. Each event follows [NEP-297](https://nomicon.io/Standards/EventsFormat). -The following sections provide an overview of all available plugins. More examples and usage patterns are available in: - -- [`examples/`](/examples/) -- [`near-plugins-derive/tests/contracts/`](/near-plugins-derive/tests/contracts/) +The following sections provide an overview of all available plugins. More examples and usage patterns are available in [tests](/near-plugins-derive/tests) and [demo contracts](/near-plugins-derive/tests/contracts/). ### [Ownable](/near-plugins/src/ownable.rs) @@ -21,33 +18,6 @@ Basic access control mechanism that allows _only_ an authorized account id to ca Documentation of all methods provided by the derived implementation of `Ownable` is available in the [definition of the trait](/near-plugins/src/ownable.rs). -### [Full Access Key Fallback](/near-plugins/src/full_access_key_fallback.rs) - -Allows an authorized account to attach a Full Access Key to the contract. - -Contract example using _Full Access Key Fallback_ plugin. Note that it requires the contract to be Ownable. - -```rust -#[near_bindgen] -#[derive(Ownable, FullAccessKeyFallback)] -struct Counter { - counter: u64 -} - -#[near_bindgen] -impl Counter { - /// Specify the owner of the contract in the constructor - #[init] - fn new() -> Self { - let contract = Self { counter: 0 }; - contract.owner_set(Some(near_sdk::env::predecessor_account_id())); - contract - } -} -``` - -Documentation of all methods provided by the derived implementation of `FullAccessKeyFallback` is available in the [definition of the trait](/near-plugins/src/full_access_key_fallback.rs). More examples and guidelines for interacting with a `FullAccessKeyFallback` contract can be found [here](/examples/full-access-key-fallback-examples/README.md). - ### [Pausable](/near-plugins/src/pausable.rs) Allow contracts to implement an emergency stop mechanism that can be triggered by an authorized account. Pauses can be diff --git a/examples/.catalog-info.yaml b/examples/.catalog-info.yaml deleted file mode 100644 index 4b99648..0000000 --- a/examples/.catalog-info.yaml +++ /dev/null @@ -1,18 +0,0 @@ ---- -apiVersion: backstage.io/v1alpha1 -kind: Component -metadata: - name: near-plugins-examples - description: |- - Aurora Engine Tests - tags: - - near - links: [] - annotations: - aurora.dev/security-tier: "-1" -spec: - owner: protocol-team - type: contract-examples - lifecycle: production - system: near-plugins - interactsWith: [] diff --git a/examples/Cargo.toml b/examples/Cargo.toml deleted file mode 100644 index ecaf5e4..0000000 --- a/examples/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[workspace] - -members = [ - "full-access-key-fallback-examples/full_access_key_fallback_base", - "near-plugins-test-utils", -] diff --git a/examples/build.sh b/examples/build.sh deleted file mode 100755 index edc6224..0000000 --- a/examples/build.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash - -# Create res folder if not exists -mkdir -p res - -# Iterate over each member -for dir in ./*/*/ -do - echo $dir - # Run build.sh for each member if exsists - if [ -f "$dir/build.sh" ]; then - cd $dir - ./build.sh - cd ../.. - fi -done - -# Copy wasm fils -cp ./target/wasm32-unknown-unknown/release/*.wasm ./res/ diff --git a/examples/full-access-key-fallback-examples/README.md b/examples/full-access-key-fallback-examples/README.md deleted file mode 100644 index 4063ffd..0000000 --- a/examples/full-access-key-fallback-examples/README.md +++ /dev/null @@ -1,137 +0,0 @@ -# Example of using Full Access Key Fallback plugin - -Allows an authorized account to attach a Full Access Key to the contract. - -Contract example using Full Access Key Fallback plugin. Note that it requires the contract to be Ownable. - -```rust -use near_plugins::{Ownable, FullAccessKeyFallback}; -use near_sdk::near_bindgen; -use near_plugins_derive::only; -use borsh::{BorshSerialize, BorshDeserialize}; - -#[near_bindgen] -#[derive(Ownable, FullAccessKeyFallback, Default, BorshSerialize, BorshDeserialize)] -struct Counter { - counter: u64, -} - -#[near_bindgen] -impl Counter { - /// Specify the owner of the contract in the constructor - #[init] - pub fn new() -> Self { - let mut contract = Self { counter: 0 }; - contract.owner_set(Some(near_sdk::env::predecessor_account_id())); - contract - } - - /// *Only* self account can call this method. This can be used even if the contract is not Ownable. - #[only(self)] - pub fn protected_self(&mut self) { - self.counter += 1; - } - - pub fn get_counter(&self) -> u64 { - self.counter - } -} -``` - -## The contract methods description -### attach_full_access_key -`attach_full_access_key` is a method that attaches new full access for the current account. -Only the owner of the contract can use this function. - -```shell -near call attach_full_access_key '{"public_key": "ed25519:ErVTCTvmepb4NDhQ7infTomkLVsd1iTWwLR84FBhV7UC"}' --accountId -``` - -If the method succeeds, the following event will be emitted: -```json -{ - "standard":"FullAccessKeyFallback", - "version":"1.0.0", - "event":"full_access_key_added", - "data": { - "by":"", - "public_key":"ed25519:ErVTCTvmepb4NDhQ7infTomkLVsd1iTWwLR84FBhV7UC" - } -} -``` - -## Preparation steps for demonstration -In that document, we are providing some examples of using a contract with a full access key fallback plugin. You also can explore the usage examples in the tests in `./full_access_key_fallback_base/src/lib.rs`. For running tests, please take a look at the **Test running instruction** section. - -1. **Creating an account on testnet** - For demonstration let's create 2 accounts: ``, `` - ```shell - $ near create-account ..testnet --masterAccount .testnet --initialBalance 10 - $ near create-account ..testnet --masterAccount .testnet --initialBalance 10 - ``` - - In the next section we will refer to the `..testnet` as ``, - to the `..testnet` as ``. - -2. **Compile Contract to wasm file** - For compiling the contract just run the `full_access_key_fallback_base/build.sh` script. The target file with compiled contract will be `../target/wasm32-unknown-unknown/release/full_access_key_fallback_base.wasm` - - ```shell - $ cd full_access_key_fallback_base - $ ./build.sh - $ cd .. - ``` - -3. **Deploy and init a contract** - ```shell - $ near deploy --accountId --wasmFile ../target/wasm32-unknown-unknown/release/full_access_key_fallback_base.wasm --initFunction new --initArgs '{}' - ``` - -## Example of using the contract with full access key fallback plugin -Our plan for this example is to remove the full access key and after that bring it back. -The keys usually storage at `$HOME/.near-credentials/testnet/.json`. Also let's -choose some operations that only the contract with a full access key can do, for example, -money transfer. - -Moving ownership rights to the Alice account. -```shell -$ near call owner_set '{"owner": ""}' --accountId -``` - -Checking that currently we can transfer the money for example to Alice account -```shell -$ near send 1 -``` - -Removing the full access key for contract account -```shell -$ near delete-key "ed25519:ErVTCTvmepb4NDhQ7infTomkLVsd1iTWwLR84FBhV7UC" -``` -The value of public-key can be found at `$HOME/.near-credentials/testnet/.json`. - -Checking, that now the money transfer will not work: -```shell -$ near send 1 -ERROR -``` - -Adding the key back and check that it will work -```shell -$ near call attach_full_access_key '{"public_key": "ed25519:ErVTCTvmepb4NDhQ7infTomkLVsd1iTWwLR84FBhV7UC"}' --accountId -$ near send 1 -``` - -## Tests running instruction -Tests in `full_access_key_fallback_base/src/lib.rs` contain examples of interaction with a contract. - -For running test: -1. Generate `wasm` file by running `build.sh` script. The target file will be `../target/wasm32-unknown-unknown/release/full_access_key_fallback_base.wasm` -2. Run tests `cargo test` - -```shell -$ cd full_access_key_fallback_base -$ ./build.sh -$ cargo test -``` - -For tests, we use `workspaces` library and `sandbox` environment. For details, you can explore `../near-plugins-test-utils` crate. \ No newline at end of file diff --git a/examples/full-access-key-fallback-examples/full_access_key_fallback_base/Cargo.toml b/examples/full-access-key-fallback-examples/full_access_key_fallback_base/Cargo.toml deleted file mode 100644 index eb14035..0000000 --- a/examples/full-access-key-fallback-examples/full_access_key_fallback_base/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "full_access_key_fallback_base" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[lib] -crate-type = ["cdylib", "rlib"] - -[dependencies] -near-sdk = "4.0.0" -near-plugins = { path = "../../../near-plugins" } -near-plugins-derive = { path = "../../../near-plugins-derive" } -borsh = "0.9.3" - -[dev-dependencies] -near-primitives = "0.14.0" -workspaces = "0.6" -tokio = { version = "1.1", features = ["rt", "macros"] } -serde_json = "1.0.74" -near-plugins-test-utils = { path = "../../near-plugins-test-utils" } diff --git a/examples/full-access-key-fallback-examples/full_access_key_fallback_base/build.sh b/examples/full-access-key-fallback-examples/full_access_key_fallback_base/build.sh deleted file mode 100755 index cf95844..0000000 --- a/examples/full-access-key-fallback-examples/full_access_key_fallback_base/build.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -mkdir -p ../../res - -RUSTFLAGS='-C link-arg=-s' cargo build --target wasm32-unknown-unknown --release - -cp ../../target/wasm32-unknown-unknown/release/full_access_key_fallback_base.wasm ../../res/ \ No newline at end of file diff --git a/examples/full-access-key-fallback-examples/full_access_key_fallback_base/src/lib.rs b/examples/full-access-key-fallback-examples/full_access_key_fallback_base/src/lib.rs deleted file mode 100644 index 329d4ba..0000000 --- a/examples/full-access-key-fallback-examples/full_access_key_fallback_base/src/lib.rs +++ /dev/null @@ -1,82 +0,0 @@ -use near_plugins::{Ownable, FullAccessKeyFallback}; -use near_sdk::near_bindgen; -use near_plugins_derive::only; -use borsh::{BorshSerialize, BorshDeserialize}; - -#[near_bindgen] -#[derive(Ownable, FullAccessKeyFallback, Default, BorshSerialize, BorshDeserialize)] -struct Counter { - counter: u64, -} - -#[near_bindgen] -impl Counter { - /// Specify the owner of the contract in the constructor - #[init] - pub fn new() -> Self { - let mut contract = Self { counter: 0 }; - contract.owner_set(Some(near_sdk::env::predecessor_account_id())); - contract - } - - /// *Only* self account can call this method. This can be used even if the contract is not Ownable. - #[only(self)] - pub fn protected_self(&mut self) { - self.counter += 1; - } - - pub fn get_counter(&self) -> u64 { - self.counter - } -} - - -#[cfg(test)] -mod tests { - use serde_json::json; - use near_sdk::AccountId; - use near_plugins_test_utils::*; - - const WASM_FILEPATH: &str = "../../target/wasm32-unknown-unknown/release/full_access_key_fallback_base.wasm"; - - #[tokio::test] - async fn base_scenario() { - let (mut contract_holder, contract) = get_contract_testnet(WASM_FILEPATH).await; - - assert!(call!(contract,"new").await); - - check_counter(&contract, 0).await; - - let next_owner = get_subaccount(&contract_holder, "next_owner").await; - assert!(call!(contract, "owner_set", &json!({"owner": next_owner.id()})).await); - let current_owner: Option:: = view!(contract, "owner_get"); - assert_eq!(current_owner.unwrap().as_str(), next_owner.id().as_str()); - - assert!(call!(&contract_holder, contract, "protected_self").await); - - check_counter(&contract, 1).await; - - contract_holder.set_secret_key(next_owner.secret_key().clone()); - - assert!(call!(&next_owner, contract, "attach_full_access_key", &json!({"public_key": next_owner.secret_key().public_key()})).await); - - assert!(call!(contract, "protected_self").await); - - check_counter(&contract, 2).await; - } - - #[tokio::test] - #[should_panic(expected = "AccessKeyNotFound")] - async fn base_panic_on_wrong_key() { - let (mut contract_holder, contract) = get_contract_testnet(WASM_FILEPATH).await; - - assert!(call!(contract, "new").await); - let next_owner = get_subaccount(&contract_holder, "next_owner").await; - assert!(call!(contract, "owner_set", &json!({"owner": next_owner.id()})).await); - - assert!(call!(&contract_holder, contract, "protected_self").await); - contract_holder.set_secret_key(next_owner.secret_key().clone()); - - call!(&contract_holder, contract, "protected_self").await; - } -} diff --git a/examples/near-plugins-test-utils/Cargo.toml b/examples/near-plugins-test-utils/Cargo.toml deleted file mode 100644 index cb19282..0000000 --- a/examples/near-plugins-test-utils/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "near-plugins-test-utils" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[lib] -crate-type = ["cdylib", "rlib"] - -[dependencies] -near-sdk = "4.0.0" -borsh = "0.9.3" -near-primitives = "0.14.0" -workspaces = "0.6" -tokio = { version = "1.1", features = ["rt", "macros"] } -serde_json = "1.0.74" diff --git a/examples/near-plugins-test-utils/src/lib.rs b/examples/near-plugins-test-utils/src/lib.rs deleted file mode 100644 index 9f21063..0000000 --- a/examples/near-plugins-test-utils/src/lib.rs +++ /dev/null @@ -1,90 +0,0 @@ -use serde_json::json; -use near_sdk::ONE_NEAR; -use workspaces::{Account, Contract}; - -pub async fn get_contract(wasm_path: &str) -> (Account, Contract) { - let worker = workspaces::sandbox().await.unwrap(); - - let owner = worker.root_account().unwrap(); - - let wasm = std::fs::read(wasm_path).unwrap(); - let contract = owner.deploy(&wasm).await.unwrap().unwrap(); - - (owner, contract) -} - -pub async fn view(contract: &Contract, method_name: &str, args: &serde_json::Value) -> Vec { - contract.view(method_name, - args.to_string().into_bytes()).await.unwrap().result -} - -pub async fn call_arg(contract: &Contract, method_name: &str, args: &serde_json::Value) -> bool { - contract - .call(method_name) - .args_json(args) - .max_gas() - .transact() - .await - .unwrap() - .is_success() -} - -pub async fn call_by_with_arg(account: &Account, contract: &Contract, method_name: &str, args: &serde_json::Value) -> bool { - account.call(contract.id(), method_name) - .args_json(args) - .max_gas() - .transact() - .await.unwrap().is_success() -} - -pub async fn get_subaccount(account: &Account, new_account_name: &str) -> Account { - account - .create_subaccount(new_account_name) - .initial_balance(ONE_NEAR) - .transact() - .await - .unwrap() - .unwrap() -} - -pub async fn get_contract_testnet(wasm_file: &str) -> (Account, Contract) { - let worker = workspaces::testnet().await.unwrap(); - - let wasm = std::fs::read(wasm_file).unwrap(); - let contract: Contract = worker.dev_deploy(&wasm).await.unwrap(); - - let owner = contract.as_account(); - - (owner.clone(), contract) -} - -#[macro_export] -macro_rules! view { - ($contract:ident, $method_name:literal) => { - serde_json::from_slice(&view(&$contract, $method_name, &json!({})).await).unwrap() - }; - ($contract:ident, $method_name:literal, $args:expr) => { - serde_json::from_slice(&view(&$contract, $method_name, $args).await).unwrap() - }; -} - -#[macro_export] -macro_rules! call { - ($contract:ident, $method_name:literal) => { - call_arg(&$contract, $method_name, &json!({})) - }; - ($contract:ident, $method_name:literal, $args:expr) => { - call_arg(&$contract, $method_name, $args) - }; - ($account:expr, $contract:ident, $method_name:literal) => { - call_by_with_arg($account, &$contract, $method_name, &json!({})) - }; - ($account:expr, $contract:ident, $method_name:literal, $args:expr) => { - call_by_with_arg($account, &$contract, $method_name, $args) - }; -} - -pub async fn check_counter(contract: &Contract, expect_counter: u64) { - let counter: u64 = view!(contract, "get_counter"); - assert_eq!(counter, expect_counter); -} \ No newline at end of file diff --git a/near-plugins-derive/src/full_access_key_fallback.rs b/near-plugins-derive/src/full_access_key_fallback.rs deleted file mode 100644 index e7ec512..0000000 --- a/near-plugins-derive/src/full_access_key_fallback.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::utils::cratename; -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(); - - let input = parse_macro_input!(input); - let DeriveInput { ident, .. } = input; - - let output = quote! { - #[near_bindgen] - impl FullAccessKeyFallback for #ident { - #[#cratename::only(owner)] - fn attach_full_access_key(&mut self, public_key: ::near_sdk::PublicKey) -> near_sdk::Promise { - let current_account_id = ::near_sdk::env::current_account_id(); - // TODO: Use .emit - ::near_sdk::log!(#cratename::events::AsEvent::event( - &#cratename::full_access_key_fallback::FullAccessKeyAdded { - by: current_account_id.clone(), - public_key: public_key.clone() - } - )); - ::near_sdk::Promise::new(current_account_id).add_full_access_key(public_key) - } - } - }; - - output.into() -} diff --git a/near-plugins-derive/src/lib.rs b/near-plugins-derive/src/lib.rs index 31b3c9e..3a360dc 100644 --- a/near-plugins-derive/src/lib.rs +++ b/near-plugins-derive/src/lib.rs @@ -2,7 +2,6 @@ use proc_macro::{self, TokenStream}; mod access_control_role; mod access_controllable; -mod full_access_key_fallback; mod ownable; mod pausable; mod upgradable; @@ -26,12 +25,6 @@ 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 { diff --git a/near-plugins-derive/tests/common/full_access_key_fallback_contract.rs b/near-plugins-derive/tests/common/full_access_key_fallback_contract.rs deleted file mode 100644 index cd076e8..0000000 --- a/near-plugins-derive/tests/common/full_access_key_fallback_contract.rs +++ /dev/null @@ -1,35 +0,0 @@ -use near_sdk::serde_json::json; -use near_sdk::PublicKey; -use workspaces::result::ExecutionFinalResult; -use workspaces::{Account, Contract}; - -/// Wrapper for a contract that uses `#[full_access_key_fallback]`. It allows implementing helpers -/// for calling contract methods. -pub struct FullAccessKeyFallbackContract { - contract: Contract, -} - -impl FullAccessKeyFallbackContract { - pub fn new(contract: Contract) -> Self { - Self { contract } - } - - pub fn contract(&self) -> &Contract { - &self.contract - } - - /// The `Promise` returned by trait method `attach_full_access_key` is resolved in the - /// workspaces transaction. - pub async fn attach_full_access_key( - &self, - caller: &Account, - public_key: PublicKey, - ) -> workspaces::Result { - caller - .call(self.contract.id(), "attach_full_access_key") - .args_json(json!({ "public_key": public_key })) - .max_gas() - .transact() - .await - } -} diff --git a/near-plugins-derive/tests/common/mod.rs b/near-plugins-derive/tests/common/mod.rs index 16a9f4a..3a389cc 100644 --- a/near-plugins-derive/tests/common/mod.rs +++ b/near-plugins-derive/tests/common/mod.rs @@ -1,5 +1,4 @@ pub mod access_controllable_contract; -pub mod full_access_key_fallback_contract; pub mod ownable_contract; pub mod pausable_contract; pub mod repo; diff --git a/near-plugins-derive/tests/contracts/full_access_key_fallback/Cargo.toml b/near-plugins-derive/tests/contracts/full_access_key_fallback/Cargo.toml deleted file mode 100644 index e9e34ea..0000000 --- a/near-plugins-derive/tests/contracts/full_access_key_fallback/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "full_access_key_fallback" -version = "0.0.0" -edition = "2018" - -[lib] -crate-type = ["cdylib", "rlib"] - -[dependencies] -near-plugins = { path = "../../../../near-plugins" } -near-sdk = "4.1.0" - -[profile.release] -codegen-units = 1 -opt-level = "z" -lto = true -debug = false -panic = "abort" -overflow-checks = true - -[workspace] diff --git a/near-plugins-derive/tests/contracts/full_access_key_fallback/Makefile b/near-plugins-derive/tests/contracts/full_access_key_fallback/Makefile deleted file mode 100644 index aa2cf20..0000000 --- a/near-plugins-derive/tests/contracts/full_access_key_fallback/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -build: - cargo build --target wasm32-unknown-unknown --release - -# Helpful for debugging. Requires `cargo-expand`. -expand: - cargo expand > expanded.rs - -.PHONY: build expand diff --git a/near-plugins-derive/tests/contracts/full_access_key_fallback/rust-toolchain b/near-plugins-derive/tests/contracts/full_access_key_fallback/rust-toolchain deleted file mode 100644 index 2f3cf78..0000000 --- a/near-plugins-derive/tests/contracts/full_access_key_fallback/rust-toolchain +++ /dev/null @@ -1,3 +0,0 @@ -[toolchain] -channel = "1.66.1" -components = ["clippy", "rustfmt"] diff --git a/near-plugins-derive/tests/contracts/full_access_key_fallback/src/lib.rs b/near-plugins-derive/tests/contracts/full_access_key_fallback/src/lib.rs deleted file mode 100644 index 33a4e83..0000000 --- a/near-plugins-derive/tests/contracts/full_access_key_fallback/src/lib.rs +++ /dev/null @@ -1,21 +0,0 @@ -use near_plugins::{FullAccessKeyFallback, Ownable}; -use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; -use near_sdk::{near_bindgen, AccountId, PanicOnDefault}; - -/// Deriving `FullAccessKeyFallback` requires the contract to be `Ownable.` -#[near_bindgen] -#[derive(Ownable, FullAccessKeyFallback, PanicOnDefault, BorshDeserialize, BorshSerialize)] -pub struct Counter; - -#[near_bindgen] -impl Counter { - /// Optionally set the owner in the constructor. - #[init] - pub fn new(owner: Option) -> Self { - let mut contract = Self; - if owner.is_some() { - contract.owner_set(owner); - } - contract - } -} diff --git a/near-plugins-derive/tests/full_access_key_fallback.rs b/near-plugins-derive/tests/full_access_key_fallback.rs deleted file mode 100644 index 5595448..0000000 --- a/near-plugins-derive/tests/full_access_key_fallback.rs +++ /dev/null @@ -1,180 +0,0 @@ -// Using `pub` to avoid invalid `dead_code` warnings, see -// https://users.rust-lang.org/t/invalid-dead-code-warning-for-submodule-in-integration-test/80259 -pub mod common; - -use anyhow::Ok; -use common::full_access_key_fallback_contract::FullAccessKeyFallbackContract; -use common::utils::{assert_only_owner_permission_failure, assert_success_with_unit_return}; -use near_sdk::serde::Deserialize; -use near_sdk::serde_json::{from_value, json}; -use std::iter; -use std::path::Path; -use workspaces::network::Sandbox; -use workspaces::types::{AccessKeyPermission, PublicKey}; -use workspaces::{Account, AccountId, Contract, Worker}; - -const PROJECT_PATH: &str = "./tests/contracts/full_access_key_fallback"; - -/// Returns a new PublicKey that can be used in tests. -/// -/// It returns a `near_sdk::PublicKey` since that's the type required for -/// `FullAccessKeyFallback::attach_full_access_key`. -fn new_public_key() -> near_sdk::PublicKey { - "ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp" - .parse() - .unwrap() -} - -/// Converts a `near_sdk::PublicKey` to a `workspaces::types::PublicKey`. -fn pk_sdk_to_workspaces(public_key: near_sdk::PublicKey) -> PublicKey { - // Going via json since there seems to be no direct conversion, see this issue: - // https://github.com/near/workspaces-rs/issues/262 - #[derive(Deserialize)] - #[serde(crate = "near_sdk::serde")] - struct Wrapper { - public_key: PublicKey, - } - - let ser = json!({ "public_key": public_key }); - from_value::(ser).unwrap().public_key -} - -/// Allows spinning up a setup for testing the contract in [`PROJECT_PATH`] and bundles related -/// resources. -struct Setup { - /// Instance of the deployed contract. - contract: Contract, - /// Wrapper around the deployed contract that facilitates interacting with methods provided by - /// the `FullAccessKeyFallback` plugin. - fa_key_fallback_contract: FullAccessKeyFallbackContract, - /// A newly created account without any `Ownable` permissions. - unauth_account: Account, -} - -impl Setup { - /// Deploys and initializes the contract in [`PROJECT_PATH`] and returns a new `Setup`. - /// - /// The `owner` parameter is passed on to the contract's constructor, allowing to optionally set - /// the owner during initialization. - async fn new(worker: Worker, owner: Option) -> anyhow::Result { - // Compile and deploy the contract. - let wasm = - common::repo::compile_project(Path::new(PROJECT_PATH), "full_access_key_fallback") - .await?; - let contract = worker.dev_deploy(&wasm).await?; - let fa_key_fallback_contract = FullAccessKeyFallbackContract::new(contract.clone()); - - // Call the contract's constructor. - contract - .call("new") - .args_json(json!({ - "owner": owner, - })) - .max_gas() - .transact() - .await? - .into_result()?; - - let unauth_account = worker.dev_create_account().await?; - Ok(Self { - contract, - fa_key_fallback_contract, - unauth_account, - }) - } - - /// Asserts the contract's access keys are: - /// - /// - the contracts own key plus - /// - the keys specified in `keys` - /// - /// with the order of keys being irrelevant. - /// - /// Moreover, it asserts that all access keys have `FullAccess` permission. - /// - /// Input parameter `keys` is expected to not contain duplicates. - async fn assert_full_access_keys(&self, keys: &[PublicKey]) { - // Assert the number of keys. - let access_key_infos = self - .contract - .view_access_keys() - .await - .expect("Should view access keys"); - assert_eq!( - access_key_infos.len(), - keys.len() + 1, // + 1 for the contract's key - ); - - // Assert the attached access keys are the ones we expected and all have `FullAccess`. - // - // Since `workspaces::types::PublicKey` doesn't implement `Hash`, it cannot be stored in - // `std::collections::HashSet`. Hence the search in `access_key_infos` with - // `find()`. - let contract_key = self.contract.as_account().secret_key().public_key(); - let expected_keys = iter::once(&contract_key).chain(keys.iter()); - for expected_key in expected_keys { - let attached_key = access_key_infos - .iter() - .find(|info| &info.public_key == expected_key) - .unwrap_or_else(|| panic!("PublicKey {:?} is not attached", expected_key)); - - assert!( - matches!( - attached_key.access_key.permission, - AccessKeyPermission::FullAccess, - ), - "Unexpected permission of access key {:?}: {:?}", - attached_key, - attached_key.access_key.permission, - ); - } - } -} - -/// Smoke test of contract setup. -#[tokio::test] -async fn test_setup() -> anyhow::Result<()> { - let worker = workspaces::sandbox().await?; - let _ = Setup::new(worker, None).await?; - - Ok(()) -} - -#[tokio::test] -async fn test_non_owner_cannot_attach_full_access_key() -> anyhow::Result<()> { - let worker = workspaces::sandbox().await?; - let owner = worker.dev_create_account().await?; - let setup = Setup::new(worker, Some(owner.id().clone())).await?; - - let new_fak = new_public_key(); - let res = setup - .fa_key_fallback_contract - .attach_full_access_key(&setup.unauth_account, new_fak) - .await?; - assert_only_owner_permission_failure(res); - - Ok(()) -} - -#[tokio::test] -async fn test_attach_full_access_key() -> anyhow::Result<()> { - let worker = workspaces::sandbox().await?; - let owner = worker.dev_create_account().await?; - let setup = Setup::new(worker, Some(owner.id().clone())).await?; - - // Initially there's just the contract's access key. - setup.assert_full_access_keys(&[]).await; - - // Owner may attach a full access key. - let new_fak = new_public_key(); - let res = setup - .fa_key_fallback_contract - .attach_full_access_key(&owner, new_fak.clone()) - .await?; - assert_success_with_unit_return(res); - setup - .assert_full_access_keys(&[pk_sdk_to_workspaces(new_fak)]) - .await; - - Ok(()) -} diff --git a/near-plugins/src/full_access_key_fallback.rs b/near-plugins/src/full_access_key_fallback.rs deleted file mode 100644 index 70b3f07..0000000 --- a/near-plugins/src/full_access_key_fallback.rs +++ /dev/null @@ -1,44 +0,0 @@ -//! # Full Access Key Fallback -//! -//! Smart contracts can be considered trustless, when there is no Full Access Key (FAK) -//! attached to it. Otherwise owner of the FAK can redeploy or use the funds stored on -//! the smart contract. -//! -//! However some times a FAK is required in order to prevent or fix an unexpected event. -//! This trait allows the contract not to have a FAK, and add one when needed using a -//! custom mechanism. -//! -//! ## Default implementation: -//! -//! Contract must be Ownable. Only the owner can attach a new FAK. -//! The owner can be set to any arbitrary NEAR account id, for example a DAO. -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; - // fn attach_full_access_key(&mut self, public_key: PublicKey); -} - -/// 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, -} - -impl AsEvent for FullAccessKeyAdded { - fn metadata(&self) -> EventMetadata { - EventMetadata { - standard: "FullAccessKeyFallback".to_string(), - version: "1.0.0".to_string(), - event: "full_access_key_added".to_string(), - data: Some(self.clone()), - } - } -} diff --git a/near-plugins/src/lib.rs b/near-plugins/src/lib.rs index 311915d..0f3b808 100644 --- a/near-plugins/src/lib.rs +++ b/near-plugins/src/lib.rs @@ -1,17 +1,15 @@ pub mod access_control_role; pub mod access_controllable; pub mod events; -pub mod full_access_key_fallback; pub mod ownable; pub mod pausable; pub mod upgradable; pub use access_control_role::AccessControlRole; pub use access_controllable::AccessControllable; -pub use full_access_key_fallback::FullAccessKeyFallback; pub use near_plugins_derive::{ - access_control, access_control_any, if_paused, only, pause, AccessControlRole, - FullAccessKeyFallback, Ownable, Pausable, Upgradable, + access_control, access_control_any, if_paused, only, pause, AccessControlRole, Ownable, + Pausable, Upgradable, }; pub use ownable::Ownable; pub use pausable::Pausable;