pub mod credentials;
pub mod did;
pub mod mandates;
pub mod verification;
pub use credentials::{
CredentialSubject, Proof, ProofPurpose, VerifiableCredential, VerificationMethod,
};
pub use did::{DidDocument, DidManager, DidResolver, ServiceEndpoint};
pub use mandates::{
CartItem, CartMandate, IntentMandate, Mandate, MandateStatus, MandateType, PaymentMandate,
PaymentMethod, Permission,
};
pub use verification::{
ConsensusVerification, VerificationResult, VerificationWorkflow, VerifierNode,
};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use thiserror::Error;
pub const AP2_VERSION: &str = "1.0.0";
#[derive(Debug, Error)]
pub enum Ap2Error {
#[error("Invalid credential: {0}")]
InvalidCredential(String),
#[error("Signature verification failed: {0}")]
SignatureVerificationFailed(String),
#[error("DID resolution failed: {0}")]
DidResolutionFailed(String),
#[error("Mandate validation failed: {0}")]
MandateValidationFailed(String),
#[error("Consensus verification failed: {0}")]
ConsensusVerificationFailed(String),
#[error("Expired credential or mandate")]
Expired,
#[error("Insufficient authorization: {0}")]
InsufficientAuthorization(String),
#[error("Serialization error: {0}")]
SerializationError(String),
#[error("Cryptographic error: {0}")]
CryptographicError(String),
}
pub type Result<T> = std::result::Result<T, Ap2Error>;
pub const VC_CONTEXT: &str = "https://www.w3.org/2018/credentials/v1";
pub const AP2_CONTEXT: &str = "https://ap2.protocol/v1";
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AgentIdentity {
pub did: String,
pub public_key: Vec<u8>,
pub metadata: HashMap<String, String>,
}
impl AgentIdentity {
pub fn new(did: String, public_key: Vec<u8>) -> Self {
Self {
did,
public_key,
metadata: HashMap::new(),
}
}
pub fn with_metadata(mut self, key: String, value: String) -> Self {
self.metadata.insert(key, value);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaymentAuthorization {
pub intent_mandate: VerifiableCredential,
pub cart_mandate: VerifiableCredential,
pub payment_mandate: VerifiableCredential,
pub authorization_chain: Vec<VerifiableCredential>,
pub timestamp: DateTime<Utc>,
}
impl PaymentAuthorization {
pub fn new(
intent_mandate: VerifiableCredential,
cart_mandate: VerifiableCredential,
payment_mandate: VerifiableCredential,
) -> Self {
Self {
intent_mandate: intent_mandate.clone(),
cart_mandate: cart_mandate.clone(),
payment_mandate: payment_mandate.clone(),
authorization_chain: vec![intent_mandate, cart_mandate, payment_mandate],
timestamp: Utc::now(),
}
}
pub fn verify_chain(&self, did_resolver: &DidResolver) -> Result<bool> {
for credential in &self.authorization_chain {
if !credential.verify(did_resolver)? {
return Ok(false);
}
}
Ok(true)
}
pub fn is_valid(&self) -> bool {
let now = Utc::now();
self.authorization_chain
.iter()
.all(|vc| vc.expiration_date.map_or(true, |exp| exp > now))
}
}
#[derive(Debug)]
pub struct Ap2Protocol {
did_manager: DidManager,
did_resolver: DidResolver,
verification_workflow: VerificationWorkflow,
}
impl Ap2Protocol {
pub fn new() -> Self {
Self {
did_manager: DidManager::new(),
did_resolver: DidResolver::new(),
verification_workflow: VerificationWorkflow::new(),
}
}
pub fn register_agent(&mut self, agent_id: &str, public_key: Vec<u8>) -> Result<AgentIdentity> {
let did = self.did_manager.create_did(agent_id, public_key.clone())?;
Ok(AgentIdentity::new(did, public_key))
}
pub fn create_intent_mandate(
&self,
issuer: &AgentIdentity,
subject_agent: &str,
intent_description: &str,
private_key: &[u8],
) -> Result<VerifiableCredential> {
let mandate = IntentMandate::new(
issuer.did.clone(),
subject_agent.to_string(),
intent_description.to_string(),
);
let subject = CredentialSubject {
id: subject_agent.to_string(),
claims: serde_json::to_value(mandate)
.map_err(|e| Ap2Error::SerializationError(e.to_string()))?,
};
VerifiableCredential::new(issuer.did.clone(), subject, private_key)
}
pub fn create_cart_mandate(
&self,
issuer: &AgentIdentity,
items: Vec<CartItem>,
total_amount: u64,
currency: &str,
private_key: &[u8],
) -> Result<VerifiableCredential> {
let mandate = CartMandate::new(issuer.did.clone(), items, total_amount, currency.to_string());
let subject = CredentialSubject {
id: issuer.did.clone(),
claims: serde_json::to_value(mandate)
.map_err(|e| Ap2Error::SerializationError(e.to_string()))?,
};
VerifiableCredential::new(issuer.did.clone(), subject, private_key)
}
pub fn create_payment_mandate(
&self,
issuer: &AgentIdentity,
recipient: &str,
amount: u64,
currency: &str,
payment_method: &str,
private_key: &[u8],
) -> Result<VerifiableCredential> {
let mandate = PaymentMandate::new(
issuer.did.clone(),
recipient.to_string(),
amount,
currency.to_string(),
payment_method.to_string(),
);
let subject = CredentialSubject {
id: recipient.to_string(),
claims: serde_json::to_value(mandate)
.map_err(|e| Ap2Error::SerializationError(e.to_string()))?,
};
VerifiableCredential::new(issuer.did.clone(), subject, private_key)
}
pub async fn verify_payment_authorization(
&self,
authorization: &PaymentAuthorization,
verifier_nodes: Vec<VerifierNode>,
) -> Result<VerificationResult> {
if !authorization.verify_chain(&self.did_resolver)? {
return Err(Ap2Error::ConsensusVerificationFailed(
"Credential chain verification failed".to_string(),
));
}
self.verification_workflow
.verify_with_consensus(
&authorization.payment_mandate,
verifier_nodes,
&self.did_resolver,
)
.await
}
pub fn resolve_did(&self, did: &str) -> Result<DidDocument> {
self.did_resolver.resolve(did)
}
pub fn did_resolver(&self) -> &DidResolver {
&self.did_resolver
}
pub fn did_manager(&self) -> &DidManager {
&self.did_manager
}
}
impl Default for Ap2Protocol {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_agent_identity_creation() {
let public_key = vec![1, 2, 3, 4];
let identity = AgentIdentity::new("did:example:123".to_string(), public_key.clone());
assert_eq!(identity.did, "did:example:123");
assert_eq!(identity.public_key, public_key);
}
#[test]
fn test_ap2_protocol_initialization() {
let protocol = Ap2Protocol::new();
assert!(protocol.did_resolver.resolve("test").is_err());
}
#[test]
fn test_payment_authorization_validity() {
let protocol = Ap2Protocol::new();
assert!(protocol.did_resolver.resolve("did:example:test").is_err());
}
}