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 icp proxy #993

Merged
merged 34 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
b66d6c1
feat: initial ICP Context contract integration
alenmestrov Nov 26, 2024
347d0b6
fix: lint
alenmestrov Nov 26, 2024
cc2afb2
feat: updated tests to use pocket-ic
alenmestrov Nov 26, 2024
ae49bcf
feat: seeded contract for testing
alenmestrov Nov 26, 2024
f047920
feat: added gitignore
alenmestrov Nov 27, 2024
2c29899
Remove .dfx/ folder from Git tracking
alenmestrov Nov 27, 2024
c2955d8
feat: final implementation of ICP Context contract
alenmestrov Nov 27, 2024
d45c821
fix: lint
alenmestrov Nov 27, 2024
9867616
fix: removed prints
alenmestrov Nov 27, 2024
8684eeb
feat: improved build.sh script, remove unused script, fixed types
alenmestrov Nov 27, 2024
0630e24
feat: updated types to better reflect NEAR implementation, fixed buil…
alenmestrov Nov 27, 2024
bf19d59
fix: linter
alenmestrov Nov 27, 2024
e41e3bc
fix: fixed tests to better handle responses and new version of types
alenmestrov Nov 27, 2024
f8b5118
fix: linter
alenmestrov Nov 27, 2024
26918fd
fix: removed pocket-ic bin from git
alenmestrov Nov 27, 2024
1925da1
merged master
alenmestrov Nov 28, 2024
099957a
feat: implemented Proxy capability
alenmestrov Nov 28, 2024
8e131ca
feat: inital version of proxy contract code
alenmestrov Nov 28, 2024
893fad3
merged icp-contracts branch
alenmestrov Nov 29, 2024
5bf9397
feat: finished tests for proxy contract2
alenmestrov Nov 29, 2024
07d797e
fix: lint
alenmestrov Nov 29, 2024
b01a656
Merge branch 'feat--icp-contracts' into feat--icp-proxy
alenmestrov Nov 30, 2024
6fb0302
fix: adjusted types to corespond to Context contract, fixed query met…
alenmestrov Nov 30, 2024
9eb9174
fix: linter
alenmestrov Nov 30, 2024
08ca472
merged context contract
alenmestrov Dec 1, 2024
1fd6bf8
fix: fixed transfer method, adjusted mock contract, resolved PR comments
alenmestrov Dec 2, 2024
7703289
fix: removed unused imports & lint
alenmestrov Dec 2, 2024
89de2fb
fix: resolved PR comments
alenmestrov Dec 3, 2024
c9ac300
fix: resolved PR comments
alenmestrov Dec 3, 2024
7a08e59
fix: lint
alenmestrov Dec 3, 2024
7cb099a
Merge branch 'feat--icp-contracts' into feat--icp-proxy
alenmestrov Dec 4, 2024
849979c
feat: introduced build scripts for proxy contract and its dependecies
alenmestrov Dec 4, 2024
601293a
feat: added scripts to test.sh script for CI and lint
alenmestrov Dec 4, 2024
1d8f611
Feat Implement connection functionality between ICP Context and Proxy…
alenmestrov Dec 4, 2024
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
334 changes: 207 additions & 127 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 @@ -39,6 +39,9 @@ members = [
"./contracts/proxy-lib",
"./contracts/test-counter",
"./contracts/icp/context-config",
"./contracts/icp/proxy-contract",
"./contracts/icp/proxy-contract/mock/ledger",
"./contracts/icp/proxy-contract/mock/external",

"./e2e-tests",
]
Expand Down
3 changes: 2 additions & 1 deletion contracts/icp/context-config/context_contract.did
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ service : () -> {
privileges : (blob, vec blob) -> (
vec record { blob; vec ICCapability },
) query;
proxy_contract : (blob) -> (text) query;
proxy_contract : (blob) -> (principal) query;
set_proxy_code : (blob) -> (Result);
}
14 changes: 10 additions & 4 deletions contracts/icp/context-config/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::cell::RefCell;
use std::collections::{BTreeMap, HashMap};

use candid::CandidType;
use candid::{CandidType, Principal};
use guard::Guard;
use serde::{Deserialize, Serialize};

