Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: self update #70

Merged
merged 4 commits into from
Apr 22, 2024
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
File renamed without changes.
230 changes: 160 additions & 70 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,7 @@ near-contract-standards = { git = "https://github.com/sweatco/near-sdk-rs", rev
nitka = "0.3.0"
nitka-proc = "0.3.0"

near-self-update = "0.1.2"
near-self-update-proc = "0.1.2"

sweat-model = { path = "model" }
1 change: 1 addition & 0 deletions integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ async-trait = "0.1.77"

sweat-model = { workspace = true, features = ["integration-api"] }
nitka = { workspace = true }
near-self-update = { workspace = true }
1 change: 1 addition & 0 deletions integration-tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ mod measure;
mod mint;
mod prepare;
mod transfer;
mod update;

#[tokio::test]
async fn happy_flow() -> anyhow::Result<()> {
Expand Down
5 changes: 3 additions & 2 deletions integration-tests/src/measure/record_batch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@

use anyhow::Result;
use near_workspaces::types::Gas;
use nitka::measure::utils::pretty_gas_string;
use sweat_model::SweatApiIntegration;

use crate::{prepare::IntegrationContext, prepare_contract};

#[ignore]
#[tokio::test]
async fn single_record_batch() -> anyhow::Result<()> {
async fn single_record_batch() -> Result<()> {
let gas = measure_record_batch().await?;

dbg!(&gas);
println!("{}", pretty_gas_string(gas));

Ok(())
}
Expand Down
51 changes: 51 additions & 0 deletions integration-tests/src/update.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use std::fs;

use anyhow::Result;
use near_self_update::UpdateApiIntegration;

use crate::prepare::{prepare_contract, IntegrationContext};

#[tokio::test]
async fn update() -> Result<()> {
let mut context = prepare_contract().await?;

assert_eq!(context.ft_contract().contract_version().await?, "sweat-1.2.1");

context
.ft_contract()
.update_contract(vec![], None)
.expect_error("Unauthorized access! Only oracle can call that!")
.await?;

let new_version = fs::read("../res_test/sweat_new_version.wasm")?;
let old_version = fs::read("../res/sweat.wasm")?;

let oracle = context.oracle().await?;

context
.ft_contract()
.update_contract(old_version, "test_update_callback".to_string().into())
.with_user(&oracle)
.expect_log("test_update_callback called")
.await?;

assert_eq!(context.ft_contract().contract_version().await?, "sweat-1.2.1");

context
.ft_contract()
.update_contract(new_version.clone(), "non_existing_method".to_string().into())
.with_user(&oracle)
.expect_error("MethodResolveError(MethodNotFound)")
.await?;

context
.ft_contract()
.update_contract(new_version.clone(), None)
.with_user(&oracle)
.dont_expect_log("test_update_callback called")
.await?;

assert_eq!(context.ft_contract().contract_version().await?, "sweat-9999.9.9");

Ok(())
}
3 changes: 2 additions & 1 deletion model/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ edition = "2021"
[features]
default = []
integration-test = ["dep:nitka", "dep:near-workspaces"]
integration-api = ["dep:nitka", "dep:near-workspaces"]
integration-api = ["dep:nitka", "dep:near-workspaces", "dep:near-self-update"]
release-api = []

[dependencies]
Expand All @@ -17,3 +17,4 @@ near-contract-standards = { workspace = true }

nitka = { workspace = true, optional = true }
near-workspaces = { workspace = true, optional = true }
near-self-update = { workspace = true, optional = true }
8 changes: 8 additions & 0 deletions model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ pub struct SweatContract<'a> {
pub contract: &'a near_workspaces::Contract,
}

#[cfg(feature = "integration-api")]
impl near_self_update::HasContract for SweatContract<'_> {
fn contract(&self) -> &near_workspaces::Contract {
self.contract
}
}

#[make_integration_version]
pub trait SweatApi {
fn new(postfix: Option<String>) -> Self;
Expand Down Expand Up @@ -98,6 +105,7 @@ pub trait StorageManagement {
#[make_integration_version]
pub trait IntegrationTestMethods {
fn calculate_payout_with_fee_for_batch(&self, batch_size: u32, claim_amount: u32) -> (U128, U128);
fn test_update_callback(&mut self);
}

pub struct Payout {
Expand Down
Binary file modified res/sweat.wasm
Binary file not shown.
Binary file added res_test/sweat_new_version.wasm
Binary file not shown.
1 change: 1 addition & 0 deletions sweat/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ sweat-model = { workspace = true }

near-sdk = { workspace = true, features = ["unit-testing"] }
near-contract-standards = { workspace = true }
near-self-update-proc = { workspace = true }
5 changes: 5 additions & 0 deletions sweat/src/integration.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![cfg(feature = "integration-test")]

use near_sdk::{
env::log_str,
json_types::{U128, U64},
near_bindgen,
};
Expand Down Expand Up @@ -28,4 +29,8 @@ impl IntegrationTestMethods for Contract {

(U128(total_fee), U128(total_for_user))
}

fn test_update_callback(&mut self) {
log_str("test_update_callback called");
}
}
32 changes: 30 additions & 2 deletions sweat/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use near_sdk::{
json_types::{U128, U64},
near, near_bindgen, require, AccountId, PanicOnDefault,
};
use near_self_update_proc::SelfUpdate;
use sweat_model::{Payout, SweatApi};

mod core;
Expand All @@ -17,7 +18,7 @@ mod integration;
mod math;

#[near(contract_state)]
#[derive(PanicOnDefault)]
#[derive(PanicOnDefault, SelfUpdate)]
pub struct Contract {
oracles: UnorderedSet<AccountId>,
token: FungibleToken,
Expand All @@ -34,6 +35,7 @@ impl SweatApi for Contract {
steps_since_tge: U64::from(0),
}
}

fn add_oracle(&mut self, account_id: &AccountId) {
require!(
env::predecessor_account_id() == env::current_account_id(),
Expand Down Expand Up @@ -154,6 +156,13 @@ impl Contract {

(payout.amount_for_user, payout.fee)
}

fn assert_account_can_update(&self) {
require!(
self.oracles.contains(&env::predecessor_account_id()),
"Unauthorized access! Only oracle can call that!"
);
}
}

/// Taken from contract standards but modified to default if account isn't initialized
Expand Down Expand Up @@ -190,7 +199,7 @@ impl FungibleTokenMetadataProvider for Contract {

#[cfg(test)]
mod tests {
use std::str::FromStr;
use std::{fs, str::FromStr};

use near_contract_standards::fungible_token::core::FungibleTokenCore;
use near_sdk::{
Expand Down Expand Up @@ -418,4 +427,23 @@ mod tests {

assert!((9.499_999_991_723_028 * 2.0 - token.token.ft_balance_of(user2()).0 as f64 / 1e+18).abs() < EPS);
}

#[test]
#[should_panic(expected = r#"Unauthorized access! Only oracle can call that!"#)]
fn self_update_without_access() {
testing_env!(get_context(sweat_the_token(), sweat_the_token()).build());
let mut token = Contract::new(Some(".u.sweat".to_string()));
token.add_oracle(&sweat_oracle());
token.update_contract(vec![], None);
}

#[test]
fn self_update() {
testing_env!(get_context(sweat_the_token(), sweat_the_token()).build());
let mut token = Contract::new(Some(".u.sweat".to_string()));
token.add_oracle(&sweat_oracle());
testing_env!(get_context(sweat_the_token(), sweat_oracle()).build());
let wasm = fs::read("../res/sweat.wasm").unwrap();
token.update_contract(wasm, None);
}
}
Loading