Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
S0c5 committed Jun 26, 2024
2 parents 277bedc + 0bc3aa8 commit 3f8ae6e
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 105 deletions.
1 change: 1 addition & 0 deletions libwallet/src/key_pair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub trait Signature: AsRef<[u8]> + Debug + PartialEq {
self.as_ref().try_into().expect("error")
}
}

impl<const N: usize> Signature for Bytes<N> {}

/// Something that can sign messages
Expand Down
6 changes: 3 additions & 3 deletions sube/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ libwallet = { path = "../libwallet", default_features=false, features=["substrat
rand_core = "0.6.3"

[features]
default = ["v14", "dep:heapless"]
test = ["std", "wss", "ws", "http", "json", "v14", "dep:async-std"]
default = ["v14", "dep:heapless", "dep:paste"]
test = ["std", "wss", "ws", "http", "json", "v14", "dep:async-std", "dep:rand_core"]
http = ["dep:jsonrpc", "surf/h1-client-rustls"]
http-web = ["dep:jsonrpc", "dep:wasm-bindgen", "surf/wasm-client"]
json = ["scales/json"]
Expand All @@ -63,7 +63,7 @@ v14 = ["dep:scale-info", "frame-metadata/current"]
ws = ["dep:async-mutex", "dep:async-std", "dep:ewebsock", "dep:futures-channel", "dep:futures-util", "dep:jsonrpc", "async-std/unstable"]
wss = ["dep:async-tls", "ws", "ewebsock/tls", "async-std/unstable"]
examples = ["dep:rand_core"]
js = ["http-web", "json", "v14", 'async-std/unstable', "ws"]
js = ["http-web", "json", "v14", 'async-std/unstable', "ws", "dep:rand_core"]

[package.metadata.docs.rs]
features = ["http"]
Expand Down
2 changes: 1 addition & 1 deletion sube/examples/pallet_communities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ async fn main() -> Result<()> {

let response = sube!("ws://127.0.0.1:12281/communityMemberships/collection").await?;

if let Response::ValueSet(value) = result {
if let Response::ValueSet(value) = response {
let data = serde_json::to_value(&value).expect("to be serializable");
println!("Collection {}", serde_json::to_string_pretty(&data).expect("it must return an str"));
}
Expand Down
42 changes: 0 additions & 42 deletions sube/examples/send_tx.rs

This file was deleted.

51 changes: 28 additions & 23 deletions sube/examples/send_tx_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ use jsonrpc::error;
use libwallet::{self, vault, Account, Signature};
use rand_core::OsRng;
use serde_json::json;
use std::{env, error::Error};
use sube::SubeBuilder;
use std::{env, rc::Rc};
use sube::{Error, Signer, SignerFn, SubeBuilder};

type Wallet = libwallet::Wallet<vault::Simple<String>>;

#[async_std::main]
async fn main() -> Result<(), Box<dyn Error>> {
async fn main() -> Result<(), Error> {
let phrase = env::args().skip(1).collect::<Vec<_>>().join(" ");

let (vault, phrase) = if phrase.is_empty() {
Expand All @@ -19,28 +19,33 @@ async fn main() -> Result<(), Box<dyn Error>> {
};

let mut wallet = Wallet::new(vault);
wallet.unlock(None, None).await?;

wallet.unlock(None, None).await.expect("unlocked");
let account = wallet.default_account().unwrap();

let response = SubeBuilder::default()
.with_url("wss://rococo-rpc.polkadot.io/balances/transfer")
.with_body(json!({
"dest": {
"Id": account.public().as_ref()
},
"value": 100000
}))
.with_signer(sube::SignerFn::from((
account.public().as_ref(),
|message: &[u8]| async {
Ok(wallet
.sign(message)
.await
.map_err(|_| sube::Error::Signing)?)
},
)))
.await?;
let signer = sube::SignerFn::from((account.public().as_ref(), move |message: &[u8]| async {
let f = wallet
.sign(message)
.await
.map_err(|_| sube::Error::Signing)?
.as_bytes();

Ok::<_, Error>(f)
}));

signer.sign(&[0; 64][..]).await;

// let response = SubeBuilder::default()
// .with_url("wss://rococo-rpc.polkadot.io/balances/transfer")
// .with_body(json!({
// "dest": {
// "Id": account.public().as_ref()
// },
// "value": 100000
// }))
// .with_signer()
// .await
// .expect("it must work");

Ok(())
}
2 changes: 1 addition & 1 deletion sube/examples/send_tx_libwallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
"value": 100000
}))
)
.await?;
.await.expect("it must to work");

println!("Secret phrase: \"{phrase}\"");
// println!("Default Account: 0x{account}");
Expand Down
91 changes: 66 additions & 25 deletions sube/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,20 @@ use crate::{
};
use crate::{prelude::*, Offline, StorageChangeSet};

use alloc::rc::Rc;
use alloc::sync::Arc;
use async_trait::async_trait;

use async_mutex::Mutex;
use core::borrow::Borrow;
use core::{
cell::OnceCell,
future::{Future, IntoFuture},
marker::PhantomData,
};
// use heapless::Vec as HVec;
use url::Url;

// type PairHostBackend<'a> = (&'a str, AnyBackend, Metadata);
// static INSTANCE: OnceCell<HVec<PairHostBackend, 10>> = OnceCell::new();
// use std::os::macos::raw::stat;

use url::Url;

pub struct SubeBuilder<'a, Body, Signer> {
url: Option<&'a str>,
Expand Down Expand Up @@ -85,6 +88,8 @@ impl<'a> SubeBuilder<'a, (), ()> {
})
.await?;

// let (backend, meta) = get_cache_by_url(url.clone(), metadata).await?;

Ok(match path {
"_meta" => Response::Meta(meta),
"_meta/registry" => Response::Registry(&meta.types),
Expand Down Expand Up @@ -131,6 +136,8 @@ where
let path = url.path();
let body = body.ok_or(Error::BadInput)?;

// let (backend, meta) = get_cache_by_url(url.clone(), metadata).await?;

let backend = BACKEND
.get_or_try_init(get_backend_by_url(url.clone()))
.await?;
Expand All @@ -139,7 +146,7 @@ where
.get_or_try_init(async {
match metadata {
Some(m) => Ok(m),
None => backend.metadata().await.map_err(|err| Error::BadMetadata),
None => backend.metadata().await.map_err(|_| Error::BadMetadata),
}
})
.await?;
Expand All @@ -156,6 +163,58 @@ where
}
}

#[inline]
async fn get_cache_by_url<'a>(
url: Url,
metadata: Option<Metadata>,
) -> SubeResult<(&'a AnyBackend, &'a Metadata)> {
let mut instance_backend = INSTANCE_BACKEND
.get_or_init(async { Mutex::new(Map::new()) })
.await
.lock()
.await;

let mut instance_metadata = INSTANCE_METADATA
.get_or_init(async { Mutex::new(Map::new()) })
.await
.lock()
.await;

let base_path = format!(
"{}://{}",
url.scheme(),
url.host_str().expect("url to have a host")
);

let cached_b = instance_backend.get(&base_path);
let cached_m = instance_metadata.get(&base_path);

match (cached_b, cached_m) {
(Some(b), Some(m)) => Ok((b, m)),
_ => {
let backend = Box::new(get_backend_by_url(url.clone()).await?);
let backend = Box::leak::<'static>(backend);

instance_backend.insert(base_path.clone(), backend);

let metadata = Box::new(get_metadata(backend, metadata).await?);
let metadata = Box::leak::<'static>(metadata);

instance_metadata.insert(base_path.clone(), metadata);

Ok((backend, metadata))
}
}
}

