Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce rough replicated actor model and atomic counter example #3

Draft
wants to merge 4 commits into
base: no_std_sample
Choose a base branch
from
Draft
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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ categories = ["algorithms", "database-implementations", "no-std"]
edition = "2021"

[workspace]
members = ["proto", "harness", "datadriven"]
members = ["proto", "harness", "datadriven", "trusted/pal", "trusted/platform", "trusted/model", "trusted/example"]

[features]
default = ["protobuf-codec", "default-logger", "std"]
Expand Down
17 changes: 17 additions & 0 deletions trusted/example/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "trusted_example"
version = "0.1.0"
authors = ["people"]

[dependencies]
trusted_pal = { path = "../pal" } # no_std
trusted_model = { path = "../model" } # no_std
trusted_platform = { path = "../platform" } # std for example implementation
prost = { version = "0.11", default-features = false, features = ["prost-derive"] }
hashbrown = { version = "0.14.0"}

[build-dependencies]
prost-build = { version = "0.11" }

[[bin]]
name = "trusted-example" # std for example
1 change: 1 addition & 0 deletions trusted/example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Trusted Application Example
8 changes: 8 additions & 0 deletions trusted/example/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use std::io::Result;

fn main() -> Result<()> {
let mut prost_build = prost_build::Config::new();
prost_build.btree_map(["."]);
prost_build.compile_protos(&["proto/counter.proto"], &["proto/"])?;
Ok(())
}
43 changes: 43 additions & 0 deletions trusted/example/proto/counter.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
syntax = "proto3";
package counter;

message CounterRequest {
uint64 id = 1;
string name = 2;
oneof op {
CounterCompareAndSwapRequest compare_and_swap = 3;
}
}

message CounterCompareAndSwapRequest {
int64 expected_value = 1;
int64 new_value = 2;
}

message CounterResponse {
uint64 id = 1;
CounterStatus status = 2;
oneof op {
CounterCompareAndSwapResponse compare_and_swap = 3;
}
}

enum CounterStatus {
UNSPECIFIED = 0;
SUCCESS = 1;
NOT_LEADER_ERROR = 2;
INVALID_ARGUMENT_ERROR = 3;
}

message CounterCompareAndSwapResponse {
int64 old_value = 1;
int64 new_value = 2;
}

message CounterConfig {
map<string, int64> initial_values = 1;
}

message CounterSnapshot {
map<string, int64> values = 1;
}
156 changes: 156 additions & 0 deletions trusted/example/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// extern crate anyhow;
extern crate alloc;
extern crate core;
extern crate hashbrown;
extern crate prost;
extern crate trusted_model;

pub mod counter {
include!(concat!(env!("OUT_DIR"), "/counter.rs"));
}

use crate::counter::{
counter_request, counter_response, CounterCompareAndSwapRequest, CounterCompareAndSwapResponse,
CounterConfig, CounterRequest, CounterResponse, CounterSnapshot, CounterStatus,
};
use alloc::collections::BTreeMap;
use alloc::string::String;
use hashbrown::HashMap;
use prost::Message;
use trusted_model::model::{Actor, ActorContext, ActorError};

struct CounterActor {
context: Option<Box<dyn ActorContext>>,
values: HashMap<String, i64>,
}

impl CounterActor {
pub fn new() -> Self {
CounterActor {
context: None,
values: HashMap::new(),
}
}

fn get_context(&mut self) -> &mut dyn ActorContext {
self.context.as_mut().expect("context").as_mut()
}

fn send_message<M: Message>(&mut self, message: &M) {
self.get_context()
.send_message(message.encode_to_vec().as_ref())
}

fn apply_compare_and_swap(
&mut self,
id: u64,
counter_name: &String,
compare_and_swap_request: &CounterCompareAndSwapRequest,
) -> CounterResponse {
let mut response = CounterResponse {
id,
status: CounterStatus::Unspecified.into(),
op: None,
};
let mut compare_and_swap_response = CounterCompareAndSwapResponse {
..Default::default()
};

let existing_value_ref = self.values.entry_ref(counter_name);
let existing_value = existing_value_ref.or_insert(0);
compare_and_swap_response.old_value = *existing_value;
if *existing_value == compare_and_swap_request.expected_value {
*existing_value = compare_and_swap_request.new_value;

response.status = CounterStatus::Success.into();
compare_and_swap_response.new_value = compare_and_swap_request.new_value;
} else {
response.status = CounterStatus::InvalidArgumentError.into();
}
response.op = Some(counter_response::Op::CompareAndSwap(
compare_and_swap_response,
));

response
}
}

