diff --git a/Makefile b/Makefile
index a8fbc311..78a295dc 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/account_sdk/src/abigen.rs b/account_sdk/src/abigen.rs
index b5fd3aa9..4df025b1 100644
--- a/account_sdk/src/abigen.rs
+++ b/account_sdk/src/abigen.rs
@@ -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;
}
);
}
diff --git a/account_sdk/src/session_token/account.rs b/account_sdk/src/session_token/account.rs
index 7fb4efbe..0cbdc18b 100644
--- a/account_sdk/src/session_token/account.rs
+++ b/account_sdk/src/session_token/account.rs
@@ -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::{
@@ -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
ExecutionEncoder for SessionAccount
where
P: Provider + Send,
S: Signer + Send,
{
- fn encode_calls(&self, calls: &[Call]) -> Vec {
+ fn encode_calls(&self, calls: &[StarknetCall]) -> Vec {
// analogous to SingleOwnerAccount::encode_calls for ExecutionEncoding::New
let mut serialized = vec![calls.len().into()];
@@ -68,6 +71,7 @@ where
address: FieldElement,
chain_id: FieldElement,
) -> Self {
+ assert_eq!(session.permitted_calls().len(), 1);
Self {
provider,
signer,
@@ -76,6 +80,10 @@ where
address,
}
}
+
+ pub fn session(&mut self) -> &mut Session {
+ &mut self.session
+ }
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
@@ -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(
@@ -144,7 +168,7 @@ where
unimplemented!("sign_legacy_declaration")
}
- fn execute(&self, calls: Vec) -> Execution {
+ fn execute(&self, calls: Vec) -> Execution {
Execution::new(calls, self)
}
diff --git a/account_sdk/src/session_token/mod.rs b/account_sdk/src/session_token/mod.rs
index 5380f2de..0f7976e2 100644
--- a/account_sdk/src/session_token/mod.rs
+++ b/account_sdk/src/session_token/mod.rs
@@ -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'
@@ -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();
}
}
diff --git a/account_sdk/src/session_token/session.rs b/account_sdk/src/session_token/session.rs
index dd010df2..6bd586a8 100644
--- a/account_sdk/src/session_token/session.rs
+++ b/account_sdk/src/session_token/session.rs
@@ -1,12 +1,12 @@
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,
session_token: Vec,
+ permitted_calls: Vec,
}
impl Session {
@@ -14,20 +14,17 @@ impl Session {
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 {
+ self.session_token.clone()
}
- pub fn proofs(&self) -> Vec {
- self.proofs.clone()
+ pub fn permitted_calls(&self) -> &Vec {
+ &self.permitted_calls
}
- pub fn session_token(&self) -> Vec {
- self.session_token.clone()
+ #[cfg(test)]
+ pub fn set_permitted_calls(&mut self, calls: Vec) {
+ self.permitted_calls = calls;
}
}
@@ -35,10 +32,8 @@ 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![],
}
}
}
diff --git a/account_sdk/src/session_token/signature.rs b/account_sdk/src/session_token/signature.rs
deleted file mode 100644
index e3057806..00000000
--- a/account_sdk/src/session_token/signature.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-use starknet::core::types::FieldElement;
-
-#[derive(Clone)]
-pub struct SessionSignature {
- pub r: FieldElement,
- pub s: FieldElement,
- pub session_key: FieldElement,
- pub session_expires: u64,
- pub root: FieldElement,
- pub proof_len: u32,
- pub proofs: Vec,
- pub session_token: Vec,
-}
-
-impl Into> for SessionSignature {
- fn into(self) -> Vec {
- let mut result = vec![super::SIGNATURE_TYPE];
- result.push(self.r);
- result.push(self.s);
- result.push(self.session_key);
- result.push(self.session_expires.into());
- result.push(self.root);
- result.push(self.proof_len.into());
- result.push(self.proofs.len().into());
- result.extend(self.proofs);
- result.push(self.session_token.len().into());
- result.extend(self.session_token);
- result
- }
-}
diff --git a/account_sdk/src/session_token/test_utils.rs b/account_sdk/src/session_token/test_utils.rs
new file mode 100644
index 00000000..eb878d3a
--- /dev/null
+++ b/account_sdk/src/session_token/test_utils.rs
@@ -0,0 +1,37 @@
+use starknet::{
+ accounts::{Account, ConnectedAccount, SingleOwnerAccount},
+ core::types::FieldElement,
+ macros::selector,
+ providers::{jsonrpc::HttpTransport, JsonRpcClient},
+ signers::{LocalWallet, SigningKey},
+};
+
+use crate::abigen::account::Call;
+use crate::session_token::SessionAccount;
+use crate::tests::deployment_test::create_account;
+
+use super::Session;
+
+pub async fn create_session_account<'a>(
+ from: &SingleOwnerAccount<&'a JsonRpcClient, LocalWallet>,
+) -> SessionAccount<&'a JsonRpcClient, LocalWallet> {
+ let (provider, chain_id) = (from.provider(), from.chain_id());
+
+ let master = create_account(from).await;
+ let address = master.address();
+ let cainome_address = cainome::cairo_serde::ContractAddress::from(address);
+
+ let call = Call {
+ to: cainome_address,
+ selector: selector!("revoke_session"),
+ calldata: vec![FieldElement::from(0x2137u32)],
+ };
+
+ let mut session = Session::default();
+ let permited_calls = vec![call];
+ session.set_permitted_calls(permited_calls);
+
+ let session_key = LocalWallet::from(SigningKey::from_random());
+
+ SessionAccount::new(provider, session_key, session, address, chain_id)
+}
diff --git a/cartridge_account/src/lib.cairo b/cartridge_account/src/lib.cairo
index d004436d..705d1c72 100644
--- a/cartridge_account/src/lib.cairo
+++ b/cartridge_account/src/lib.cairo
@@ -116,7 +116,16 @@ mod Account {
}
fn __validate__(self: @ContractState, mut calls: Array) -> felt252 {
- self.validate_transaction()
+ let signature_type = self.validate_transaction();
+ let tx_info = get_tx_info().unbox();
+
+ if signature_type == starknet::VALIDATED {
+ starknet::VALIDATED
+ } else if signature_type == 'Session Token v1' {
+ SessionImpl::validate_session(self, tx_info.signature, calls.span())
+ } else {
+ signature_type
+ }
}
fn is_valid_signature(
@@ -242,20 +251,13 @@ mod Account {
return starknet::VALIDATED;
}
- let signature_type = *signature.pop_front().unwrap();
-
- if signature_type == 'Session Token v1' {
- // TODO: replace this mock
- let calls = array![Call {
- to: get_caller_address(),
- selector: 1485514293835613587393115454149572371807630552041622198027280818831729347259,
- calldata: array![0x2137]
- }];
- SessionImpl::validate_session(self, signature, calls.span())
- } else {
+ let signature_type = *signature.at(0);
+
+ if signature_type == starknet::VALIDATED {
assert(false, Errors::INVALID_SIGNATURE);
}
- starknet::VALIDATED
+
+ signature_type
}
fn _set_public_key(ref self: ContractState, new_public_key: felt252) {
diff --git a/src/session/src/hash.cairo b/src/session/src/hash.cairo
index 39c9f076..7797a78d 100644
--- a/src/session/src/hash.cairo
+++ b/src/session/src/hash.cairo
@@ -3,7 +3,7 @@ use core::poseidon::{PoseidonImpl, PoseidonTrait};
use starknet::{contract_address::ContractAddress};
-use webauthn_session::signature::TxInfoSignature;
+use webauthn_session::signature::SessionSignature;
use webauthn_session::Call;
// H('StarkNetDomain(chainId:felt)')
@@ -17,7 +17,7 @@ const POLICY_TYPE_HASH: felt252 = 0x2f0026e78543f036f33e26a8f5891b88c58dc1e20cbb
// https://github.com/starkware-libs/cairo/blob/1cd1d242883787392f38f5a775ab045b5e2201f3/corelib/src/hash.cairo#L4
// https://github.com/starkware-libs/cairo/blob/1cd1d242883787392f38f5a775ab045b5e2201f3/examples/hash_chain.cairo#L4
fn compute_session_hash(
- signature: TxInfoSignature, chain_id: felt252, account: ContractAddress
+ signature: SessionSignature, chain_id: felt252, account: ContractAddress
) -> felt252 {
let domain_hash = hash_domain(chain_id);
let message_hash = hash_message(
diff --git a/src/session/src/lib.cairo b/src/session/src/lib.cairo
index 2755a71d..d8f53c88 100644
--- a/src/session/src/lib.cairo
+++ b/src/session/src/lib.cairo
@@ -1,4 +1,3 @@
-use webauthn_session::signature::SignatureProofsTrait;
use alexandria_data_structures::array_ext::ArrayTraitExt;
use core::box::BoxTrait;
use core::array::SpanTrait;
@@ -9,14 +8,14 @@ use option::OptionTrait;
use array::ArrayTrait;
use core::{TryInto, Into};
use starknet::{contract_address::ContractAddress};
+use alexandria_merkle_tree::merkle_tree::{Hasher, MerkleTree, poseidon::PoseidonHasherImpl, MerkleTreeTrait};
-use webauthn_session::signature::{TxInfoSignature, FeltSpanTryIntoSignature, SignatureProofs};
+use core::ecdsa::check_ecdsa_signature;
+
+use webauthn_session::signature::{SessionSignature, FeltSpanTryIntoSignature, SignatureProofs, SignatureProofsTrait};
use webauthn_session::hash::{compute_session_hash, compute_call_hash};
-use alexandria_merkle_tree::merkle_tree::{Hasher, MerkleTree, pedersen::PedersenHasherImpl, MerkleTreeTrait};
-use core::ecdsa::check_ecdsa_signature;
-
mod hash;
mod signature;
@@ -25,8 +24,12 @@ mod tests;
#[starknet::interface]
trait ISession {
- fn validate_session(self: @TContractState, signature: Span, calls: Span);
+ fn validate_session_abi(self: @TContractState, signature: SessionSignature, calls: Span);
+ fn validate_session(self: @TContractState, signature: Span, calls: Span) -> felt252;
fn revoke_session(ref self: TContractState, token: felt252);
+
+ fn compute_proof(self: @TContractState, calls: Array, position: u64) -> Span;
+ fn compute_root(self: @TContractState, call: Call, proof: Span) -> felt252;
}
// Based on https://github.com/argentlabs/starknet-plugin-account/blob/3c14770c3f7734ef208536d91bbd76af56dc2043/contracts/plugins/SessionKey.cairo
@@ -36,9 +39,10 @@ mod session_component {
use super::check_policy;
use starknet::info::{TxInfo, get_tx_info, get_block_timestamp};
use starknet::account::Call;
- use webauthn_session::signature::{TxInfoSignature, FeltSpanTryIntoSignature, SignatureProofs, SignatureProofsTrait};
+ use webauthn_session::signature::{SessionSignature, FeltSpanTryIntoSignature, SignatureProofs, SignatureProofsTrait};
use webauthn_session::hash::{compute_session_hash, compute_call_hash};
use ecdsa::check_ecdsa_signature;
+ use alexandria_merkle_tree::merkle_tree::{Hasher, MerkleTree, poseidon::PoseidonHasherImpl, MerkleTreeTrait};
#[storage]
struct Storage {
@@ -68,10 +72,15 @@ mod session_component {
impl SessionImpl<
TContractState, +HasComponent
> of super::ISession> {
- fn validate_session(self: @ComponentState, mut signature: Span, calls: Span) {
- let sig: TxInfoSignature = Serde::::deserialize(ref signature).unwrap();
+ fn validate_session_abi(self: @ComponentState, signature: SessionSignature, calls: Span) {
+ self.validate_signature(signature, calls).unwrap();
+ }
+
+ fn validate_session(self: @ComponentState, mut signature: Span, calls: Span) -> felt252 {
+ let sig: SessionSignature = Serde::::deserialize(ref signature).unwrap();
self.validate_signature(sig, calls).unwrap();
+ starknet::VALIDATED
}
fn revoke_session(
@@ -80,13 +89,39 @@ mod session_component {
self.revoked.write(token, 1);
self.emit(TokenRevoked { token: token });
}
+
+ fn compute_proof(self: @ComponentState, mut calls: Array, position: u64) -> Span {
+ assert(calls.len() > 0, 'No calls provided');
+ let mut merkle: MerkleTree = MerkleTreeTrait::new();
+
+ let mut leaves = array![];
+
+ // Hashing all the calls
+ loop {
+ let pub_key = match calls.pop_front() {
+ Option::Some(single) => {
+ leaves.append(compute_call_hash(@single));
+ },
+ Option::None(_) => { break; },
+ };
+ };
+
+ merkle.compute_proof(leaves.clone(), 0)
+ }
+
+ fn compute_root(self: @ComponentState, call: Call, proof: Span) -> felt252 {
+ let mut merkle: MerkleTree = MerkleTreeTrait::new();
+ let leaf = compute_call_hash(@call);
+
+ merkle.compute_root(leaf, proof)
+ }
}
#[generate_trait]
impl InternalImpl<
TContractState, +HasComponent
> of InternalTrait {
- fn validate_signature(self: @ComponentState, signature: TxInfoSignature, calls: Span) -> Result<(), felt252> {
+ fn validate_signature(self: @ComponentState, signature: SessionSignature, calls: Span) -> Result<(), felt252> {
if signature.proofs.len() != calls.len() {
return Result::Err(Errors::LENGHT_MISMATCH);
};
@@ -118,9 +153,9 @@ mod session_component {
return Result::Err(Errors::SESSION_SIGNATURE_INVALID);
}
- // if check_policy(calls, signature.root, signature.proofs).is_err() {
- // return Result::Err(Errors::POLICY_CHECK_FAILED);
- // }
+ if check_policy(calls, signature.root, signature.proofs).is_err() {
+ return Result::Err(Errors::POLICY_CHECK_FAILED);
+ }
Result::Ok(())
}
@@ -128,7 +163,7 @@ mod session_component {
}
fn check_policy(
- call_array: Array, root: felt252, proofs: SignatureProofs,
+ call_array: Span, root: felt252, proofs: SignatureProofs,
) -> Result<(), ()> {
let mut i = 0_usize;
loop {
@@ -137,6 +172,7 @@ fn check_policy(
}
let leaf = compute_call_hash(call_array.at(i));
let mut merkle: MerkleTree = MerkleTreeTrait::new();
+
if merkle.verify(root, leaf, proofs.at(i)) == false {
break Result::Err(());
};
diff --git a/src/session/src/signature.cairo b/src/session/src/signature.cairo
index d084b21b..9d6a5cc6 100644
--- a/src/session/src/signature.cairo
+++ b/src/session/src/signature.cairo
@@ -3,7 +3,8 @@ use core::Into;
use debug::PrintTrait;
#[derive(Copy, Drop, Serde)]
-struct TxInfoSignature {
+struct SessionSignature {
+ signature_type: felt252,
r: felt252,
s: felt252,
session_key: felt252,
@@ -13,16 +14,16 @@ struct TxInfoSignature {
session_token: Span
}
-impl FeltSpanTryIntoSignature of TryInto, TxInfoSignature> {
- // Convert a span of felts to TxInfoSignature struct
+impl FeltSpanTryIntoSignature of TryInto, SessionSignature> {
+ // Convert a span of felts to SessionSignature struct
// The layout of the span should look like:
// [r, s, session_key, session_expires, root, proof_len, proofs_len, { proofs ... } , session_token_len, { session_token ... }]
// ^-proofs_len-^ ^-session_token_len-^
// See details in the implementation
- fn try_into(self: Span) -> Option {
- let single_proof_len: usize = (*self[6]).try_into()?;
- let total_proofs_len: usize = (*self[7]).try_into()?;
- let session_token_offset: usize = 8 + total_proofs_len;
+ fn try_into(self: Span) -> Option {
+ let single_proof_len: usize = (*self[7]).try_into()?;
+ let total_proofs_len: usize = (*self[8]).try_into()?;
+ let session_token_offset: usize = 9 + total_proofs_len;
let session_token_len: usize = (*self[session_token_offset]).try_into()?;
if self.len() != session_token_offset + 1 + session_token_len {
@@ -32,16 +33,17 @@ impl FeltSpanTryIntoSignature of TryInto, TxInfoSignature> {
let session_token: Span = self
.slice(session_token_offset + 1, session_token_len);
- let proofs_flat: Span = self.slice(8, total_proofs_len);
+ let proofs_flat: Span = self.slice(9, total_proofs_len);
let proofs = ImplSignatureProofs::try_new(proofs_flat, single_proof_len)?;
Option::Some(
- TxInfoSignature {
- r: *self[1],
- s: *self[2],
- session_key: *self[3],
- session_expires: (*self[4]).try_into()?,
- root: *self[5],
+ SessionSignature {
+ signature_type: *self[0],
+ r: *self[2],
+ s: *self[3],
+ session_key: *self[4],
+ session_expires: (*self[5]).try_into()?,
+ root: *self[6],
proofs: proofs,
session_token: session_token
}
@@ -68,6 +70,9 @@ impl ImplSignatureProofs of SignatureProofsTrait {
}
fn len(self: SignatureProofs) -> usize {
+ if self.single_proof_len == 0 {
+ return 1; // When there is only one leaf, it is equal to the root, so the only proof is empty
+ }
U32Div::div(self.proofs_flat.len(), self.single_proof_len)
}
diff --git a/src/session/src/tests/signature_deserialization.cairo b/src/session/src/tests/signature_deserialization.cairo
index 21aa582b..18318a61 100644
--- a/src/session/src/tests/signature_deserialization.cairo
+++ b/src/session/src/tests/signature_deserialization.cairo
@@ -9,7 +9,7 @@ use array::ArrayTrait;
use core::{TryInto, Into};
use starknet::{contract_address::ContractAddress};
-use webauthn_session::signature::{TxInfoSignature, FeltSpanTryIntoSignature, SignatureProofs};
+use webauthn_session::signature::{SessionSignature, FeltSpanTryIntoSignature, SignatureProofs};
use webauthn_session::hash::{compute_session_hash, compute_call_hash};
use alexandria_merkle_tree::merkle_tree::{Hasher, MerkleTree, pedersen::PedersenHasherImpl, MerkleTreeTrait};
@@ -33,7 +33,7 @@ fn test_session() {
0x2137,
].span();
- let deser: TxInfoSignature = Serde::::deserialize(ref sig).unwrap();
+ let deser: SessionSignature = Serde::::deserialize(ref sig).unwrap();
assert(deser.r == 0x42, 'invalid r');
assert(deser.proofs.len() == 1, 'invalid proofs len');
}