Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "uqwit"]
path = uqwit
url = https://github.com/uqbar-dao/uqwit.git
170 changes: 170 additions & 0 deletions src/capability.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
pub use crate::{Address, Capability, PackageId, ProcessId};
use serde::{Deserialize, Serialize};
use std::hash::{Hash, Hasher};

/// Capability is defined in the wit bindings, but constructors and methods here.
/// A `Capability` is a combination of an Uqbar Address and a set of Params (a serialized
/// json string). Capabilities are attached to messages to either share that capability
/// with the receiving process, or to prove that a process has authority to perform a
/// certain action.
impl Capability {
/// Create a new `Capability`. Takes a node ID and a process ID.
pub fn new<T, U>(address: T, params: U) -> Capability
where
T: Into<Address>,
U: Into<String>,
{
Capability {
issuer: address.into(),
params: params.into(),
}
}
/// Read the node ID from a `Capability`.
pub fn issuer(&self) -> &Address {
&self.issuer
}
/// Read the params from a `Capability`.
pub fn params(&self) -> &str {
&self.params
}
/// Attempt to parse a `Capability` from a string. The formatting structure for
/// a Capability is `issuer^params`.
/// TODO not tested
pub fn from_str(input: &str) -> Result<Self, CapabilityParseError> {
// split string on colons into 4 segments,
// first one with @, next 3 with :
let mut name_rest = input.split('@');
let node = name_rest
.next()
.ok_or(CapabilityParseError::MissingField)?
.to_string();
let mut param_segments = name_rest
.next()
.ok_or(CapabilityParseError::MissingNodeId)?
.split('^');
let mut segments = param_segments
.next()
.ok_or(CapabilityParseError::MissingNodeId)?
.split(':');
let process_name = segments
.next()
.ok_or(CapabilityParseError::MissingField)?
.to_string();
let package_name = segments
.next()
.ok_or(CapabilityParseError::MissingField)?
.to_string();
let publisher_node = segments
.next()
.ok_or(CapabilityParseError::MissingField)?
.to_string();
let params = param_segments
.next()
.ok_or(CapabilityParseError::MissingParams)?
.to_string();
if segments.next().is_some() {
return Err(CapabilityParseError::TooManyColons);
}
Ok(Capability {
issuer: Address {
node,
process: ProcessId {
process_name,
package_name,
publisher_node,
},
},
params,
})
}
}

impl Serialize for Capability {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
format!("{}", self).serialize(serializer)
}
}

impl<'a> Deserialize<'a> for Capability {
fn deserialize<D>(deserializer: D) -> Result<Capability, D::Error>
where
D: serde::de::Deserializer<'a>,
{
let s = String::deserialize(deserializer)?;
Capability::from_str(&s).map_err(serde::de::Error::custom)
}
}

impl Hash for Capability {
fn hash<H: Hasher>(&self, state: &mut H) {
self.issuer.hash(state);
self.params.hash(state);
}
}

impl Eq for Capability {}

impl PartialEq for Capability {
fn eq(&self, other: &Self) -> bool {
self.issuer == other.issuer && self.params == other.params
}
}

impl From<&Capability> for Capability {
fn from(input: &Capability) -> Self {
input.clone()
}
}

impl<T> From<(T, &str)> for Capability
where
T: Into<Address>,
{
fn from(input: (T, &str)) -> Self {
Capability::new(input.0, input.1)
}
}

impl std::fmt::Display for Capability {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}^{}", self.issuer, self.params)
}
}

/// Error type for parsing an `Address` from a string.
#[derive(Debug)]
pub enum CapabilityParseError {
TooManyColons,
MissingNodeId,
MissingField,
MissingParams,
}

impl std::fmt::Display for CapabilityParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
CapabilityParseError::TooManyColons => "Too many colons in ProcessId string",
CapabilityParseError::MissingNodeId => "Node ID missing",
CapabilityParseError::MissingField => "Missing field in ProcessId string",
CapabilityParseError::MissingParams => "Missing params in Capability string",
}
)
}
}

impl std::error::Error for CapabilityParseError {
fn description(&self) -> &str {
match self {
CapabilityParseError::TooManyColons => "Too many colons in ProcessId string",
CapabilityParseError::MissingNodeId => "Node ID missing",
CapabilityParseError::MissingField => "Missing field in ProcessId string",
CapabilityParseError::MissingParams => "Missing params in Capability string",
}
}
}
63 changes: 47 additions & 16 deletions src/kernel_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ pub struct Request {
pub expects_response: Option<u64>, // number of seconds until timeout
pub ipc: Vec<u8>,
pub metadata: Option<String>, // JSON-string
pub capabilities: Vec<Capability>,
}

