Skip to content

Commit

Permalink
chore: introduce framework crate (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
scarmuega authored Sep 17, 2024
1 parent 5980db8 commit e6a51be
Show file tree
Hide file tree
Showing 4 changed files with 243 additions and 0 deletions.
19 changes: 19 additions & 0 deletions balius/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "balius"
version = "0.1.0"
edition = "2021"

description = "SDK for building Headless dApps on UTxO-based blockchains"
authors = ["TxPipe <[email protected]>"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/txpipe/balius"
documentation = "https://docs.rs/balius"
readme = "README.md"
keywords = ["blockchain", "dapp", "sdk", "utxo", "cardano"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
serde = "1.0.204"
serde_json = "1.0.122"
wit-bindgen = "0.29.0"
12 changes: 12 additions & 0 deletions balius/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Balius

Balius is a framework for building Headless dApps on UTxO-based blockchains.

## Usage

Add this to your `Cargo.toml`:

```toml
[dependencies]
balius = "0.1.0"
```
123 changes: 123 additions & 0 deletions balius/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
pub mod bindings {
wit_bindgen::generate!({
pub_export_macro: true,
default_bindings_module: "balius::bindings",
});
}

use bindings::{balius::odk, Response};
use serde::Serialize;
use std::{
collections::HashMap,
sync::{LazyLock, RwLock},
};

pub trait Handler: Send + Sync {
fn handle(
&self,
evt: odk::driver::Event,
) -> Result<odk::driver::Response, odk::driver::HandleError>;
}

impl<F, R> Handler for F
where
Response: From<R>,
Self: Fn(odk::driver::Event) -> Result<R, odk::driver::HandleError> + Send + Sync,
{
fn handle(
&self,
evt: odk::driver::Event,
) -> Result<odk::driver::Response, odk::driver::HandleError> {
let temp = self(evt)?;
Ok(temp.into())
}
}

pub struct Json<T>(pub T);

impl<T> From<Json<T>> for odk::driver::Response
where
T: Serialize,
{
fn from(value: Json<T>) -> Self {
Self::Json(serde_json::to_vec(&value.0).unwrap())
}
}

struct Channel {
handler: Box<dyn Handler>,
pattern: odk::driver::EventPattern,
}

type ChannelId = u32;
type ChannelRegistry = HashMap<ChannelId, Channel>;

static WORKER: LazyLock<RwLock<Worker>> = LazyLock::new(|| RwLock::new(Worker::new()));

#[derive(Default)]
pub struct Worker {
channels: ChannelRegistry,
}

impl Worker {
pub fn new() -> Self {
Self::default()
}

pub fn handle_request<F>(mut self, method: &str, handler: F) -> Self
where
F: Handler + 'static,
{
self.channels.insert(
self.channels.len() as u32,
Channel {
handler: Box::new(handler),
pattern: odk::driver::EventPattern::Request(method.to_owned()),
},
);

self
}
}

pub trait Main {
fn main() -> Worker;
}

impl<T: Main> self::bindings::Guest for T {
fn handle(
id: u32,
evt: odk::driver::Event,
) -> Result<odk::driver::Response, odk::driver::HandleError> {
let worker = WORKER.read().unwrap();
let channel = worker.channels.get(&id).ok_or(1u32)?;

channel.handler.handle(evt)
}

fn init(config: odk::driver::Cbor) {
let worker = Self::main();

let mut singelton = WORKER.write().unwrap();
*singelton = worker;

for (id, handler) in singelton.channels.iter() {
odk::driver::register_channel(*id, &handler.pattern);
}
}
}

#[macro_export]
macro_rules! entrypoint {
($main:ident) => {
struct _Main;

impl balius::Main for _Main {
fn main() -> balius::Worker {
$main(Default::default())
}
}

balius::bindings::export!(_Main);
};
}
89 changes: 89 additions & 0 deletions balius/wit/balius.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package balius:odk;

interface kv {
type payload = list<u8>;
type kv-error = u32;

get-value: func(key: string) -> result<payload, kv-error>;
set-value: func(key: string, value: payload) -> result<_, kv-error>;
list-values: func(prefix: string) -> result<list<string>, kv-error>;
}

interface submit {
type cbor = list<u8>;
type submit-error = u32;

submit-tx: func(tx: cbor) -> result<_, submit-error>;
}

interface broadcast {
type msg = list<u8>;
type broadcast-error = u32;

publish-msg: func(topic: string, payload: msg) -> result<_, broadcast-error>;
}

interface driver {
type handle-error = u32;
type cbor = list<u8>;
type json = list<u8>;
type timestamp = u64;
type params = json;

variant event {
utxo(cbor),
utxo-undo(cbor),
timer(timestamp),
request(params),
message(json),
}

type address = list<u8>;

record token-pattern {
policy: list<u8>,
name: option<list<u8>>,
}

record utxo-pattern {
address: option<address>,
token: option<token-pattern>,
}

type timer-interval = string;

type method = string;

type topic = string;

variant event-pattern {
utxo(utxo-pattern),
utxo-undo(utxo-pattern),
timer(timer-interval),
request(method),
message(topic),
}

variant response {
acknowledge,
json(json),
cbor(cbor),
partial-tx(cbor)
}

register-channel: func(id: u32, pattern: event-pattern);
}

world worker {
import kv;
import broadcast;
import submit;

use driver.{event, handle-error, response};

type env = list<u8>;

export init: func(env: env);

export handle: func(channel: u32, evt: event) -> result<response, handle-error>;
}

0 comments on commit e6a51be

Please sign in to comment.