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 introduced icp nonces for context contract #1031

Open
wants to merge 28 commits into
base: feat--add-near-calls-nonce
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
945be8f
fix: remove timestamp from contracts
frdomovic Dec 20, 2024
bbd07ee
fix: tests 2
frdomovic Dec 20, 2024
964fa46
fix: removed prints in mock contract, optimized proxy contract deploy…
alenmestrov Dec 20, 2024
9bbc038
feat: introduced nonces in icp context contract
alenmestrov Dec 20, 2024
545b72d
fix: lint
alenmestrov Dec 20, 2024
39e5c26
fix: check the transfer result before doing cross contract call
alenmestrov Dec 20, 2024
2fa86ac
fix: resolved PR comments
alenmestrov Dec 20, 2024
d3d22a7
fix:lint
alenmestrov Dec 20, 2024
fcd5b3a
fix: reverted back grouping of members and nonces
alenmestrov Dec 20, 2024
14db1bb
fix: lint
alenmestrov Dec 20, 2024
bc99546
fix: resolved PR comments
alenmestrov Dec 20, 2024
244aab0
simplify nonce validation
miraclx Dec 20, 2024
0182e26
prevent spurious proxy deployment for existing context
miraclx Dec 20, 2024
647192c
pulled latest changes
alenmestrov Dec 23, 2024
f440214
fix: removed mock_ledger from Cargo
alenmestrov Dec 23, 2024
dcac961
fix: lint
alenmestrov Dec 23, 2024
d73e5e5
merged feat--add-near-calls-nonce
alenmestrov Dec 23, 2024
af88a83
fix: removed mock_ledger build process
alenmestrov Dec 23, 2024
ba80769
feat: added automated script for devnet deployment
alenmestrov Dec 23, 2024
a5f839d
fix: resolved issue with NEAR nonce
alenmestrov Dec 23, 2024
42a9528
feat: added container_ids.json file to gitignore
alenmestrov Dec 23, 2024
df5b56f
fix: check if canister_ids.json file exists before trying to delete it
alenmestrov Dec 23, 2024
84ff731
fix: fixed getting proxy contract wasm content
alenmestrov Dec 23, 2024
f99d909
fix: fixed fetching nonce encoding
alenmestrov Dec 24, 2024
95d0d1d
fix: updated script for devnet deployment, adjusted initial context c…
alenmestrov Dec 26, 2024
5fb103d
feat: implemented endpoint for fetching proxy contract ID
alenmestrov Dec 26, 2024
5ee39c9
fix: lint
alenmestrov Dec 26, 2024
8912db4
fix: cleaned deployment script and created test recipient account
alenmestrov Dec 26, 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
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type Result = variant { Ok; Err : text };
service : () -> {
application : (blob) -> (ICApplication) query;
application_revision : (blob) -> (nat64) query;
fetch_nonce : (blob, blob) -> (opt nat64) query;
has_member : (blob, blob) -> (bool) query;
members : (blob, nat64, nat64) -> (vec blob) query;
members_revision : (blob) -> (nat64) query;
Expand Down
4 changes: 2 additions & 2 deletions contracts/icp/context-config/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::cell::RefCell;
use std::collections::{BTreeMap, BTreeSet};
use std::collections::BTreeMap;

use calimero_context_config::icp::repr::ICRepr;
use calimero_context_config::icp::types::{ICApplication, ICCapability, ICRequest, ICSigned};
Expand All @@ -21,7 +21,7 @@ thread_local! {
#[derive(CandidType, Deserialize, Debug)]
pub struct Context {
pub application: Guard<ICApplication>,
pub members: Guard<BTreeSet<ICRepr<ContextIdentity>>>,
pub members: Guard<BTreeMap<ICRepr<ContextIdentity>, u64>>,
pub proxy: Guard<Principal>,
}

Expand Down
88 changes: 60 additions & 28 deletions contracts/icp/context-config/src/mutate.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::collections::BTreeMap;
use std::ops::Deref;

use calimero_context_config::icp::repr::ICRepr;
Expand All @@ -21,39 +22,29 @@ pub async fn mutate(signed_request: ICSigned<ICRequest>) -> Result<(), String> {
.parse(|r| *r.signer_id)
.map_err(|e| format!("Failed to verify signature: {}", e))?;

// Add debug logging
let current_time = ic_cdk::api::time() / 1_000_000;
let time_diff = current_time.saturating_sub(request.timestamp_ms);
if time_diff > 1000 * 5 {
return Err(format!(
"request expired: diff={}ms, current={}, request={}",
time_diff, current_time, request.timestamp_ms
));
}

match request.kind {
ICRequestKind::Context(ICContextRequest { context_id, kind }) => match kind {
ICContextRequestKind::Add {
author_id,
application,
} => add_context(&request.signer_id, context_id, *author_id, application).await,
ICContextRequestKind::UpdateApplication { application } => {
update_application(&request.signer_id, &context_id, application)
update_application(&request.signer_id, &context_id, request.nonce, application)
}
ICContextRequestKind::AddMembers { members } => {
add_members(&request.signer_id, &context_id, members)
add_members(&request.signer_id, &context_id, request.nonce, members)
}
ICContextRequestKind::RemoveMembers { members } => {
remove_members(&request.signer_id, &context_id, members)
remove_members(&request.signer_id, &context_id, request.nonce, members)
}
ICContextRequestKind::Grant { capabilities } => {
grant(&request.signer_id, &context_id, capabilities)
grant(&request.signer_id, &context_id, request.nonce, capabilities)
}
ICContextRequestKind::Revoke { capabilities } => {
revoke(&request.signer_id, &context_id, capabilities)
revoke(&request.signer_id, &context_id, request.nonce, capabilities)
}
ICContextRequestKind::UpdateProxyContract => {
update_proxy_contract(&request.signer_id, context_id).await
update_proxy_contract(&request.signer_id, context_id, request.nonce).await
}
},
}
Expand All @@ -79,7 +70,9 @@ async fn add_context(
application: Guard::new(author_id.rt().expect("infallible conversion"), application),
members: Guard::new(
author_id.rt().expect("infallible conversion"),
[author_id.rt().expect("infallible conversion")].into(),
[(author_id.rt().expect("infallible conversion"), 0)]
.into_iter()
.collect(),
),
proxy: Guard::new(
author_id.rt().expect("infallible conversion"),
Expand Down Expand Up @@ -116,7 +109,7 @@ async fn deploy_proxy_contract(context_id: ICRepr<ContextId>) -> Result<Principa
}),
};

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

Expand All @@ -143,23 +136,24 @@ async fn deploy_proxy_contract(context_id: ICRepr<ContextId>) -> Result<Principa
fn update_application(
signer_id: &SignerId,
context_id: &ContextId,
nonce: u64,
application: ICApplication,
) -> Result<(), String> {
with_state_mut(|configs| {
// Get the context or return error if it doesn't exist
let context = configs
.contexts
.get_mut(context_id)
.ok_or_else(|| "context does not exist".to_string())?;

// Get mutable access to the application through the Guard
// Add nonce check
check_and_increment_nonce(context, nonce, signer_id)?;

// Original implementation continues unchanged
let guard_ref = context
.application
.get(signer_id)
.map_err(|e| e.to_string())?;
let mut app_ref = guard_ref.get_mut();

// Replace the application with the new one
*app_ref = application;

Ok(())
Expand All @@ -169,22 +163,26 @@ fn update_application(
fn add_members(
signer_id: &SignerId,
context_id: &ContextId,
nonce: u64,
members: Vec<ICRepr<ContextIdentity>>,
) -> Result<(), String> {
with_state_mut(|configs| {
// Get the context or return error if it doesn't exist
let context = configs
.contexts
.get_mut(context_id)
.ok_or_else(|| "context does not exist".to_string())?;

// Get mutable access to the members through the Guard
// Check nonce
check_and_increment_nonce(context, nonce, signer_id)?;

// Rest of the function...
let guard_ref = context.members.get(signer_id).map_err(|e| e.to_string())?;
let mut ctx_members = guard_ref.get_mut();

// Add each member
for member in members {
ctx_members.insert(member);
if !ctx_members.contains_key(&member) {
ctx_members.insert(member, 0); // Only insert if member doesn't exist
alenmestrov marked this conversation as resolved.
Show resolved Hide resolved
}
}

Ok(())
Expand All @@ -194,15 +192,18 @@ fn add_members(
fn remove_members(
signer_id: &SignerId,
context_id: &ContextId,
nonce: u64,
alenmestrov marked this conversation as resolved.
Show resolved Hide resolved
members: Vec<ICRepr<ContextIdentity>>,
) -> Result<(), String> {
with_state_mut(|configs| {
// Get the context or return error if it doesn't exist
let context = configs
.contexts
.get_mut(context_id)
.ok_or_else(|| "context does not exist".to_string())?;

// Check nonce
check_and_increment_nonce(context, nonce, signer_id)?;

// Get mutable access to the members through the Guard
let mut ctx_members = context
.members
Expand Down Expand Up @@ -230,6 +231,7 @@ fn remove_members(
fn grant(
signer_id: &SignerId,
context_id: &ContextId,
nonce: u64,
capabilities: Vec<(ICRepr<ContextIdentity>, ICCapability)>,
) -> Result<(), String> {
with_state_mut(|configs| {
Expand All @@ -238,8 +240,11 @@ fn grant(
.get_mut(context_id)
.ok_or_else(|| "context does not exist".to_string())?;

// Check nonce
check_and_increment_nonce(context, nonce, signer_id)?;

for (identity, capability) in capabilities {
let is_member = context.members.deref().contains(&identity);
let is_member = context.members.deref().contains_key(&identity);

if !is_member {
return Err("unable to grant privileges to non-member".to_string());
Expand Down Expand Up @@ -280,6 +285,7 @@ fn grant(
fn revoke(
signer_id: &SignerId,
context_id: &ContextId,
nonce: u64,
capabilities: Vec<(ICRepr<ContextIdentity>, ICCapability)>,
) -> Result<(), String> {
with_state_mut(|configs| {
Expand All @@ -288,6 +294,9 @@ fn revoke(
.get_mut(context_id)
.ok_or_else(|| "context does not exist".to_string())?;

// Check nonce
check_and_increment_nonce(context, nonce, signer_id)?;

for (identity, capability) in capabilities {
match capability {
ICCapability::ManageApplication => {
Expand Down Expand Up @@ -324,13 +333,17 @@ fn revoke(
async fn update_proxy_contract(
signer_id: &SignerId,
context_id: ICRepr<ContextId>,
nonce: u64,
) -> Result<(), String> {
let (proxy_canister_id, proxy_code) = with_state_mut(|configs| {
let context = configs
.contexts
.get_mut(&context_id)
.ok_or_else(|| "context does not exist".to_string())?;

// Check nonce
check_and_increment_nonce(context, nonce, signer_id)?;

let proxy_cannister = *context
.proxy
.get(signer_id)
Expand All @@ -356,3 +369,22 @@ async fn update_proxy_contract(

Ok(())
}

fn check_and_increment_nonce(
context: &mut Context,
nonce: u64,
signer_id: &SignerId,
) -> Result<(), String> {
let context_identity = signer_id.rt().expect("infallible conversion");
let guard_ref = context.members.get(signer_id).map_err(|e| e.to_string())?;
let mut members = guard_ref.get_mut();

let current_nonce = members.get(&context_identity).copied().unwrap_or(0);

if current_nonce != nonce {
return Err("invalid nonce".into());
}

members.insert(context_identity, nonce + 1);
Ok(())
}
18 changes: 15 additions & 3 deletions contracts/icp/context-config/src/query.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::collections::BTreeMap;
use std::ops::Deref;

use calimero_context_config::icp::repr::ICRepr;
use calimero_context_config::icp::types::{ICApplication, ICCapability};
Expand Down Expand Up @@ -56,8 +57,8 @@ fn members(
.get(&context_id)
.expect("context does not exist");

let members = &*context.members;
members.iter().skip(offset).take(length).cloned().collect()
let members = context.members.deref();
members.keys().skip(offset).take(length).cloned().collect()
})
}

Expand All @@ -69,7 +70,7 @@ fn has_member(context_id: ICRepr<ContextId>, identity: ICRepr<ContextIdentity>)
.get(&context_id)
.expect("context does not exist");

context.members.contains(&identity)
context.members.deref().contains_key(&identity)
})
}

Expand Down Expand Up @@ -134,3 +135,14 @@ fn privileges(
privileges
})
}

#[ic_cdk::query]
fn fetch_nonce(context_id: ICRepr<ContextId>, member_id: ICRepr<ContextIdentity>) -> Option<u64> {
with_state(|configs| {
configs
.contexts
.get(&context_id)
.and_then(|context| context.members.deref().get(&member_id))
.copied()
})
}
Loading
Loading