Skip to content

Commit

Permalink
merged feat--add-near-calls-nonce
Browse files Browse the repository at this point in the history
  • Loading branch information
alenmestrov committed Dec 19, 2024
2 parents 94c975f + 7cfddfa commit 58ae04c
Show file tree
Hide file tree
Showing 7 changed files with 226 additions and 13 deletions.
2 changes: 2 additions & 0 deletions contracts/near/context-config/src/mutate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ impl ContextConfigs {
for member in members {
env::log_str(&format!("Added `{member}` as a member of `{context_id}`"));

let _ = context.member_nonces.insert(*member, 0);

let _ = ctx_members.insert(*member);
}
}
Expand Down
5 changes: 1 addition & 4 deletions contracts/near/context-config/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,7 @@ impl ContextConfigs {
member_id: Repr<ContextIdentity>,
context_id: Repr<ContextId>,
) -> Option<&u64> {
let context = self
.contexts
.get(&context_id)
.expect("context does not exist");
let context = self.contexts.get(&context_id)?;
context.member_nonces.get(&member_id)
}
}
65 changes: 63 additions & 2 deletions contracts/near/context-config/tests/sandbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ async fn main() -> eyre::Result<()> {
assert_eq!(res, [alice_cx_id]);

let mut nonces: HashMap<Member, u64> =
Member::all().iter().map(|&member| (member, 1)).collect();
Member::all().iter().map(|&member| (member, 0)).collect();

let res = node1
.call(contract.id(), "mutate")
Expand Down Expand Up @@ -294,12 +294,13 @@ async fn main() -> eyre::Result<()> {
bob_cx_id, context_id
),]
);

assert_eq!(
fetch_nonce(&contract, context_id, alice_cx_id)
.await?
.unwrap(),
1,
"sssss"
"Alice: Nonce should be incremented to 1 after request is sent"
);
*nonces.entry(Member::Alice).or_insert(0) += 1;

Expand Down Expand Up @@ -381,6 +382,14 @@ async fn main() -> eyre::Result<()> {
);
}

assert_eq!(
fetch_nonce(&contract, context_id, bob_cx_id)
.await?
.unwrap(),
0,
"Nonce should be 0 after request is reverted"
);

let res = contract
.view("application_revision")
.args_json(json!({ "context_id": context_id }))
Expand Down Expand Up @@ -428,6 +437,15 @@ async fn main() -> eyre::Result<()> {
)]
);

assert_eq!(
fetch_nonce(&contract, context_id, alice_cx_id)
.await?
.unwrap(),
2,
"Alice: Nonce should be incremented to 2 after request is sent"
);
*nonces.entry(Member::Alice).or_insert(0) += 1;

let res = contract
.view("application_revision")
.args_json(json!({ "context_id": context_id }))
Expand Down Expand Up @@ -475,6 +493,15 @@ async fn main() -> eyre::Result<()> {
),]
);

assert_eq!(
fetch_nonce(&contract, context_id, bob_cx_id)
.await?
.unwrap(),
1,
"Bob: Nonce should be incremented to 1 after request is sent"
);
*nonces.entry(Member::Bob).or_insert(0) += 1;

let res: Vec<Repr<ContextIdentity>> = contract
.view("members")
.args_json(json!({
Expand Down Expand Up @@ -573,6 +600,14 @@ async fn main() -> eyre::Result<()> {
);
}

assert_eq!(
fetch_nonce(&contract, context_id, bob_cx_id)
.await?
.unwrap(),
1,
"Bob: Nonce should be 1 after request is reverted"
);

let res = contract
.view("application")
.args_json(json!({ "context_id": context_id }))
Expand Down Expand Up @@ -638,6 +673,15 @@ async fn main() -> eyre::Result<()> {
)]
);

assert_eq!(
fetch_nonce(&contract, context_id, alice_cx_id)
.await?
.unwrap(),
3,
"Alice: Nonce should be incremented to 3 after request is sent"
);
*nonces.entry(Member::Alice).or_insert(0) += 1;

