From 610a9f69250bb4468fb06c52d83423890541eca2 Mon Sep 17 00:00:00 2001 From: Moritz Date: Fri, 10 Feb 2023 19:36:19 +0100 Subject: [PATCH 1/3] Remove Upgradable examples --- examples/Cargo.toml | 2 - examples/upgradable-examples/README.md | 223 ------------------ .../up_stage_code/Cargo.toml | 16 -- .../up_stage_code/src/main.rs | 57 ----- .../upgradable_base/Cargo.toml | 24 -- .../upgradable_base/build.sh | 10 - .../upgradable_base/src/lib.rs | 89 ------- 7 files changed, 421 deletions(-) delete mode 100644 examples/upgradable-examples/README.md delete mode 100644 examples/upgradable-examples/up_stage_code/Cargo.toml delete mode 100644 examples/upgradable-examples/up_stage_code/src/main.rs delete mode 100644 examples/upgradable-examples/upgradable_base/Cargo.toml delete mode 100755 examples/upgradable-examples/upgradable_base/build.sh delete mode 100644 examples/upgradable-examples/upgradable_base/src/lib.rs diff --git a/examples/Cargo.toml b/examples/Cargo.toml index f434058..ecaf5e4 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -3,6 +3,4 @@ members = [ "full-access-key-fallback-examples/full_access_key_fallback_base", "near-plugins-test-utils", - "upgradable-examples/upgradable_base", - "upgradable-examples/up_stage_code", ] diff --git a/examples/upgradable-examples/README.md b/examples/upgradable-examples/README.md deleted file mode 100644 index 2bd9fd8..0000000 --- a/examples/upgradable-examples/README.md +++ /dev/null @@ -1,223 +0,0 @@ -# Example of using Upgradable plugin - -Allows a contract to be upgraded by the owner without having a Full Access Key. - -Contract example using Upgradable plugin. Note, that it requires the contract to be Ownable. - -```rust -use near_plugins::{Ownable, Upgradable}; -use near_sdk::near_bindgen; -use borsh::{BorshSerialize, BorshDeserialize}; - -#[near_bindgen] -#[derive(Ownable, Upgradable, Default, BorshSerialize, BorshDeserialize)] -struct Counter1 { - counter: u64, -} - -#[near_bindgen] -impl Counter1 { - /// 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 - } - - pub fn inc1(&mut self) { - self.counter += 1; - } - - pub fn get_counter(&self) -> u64 { - self.counter - } -} -``` - -The second contract: -```rust -#[near_bindgen] -#[derive(Ownable, Upgradable, Default, BorshSerialize, BorshDeserialize)] -struct Counter2 { - counter: u64, -} - -#[near_bindgen] -impl Counter2 { - #[init] - pub fn new() -> Self { - let mut contract = Self { counter: 0 }; - contract.owner_set(Some(near_sdk::env::predecessor_account_id())); - contract - } - - pub fn inc2(&mut self) { - self.counter += 2; - } - - pub fn get_counter(&self) -> u64 { - self.counter - } -} -``` - -To upgrade the contract first call up_stage_code passing the binary as first argument serialized as borsh. Then call up_deploy_code. This functions must be called from the owner. - -## The contract methods description -### up_storage_key -`up_storage_prefix` is a _view_ method that returns the storage prefix for slots related to upgradable. -By default, `b"__up__"` is used. For changing, the attribute `upgradable` can be used. - -```shell -$ near view up_storage_prefix -View call: .up_storage_prefix() -[ - 95, 95, 117, - 112, 95, 95 -] -$ python3 ->>> print(' '.join(str(b) for b in bytes("__up__", 'utf8'))) -95 95 117 112 95 95 -``` - -Example of changing the storage prefix: -```rust -#[near_bindgen] -#[derive(Ownable, Upgradable, Default, BorshSerialize, BorshDeserialize)] -#[upgradable(storage_prefix="OTHER_CODE_STORAGE_PREFIX")] -struct Counter { - counter: u64, -} -``` - -### up_stage_code -`up_stage_code` is the method to stage some code to be potentially deployed later. If a previous code was staged but not deployed, it is discarded. -The method can be called only by the owner. - -```shell -$ export CODE=$(cat ../upgradable_base_second/target/wasm32-unknown-unknown/release/upgradable_base_second.wasm | xxd -ps | sed -z 's/\n//g') -$ near call up_stage_code --base64 $CODE --accountId -``` - -But it doesn't work in that way because we can't provide in Bash so long args... So, probable here we can't use just NEAR CLI for interaction with the contract :( - -For running `up_satge_code` take a look on `up_stage_code/src/main.rs` script. -```shell -$ cd up_stage_code -$ cargo run -- -p '' -$ cd .. -``` -Where `` is `$HOME/.near-credentials/testnet/.json` - -### up_staged_code -`up_staged_code` is a _view_ method which returns a staged code. - -```shell -$ near call up_staged_code --accountId -``` - -### up_staged_code_hash -`up_staged_code_hash` is a _view_ method that returns the hash of the staged code. - -```shell -$ near view up_staged_code_hash -View call: .up_staged_code_hash() -[ - 63, 26, 245, 200, 217, 12, 109, 77, - 40, 222, 40, 173, 192, 197, 28, 236, - 231, 239, 19, 223, 212, 99, 98, 228, - 162, 119, 89, 37, 250, 173, 87, 5 -] -``` - -### up_deploy_code -`up_deploy_code` is a method that deploys a staged code. If no code is staged, the method fails. -The method can be called only by the owner. - -```shell -$ near call up_deploy_code --accountId -``` - -## Preparation steps for demonstration -In that document, we are providing some examples of using a contract with an access control plugin. You also can explore the usage examples in the tests in `upgradable_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 sections we will refer to the `..testnet` as ``, - to the `..testnet` as ``. - -2. **Compile 2 Contract to wasm file** - - ```shell - $ cd upgradable_base - $ ./build.sh - $ cd .. - ``` - - The contracts will be compiled into `../target/wasm32-unknown-unknown/release/upgradable_base.wasm` and `../target/wasm32-unknown-unknown/release/upgradable_base_second.wasm` - -3. **Deploy and init a contract** - ```shell - $ near deploy --accountId --wasmFile ../target/wasm32-unknown-unknown/release/upgradable_base.wasm --initFunction new --initArgs '{}' - ``` - -## Example of using the contract with upgradable plugin -### Upgrade contract -Currently on `` contract `Counter1` is deployed, and we would like to upgrade it to `Counter2`. - -#### Increment counter on first contract -```shell -$ near view get_counter -0 -$ near call inc1 --accountId -$ near view get_counter -1 -$ near call inc2 --accountId -$ near view get_counter -1 -``` - -#### Stage new contract -```shell -cargo run -- "" -``` - -Where `` is `$HOME/.near-credentials/testnet/.json` - -#### Deploy new contract -```shell -$ near call up_deploy_code --accountId -``` - -#### Increment counter on second contract -```shell -$ near view get_counter -1 -$ near call inc1 --accountId -$ near view get_counter -1 -$ near call inc2 --accountId -$ near view get_counter -2 -``` - -## Tests running instruction -Tests in `upgradable_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/upgradable_base.wasm` -2. Run tests `cargo test` - -```shell -$ cd upgradable_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/upgradable-examples/up_stage_code/Cargo.toml b/examples/upgradable-examples/up_stage_code/Cargo.toml deleted file mode 100644 index 1794777..0000000 --- a/examples/upgradable-examples/up_stage_code/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "up_stage_code" -version = "0.1.0" -edition = "2021" - -[dependencies] -near-sdk = "4.0.0" -near-plugins = { path = "../../../near-plugins" } -near-plugins-derive = { path = "../../../near-plugins-derive" } -borsh = "0.9.3" -near-primitives = { version = "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" } -clap = { version = "3.1.6", features = ["derive"] } diff --git a/examples/upgradable-examples/up_stage_code/src/main.rs b/examples/upgradable-examples/up_stage_code/src/main.rs deleted file mode 100644 index 9686c14..0000000 --- a/examples/upgradable-examples/up_stage_code/src/main.rs +++ /dev/null @@ -1,57 +0,0 @@ -use clap::Parser; -use workspaces::Account; - -const DEFAULT_WASM_FILEPATH_SECOND: &str = - "../../target/wasm32-unknown-unknown/release/upgradable_base_second.wasm"; - -#[derive(Parser, Default, Debug)] -#[clap(version, about = "Up stage code")] -struct Arguments { - #[clap(short, long)] - // Path to key for contract account (for example `$HOME/.near-credentials/testnet/.json`) - path_to_key: String, - - #[clap(short, long, default_value_t = String::from(DEFAULT_WASM_FILEPATH_SECOND))] - /// Path to wasm file with the new contract - wasm: String, - - #[clap(long, default_value_t = String::from("testnet"))] - /// NEAR network (testnet, mainnet, betanet) - network: String, -} - -#[macro_export] -macro_rules! get_contract { - ($network_name:ident, $path_to_key:expr) => { - Account::from_file($path_to_key, &workspaces::$network_name().await.unwrap()).unwrap() - }; -} - -#[tokio::main] -async fn main() { - let args = Arguments::parse(); - - let contract: Account = match &*args.network { - "testnet" => get_contract!(testnet, args.path_to_key), - "mainnet" => get_contract!(mainnet, args.path_to_key), - "betanet" => get_contract!(betanet, args.path_to_key), - network => panic!( - "Unknown network {}. Possible networks: testnet, mainnet, betanet", - network - ), - }; - - let wasm = std::fs::read(&args.wasm).unwrap(); - - println!( - "{}", - contract - .call(contract.id(), "up_stage_code") - .args_borsh(wasm) - .max_gas() - .transact() - .await - .unwrap() - .is_success() - ); -} diff --git a/examples/upgradable-examples/upgradable_base/Cargo.toml b/examples/upgradable-examples/upgradable_base/Cargo.toml deleted file mode 100644 index 0d5bdae..0000000 --- a/examples/upgradable-examples/upgradable_base/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "upgradable_base" -version = "0.1.0" -edition = "2021" - -[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 = { version = "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" } - -[features] -counter1 = [] -counter2 = [] diff --git a/examples/upgradable-examples/upgradable_base/build.sh b/examples/upgradable-examples/upgradable_base/build.sh deleted file mode 100755 index 7429687..0000000 --- a/examples/upgradable-examples/upgradable_base/build.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash - -mkdir -p ../../res - -RUSTFLAGS='-C link-arg=-s' cargo build --features "counter2" --target wasm32-unknown-unknown --release -mv ../../target/wasm32-unknown-unknown/release/upgradable_base.wasm ../../target/wasm32-unknown-unknown/release/upgradable_base_second.wasm -RUSTFLAGS='-C link-arg=-s' cargo build --features "counter1" --target wasm32-unknown-unknown --release - -cp ../../target/wasm32-unknown-unknown/release/upgradable_base_second.wasm ../../res/ -cp ../../target/wasm32-unknown-unknown/release/upgradable_base.wasm ../../res/ \ No newline at end of file diff --git a/examples/upgradable-examples/upgradable_base/src/lib.rs b/examples/upgradable-examples/upgradable_base/src/lib.rs deleted file mode 100644 index 8d74504..0000000 --- a/examples/upgradable-examples/upgradable_base/src/lib.rs +++ /dev/null @@ -1,89 +0,0 @@ -use near_plugins::{Ownable, Upgradable}; -use near_sdk::near_bindgen; -use borsh::{BorshSerialize, BorshDeserialize}; - -#[near_bindgen] -#[derive(Ownable, Upgradable, 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.up_init_staging_duration(std::time::Duration::from_secs(60).as_nanos().try_into().unwrap()); // 1 minute - contract - } - - #[cfg(feature = "counter1")] - pub fn inc1(&mut self) { - self.counter += 1; - } - - #[cfg(feature = "counter2")] - pub fn inc2(&mut self) { - self.counter += 2; - } - - pub fn get_counter(&self) -> u64 { - self.counter - } -} - -#[cfg(test)] -mod tests { - use serde_json::json; - use workspaces::{Account, Contract}; - use near_plugins_test_utils::*; - - const WASM_FILEPATH: &str = "../../target/wasm32-unknown-unknown/release/upgradable_base.wasm"; - const WASM_FILEPATH_SECOND: &str = "../../target/wasm32-unknown-unknown/release/upgradable_base_second.wasm"; - - async fn get_contract() -> (Account, Contract) { - let worker = workspaces::testnet().await.unwrap(); - - let owner = worker.dev_create_account().await.unwrap(); - - let wasm = std::fs::read(WASM_FILEPATH).unwrap(); - let contract = owner.deploy(&wasm).await.unwrap().unwrap(); - - (owner, contract) - } - - async fn call_method_with_borsh_args(contract: &Contract, method_name: &str, args: Vec) -> bool { - contract.call(method_name) - .args_borsh(args) - .max_gas() - .transact() - .await.unwrap().is_success() - } - - //https://docs.near.org/sdk/rust/promises/deploy-contract - #[tokio::test] - async fn base_scenario() { - let (_, contract) = get_contract().await; - assert!(call!(contract,"new").await); - - assert!(call!(contract, "inc1").await); - check_counter(&contract, 1).await; - - assert!(!call!(contract, "inc2").await); - check_counter(&contract, 1).await; - - let wasm = std::fs::read(WASM_FILEPATH_SECOND).unwrap(); - - assert!(call_method_with_borsh_args(&contract, "up_stage_code", wasm).await); - assert!(call!(contract, "up_deploy_code").await); - check_counter(&contract, 1).await; - - assert!(!call!(contract, "inc1").await); - check_counter(&contract, 1).await; - - assert!(call!(contract, "inc2").await); - check_counter(&contract, 3).await; - } -} From 824a380fa04a2ee749c734ff46f7a01d2cb7a492 Mon Sep 17 00:00:00 2001 From: Moritz Date: Mon, 13 Feb 2023 10:06:48 +0100 Subject: [PATCH 2/3] Extend `Upgradable` docs --- near-plugins/src/upgradable.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/near-plugins/src/upgradable.rs b/near-plugins/src/upgradable.rs index 2184fef..63e5fd1 100644 --- a/near-plugins/src/upgradable.rs +++ b/near-plugins/src/upgradable.rs @@ -50,6 +50,14 @@ use near_sdk::{AccountId, CryptoHash, Promise}; /// Trait describing the functionality of the _Upgradable_ plugin. pub trait Upgradable { /// Returns the storage prefix for slots related to upgradable. + /// + /// Attribute `storage_prefix` can be used to set a different prefix: + /// + /// ```ignore + /// #[derive(Upgradable)] + /// #[upgradable(storage_prefix="CUSTOM_KEY")] + /// struct Contract { /* ... */} + /// ``` fn up_storage_prefix(&self) -> &'static [u8]; /// Returns all staging durations and timestamps. From 3aabc567845f7db4d1619c989188f50af17f80fb Mon Sep 17 00:00:00 2001 From: Moritz Date: Mon, 13 Feb 2023 10:09:56 +0100 Subject: [PATCH 3/3] Update README --- README.md | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index f394a85..b1085c7 100644 --- a/README.md +++ b/README.md @@ -61,35 +61,17 @@ Documentation of all methods provided by `Pausable` is available in the [definit ### [Upgradable](/near-plugins/src/upgradable.rs) -Allows a contract to be upgraded by owner with delay and without having a Full Access Key. +Allows a contract to be upgraded by the owner without requiring a full access key. Optionally a staging duration can be set, which defines the minimum duration that must pass before staged code can be deployed. The staging duration is a safety mechanism to protect users that interact with the contract, giving them time to opt-out before an unfavorable update is deployed. -Contract example using _Upgradable_ plugin. Note that it requires the contract to be Ownable. +Using the `Upgradable` plugin requires a contract to be `Ownable`. -```rust -#[near_bindgen] -#[derive(Ownable, Upgradable)] -struct Counter; - -#[near_bindgen] -impl Counter { - /// Specify the owner of the contract in the constructor - #[init] - fn new() -> Self { - let mut contract = Self {}; - contract.owner_set(Some(near_sdk::env::predecessor_account_id())); - contract.up_init_staging_duration(std::time::Duration::from_secs(60).as_nanos().try_into().unwrap()); // 1 minute - contract - } -} -``` +To upgrade the contract first call `up_stage_code` passing the binary as first argument serialized as borsh. Then call `up_deploy_code`. Both functions must be called by the owner of the contract. -To upgrade the contract first call `up_stage_code` passing the binary as first argument serialized as borsh. Then call `up_deploy_code`. -This functions must be called from the owner. +To set a staging duration, call `up_stage_init_staging_duration`. After initialization the staging duration can be updated by calling `up_stage_update_staging_duration` followed by `up_apply_update_staging_duration`. Updating the staging duration is itself subject to a delay: at least the currently set staging duration must pass before a staged update can be applied. The functions mentioned in this paragraph must be called by the owner of the contract. -To update the staging delay first call `up_stage_update_staging_duration` passing the new delay duration. Then call `up_apply_update_staging_duration`. -This functions must be called from the owner. +[This contract](/near-plugins/tests/contracts/upgradable/src/lib.rs) provides an example of using `Upgradable`. It is compiled, deployed on chain and interacted with in [integration tests](/near-plugins/tests/upgradable.rs). -Documentation of all methods provided by the derived implementation of `Upgradable` is available in the [definition of the trait](/near-plugins/src/upgradable.rs). More examples and guidelines for interacting with an `Upgradable` contract can be found [here](/examples/upgradable-examples/README.md). +Documentation of all methods provided by `Upgradable` is available in the [definition of the trait](/near-plugins/src/upgradable.rs). ### [AccessControllable](/near-plugins/src/access_controllable.rs)