Skip to content
Merged
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
20 changes: 20 additions & 0 deletions near-plugins-derive/tests/common/key.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use workspaces::result::ExecutionFinalResult;
use workspaces::types::{AccessKeyInfo, PublicKey};
use workspaces::{Account, AccountId, Contract};

/// Creates a transaction signed by `signer` to delete `key` from `contract`.
pub async fn delete_access_key(
signer: &Account,
contract: &AccountId,
key: PublicKey,
) -> workspaces::Result<ExecutionFinalResult> {
signer.batch(contract).delete_key(key).transact().await
}

/// Panics if access key info cannot be retrieved.
pub async fn get_access_key_infos(contract: &Contract) -> Vec<AccessKeyInfo> {
contract
.view_access_keys()
.await
.expect("Should retrieve access keys")
}
1 change: 1 addition & 0 deletions near-plugins-derive/tests/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod access_controllable_contract;
pub mod key;
pub mod ownable_contract;
pub mod pausable_contract;
pub mod repo;
Expand Down
18 changes: 18 additions & 0 deletions near-plugins-derive/tests/common/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,24 @@ pub fn assert_failure_with(res: ExecutionFinalResult, must_contain: &str) {
);
}

pub fn assert_access_key_not_found_error(
res: workspaces::Result<ExecutionFinalResult, workspaces::error::Error>,
) {
let err = res.expect_err("Transaction should not have been executed");

// Debug formatting is required to get the full error message containing `AccessKeyNotFound`.
// Assume that is acceptable since this function is avaible only in tests.
let err = format!("{:?}", err);
let must_contain = "InvalidAccessKeyError";

assert!(
err.contains(must_contain),
"The expected message\n'{}'\nis not contained in error\n'{}'",
must_contain,
err,
);
}

/// Returns the block timestamp in nanoseconds. Panics on failure.
async fn block_timestamp(worker: &Worker<Sandbox>) -> u64 {
worker
Expand Down
50 changes: 48 additions & 2 deletions near-plugins-derive/tests/ownable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
pub mod common;

use anyhow::Ok;
use common::key::{delete_access_key, get_access_key_infos};
use common::ownable_contract::OwnableContract;
use common::utils::{
assert_only_owner_permission_failure, assert_ownable_permission_failure,
assert_owner_update_failure, assert_success_with,
assert_access_key_not_found_error, assert_only_owner_permission_failure,
assert_ownable_permission_failure, assert_owner_update_failure, assert_success_with,
};
use near_sdk::serde_json::json;
use std::path::Path;
Expand Down Expand Up @@ -309,3 +310,48 @@ async fn test_only_self_owner_fail() -> anyhow::Result<()> {

Ok(())
}

/// Verifies that the contract cannot set a new owner after its access keys are removed.
#[tokio::test]
async fn test_removing_contract_keys_freezes_owner() -> anyhow::Result<()> {
let worker = workspaces::sandbox().await?;
let owner = worker.dev_create_account().await?;
let setup = Setup::new(worker, Some(owner.id().clone())).await?;

setup.assert_owner_is(Some(owner.id())).await;

// Remove the contract's access key.
let contract_key = setup.contract.as_account().secret_key().public_key();
delete_access_key(
setup.contract.as_account(),
setup.contract.id(),
contract_key,
)
.await?
.into_result()?;

// Assert the contract has no access keys anymore.
let access_key_infos = get_access_key_infos(&setup.contract).await;
assert_eq!(access_key_infos.len(), 0, "There should be no access keys");

// Remove the current owner.
setup
.ownable_contract
.owner_set(&owner, None)
.await?
.into_result()?;
setup.assert_owner_is(None).await;

// Verify setting a new owner fails since the contract has no access keys.
let res = setup
.ownable_contract
.owner_set(
setup.contract.as_account(),
Some(setup.unauth_account.id().clone()),
)
.await;
assert_access_key_not_found_error(res);
setup.assert_owner_is(None).await;

Ok(())
}