mirror of
https://github.com/uqbar-dao/nectar.git
synced 2024-11-23 03:44:04 +03:00
WIP
This commit is contained in:
parent
1620538e46
commit
f5257ae40d
BIN
modules/chess/package.zip
Normal file
BIN
modules/chess/package.zip
Normal file
Binary file not shown.
17
modules/chess/package/chess.html
Normal file
17
modules/chess/package/chess.html
Normal file
File diff suppressed because one or more lines are too long
BIN
modules/chess/package/chess.wasm
Normal file
BIN
modules/chess/package/chess.wasm
Normal file
Binary file not shown.
1
modules/chess/package/index.css
Normal file
1
modules/chess/package/index.css
Normal file
File diff suppressed because one or more lines are too long
113
modules/chess/package/index.js
Normal file
113
modules/chess/package/index.js
Normal file
File diff suppressed because one or more lines are too long
19
modules/chess/package/manifest.json
Normal file
19
modules/chess/package/manifest.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"process_id": "chess",
|
||||||
|
"process_wasm": "chess.wasm",
|
||||||
|
"on_panic": {
|
||||||
|
"on_panic": "Restart"
|
||||||
|
},
|
||||||
|
"networking": true,
|
||||||
|
"request_messaging": [
|
||||||
|
"http_bindings",
|
||||||
|
"encryptor"
|
||||||
|
],
|
||||||
|
"grant_messaging": [
|
||||||
|
"terminal",
|
||||||
|
"http_bindings",
|
||||||
|
"encryptor"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
BIN
modules/terminal/package.zip
Normal file
BIN
modules/terminal/package.zip
Normal file
Binary file not shown.
14
modules/terminal/package/manifest.json
Normal file
14
modules/terminal/package/manifest.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"process_id": "terminal",
|
||||||
|
"process_wasm": "terminal.wasm",
|
||||||
|
"on_panic": {
|
||||||
|
"on_panic": "Restart"
|
||||||
|
},
|
||||||
|
"networking": true,
|
||||||
|
"request_messaging": [
|
||||||
|
"net"
|
||||||
|
],
|
||||||
|
"grant_messaging": "all"
|
||||||
|
}
|
||||||
|
]
|
1
modules/terminal/package/my_directory/my_file.txt
Normal file
1
modules/terminal/package/my_directory/my_file.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
hello
|
BIN
modules/terminal/package/terminal.wasm
Normal file
BIN
modules/terminal/package/terminal.wasm
Normal file
Binary file not shown.
@ -81,12 +81,9 @@ impl Guest for Component {
|
|||||||
assert_eq!(our.process, ProcessId::Name("terminal".into()));
|
assert_eq!(our.process, ProcessId::Name("terminal".into()));
|
||||||
print_to_terminal(1, &format!("terminal: start"));
|
print_to_terminal(1, &format!("terminal: start"));
|
||||||
loop {
|
loop {
|
||||||
let message = match receive() {
|
let (source, message) = match receive() {
|
||||||
Ok((source, message)) => {
|
Ok((source, message)) => {
|
||||||
if our.node != source.node {
|
(source, message)
|
||||||
continue;
|
|
||||||
}
|
|
||||||
message
|
|
||||||
}
|
}
|
||||||
Err((error, _context)) => {
|
Err((error, _context)) => {
|
||||||
print_to_terminal(0, &format!("net error: {:?}!", error.kind));
|
print_to_terminal(0, &format!("net error: {:?}!", error.kind));
|
||||||
@ -99,6 +96,10 @@ impl Guest for Component {
|
|||||||
ipc,
|
ipc,
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
|
if our.node != source.node
|
||||||
|
|| our.process != source.process {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let Some(command) = ipc else {
|
let Some(command) = ipc else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
34
src/bootstrapping.md
Normal file
34
src/bootstrapping.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
Bootstrapping the kernel
|
||||||
|
|
||||||
|
1. We have a bunch of packages that are built and zipped into "packages"
|
||||||
|
|
||||||
|
2. On startup, if we don't yet have a filesystem, we grab these packages by name from unix
|
||||||
|
|
||||||
|
3. For each package, "unzip" it and read the "manifest" file
|
||||||
|
|
||||||
|
4. For each entry in the manifest, start the named process by sending a message to kernel.
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
package.zip
|
||||||
|
key_value.wasm
|
||||||
|
key_value_worker.wasm
|
||||||
|
index.html
|
||||||
|
my_directory
|
||||||
|
cool_image.png
|
||||||
|
.manifest
|
||||||
|
```
|
||||||
|
|
||||||
|
inside .manifest:
|
||||||
|
(describes processes to start on-install)
|
||||||
|
```
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"process_id": "key_value",
|
||||||
|
"process_wasm": "key_value.wasm",
|
||||||
|
"on_panic": {"on_panic": true},
|
||||||
|
"networking": true,
|
||||||
|
"messaging": ["vfs", "http_server", "http_bindings"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
@ -15,6 +15,7 @@ pub async fn load_fs(
|
|||||||
home_directory_path: String,
|
home_directory_path: String,
|
||||||
file_key: Vec<u8>,
|
file_key: Vec<u8>,
|
||||||
fs_config: FsConfig,
|
fs_config: FsConfig,
|
||||||
|
vfs_message_sender: MessageSender,
|
||||||
) -> Result<(ProcessMap, Manifest), FsError> {
|
) -> Result<(ProcessMap, Manifest), FsError> {
|
||||||
// load/create fs directory, manifest + log if none.
|
// load/create fs directory, manifest + log if none.
|
||||||
let fs_directory_path_str = format!("{}/fs", &home_directory_path);
|
let fs_directory_path_str = format!("{}/fs", &home_directory_path);
|
||||||
@ -80,6 +81,7 @@ pub async fn load_fs(
|
|||||||
&kernel_process_id,
|
&kernel_process_id,
|
||||||
&mut process_map,
|
&mut process_map,
|
||||||
&mut manifest,
|
&mut manifest,
|
||||||
|
&vfs_message_sender,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.expect("fresh bootstrap failed!");
|
.expect("fresh bootstrap failed!");
|
||||||
@ -88,56 +90,52 @@ pub async fn load_fs(
|
|||||||
Ok((process_map, manifest))
|
Ok((process_map, manifest))
|
||||||
}
|
}
|
||||||
|
|
||||||
// function run only upon fresh boot.
|
/// function run only upon fresh boot.
|
||||||
// goes through /modules, gets their .wasm bytes, injects into fs and kernel state.
|
///
|
||||||
|
/// for each folder in /modules, looks for a package.zip file, extracts the contents,
|
||||||
|
/// sends the contents to VFS, and reads the manifest.json.
|
||||||
|
///
|
||||||
|
/// the manifest.json contains instructions for which processes to boot and what
|
||||||
|
/// capabilities to give them. since we are inside runtime, can spawn those out of
|
||||||
|
/// thin air.
|
||||||
async fn bootstrap(
|
async fn bootstrap(
|
||||||
our_name: &str,
|
our_name: &str,
|
||||||
kernel_process_id: &FileIdentifier,
|
kernel_process_id: &FileIdentifier,
|
||||||
process_map: &mut ProcessMap,
|
process_map: &mut ProcessMap,
|
||||||
manifest: &mut Manifest,
|
manifest: &mut Manifest,
|
||||||
|
vfs_message_sender: &MessageSender,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let names_and_bytes = get_processes_from_directories().await;
|
let packages: Vec<zip::ZipArchive<std::io::Cursor<Vec<u8>>>> = get_zipped_packages().await;
|
||||||
const RUNTIME_MODULES: [&str; 8] = [
|
|
||||||
"filesystem",
|
|
||||||
"http_server",
|
|
||||||
"http_client",
|
|
||||||
"encryptor",
|
|
||||||
"net",
|
|
||||||
"vfs",
|
|
||||||
"kernel",
|
|
||||||
"eth_rpc",
|
|
||||||
];
|
|
||||||
|
|
||||||
let mut special_capabilities: HashSet<Capability> = HashSet::new();
|
for package in packages {
|
||||||
for (process_name, _) in &names_and_bytes {
|
// for each file in package.zip, recursively through all dirs, send a newfile KM to VFS
|
||||||
special_capabilities.insert(Capability {
|
let mut stack = Vec::new();
|
||||||
issuer: Address {
|
stack.push(package);
|
||||||
node: our_name.to_string(),
|
|
||||||
process: ProcessId::Name(process_name.into()),
|
|
||||||
},
|
|
||||||
params: "\"messaging\"".into(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for runtime_module in RUNTIME_MODULES {
|
|
||||||
special_capabilities.insert(Capability {
|
|
||||||
issuer: Address {
|
|
||||||
node: our_name.to_string(),
|
|
||||||
process: ProcessId::Name(runtime_module.into()),
|
|
||||||
},
|
|
||||||
params: "\"messaging\"".into(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// give all distro processes the ability to send messages across the network
|
|
||||||
special_capabilities.insert(Capability {
|
|
||||||
issuer: Address {
|
|
||||||
node: our_name.to_string(),
|
|
||||||
process: ProcessId::Name("kernel".into()),
|
|
||||||
},
|
|
||||||
params: "\"network\"".into(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// for a module in /modules, put its bytes into filesystem, add to process_map
|
while let Some(mut package) = stack.pop() {
|
||||||
for (process_name, wasm_bytes) in names_and_bytes {
|
for i in 0..package.len() {
|
||||||
|
let mut file = package.by_index(i).unwrap();
|
||||||
|
if file.name().ends_with('/') {
|
||||||
|
let new_package = zip::ZipArchive::new(std::io::Cursor::new(file.into_inner())).unwrap();
|
||||||
|
stack.push(new_package);
|
||||||
|
} else {
|
||||||
|
let file_path = file.sanitized_name();
|
||||||
|
let mut file_content = Vec::new();
|
||||||
|
file.read_to_end(&mut file_content).unwrap();
|
||||||
|
let km = KernelMessage::NewFile {
|
||||||
|
path: file_path,
|
||||||
|
content: file_content,
|
||||||
|
};
|
||||||
|
vfs_message_sender.send(km).await.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get and read manifest.json
|
||||||
|
|
||||||
|
// for each process-entry in manifest.json:
|
||||||
|
for entry in process_manifest {
|
||||||
|
// save in process map
|
||||||
let hash: [u8; 32] = hash_bytes(&wasm_bytes);
|
let hash: [u8; 32] = hash_bytes(&wasm_bytes);
|
||||||
|
|
||||||
if let Some(id) = manifest.get_uuid_by_hash(&hash).await {
|
if let Some(id) = manifest.get_uuid_by_hash(&hash).await {
|
||||||
@ -169,7 +167,43 @@ async fn bootstrap(
|
|||||||
entry.capabilities.extend(special_capabilities.clone());
|
entry.capabilities.extend(special_capabilities.clone());
|
||||||
entry.wasm_bytes_handle = id;
|
entry.wasm_bytes_handle = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// spawn the requested capabilities
|
||||||
|
|
||||||
|
// spawn the granted capabilities
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const RUNTIME_MODULES: [&str; 8] = [
|
||||||
|
"filesystem",
|
||||||
|
"http_server",
|
||||||
|
"http_client",
|
||||||
|
"encryptor",
|
||||||
|
"net",
|
||||||
|
"vfs",
|
||||||
|
"kernel",
|
||||||
|
"eth_rpc",
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut runtime_caps: HashSet<Capability> = HashSet::new();
|
||||||
|
for runtime_module in RUNTIME_MODULES {
|
||||||
|
runtime_caps.insert(Capability {
|
||||||
|
issuer: Address {
|
||||||
|
node: our_name.to_string(),
|
||||||
|
process: ProcessId::Name(runtime_module.into()),
|
||||||
|
},
|
||||||
|
params: "\"messaging\"".into(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// give all runtime processes the ability to send messages across the network
|
||||||
|
runtime_caps.insert(Capability {
|
||||||
|
issuer: Address {
|
||||||
|
node: our_name.to_string(),
|
||||||
|
process: ProcessId::Name("kernel".into()),
|
||||||
|
},
|
||||||
|
params: "\"network\"".into(),
|
||||||
|
});
|
||||||
|
|
||||||
// finally, save runtime modules in state map as well, somewhat fakely
|
// finally, save runtime modules in state map as well, somewhat fakely
|
||||||
for runtime_module in RUNTIME_MODULES {
|
for runtime_module in RUNTIME_MODULES {
|
||||||
@ -178,9 +212,8 @@ async fn bootstrap(
|
|||||||
.or_insert(PersistedProcess {
|
.or_insert(PersistedProcess {
|
||||||
wasm_bytes_handle: 0,
|
wasm_bytes_handle: 0,
|
||||||
on_panic: OnPanic::Restart,
|
on_panic: OnPanic::Restart,
|
||||||
capabilities: HashSet::new(),
|
capabilities: runtime_caps.clone(),
|
||||||
});
|
});
|
||||||
entry.capabilities.extend(special_capabilities.clone());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// save kernel process state. FsAction::SetState(kernel)
|
// save kernel process state. FsAction::SetState(kernel)
|
||||||
@ -196,6 +229,33 @@ async fn bootstrap(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// go into /modules folder and get all
|
||||||
|
async fn get_zipped_packages() -> Vec<zip::ZipArchive<std::io::Cursor<Vec<u8>>>> {
|
||||||
|
let modules_path = std::path::Path::new("modules");
|
||||||
|
|
||||||
|
let mut packages = Vec::new();
|
||||||
|
|
||||||
|
if let Ok(mut entries) = fs::read_dir(modules_path).await {
|
||||||
|
while let Ok(Some(entry)) = entries.next_entry().await {
|
||||||
|
// get a file named package.zip
|
||||||
|
if let Some(pkg) = entry.file_name().to_str() {
|
||||||
|
if pkg == "package.zip" {
|
||||||
|
// read the file
|
||||||
|
if let Ok(bytes) = fs::read(entry.path()).await {
|
||||||
|
// extract the zip
|
||||||
|
if let Ok(zip) = zip::ZipArchive::new(std::io::Cursor::new(bytes)) {
|
||||||
|
// add to list of packages
|
||||||
|
packages.push(zip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return packages;
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_processes_from_directories() -> Vec<(String, Vec<u8>)> {
|
async fn get_processes_from_directories() -> Vec<(String, Vec<u8>)> {
|
||||||
let mut processes = Vec::new();
|
let mut processes = Vec::new();
|
||||||
|
|
||||||
|
@ -192,6 +192,18 @@ impl UqProcessImports for ProcessWasi {
|
|||||||
// Ok(())
|
// Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_state(&mut self) -> Result<Option<Vec<u8>>> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_state(&mut self, bytes: Vec<u8>) -> Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn clear_state(&mut self) -> Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
async fn spawn(
|
async fn spawn(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: wit::ProcessId,
|
id: wit::ProcessId,
|
||||||
|
@ -378,6 +378,7 @@ async fn main() {
|
|||||||
home_directory_path.clone(),
|
home_directory_path.clone(),
|
||||||
file_key,
|
file_key,
|
||||||
fs_config,
|
fs_config,
|
||||||
|
vfs_message_sender.clone(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.expect("fs load failed!");
|
.expect("fs load failed!");
|
||||||
|
26
terminal/deps/random/insecure-seed.wit
Normal file
26
terminal/deps/random/insecure-seed.wit
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// https://github.com/bytecodealliance/wasmtime/blob/432b5471ec4bf6d51173def284cd418be6849a49/crates/wasi/wit/deps/random/insecure-seed.wit
|
||||||
|
|
||||||
|
/// The insecure-seed interface for seeding hash-map DoS resistance.
|
||||||
|
///
|
||||||
|
/// It is intended to be portable at least between Unix-family platforms and
|
||||||
|
/// Windows.
|
||||||
|
interface insecure-seed {
|
||||||
|
/// Return a 128-bit value that may contain a pseudo-random value.
|
||||||
|
///
|
||||||
|
/// The returned value is not required to be computed from a CSPRNG, and may
|
||||||
|
/// even be entirely deterministic. Host implementations are encouraged to
|
||||||
|
/// provide pseudo-random values to any program exposed to
|
||||||
|
/// attacker-controlled content, to enable DoS protection built into many
|
||||||
|
/// languages' hash-map implementations.
|
||||||
|
///
|
||||||
|
/// This function is intended to only be called once, by a source language
|
||||||
|
/// to initialize Denial Of Service (DoS) protection in its hash-map
|
||||||
|
/// implementation.
|
||||||
|
///
|
||||||
|
/// # Expected future evolution
|
||||||
|
///
|
||||||
|
/// This will likely be changed to a value import, to prevent it from being
|
||||||
|
/// called multiple times and potentially used for purposes other than DoS
|
||||||
|
/// protection.
|
||||||
|
insecure-seed: func() -> tuple<u64, u64>
|
||||||
|
}
|
23
terminal/deps/random/insecure.wit
Normal file
23
terminal/deps/random/insecure.wit
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// https://github.com/bytecodealliance/wasmtime/blob/432b5471ec4bf6d51173def284cd418be6849a49/crates/wasi/wit/deps/random/insecure.wit
|
||||||
|
|
||||||
|
/// The insecure interface for insecure pseudo-random numbers.
|
||||||
|
///
|
||||||
|
/// It is intended to be portable at least between Unix-family platforms and
|
||||||
|
/// Windows.
|
||||||
|
interface insecure {
|
||||||
|
/// Return `len` insecure pseudo-random bytes.
|
||||||
|
///
|
||||||
|
/// This function is not cryptographically secure. Do not use it for
|
||||||
|
/// anything related to security.
|
||||||
|
///
|
||||||
|
/// There are no requirements on the values of the returned bytes, however
|
||||||
|
/// implementations are encouraged to return evenly distributed values with
|
||||||
|
/// a long period.
|
||||||
|
get-insecure-random-bytes: func(len: u64) -> list<u8>
|
||||||
|
|
||||||
|
/// Return an insecure pseudo-random `u64` value.
|
||||||
|
///
|
||||||
|
/// This function returns the same type of pseudo-random data as
|
||||||
|
/// `get-insecure-random-bytes`, represented as a `u64`.
|
||||||
|
get-insecure-random-u64: func() -> u64
|
||||||
|
}
|
23
terminal/deps/random/random.wit
Normal file
23
terminal/deps/random/random.wit
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// https://github.com/bytecodealliance/wasmtime/blob/432b5471ec4bf6d51173def284cd418be6849a49/crates/wasi/wit/deps/random/random.wit
|
||||||
|
|
||||||
|
package wasi:random
|
||||||
|
|
||||||
|
interface random {
|
||||||
|
/// Return `len` cryptographically-secure pseudo-random bytes.
|
||||||
|
///
|
||||||
|
/// This function must produce data from an adequately seeded
|
||||||
|
/// cryptographically-secure pseudo-random number generator (CSPRNG), so it
|
||||||
|
/// must not block, from the perspective of the calling program, and the
|
||||||
|
/// returned data is always unpredictable.
|
||||||
|
///
|
||||||
|
/// This function must always return fresh pseudo-random data. Deterministic
|
||||||
|
/// environments must omit this function, rather than implementing it with
|
||||||
|
/// deterministic data.
|
||||||
|
get-random-bytes: func(len: u64) -> list<u8>
|
||||||
|
|
||||||
|
/// Return a cryptographically-secure pseudo-random `u64` value.
|
||||||
|
///
|
||||||
|
/// This function returns the same type of pseudo-random data as
|
||||||
|
/// `get-random-bytes`, represented as a `u64`.
|
||||||
|
get-random-u64: func() -> u64
|
||||||
|
}
|
183
terminal/uqbar.wit
Normal file
183
terminal/uqbar.wit
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
package component:uq-process
|
||||||
|
|
||||||
|
interface types {
|
||||||
|
// JSON is passed over WASM boundary as a string.
|
||||||
|
type json = string
|
||||||
|
|
||||||
|
// context is a string of UTF-8 JSON.
|
||||||
|
// it is used when building a Request to save information
|
||||||
|
// that will not be part of a Response, in order to more
|
||||||
|
// easily handle ("contextualize") that Response.
|
||||||
|
type context = json
|
||||||
|
|
||||||
|
variant process-id {
|
||||||
|
id(u64),
|
||||||
|
name(string),
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO better name for this
|
||||||
|
record address {
|
||||||
|
node: string,
|
||||||
|
process: process-id,
|
||||||
|
}
|
||||||
|
|
||||||
|
record payload {
|
||||||
|
mime: option<string>,
|
||||||
|
bytes: list<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
record request {
|
||||||
|
// if true, this request inherits context AND payload of incipient
|
||||||
|
// request, and cannot have its own context.
|
||||||
|
inherit: bool,
|
||||||
|
// if Some, this request expects a response in the number of seconds given
|
||||||
|
expects-response: option<u64>,
|
||||||
|
ipc: option<json>,
|
||||||
|
metadata: option<json>,
|
||||||
|
// to grab payload, use get_payload()
|
||||||
|
}
|
||||||
|
|
||||||
|
record response {
|
||||||
|
ipc: option<json>,
|
||||||
|
metadata: option<json>,
|
||||||
|
// to grab payload, use get_payload()
|
||||||
|
}
|
||||||
|
|
||||||
|
// a message can be a request or a response.
|
||||||
|
// within a response, there is a result which surfaces any error
|
||||||
|
// that happened because of a request.
|
||||||
|
// a successful response will contain the context of the request
|
||||||
|
// it matches, if any was set.
|
||||||
|
variant message {
|
||||||
|
request(request),
|
||||||
|
response(tuple<response, option<context>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
variant capabilities {
|
||||||
|
none,
|
||||||
|
all,
|
||||||
|
some(list<signed-capability>),
|
||||||
|
}
|
||||||
|
|
||||||
|
record signed-capability {
|
||||||
|
issuer: address,
|
||||||
|
params: json,
|
||||||
|
signature: list<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// network errors come from trying to send a message to another node.
|
||||||
|
// a message can fail by timing out, or by the node being entirely unreachable (offline).
|
||||||
|
// in either case, the message is not delivered, and the process that sent it
|
||||||
|
// receives that message along with any assigned context and/or payload,
|
||||||
|
// and is free to handle it as it sees fit.
|
||||||
|
// note that if the message is a response, the process can issue a response again,
|
||||||
|
// and it will be directed to the same (remote) request as the original.
|
||||||
|
record send-error {
|
||||||
|
kind: send-error-kind,
|
||||||
|
message: message,
|
||||||
|
payload: option<payload>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum send-error-kind {
|
||||||
|
offline,
|
||||||
|
timeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
// on-panic is a setting that determines what happens when a process panics.
|
||||||
|
// NOTE: requests should have expects-response set to false, will always be set to that by kernel
|
||||||
|
variant on-panic {
|
||||||
|
none,
|
||||||
|
restart,
|
||||||
|
requests(list<tuple<address, request, option<payload>>>),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
world uq-process {
|
||||||
|
use types.{
|
||||||
|
json,
|
||||||
|
context,
|
||||||
|
address,
|
||||||
|
process-id,
|
||||||
|
|
||||||
|
payload,
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
message,
|
||||||
|
|
||||||
|
capabilities,
|
||||||
|
signed-capability,
|
||||||
|
|
||||||
|
send-error,
|
||||||
|
send-error-kind,
|
||||||
|
on-panic,
|
||||||
|
}
|
||||||
|
|
||||||
|
// entry point to all programs
|
||||||
|
export init: func(our: address)
|
||||||
|
|
||||||
|
// system utils:
|
||||||
|
|
||||||
|
import print-to-terminal: func(verbosity: u8, message: string)
|
||||||
|
import get-unix-time: func() -> u64
|
||||||
|
import get-eth-block: func() -> u64
|
||||||
|
|
||||||
|
// process management:
|
||||||
|
|
||||||
|
import set-on-panic: func(on-panic: on-panic)
|
||||||
|
|
||||||
|
import get-state: func() -> option<list<u8>>
|
||||||
|
|
||||||
|
import set-state: func(bytes: list<u8>)
|
||||||
|
|
||||||
|
import clear-state: func()
|
||||||
|
|
||||||
|
import spawn: func(id: process-id, %package: string, full-path: string, on-panic: on-panic, capabilities: capabilities) ->
|
||||||
|
option<process-id>
|
||||||
|
|
||||||
|
// capabilities management
|
||||||
|
|
||||||
|
// gives us all our signed capabilities so we can send them to others
|
||||||
|
import get-capabilities: func() -> list<signed-capability>
|
||||||
|
|
||||||
|
// gets a single specific capability
|
||||||
|
import get-capability: func(issuer: address, params: json) -> option<signed-capability>
|
||||||
|
|
||||||
|
// attaches a specific signed capability to our next message
|
||||||
|
import attach-capability: func(capability: signed-capability)
|
||||||
|
|
||||||
|
// saves capabilities to our store, so we can use them
|
||||||
|
import save-capabilities: func(capabilities: list<signed-capability>)
|
||||||
|
|
||||||
|
// check to see if the sender of a prompting message has a given capability, issued by us
|
||||||
|
// if the prompting message has a remote source, they must have attached it.
|
||||||
|
import has-capability: func(params: json) -> bool
|
||||||
|
|
||||||
|
|
||||||
|
// message I/O:
|
||||||
|
|
||||||
|
// ingest next message when it arrives along with its source.
|
||||||
|
// almost all long-running processes will call this in a loop
|
||||||
|
import receive: func() -> result<tuple<address, message>, tuple<send-error, option<context>>>
|
||||||
|
|
||||||
|
// gets payload, if any, of the message we just received
|
||||||
|
import get-payload: func() -> option<payload>
|
||||||
|
|
||||||
|
// send message(s) to target(s)
|
||||||
|
import send-request:
|
||||||
|
func(target: address, request: request, context: option<context>, payload: option<payload>)
|
||||||
|
import send-requests:
|
||||||
|
func(requests: list<tuple<address, request, option<context>, option<payload>>>)
|
||||||
|
import send-response:
|
||||||
|
func(response: response, payload: option<payload>)
|
||||||
|
|
||||||
|
// send a single request, then block (internally) until its response
|
||||||
|
// the type is Message but will always contain Response
|
||||||
|
import send-and-await-response:
|
||||||
|
func(target: address, request: request, payload: option<payload>) ->
|
||||||
|
result<tuple<address, message>, send-error>
|
||||||
|
|
||||||
|
// wasi
|
||||||
|
import wasi:random/insecure
|
||||||
|
import wasi:random/insecure-seed
|
||||||
|
import wasi:random/random
|
||||||
|
}
|
@ -125,6 +125,12 @@ world uq-process {
|
|||||||
|
|
||||||
import set-on-panic: func(on-panic: on-panic)
|
import set-on-panic: func(on-panic: on-panic)
|
||||||
|
|
||||||
|
import get-state: func() -> option<list<u8>>
|
||||||
|
|
||||||
|
import set-state: func(bytes: list<u8>)
|
||||||
|
|
||||||
|
import clear-state: func()
|
||||||
|
|
||||||
import spawn: func(id: process-id, %package: string, full-path: string, on-panic: on-panic, capabilities: capabilities) ->
|
import spawn: func(id: process-id, %package: string, full-path: string, on-panic: on-panic, capabilities: capabilities) ->
|
||||||
option<process-id>
|
option<process-id>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user