let res = contract
.view("application")
.args_json(json!({ "context_id": context_id }))
Expand Down Expand Up @@ -697,6 +741,15 @@ async fn main() -> eyre::Result<()> {
)]
);

assert_eq!(
fetch_nonce(&contract, context_id, alice_cx_id)
.await?
.unwrap(),
4,
"Alice: Nonce should be incremented to 4 after request is sent"
);
*nonces.entry(Member::Alice).or_insert(0) += 1;

let res: BTreeMap<Repr<SignerId>, Vec<Capability>> = contract
.view("privileges")
.args_json(json!({
Expand Down Expand Up @@ -776,6 +829,14 @@ async fn main() -> eyre::Result<()> {
|p| alice_cx_sk.sign(p),
)?);

assert_eq!(
fetch_nonce(&contract, context_id, alice_cx_id)
.await?
.unwrap(),
4,
"Alice: Nonce should be 4 after request reverted - expired"
);

time::sleep(time::Duration::from_secs(5)).await;

let res = req
Expand Down
6 changes: 2 additions & 4 deletions crates/context/config/src/client/env/config/mutate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,12 +159,10 @@ impl<'a> Method<Icp> for Mutate<'a> {
}

impl<'a, T: Transport> ContextConfigMutateRequest<'a, T> {
pub async fn send(self, signing_key: [u8; 32]) -> Result<(), ClientError<T>> {
pub async fn send(self, signing_key: [u8; 32], nonce: u64) -> Result<(), ClientError<T>> {
let request = Mutate {
signing_key,
// todo! when nonces are implemented in context
// todo! config contract, we fetch it here first
nonce: 0,
nonce,
kind: self.kind,
};

Expand Down
11 changes: 11 additions & 0 deletions crates/context/config/src/client/env/config/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::types::{Application, Capability, ContextId, ContextIdentity, Revision

pub mod application;
pub mod application_revision;
pub mod fetch_nonce;
pub mod has_member;
pub mod members;
pub mod members_revision;
Expand Down Expand Up @@ -101,4 +102,14 @@ impl<'a, T: Transport> ContextConfigQuery<'a, T> {

utils::send(&self.client, Operation::Read(params)).await
}

pub async fn fetch_nonce(
&self,
context_id: ContextId,
member_id: ContextIdentity,
) -> Result<u64, ClientError<T>> {
let params = fetch_nonce::FetchNonceRequest::new(context_id, member_id);

utils::send(&self.client, Operation::Read(params)).await
}
}
102 changes: 102 additions & 0 deletions crates/context/config/src/client/env/config/query/fetch_nonce.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use candid::{Decode, Encode};
use serde::Serialize;
use starknet::core::codec::Encode as StarknetEncode;

use crate::client::env::config::types::starknet::{
CallData, ContextId as StarknetContextId, ContextIdentity as StarknetContextIdentity,
};
use crate::client::env::Method;
use crate::client::protocol::icp::Icp;
use crate::client::protocol::near::Near;
use crate::client::protocol::starknet::Starknet;
use crate::icp::repr::ICRepr;
use crate::repr::Repr;
use crate::types::{ContextId, ContextIdentity};

#[derive(Copy, Clone, Debug, Serialize)]
pub(super) struct FetchNonceRequest {
pub(super) context_id: Repr<ContextId>,
pub(super) member: Repr<ContextIdentity>,
}

impl FetchNonceRequest {
pub const fn new(context_id: ContextId, member: ContextIdentity) -> Self {
Self {
context_id: Repr::new(context_id),
member: Repr::new(member),
}
}
}

impl Method<Near> for FetchNonceRequest {
const METHOD: &'static str = "fetch_nonce";

type Returns = u64;

fn encode(self) -> eyre::Result<Vec<u8>> {
serde_json::to_vec(&self).map_err(Into::into)
}

fn decode(response: Vec<u8>) -> eyre::Result<Self::Returns> {
let nonce: u64 = serde_json::from_slice(&response)?;

Ok(nonce)
}
}

impl Method<Starknet> for FetchNonceRequest {
type Returns = u64;

const METHOD: &'static str = "fetch_nonce";

fn encode(self) -> eyre::Result<Vec<u8>> {
let mut call_data = CallData::default();

// Dereference Repr and encode context_id
let context_id: StarknetContextId = (*self.context_id).into();
context_id.encode(&mut call_data)?;

let member: StarknetContextIdentity = (*self.member).into();
member.encode(&mut call_data)?;

Ok(call_data.0)
}

fn decode(response: Vec<u8>) -> eyre::Result<Self::Returns> {
if response.len() != 8 {
return Err(eyre::eyre!(
"Invalid response length: expected 8 bytes, got {}",
response.len()
));
}

let nonce = u64::from_be_bytes(
response
.try_into()
.map_err(|_| eyre::eyre!("Failed to convert response to u64"))?,
);

Ok(nonce)
}
}

impl Method<Icp> for FetchNonceRequest {
type Returns = u64;

const METHOD: &'static str = "fetch_nonce";

fn encode(self) -> eyre::Result<Vec<u8>> {
let context_id = ICRepr::new(*self.context_id);
let member = ICRepr::new(*self.member);

let payload = (context_id, member);

Encode!(&payload).map_err(Into::into)
}

fn decode(response: Vec<u8>) -> eyre::Result<Self::Returns> {
let decoded = Decode!(&response, u64)?;

Ok(decoded)
}
}
48 changes: 45 additions & 3 deletions crates/context/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,22 @@ impl ContextManager {
)
}

let nonce: u64 = this
.config_client
.query::<ContextConfigEnv>(
this.client_config.new.protocol.as_str().into(),
this.client_config.new.network.as_str().into(),
this.client_config.new.contract_id.as_str().into(),
)
.fetch_nonce(
context.id.rt().expect("infallible conversion"),
identity_secret
.public_key()
.rt()
.expect("infallible conversion"),
)
.await?;

this.config_client
.mutate::<ContextConfigEnv>(
this.client_config.new.protocol.as_str().into(),
Expand All @@ -237,7 +253,7 @@ impl ContextManager {
ApplicationMetadataConfig(Repr::new(application.metadata.into())),
),
)
.send(*context_secret)
.send(*context_secret, nonce)
.await?;

let proxy_contract = this
Expand Down Expand Up @@ -411,6 +427,18 @@ impl ContextManager {
return Ok(None);
};

let member_id = inviter_id.rt().expect("infallible conversion");

let nonce: u64 = self
.config_client
.query::<ContextConfigEnv>(
context_config.protocol.as_ref().into(),
context_config.network.as_ref().into(),
context_config.contract.as_ref().into(),
)
.fetch_nonce(context_id.rt().expect("infallible conversion"), member_id)
.await?;

self.config_client
.mutate::<ContextConfigEnv>(
context_config.protocol.as_ref().into(),
Expand All @@ -421,7 +449,7 @@ impl ContextManager {
context_id.rt().expect("infallible conversion"),
&[invitee_id.rt().expect("infallible conversion")],
)
.send(requester_secret)
.send(requester_secret, nonce)
.await?;

let invitation_payload = ContextInvitationPayload::new(
Expand Down Expand Up @@ -928,6 +956,20 @@ impl ContextManager {
context_id
);
};

let nonce: u64 = self
.config_client
.query::<ContextConfigEnv>(
context_config.protocol.as_ref().into(),
context_config.network.as_ref().into(),
context_config.contract.as_ref().into(),
)
.fetch_nonce(
context_id.rt().expect("infallible conversion"),
signer_id.rt().expect("infallible conversion"),
)
.await?;

let _ = self
.config_client
.mutate::<ContextConfigEnv>(
Expand All @@ -945,7 +987,7 @@ impl ContextManager {
ApplicationMetadataConfig(Repr::new(application.metadata.into())),
),
)
.send(requester_secret)
.send(requester_secret, nonce)
.await?;

context_meta.application = ApplicationMetaKey::new(application_id);
Expand Down

0 comments on commit 58ae04c

Please sign in to comment.