impl Actor for CounterActor {
fn on_init(&mut self, context: Box<dyn ActorContext>) -> Result<(), ActorError> {
self.context = Some(context);
self.values = HashMap::new();
let config = CounterConfig::decode(self.get_context().get_config().as_ref())
.map_err(|_e| ActorError::Decoding)?;
for (counter_name, counter_value) in config.initial_values {
self.values.insert(counter_name, counter_value);
}

Ok(())
}

fn on_shutdown(&mut self) {}

fn on_save_snapshot(&mut self) -> Result<Vec<u8>, ActorError> {
let mut snapshot = CounterSnapshot {
values: BTreeMap::new(),
};
for (counter_name, counter_value) in &self.values {
snapshot
.values
.insert(counter_name.to_string(), *counter_value);
}

Ok(snapshot.encode_to_vec())
}

fn on_load_snapshot(&mut self, snapshot: &[u8]) -> Result<(), ActorError> {
let snapshot = CounterSnapshot::decode(snapshot).map_err(|_e| ActorError::Decoding)?;
for (counter_name, counter_value) in snapshot.values {
self.values.insert(counter_name, counter_value);
}

Ok(())
}

fn on_process_command(&mut self, command: &[u8]) -> Result<(), ActorError> {
let request = CounterRequest::decode(command).map_err(|_e| ActorError::Decoding)?;
let mut response = CounterResponse {
id: request.id,
..Default::default()
};
let mut status = CounterStatus::Success;
if request.op.is_none() {
status = CounterStatus::InvalidArgumentError;
}
if !self.get_context().is_leader() {
status = CounterStatus::NotLeaderError;
}

if let CounterStatus::Success = status {
self.get_context().propose_event(command)?;
} else {
response.status = status.into();
self.send_message(&response);
}

Ok(())
}

fn on_apply_event(&mut self, _index: u64, event: &[u8]) -> Result<(), ActorError> {
let request = CounterRequest::decode(event).map_err(|_e| ActorError::Decoding)?;
let op = request.op.unwrap();

let response = match op {
counter_request::Op::CompareAndSwap(ref compare_and_swap_request) => {
self.apply_compare_and_swap(request.id, &request.name, compare_and_swap_request)
}
};
self.send_message(&response);

Ok(())
}
}

fn main() {
let _acounter_actor = CounterActor::new();
}
13 changes: 13 additions & 0 deletions trusted/model/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "trusted_model"
version = "0.1.0"
authors = ["people"]

[features]
default = ["std"]
std = []

[dependencies]
trusted_pal = { path = "../pal" }
raft = { path = "../../"}
prost = { version = "0.11", default-features = false, features = ["prost-derive"] }
1 change: 1 addition & 0 deletions trusted/model/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Trusted Computations Programming Model
12 changes: 12 additions & 0 deletions trusted/model/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(not(feature = "std"), feature(error_in_core))]

extern crate alloc;
extern crate core;

pub mod model;

#[cfg(not(feature = "std"))]
use core::error::Error as StdError;
#[cfg(feature = "std")]
use std::error::Error as StdError;
55 changes: 55 additions & 0 deletions trusted/model/src/model.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use crate::StdError;
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::fmt;
use core::option::Option;
use core::result::Result;

#[derive(Debug)]
pub enum ActorError {
Decoding,
Internal,
}

impl StdError for ActorError {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
None
}
}

impl fmt::Display for ActorError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ActorError::Decoding => write!(f, "Failed to decode"),
ActorError::Internal => write!(f, "Intern error"),
}
}
}

pub trait ActorContext {
fn get_instant(&self) -> u64;

fn get_config(&self) -> Vec<u8>;

fn is_leader(&self) -> bool;

fn propose_event(&mut self, event: &[u8]) -> Result<(), ActorError>;

fn send_message(&mut self, message: &[u8]);

fn log_entry(&mut self, entry: &[u8]);
}

pub trait Actor {
fn on_init(&mut self, context: Box<dyn ActorContext>) -> Result<(), ActorError>;

fn on_shutdown(&mut self);

fn on_save_snapshot(&mut self) -> Result<Vec<u8>, ActorError>;

fn on_load_snapshot(&mut self, snapshot: &[u8]) -> Result<(), ActorError>;

fn on_process_command(&mut self, command: &[u8]) -> Result<(), ActorError>;

fn on_apply_event(&mut self, index: u64, event: &[u8]) -> Result<(), ActorError>;
}
2 changes: 1 addition & 1 deletion trusted/pal/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "trusted-pal"
name = "trusted_pal"
version = "0.1.0"
authors = ["people"]

Expand Down
7 changes: 7 additions & 0 deletions trusted/platform/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "trusted_platform"
version = "0.1.0"
authors = ["people"]

[dependencies]
trusted_pal = { path = "../pal" }
1 change: 1 addition & 0 deletions trusted/platform/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Platform Implementation
1 change: 1 addition & 0 deletions trusted/platform/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@