use crate::Result;
use data_encoding::HEXUPPER;
use ring::digest::{Context, Digest, SHA256};
use serde::{Deserialize, Serialize};
use std::io::Read;
use uuid::Uuid;
use wascap::jwt::Claims;
use wascap::prelude::KeyPair;
const URL_SCHEME: &str = "wasmbus";
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Invocation {
pub origin: Entity,
pub target: Entity,
pub operation: String,
pub msg: Vec<u8>,
pub id: String,
pub encoded_claims: String,
pub host_id: String,
}
impl Invocation {
pub fn new(
hostkey: &KeyPair,
origin: Entity,
target: Entity,
op: &str,
msg: Vec<u8>,
) -> Invocation {
let subject = format!("{}", Uuid::new_v4());
let issuer = hostkey.public_key();
let target_url = format!("{}/{}", target.url(), op);
let claims = Claims::<wascap::prelude::Invocation>::new(
issuer.to_string(),
subject.to_string(),
&target_url,
&origin.url(),
&invocation_hash(&target_url, &origin.url(), &msg),
);
Invocation {
origin,
target,
operation: op.to_string(),
msg,
id: subject,
encoded_claims: claims.encode(&hostkey).unwrap(),
host_id: issuer,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct InvocationResponse {
pub msg: Vec<u8>,
pub error: Option<String>,
pub invocation_id: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub enum Entity {
Actor(String),
Capability {
id: String,
contract_id: String,
link_name: String,
},
}
impl Entity {
pub fn url(&self) -> String {
match self {
Entity::Actor(pk) => format!("{}://{}", URL_SCHEME, pk),
Entity::Capability {
id,
contract_id,
link_name,
} => format!(
"{}://{}/{}/{}",
URL_SCHEME,
contract_id
.replace(":", "/")
.replace(" ", "_")
.to_lowercase(),
link_name.replace(" ", "_").to_lowercase(),
id
),
}
}
}
fn sha256_digest<R: Read>(mut reader: R) -> Result<Digest> {
let mut context = Context::new(&SHA256);
let mut buffer = [0; 1024];
loop {
let count = reader.read(&mut buffer)?;
if count == 0 {
break;
}
context.update(&buffer[..count]);
}
Ok(context.finish())
}
pub(crate) fn invocation_hash(target_url: &str, origin_url: &str, msg: &[u8]) -> String {
use std::io::Write;
let mut cleanbytes: Vec<u8> = Vec::new();
cleanbytes.write_all(origin_url.as_bytes()).unwrap();
cleanbytes.write_all(target_url.as_bytes()).unwrap();
cleanbytes.write_all(msg).unwrap();
let digest = sha256_digest(cleanbytes.as_slice()).unwrap();
HEXUPPER.encode(digest.as_ref())
}