use std::sync::Arc;
use std::time::Duration;
use macula_rust::{
cbor::Value, connection, connection::CallHandler, identity::KeyPair, transport::Trust, ucan,
};
const HOST: &str = "station-de-frankfurt.macula.io";
const PORT: u16 = 4433;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let provider_id = KeyPair::generate_with_default_puzzle();
let caller_id = KeyPair::generate_with_default_puzzle();
let authority = KeyPair::generate_with_default_puzzle();
let mut provider = connection::connect(HOST, PORT, Trust::WebPki, &provider_id).await?;
let mut caller = connection::connect(HOST, PORT, Trust::WebPki, &caller_id).await?;
let realm = [0u8; 32];
let procedure = "macula_rust.examples.ucan_gated";
let advertise_spec =
macula_rust::frame::AdvertiseSpec::new(realm, procedure, provider_id.node_id());
provider.advertise(&advertise_spec, &provider_id).await?;
tokio::time::sleep(Duration::from_millis(1200)).await;
let issuer_pub = authority.node_id();
let handler: CallHandler = Arc::new(|payload: Value| {
Box::pin(async move { Ok(Value::Text(format!("granted: {payload:?}"))) })
});
let serve_task = tokio::spawn(async move {
for _ in 0..2 {
let handler = handler.clone();
provider
.serve_one_call_gated(
move |_realm, proc| {
if proc == procedure {
Some(handler.clone())
} else {
None
}
},
move |_, _| ucan::Policy::required(issuer_pub),
&provider_id,
Duration::from_secs(15),
)
.await?;
}
tokio::time::sleep(Duration::from_millis(300)).await;
Ok::<(), connection::ServeCallError>(())
});
let rejected = caller
.call(
procedure,
realm,
Value::Null,
0,
&caller_id,
Duration::from_secs(5),
)
.await;
println!("call without a token: {rejected:?}");
let token = ucan::create(
"did:key:example-issuer",
"did:key:example-audience",
vec![],
&authority,
ucan::CreateOpts::default(),
)?;
let granted = caller
.call_with_ucan(
procedure,
realm,
Value::Text("hello".into()),
0,
&caller_id,
Duration::from_secs(5),
token,
)
.await;
println!("call with a valid token: {granted:?}");
serve_task.await??;
Ok(())
}