Expand All @@ -12,25 +12,31 @@ use crate::types::{
pub mod guard;
pub mod mutate;
pub mod query;
pub mod sys;
pub mod types;

#[derive(CandidType, Serialize, Deserialize, Clone, Debug)]
pub struct Context {
pub application: Guard<ICApplication>,
pub members: Guard<Vec<ICContextIdentity>>,
pub proxy: Guard<String>,
pub proxy: Guard<Principal>,
}

#[derive(CandidType, Deserialize, Clone, Debug)]
pub struct ContextConfigs {
pub contexts: HashMap<ICContextId, Context>,
pub next_proxy_id: u64,
pub proxy_code: Option<Vec<u8>>,
pub owner: Principal,
pub ledger_id: Principal,
}

impl Default for ContextConfigs {
fn default() -> Self {
Self {
contexts: HashMap::new(),
next_proxy_id: 0,
proxy_code: None,
owner: ic_cdk::api::caller(),
ledger_id: Principal::anonymous(),
}
}
}
Expand Down
108 changes: 99 additions & 9 deletions contracts/icp/context-config/src/mutate.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use std::ops::Deref;

use calimero_context_config::repr::{ReprBytes, ReprTransmute};
use candid::Principal;
use ic_cdk::api::management_canister::main::{
create_canister, install_code, CanisterSettings, CreateCanisterArgument, InstallCodeArgument,
};

use crate::guard::Guard;
use crate::types::{
Expand All @@ -10,7 +14,7 @@ use crate::types::{
use crate::{Context, CONTEXT_CONFIGS};

#[ic_cdk::update]
pub fn mutate(signed_request: ICPSigned<Request>) -> Result<(), String> {
pub async fn mutate(signed_request: ICPSigned<Request>) -> Result<(), String> {
let request = signed_request
.parse(|r| r.signer_id)
.map_err(|e| format!("Failed to verify signature: {}", e))?;
Expand All @@ -27,7 +31,7 @@ pub fn mutate(signed_request: ICPSigned<Request>) -> Result<(), String> {
ContextRequestKind::Add {
author_id,
application,
} => add_context(&request.signer_id, context_id, author_id, application),
} => add_context(&request.signer_id, context_id, author_id, application).await,
ContextRequestKind::UpdateApplication { application } => {
update_application(&request.signer_id, &context_id, application)
}
Expand All @@ -44,24 +48,26 @@ pub fn mutate(signed_request: ICPSigned<Request>) -> Result<(), String> {
revoke(&request.signer_id, &context_id, capabilities)
}
ContextRequestKind::UpdateProxyContract => {
// TODO: Implement update_proxy_contract
Ok(())
update_proxy_contract(&request.signer_id, context_id).await
}
},
}
}

fn add_context(
async fn add_context(
signer_id: &ICSignerId,
context_id: ICContextId,
author_id: ICContextIdentity,
application: ICApplication,
) -> Result<(), String> {
// 1. Verify signer is the context itself - direct array comparison
if signer_id.as_bytes() != context_id.as_bytes() {
return Err("context addition must be signed by the context itself".into());
}

let proxy_canister_id = deploy_proxy_contract(&context_id)
.await
.unwrap_or_else(|e| panic!("Failed to deploy proxy contract: {}", e));

CONTEXT_CONFIGS.with(|configs| {
let mut configs = configs.borrow_mut();

Expand All @@ -74,7 +80,7 @@ fn add_context(
),
proxy: Guard::new(
author_id.rt().expect("infallible conversion"),
format!("{}.{}", configs.next_proxy_id, ic_cdk::api::id()),
proxy_canister_id,
),
};

Expand All @@ -83,12 +89,55 @@ fn add_context(
return Err("context already exists".into());
}

configs.next_proxy_id += 1;

Ok(())
})
}

async fn deploy_proxy_contract(context_id: &ICContextId) -> Result<Principal, String> {
// Get the proxy code
let proxy_code = CONTEXT_CONFIGS
.with(|configs| configs.borrow().proxy_code.clone())
.ok_or("proxy code not set")?;

// Get the ledger ID
let ledger_id = CONTEXT_CONFIGS.with(|configs| configs.borrow().ledger_id.clone());
// Create canister with cycles
let create_args = CreateCanisterArgument {
settings: Some(CanisterSettings {
controllers: Some(vec![ic_cdk::api::id()]),
compute_allocation: None,
memory_allocation: None,
freezing_threshold: None,
reserved_cycles_limit: None,
log_visibility: None,
wasm_memory_limit: None,
}),
};

let (canister_record,) = create_canister(create_args, 500_000_000_000_000u128)
.await
.map_err(|e| format!("Failed to create canister: {:?}", e))?;

let canister_id = canister_record.canister_id;

// Encode init args matching the proxy's init(context_id: ICContextId, ledger_id: Principal)
let init_args = candid::encode_args((context_id.clone(), ledger_id))
.map_err(|e| format!("Failed to encode init args: {}", e))?;

let install_args = InstallCodeArgument {
mode: ic_cdk::api::management_canister::main::CanisterInstallMode::Install,
canister_id,
wasm_module: proxy_code,
arg: init_args,
};

install_code(install_args)
.await
.map_err(|e| format!("Failed to install code: {:?}", e))?;

Ok(canister_id)
}

fn update_application(
signer_id: &ICSignerId,
context_id: &ICContextId,
Expand Down Expand Up @@ -282,3 +331,44 @@ fn revoke(
Ok(())
})
}

async fn update_proxy_contract(
signer_id: &ICSignerId,
context_id: ICContextId,
) -> Result<(), String> {
let mut context = CONTEXT_CONFIGS.with(|configs| {
let configs = configs.borrow();
configs
.contexts
.get(&context_id)
.ok_or_else(|| "context does not exist".to_string())
.cloned()
})?;

// Get proxy canister ID
let proxy_canister_id = context
.proxy
.get(signer_id)
.map_err(|_| "unauthorized: Proxy capability required".to_string())?
.get_mut()
.clone();

// Get the proxy code
let proxy_code = CONTEXT_CONFIGS
.with(|configs| configs.borrow().proxy_code.clone())
.ok_or("proxy code not set")?;

// Update the proxy contract code
let install_args = InstallCodeArgument {
mode: ic_cdk::api::management_canister::main::CanisterInstallMode::Upgrade(None),
canister_id: proxy_canister_id,
wasm_module: proxy_code,
arg: candid::encode_one(&context_id).map_err(|e| format!("Encoding error: {}", e))?,
};

install_code(install_args)
.await
.map_err(|e| format!("Failed to update proxy contract: {:?}", e))?;

Ok(())
}
3 changes: 2 additions & 1 deletion contracts/icp/context-config/src/query.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::collections::BTreeMap;

use calimero_context_config::repr::ReprTransmute;
use candid::Principal;
use ic_cdk_macros::query;

use crate::types::*;
Expand Down Expand Up @@ -33,7 +34,7 @@ fn application_revision(context_id: ICContextId) -> u64 {
}

#[query]
fn proxy_contract(context_id: ICContextId) -> String {
fn proxy_contract(context_id: ICContextId) -> Principal {
CONTEXT_CONFIGS.with(|configs| {
let configs = configs.borrow();
let context = configs
Expand Down
60 changes: 60 additions & 0 deletions contracts/icp/context-config/src/sys.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use candid::{CandidType, Deserialize, Principal};
use ic_cdk;

use crate::CONTEXT_CONFIGS;

#[derive(CandidType, Deserialize)]
struct StableStorage {
configs: crate::ContextConfigs,
}

#[ic_cdk::pre_upgrade]
fn pre_upgrade() {
// Verify caller is the owner
CONTEXT_CONFIGS.with(|configs| {
let configs = configs.borrow();
if ic_cdk::api::caller() != configs.owner {
ic_cdk::trap("unauthorized: only owner can upgrade context contract");
}
});

// Store the contract state
let state = CONTEXT_CONFIGS.with(|configs| StableStorage {
configs: configs.borrow().clone(),
});

// Write state to stable storage
match ic_cdk::storage::stable_save((state,)) {
Ok(_) => (),
Err(err) => ic_cdk::trap(&format!("Failed to save stable storage: {}", err)),
}
}

#[ic_cdk::post_upgrade]
fn post_upgrade() {
// Restore the contract state
match ic_cdk::storage::stable_restore::<(StableStorage,)>() {
Ok((state,)) => {
CONTEXT_CONFIGS.with(|configs| {
*configs.borrow_mut() = state.configs;
});
}
Err(err) => ic_cdk::trap(&format!("Failed to restore stable storage: {}", err)),
}
}

#[ic_cdk::update]
pub fn set_proxy_code(proxy_code: Vec<u8>, ledger_id: Principal) -> Result<(), String> {
CONTEXT_CONFIGS.with(|configs| {
let mut configs = configs.borrow_mut();

// Check if caller is the owner
if ic_cdk::api::caller() != configs.owner {
return Err("Unauthorized: only owner can set proxy code".to_string());
}

configs.ledger_id = ledger_id;
configs.proxy_code = Some(proxy_code);
Ok(())
})
}
Loading
Loading