#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct Response {
pub inherit: bool,
pub ipc: Vec<u8>,
pub metadata: Option<String>, // JSON-string
pub capabilities: Vec<Capability>,
}

#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
Expand All @@ -45,13 +47,6 @@ pub struct Capability {
pub params: String, // JSON-string
}

#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct SignedCapability {
pub issuer: Address,
pub params: String, // JSON-string
pub signature: Vec<u8>, // signed by the kernel, so we can verify that the kernel issued it
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SendError {
pub kind: SendErrorKind,
Expand Down Expand Up @@ -95,13 +90,22 @@ pub enum KernelCommand {
///
/// The process that sends this command will be given messaging capabilities
/// for the new process if `public` is false.
///
/// All capabilities passed into initial_capabilities must be held by the source
/// of this message, or the kernel will discard them (silently for now).
InitializeProcess {
id: ProcessId,
wasm_bytes_handle: String,
wit_version: Option<u32>,
on_exit: OnExit,
initial_capabilities: HashSet<SignedCapability>,
initial_capabilities: HashSet<Capability>,
public: bool,
},
/// Create an arbitrary capability and grant it to a process.
GrantCapabilities {
target: ProcessId,
capabilities: Vec<Capability>,
},
/// Tell the kernel to run a process that has already been installed.
/// TODO: in the future, this command could be extended to allow for
/// resource provision.
Expand All @@ -111,6 +115,15 @@ pub enum KernelCommand {
/// RUNTIME ONLY: notify the kernel that the runtime is shutting down and it
/// should gracefully stop and persist the running processes.
Shutdown,
/// Ask kernel to produce debugging information
Debug(KernelPrint),
}

#[derive(Debug, Serialize, Deserialize)]
pub enum KernelPrint {
ProcessMap,
Process(ProcessId),
HasCap { on: ProcessId, cap: Capability },
}

/// IPC format for all KernelCommand responses
Expand Down Expand Up @@ -200,8 +213,8 @@ pub struct PackageManifestEntry {
pub process_wasm_path: String,
pub on_exit: OnExit,
pub request_networking: bool,
pub request_messaging: Option<Vec<serde_json::Value>>,
pub grant_messaging: Option<Vec<serde_json::Value>>,
pub request_capabilities: Option<Vec<serde_json::Value>>,
pub grant_capabilities: Option<Vec<serde_json::Value>>,
pub public: bool,
}

Expand Down Expand Up @@ -256,6 +269,11 @@ pub fn de_wit_request(wit: wit::Request) -> Request {
expects_response: wit.expects_response,
ipc: wit.ipc,
metadata: wit.metadata,
capabilities: wit
.capabilities
.into_iter()
.map(de_wit_capability)
.collect(),
}
}

Expand All @@ -265,6 +283,11 @@ pub fn en_wit_request(request: Request) -> wit::Request {
expects_response: request.expects_response,
ipc: request.ipc,
metadata: request.metadata,
capabilities: request
.capabilities
.into_iter()
.map(en_wit_capability)
.collect(),
}
}

Expand All @@ -273,6 +296,11 @@ pub fn de_wit_response(wit: wit::Response) -> Response {
inherit: wit.inherit,
ipc: wit.ipc,
metadata: wit.metadata,
capabilities: wit
.capabilities
.into_iter()
.map(de_wit_capability)
.collect(),
}
}

Expand All @@ -281,6 +309,11 @@ pub fn en_wit_response(response: Response) -> wit::Response {
inherit: response.inherit,
ipc: response.ipc,
metadata: response.metadata,
capabilities: response
.capabilities
.into_iter()
.map(en_wit_capability)
.collect(),
}
}

Expand All @@ -304,8 +337,8 @@ pub fn en_wit_payload(load: Option<Payload>) -> Option<wit::Payload> {
}
}

pub fn de_wit_signed_capability(wit: wit::SignedCapability) -> SignedCapability {
SignedCapability {
pub fn de_wit_capability(wit: wit::Capability) -> Capability {
Capability {
issuer: Address {
node: wit.issuer.node,
process: ProcessId {
Expand All @@ -315,15 +348,13 @@ pub fn de_wit_signed_capability(wit: wit::SignedCapability) -> SignedCapability
},
},
params: wit.params,
signature: wit.signature,
}
}

pub fn en_wit_signed_capability(cap: SignedCapability) -> wit::SignedCapability {
wit::SignedCapability {
pub fn en_wit_capability(cap: Capability) -> wit::Capability {
wit::Capability {
issuer: en_wit_address(cap.issuer),
params: cap.params,
signature: cap.signature,
}
}

Expand Down
Loading