Skip to content

Commit

Permalink
feat: integration of Context and Proxy Starknet contracts into Calimero
Browse files Browse the repository at this point in the history
  • Loading branch information
alenmestrov committed Nov 21, 2024
1 parent 4acd00b commit c42d235
Show file tree
Hide file tree
Showing 15 changed files with 1,723 additions and 1,133 deletions.
2,076 changes: 1,011 additions & 1,065 deletions Cargo.lock

Large diffs are not rendered by default.

40 changes: 37 additions & 3 deletions crates/context/config/src/client/env/config/query/has_member.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use serde::Serialize;
use starknet_crypto::Felt;

use crate::client::env::Method;
use crate::client::protocol::near::Near;
use crate::client::protocol::starknet::Starknet;
use crate::repr::Repr;
use crate::repr::ReprBytes;
use crate::types::{ContextId, ContextIdentity};

#[derive(Copy, Clone, Debug, Serialize)]
Expand Down Expand Up @@ -32,10 +34,42 @@ impl Method<Starknet> for HasMemberRequest {
const METHOD: &'static str = "has_member";

fn encode(self) -> eyre::Result<Vec<u8>> {
todo!()
let mut result = Vec::new();

// Encode context_id (2 felts)
let context_bytes = self.context_id.as_bytes();
let (context_high, context_low) = context_bytes.split_at(context_bytes.len() / 2);

// Convert to Felts and add to result
let context_high_felt = Felt::from_bytes_be_slice(context_high);
let context_low_felt = Felt::from_bytes_be_slice(context_low);
result.extend_from_slice(&context_high_felt.to_bytes_be());
result.extend_from_slice(&context_low_felt.to_bytes_be());

// Encode member identity (2 felts)
let identity_bytes = self.identity.as_bytes();
let (identity_high, identity_low) = identity_bytes.split_at(identity_bytes.len() / 2);

// Convert to Felts and add to result
let identity_high_felt = Felt::from_bytes_be_slice(identity_high);
let identity_low_felt = Felt::from_bytes_be_slice(identity_low);
result.extend_from_slice(&identity_high_felt.to_bytes_be());
result.extend_from_slice(&identity_low_felt.to_bytes_be());

Ok(result)
}

fn decode(_response: Vec<u8>) -> eyre::Result<Self::Returns> {
todo!()
fn decode(response: Vec<u8>) -> eyre::Result<Self::Returns> {
if response.is_empty() {
return Err(eyre::eyre!("Empty response"));
}

// Response should be a single felt (32 bytes) representing 0 or 1
if response.len() != 32 {
return Err(eyre::eyre!("Invalid response length"));
}

// Check the last byte for 0 or 1
Ok(response[31] == 1)
}
}
64 changes: 32 additions & 32 deletions crates/context/config/src/client/env/config/query/privileges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,39 +89,39 @@ impl<'a> Method<Starknet> for PrivilegesRequest<'a> {
}

fn decode(response: Vec<u8>) -> eyre::Result<Self::Returns> {
if response.is_empty() {
return Ok(BTreeMap::new());
}
// if response.is_empty() {
// return Ok(BTreeMap::new());
// }

let mut result = BTreeMap::new();
let mut offset = 0;

// First felt is array length
let array_len = u64::from_be_bytes(response[24..32].try_into()?);
offset += 32;

// Process each (identity, capabilities) pair
for _ in 0..array_len {
// Read identity (2 felts)
let identity_bytes = &response[offset..offset + 64];
let identity = SignerId::from_bytes(identity_bytes)?;
offset += 64;

// Read capabilities array length
let cap_len = u64::from_be_bytes(response[offset + 24..offset + 32].try_into()?);
offset += 32;

// Read capabilities
let mut capabilities = Vec::new();
for _ in 0..cap_len {
let cap_value = u64::from_be_bytes(response[offset + 24..offset + 32].try_into()?);
capabilities.push(Capability::from_u64(cap_value)?);
offset += 32;
}

result.insert(identity, capabilities);
}
// let mut result = BTreeMap::new();
// let mut offset = 0;

Ok(result)
// // First felt is array length
// let array_len = u64::from_be_bytes(response[24..32].try_into()?);
// offset += 32;

// // Process each (identity, capabilities) pair
// for _ in 0..array_len {
// // Read identity from 2 felts (32 bytes each)
// let identity = SignerId::from_bytes(|bytes| {
// // Take the second felt (low part) for SignerId
// bytes.copy_from_slice(&response[offset + 32..offset + 64]);
// Ok(32) // Return successful result with number of bytes written
// })?;
// offset += 64; // Skip both felts (high and low parts)

// // Read capability (1 felt)
// let cap_value = u64::from_be_bytes(response[offset + 24..offset + 32].try_into()?);
// offset += 32;

// // Create capabilities vector with single capability
// let capabilities = vec![Capability::from(cap_value)];

// result.insert(identity, capabilities);
// }

// Ok(result)

todo!()
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use serde::Serialize;
use starknet_crypto::Felt;

use crate::client::env::Method;
use crate::client::protocol::near::Near;
use crate::client::protocol::starknet::Starknet;
use crate::repr::Repr;
use crate::repr::ReprBytes;
use crate::types::ContextId;

#[derive(Copy, Clone, Debug, Serialize)]
Expand Down Expand Up @@ -31,10 +33,35 @@ impl Method<Starknet> for ProxyContractRequest {
type Returns = String;

fn encode(self) -> eyre::Result<Vec<u8>> {
todo!()
// Split context_id into high/low parts
let bytes = self.context_id.as_bytes();
let (high_bytes, low_bytes) = bytes.split_at(bytes.len() / 2);

// Convert to Felts
let high_felt = Felt::from_bytes_be_slice(high_bytes);
let low_felt = Felt::from_bytes_be_slice(low_bytes);

// Convert both Felts to bytes and concatenate
let mut result = Vec::new();
result.extend_from_slice(&high_felt.to_bytes_be());
result.extend_from_slice(&low_felt.to_bytes_be());
Ok(result)
}

fn decode(_response: Vec<u8>) -> eyre::Result<Self::Returns> {
todo!()
fn decode(response: Vec<u8>) -> eyre::Result<Self::Returns> {
println!("response {:?}", response);
if response.is_empty() {
return Err(eyre::eyre!("No proxy contract found"));
}

// Check if it's a None response (single zero Felt)
if response.iter().all(|&x| x == 0) {
return Err(eyre::eyre!("No proxy contract found"));
}

// Convert the Felt to a hex string representing the contract address
let hex_string = format!("0x{}", hex::encode(&response));

Ok(hex_string)
}
}
4 changes: 4 additions & 0 deletions crates/context/config/src/client/env/config/types/starknet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ pub enum ContextRequestKind {
RemoveMembers(Vec<ContextIdentity>),
Grant(Vec<CapabilityAssignment>),
Revoke(Vec<CapabilityAssignment>),
UpdateProxyContract,
}

impl From<crate::ContextRequestKind<'_>> for ContextRequestKind {
Expand Down Expand Up @@ -235,6 +236,9 @@ impl From<crate::ContextRequestKind<'_>> for ContextRequestKind {
})
.collect(),
),
crate::ContextRequestKind::UpdateProxyContract => {
ContextRequestKind::UpdateProxyContract
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/context/config/src/client/env/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use crate::client::{CallClient, Environment};

mod mutate;
mod query;
mod types;
pub use types::*;

use mutate::ContextProxyMutate;
use query::ContextProxyQuery;
Expand Down
80 changes: 69 additions & 11 deletions crates/context/config/src/client/env/proxy/mutate.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
use ed25519_dalek::{Signer, SigningKey};
use starknet_crypto::{poseidon_hash_many, Felt};
use starknet::signers::SigningKey as StarknetSigningKey;
use starknet::core::codec::Encode;
use crate::client::env::proxy::starknet::StarknetProposalWithApprovals;
use crate::Repr;
use crate::repr::ReprBytes;

use crate::client::env::{utils, Method};
use crate::client::protocol::near::Near;
use crate::client::protocol::starknet::Starknet;
use crate::client::transport::Transport;
use crate::client::{CallClient, ClientError, Operation};
use crate::types::Signed;
use crate::types::{ProposalId, Signed, SignerId};
use crate::{ProposalWithApprovals, ProxyMutateRequest};
use starknet::core::codec::Decode;

use super::types::starknet::{StarknetProxyMutateRequest, StarknetSignedRequest};

pub mod methods;

Expand Down Expand Up @@ -49,21 +58,70 @@ impl Method<Near> for Mutate {

impl Method<Starknet> for Mutate {
type Returns = Option<ProposalWithApprovals>;

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

fn encode(self) -> eyre::Result<Vec<u8>> {
// sign the params, encode it and return
// since you will have a `Vec<Felt>` here, you can
// `Vec::with_capacity(32 * calldata.len())` and then
// extend the `Vec` with each `Felt::to_bytes_le()`
// when this `Vec<u8>` makes it to `StarknetTransport`,
// reconstruct the `Vec<Felt>` from it
todo!()
// Derive ECDSA key for signing
let secret_scalar = Felt::from_bytes_be(&self.signing_key);
let signing_key = StarknetSigningKey::from_secret_scalar(secret_scalar);
let verifying_key = signing_key.verifying_key().scalar();
let verifying_key_bytes = verifying_key.to_bytes_be();

// Create signer_id from ECDSA verifying key for signature verification
let signer_id = Repr::new(SignerId::from_bytes(|bytes| {
bytes.copy_from_slice(&verifying_key_bytes);
Ok(32)
})?);

// Create request with signer_id
let request = StarknetProxyMutateRequest::from((signer_id, self.raw_request));

// Serialize -> Hash -> Sign with ECDSA
let mut serialized_request = vec![];
request.encode(&mut serialized_request)?;
let hash = poseidon_hash_many(&serialized_request);
let signature = signing_key.sign(&hash)?;

let signed_request = StarknetSignedRequest {
payload: serialized_request,
signature_r: signature.r,
signature_s: signature.s,
};

let mut signed_request_serialized = vec![];
signed_request.encode(&mut signed_request_serialized)?;

let bytes: Vec<u8> = signed_request_serialized
.iter()
.flat_map(|felt| felt.to_bytes_be())
.collect();

Ok(bytes)
}

fn decode(_response: Vec<u8>) -> eyre::Result<Self::Returns> {
todo!()
fn decode(response: Vec<u8>) -> eyre::Result<Self::Returns> {
println!("response: {:?}", response);
if response.is_empty() {
return Ok(None);
}

// Skip first 32 bytes (array length)
let response = &response[32..];

// Get proposal_id from the next 32 bytes (using only low part)
let proposal_id = Repr::new(ProposalId::from_bytes(|bytes| {
bytes.copy_from_slice(&response[..32]);
Ok(32)
})?);

// Get num_approvals from the last 32 bytes
let num_approvals = u32::from_be_bytes(response[32..][28..32].try_into()?)
as usize;

Ok(Some(ProposalWithApprovals {
proposal_id,
num_approvals,
}))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,18 @@ impl Method<Starknet> for ActiveProposalRequest {
type Returns = u16;

fn encode(self) -> eyre::Result<Vec<u8>> {
todo!()
// No parameters needed for this call
Ok(Vec::new())
}

fn decode(_response: Vec<u8>) -> eyre::Result<Self::Returns> {
todo!()
fn decode(response: Vec<u8>) -> eyre::Result<Self::Returns> {
if response.is_empty() {
return Err(eyre::eyre!("Empty response"));
}

// Take the last byte which contains our value
let value = response[31] as u16; // Get the last byte (index 31)

Ok(value)
}
}
Loading

0 comments on commit c42d235

Please sign in to comment.