Skip to content

Commit

Permalink
Merge pull request #20 from neotheprogramist/1/4-token-policies
Browse files Browse the repository at this point in the history
1/4 Session Policies
  • Loading branch information
neotheprogramist authored Dec 21, 2023
2 parents c8a315b + c23137a commit 4e85994
Show file tree
Hide file tree
Showing 12 changed files with 322 additions and 129 deletions.
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,7 @@ deploy-katana:
starkli deploy "$${account_class}" ${test_pubkey} --salt 0x1234 ${config}; \
starkli declare ${build}ERC20${sierra} ${config}; \
starkli deploy "$${erc20_class}" str:token str:tkn u256:1 ${katana_0} --salt 0x1234 ${config};

test-session: generate_artifacts
rm -rf ./account_sdk/log
cargo test --manifest-path account_sdk/Cargo.toml session
1 change: 1 addition & 0 deletions account_sdk/src/abigen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod account {
type_aliases {
openzeppelin::introspection::src5::SRC5::Event as SRC5Event;
webauthn_session::session_component::Event as SessionEvent;
webauthn_session::signature::SessionSignature as SessionSignature;
}
);
}
Expand Down
42 changes: 33 additions & 9 deletions account_sdk/src/session_token/account.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use async_trait::async_trait;
use cainome::cairo_serde::CairoSerde;
use starknet::{
accounts::{
Account, Call, ConnectedAccount, Declaration, Execution, ExecutionEncoder,
Account, Call as StarknetCall, ConnectedAccount, Declaration, Execution, ExecutionEncoder,
LegacyDeclaration, RawDeclaration, RawExecution, RawLegacyDeclaration,
},
core::types::{
Expand All @@ -11,16 +12,18 @@ use starknet::{
providers::Provider,
signers::Signer,
};
use std::sync::Arc;
use std::{sync::Arc, vec};

use super::{Session, SessionSignature};
use crate::abigen::account::{CartridgeAccount, SessionSignature, SignatureProofs};
use crate::session_token::Session;
use crate::session_token::SIGNATURE_TYPE;

impl<P, S> ExecutionEncoder for SessionAccount<P, S>
where
P: Provider + Send,
S: Signer + Send,
{
fn encode_calls(&self, calls: &[Call]) -> Vec<FieldElement> {
fn encode_calls(&self, calls: &[StarknetCall]) -> Vec<FieldElement> {
// analogous to SingleOwnerAccount::encode_calls for ExecutionEncoding::New
let mut serialized = vec![calls.len().into()];

Expand Down Expand Up @@ -68,6 +71,7 @@ where
address: FieldElement,
chain_id: FieldElement,
) -> Self {
assert_eq!(session.permitted_calls().len(), 1);
Self {
provider,
signer,
Expand All @@ -76,6 +80,10 @@ where
address,
}
}

pub fn session(&mut self) -> &mut Session {
&mut self.session
}
}

#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
Expand Down Expand Up @@ -114,18 +122,34 @@ where
.await
.map_err(SignError::SignersPubkey)?;

let account = CartridgeAccount::new(self.address, &self);
let permited_calls = self.session.permitted_calls();
let proof = account
.compute_proof(permited_calls, &0)
.call()
.await
.expect("computing proof failed");
let root = account
.compute_root(&permited_calls[0], &proof)
.call()
.await
.expect("computing root failed");

let signature = SessionSignature {
signature_type: SIGNATURE_TYPE,
r: signature.r,
s: signature.s,
session_key: session_key.scalar(),
session_expires: self.session.session_expires(),
root: self.session.root(),
proof_len: self.session.proof_len(),
proofs: self.session.proofs(),
root,
proofs: SignatureProofs {
single_proof_len: proof.len() as u32,
proofs_flat: proof,
},
session_token: self.session.session_token(),
};

Ok(signature.into())
Ok(SessionSignature::cairo_serialize(&signature))
}

async fn sign_declaration(
Expand All @@ -144,7 +168,7 @@ where
unimplemented!("sign_legacy_declaration")
}

fn execute(&self, calls: Vec<Call>) -> Execution<Self> {
fn execute(&self, calls: Vec<StarknetCall>) -> Execution<Self> {
Execution::new(calls, self)
}

Expand Down
177 changes: 148 additions & 29 deletions account_sdk/src/session_token/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
mod account;
mod sequence;
mod session;
mod signature;
#[cfg(test)]
mod test_utils;

pub use account::SessionAccount;
pub use sequence::CallSequence;
pub use session::Session;
pub use signature::SessionSignature;
use starknet::{core::types::FieldElement, macros::felt};

pub const SIGNATURE_TYPE: FieldElement = felt!("0x53657373696f6e20546f6b656e207631"); // 'Session Token v1'
Expand All @@ -15,64 +15,183 @@ pub const SIGNATURE_TYPE: FieldElement = felt!("0x53657373696f6e20546f6b656e2076
mod tests {
use std::time::Duration;

use cainome::cairo_serde::ContractAddress;
use starknet::{
accounts::{Account, Call, ConnectedAccount},
accounts::{Account, ConnectedAccount},
macros::selector,
signers::{LocalWallet, SigningKey},
};
use tokio::time::sleep;

use crate::session_token::SessionAccount;
use crate::tests::{
deployment_test::create_account,
runners::{DevnetRunner, TestnetRunner},
runners::{KatanaRunner, TestnetRunner},
};
use crate::{
abigen::{
self,
account::{Call, CartridgeAccount},
},
session_token::test_utils::create_session_account,
};

use super::*;

#[tokio::test]
async fn test_session_valid() {
let runner = DevnetRunner::load();
async fn test_session_compute_proof() {
let runner = KatanaRunner::load();
let master = create_account(&runner.prefunded_single_owner_account().await).await;

let session_key = LocalWallet::from(SigningKey::from_random());
let address = master.address();
let account = CartridgeAccount::new(address, &master);

let session = Session::default();
let (chain_id, address) = (master.chain_id(), master.address());
let provider = *master.provider();
let account = SessionAccount::new(provider, session_key, session, address, chain_id);
let cainome_address = cainome::cairo_serde::ContractAddress::from(address);

let calls = vec![Call {
to: address,
let call = abigen::account::Call {
to: cainome_address,
selector: selector!("revoke_session"),
calldata: vec![felt!("0x2137")],
}];
calldata: vec![FieldElement::from(0x2137u32)],
};

sleep(Duration::from_secs(10)).await;
account.execute(calls.clone()).send().await.unwrap();
let proof = account.compute_proof(&vec![call], &0).call().await.unwrap();

assert_eq!(proof, vec![]);
}

#[tokio::test]
async fn test_session_revoked() {
let runner = DevnetRunner::load();
async fn test_session_compute_root() {
let runner = KatanaRunner::load();
let master = create_account(&runner.prefunded_single_owner_account().await).await;

let address = master.address();
let account = CartridgeAccount::new(address, &master);

let cainome_address = ContractAddress::from(address);

let call = Call {
to: cainome_address,
selector: selector!("revoke_session"),
calldata: vec![FieldElement::from(0x2137u32)],
};

let root = account.compute_root(&call, &vec![]).call().await.unwrap();

assert_ne!(root, felt!("0x0"));
}

#[tokio::test]
async fn test_session_valid() {
let runner = KatanaRunner::load();
let master = create_account(&runner.prefunded_single_owner_account().await).await;

let session_key = LocalWallet::from(SigningKey::from_random());

let session = Session::default();
let mut session = Session::default();
let cainome_address = ContractAddress::from(master.address());
let permited_calls = vec![Call {
to: cainome_address,
selector: selector!("revoke_session"),
calldata: vec![FieldElement::from(0x2137u32)],
}];

session.set_permitted_calls(permited_calls);

let (chain_id, address) = (master.chain_id(), master.address());
let provider = *master.provider();
let account = SessionAccount::new(provider, session_key, session, address, chain_id);
let account = CartridgeAccount::new(address, &account);

let calls = vec![Call {
to: address,
selector: selector!("revoke_session"),
calldata: vec![felt!("0x2137")],
}];
account
.revoke_session(&FieldElement::from(0x2137u32))
.send()
.await
.unwrap();
}

#[tokio::test]
async fn test_session_revoked() {
let runner = KatanaRunner::load();
let session_account =
create_session_account(&runner.prefunded_single_owner_account().await).await;

let account = CartridgeAccount::new(session_account.address(), &session_account);

account
.revoke_session(&FieldElement::from(0x2137u32))
.send()
.await
.unwrap();

sleep(Duration::from_millis(100)).await;

let result = account
.revoke_session(&FieldElement::from(0x2137u32))
.send()
.await;

sleep(Duration::from_secs(10)).await;
account.execute(calls.clone()).send().await.unwrap();
let result = account.execute(calls.clone()).send().await;
assert!(result.is_err(), "Session should be revoked");
}

assert!(result.is_err());
#[tokio::test]
async fn test_session_invalid_proof() {
let runner = KatanaRunner::load();
let mut session_account =
create_session_account(&runner.prefunded_single_owner_account().await).await;

let cainome_address = ContractAddress::from(session_account.address());
session_account.session().set_permitted_calls(vec![Call {
to: cainome_address,
selector: selector!("validate_session"),
calldata: vec![FieldElement::from(0x2137u32)],
}]);

let account = CartridgeAccount::new(session_account.address(), &session_account);

let result = account
.revoke_session(&FieldElement::from(0x2137u32))
.send()
.await;

assert!(result.is_err(), "Session should be revoked");
}

#[tokio::test]
async fn test_session_many_allowed() {
let runner = KatanaRunner::load();
let mut session_account =
create_session_account(&runner.prefunded_single_owner_account().await).await;

let cainome_address = ContractAddress::from(session_account.address());
session_account.session().set_permitted_calls(vec![
Call {
to: cainome_address,
selector: selector!("revoke_session"),
calldata: vec![],
},
Call {
to: cainome_address,
selector: selector!("validate_session"),
calldata: vec![],
},
Call {
to: cainome_address,
selector: selector!("compute_root"),
calldata: vec![],
},
Call {
to: cainome_address,
selector: selector!("not_yet_defined"),
calldata: vec![],
},
]);

let account = CartridgeAccount::new(session_account.address(), &session_account);

account
.revoke_session(&FieldElement::from(0x2137u32))
.send()
.await
.unwrap();
}
}
27 changes: 11 additions & 16 deletions account_sdk/src/session_token/session.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,39 @@
use starknet::{core::types::FieldElement, macros::felt};

use crate::abigen::account::Call;

#[derive(Clone)]
pub struct Session {
session_expires: u64,
root: FieldElement,
proof_len: u32,
proofs: Vec<FieldElement>,
session_token: Vec<FieldElement>,
permitted_calls: Vec<Call>,
}

impl Session {
pub fn session_expires(&self) -> u64 {
self.session_expires
}

pub fn root(&self) -> FieldElement {
self.root
}

pub fn proof_len(&self) -> u32 {
self.proof_len
pub fn session_token(&self) -> Vec<FieldElement> {
self.session_token.clone()
}

pub fn proofs(&self) -> Vec<FieldElement> {
self.proofs.clone()
pub fn permitted_calls(&self) -> &Vec<Call> {
&self.permitted_calls
}

pub fn session_token(&self) -> Vec<FieldElement> {
self.session_token.clone()
#[cfg(test)]
pub fn set_permitted_calls(&mut self, calls: Vec<Call>) {
self.permitted_calls = calls;
}
}

impl Default for Session {
fn default() -> Self {
Self {
session_expires: u64::MAX,
root: felt!("0x0"),
proof_len: 1,
proofs: vec![felt!("44")],
session_token: vec![felt!("0x2137")],
permitted_calls: vec![],
}
}
}
Loading

0 comments on commit 4e85994

Please sign in to comment.