use heapless::FnvIndexMap as Map;

static INSTANCE_BACKEND: async_once_cell::OnceCell<Mutex<Map<String, &'static AnyBackend, 10>>> =
async_once_cell::OnceCell::new();

static INSTANCE_METADATA: async_once_cell::OnceCell<Mutex<Map<String, &'static Metadata, 10>>> =
async_once_cell::OnceCell::new();

static BACKEND: async_once_cell::OnceCell<AnyBackend> = async_once_cell::OnceCell::new();
static META: async_once_cell::OnceCell<Metadata> = async_once_cell::OnceCell::new();

Expand Down Expand Up @@ -236,7 +295,7 @@ impl Backend for &AnyBackend {
async fn query_storage_at(
&self,
keys: Vec<String>,
block: Option<String>
block: Option<String>,
) -> crate::Result<Vec<StorageChangeSet>> {
match self {
#[cfg(any(feature = "http", feature = "http-web"))]
Expand Down Expand Up @@ -320,24 +379,6 @@ macro_rules! sube {
}
};

// Two parameters
// Match when the macro is called with an expression (url) followed by a block of key-value pairs
( $url:expr => { $($key:ident: $value:expr),+ $(,)? }) => {

async {
use $crate::paste;

let mut builder = $crate::SubeBuilder::default()
.with_url($url);

paste!($(
let mut builder = builder.[<with_ $key>]($value);
)*);

builder.await
}
};

($url:expr => ($wallet:expr, $body:expr)) => {
async {
let mut builder = $crate::SubeBuilder::default();
Expand Down
4 changes: 3 additions & 1 deletion sube/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use codec::Encode;
pub use core::fmt::Display;
pub use frame_metadata::RuntimeMetadataPrefixed;
pub use signer::Signer;
pub use paste::paste;

use core::future::Future;
pub use meta::Metadata;
Expand Down Expand Up @@ -78,6 +79,7 @@ pub async fn sube(url: &str) -> SubeBuilder<(), ()> {
}

pub type Result<T> = core::result::Result<T, Error>;

async fn query<'m>(chain: &impl Backend, meta: &'m Metadata, path: &str) -> Result<Response<'m>> {
let (pallet, item_or_call, mut keys) = parse_uri(path).ok_or(Error::BadInput)?;

Expand Down Expand Up @@ -311,7 +313,7 @@ where
signature_payload
};

let signature = signer.sign(payload).await?;
let signature = signer.sign(&payload).await?;

let extrinsic_call = {
let encoded_inner = [
Expand Down
22 changes: 13 additions & 9 deletions sube/src/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,40 @@ pub trait Signer {
type Account: AsRef<[u8]>;
type Signature: AsRef<[u8]>;

fn sign(&self, data: impl AsRef<[u8]>) -> impl Future<Output = Result<Self::Signature>>;
fn sign<'a>(&'a self, data: &'a [u8]) -> impl Future<Output = Result<Self::Signature>> + 'a;

fn account(&self) -> Self::Account;
}

/// Wrapper to create a standard signer from an account and closure
pub struct SignerFn<S, SF> {
pub struct SignerFn<'a, S, SF> {
account: Bytes<32>,
signer: S,
_fut: PhantomData<SF>,
_fut: PhantomData<&'a SF>,
}

impl<S, SF> Signer for SignerFn<S, SF>
impl<'a, S, SF> Signer for SignerFn<'a, S, SF>
where
S: Fn(&[u8]) -> SF,
SF: Future<Output = Result<Bytes<64>>>,
S: for<'b> Fn(&'b [u8]) -> SF + 'a,
SF: Future<Output = Result<Bytes<64>>> + 'a,
{
type Account = Bytes<32>;
type Signature = Bytes<64>;

fn sign(&self, data: impl AsRef<[u8]>) -> impl Future<Output = Result<Self::Signature>> {
(self.signer)(data.as_ref())
fn sign<'b>(&'b self, data: &'b [u8]) -> impl Future<Output = Result<Self::Signature>> + 'b {
(self.signer)(data)
}

fn account(&self) -> Self::Account {
self.account
}
}

impl<A: AsRef<[u8]>, S, SF> From<(A, S)> for SignerFn<S, SF> {
impl<'a, A: AsRef<[u8]>, S, SF> From<(A, S)> for SignerFn<'a, S, SF>
where
for<'b> S: Fn(&'b [u8]) -> SF + 'b,
SF: Future<Output = Result<Bytes<64>>> + 'a,
{
fn from((account, signer): (A, S)) -> Self {
SignerFn {
account: account.as_ref().try_into().expect("32bit account"),
Expand Down

0 comments on commit 3f8ae6e

Please sign in to comment.