Skip to content
This repository has been archived by the owner on Sep 13, 2022. It is now read-only.

[ᚬframework] feat(core/binding): add service_sdk, chain_querier, request_context #58

Merged
merged 4 commits into from
Dec 20, 2019
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 Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions core/binding/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ rocksdb = "0.12"
lazy_static = "1.4"
byteorder = "1.3"
rlp = "0.4"
futures = "0.3"
async-trait = "0.1"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is the async-trait used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

used in tests/sdk.rs, to mock Storage trait

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move [devDependencies]

json = "0.12"
2 changes: 2 additions & 0 deletions core/binding/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@
#[cfg(test)]
mod tests;

mod request_context;
mod sdk;
mod state;
mod store;
131 changes: 131 additions & 0 deletions core/binding/src/request_context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
use std::cell::RefCell;
use std::rc::Rc;

use derive_more::{Display, From};

use protocol::traits::RequestContext;
use protocol::types::{Address, Event};
use protocol::{ProtocolError, ProtocolErrorKind, ProtocolResult};

#[derive(Debug, Clone, PartialEq)]
pub struct DefaultRequestContext {
cycles_limit: u64,
cycles_price: u64,
cycles_used: Rc<RefCell<u64>>,
caller: Address,
epoch_id: u64,
service_name: String,
service_method: String,
service_playload: String,
events: Rc<RefCell<Vec<Event>>>,
}

impl DefaultRequestContext {
pub fn new(
cycles_limit: u64,
cycles_price: u64,
cycles_used: u64,
caller: Address,
epoch_id: u64,
service_name: String,
service_method: String,
service_playload: String,
) -> Self {
Self {
cycles_limit,
cycles_price,
cycles_used: Rc::new(RefCell::new(cycles_used)),
caller,
epoch_id,
service_name,
service_method,
service_playload,
events: Rc::new(RefCell::new(Vec::new())),
}
}

pub fn with_context(
context: &DefaultRequestContext,
service_name: String,
service_method: String,
service_playload: String,
) -> Self {
Self {
cycles_limit: context.cycles_limit,
cycles_price: context.cycles_price,
cycles_used: Rc::clone(&context.cycles_used),
caller: context.caller.clone(),
epoch_id: context.epoch_id,
service_name,
service_method,
service_playload,
events: Rc::clone(&context.events),
}
}
}

impl RequestContext for DefaultRequestContext {
fn sub_cycles(&mut self, cycles: u64) -> ProtocolResult<()> {
if self.get_cycles_used() + cycles <= self.cycles_limit {
*self.cycles_used.borrow_mut() = self.get_cycles_used() + cycles;
Ok(())
} else {
Err(ContextError::OutOfCycles.into())
}
}

fn get_cycles_price(&self) -> u64 {
self.cycles_price
}

fn get_cycles_limit(&self) -> u64 {
self.cycles_limit
}

fn get_cycles_used(&self) -> u64 {
*self.cycles_used.borrow()
}

fn get_caller(&self) -> Address {
self.caller.clone()
}

fn get_current_epoch_id(&self) -> u64 {
self.epoch_id
}

fn get_service_name(&self) -> &str {
&self.service_name
}

fn get_service_method(&self) -> &str {
&self.service_method
}

fn get_playload(&self) -> &str {
&self.service_playload
}

fn emit_event(&mut self, message: String) -> ProtocolResult<()> {
self.events.borrow_mut().push(Event {
service: self.service_name.clone(),
data: message,
});

Ok(())
}
}

#[derive(Debug, Display, From)]
pub enum ContextError {
#[display(fmt = "out of cycles")]
OutOfCycles,
}

impl std::error::Error for ContextError {}

impl From<ContextError> for ProtocolError {
fn from(err: ContextError) -> ProtocolError {
ProtocolError::new(ProtocolErrorKind::Binding, Box::new(err))
}
}
62 changes: 62 additions & 0 deletions core/binding/src/sdk/chain_querier.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use std::sync::Arc;

use derive_more::{Display, From};
use futures::executor::block_on;

use protocol::traits::{ChainQuerier, Storage};
use protocol::types::{Epoch, Hash, Receipt, SignedTransaction};
use protocol::{ProtocolError, ProtocolErrorKind, ProtocolResult};

pub struct DefaultChainQuerier<S: Storage> {
storage: Arc<S>,
}

impl<S: Storage> DefaultChainQuerier<S> {
pub fn new(storage: Arc<S>) -> Self {
Self { storage }
}
}

impl<S: Storage> ChainQuerier for DefaultChainQuerier<S> {
fn get_transaction_by_hash(&self, tx_hash: &Hash) -> ProtocolResult<Option<SignedTransaction>> {
let ret = block_on(self.storage.get_transaction_by_hash(tx_hash.clone()))
.map_err(|_| ChainQueryError::AsyncStorage)?;

Ok(Some(ret))
}

fn get_epoch_by_epoch_id(&self, epoch_id: Option<u64>) -> ProtocolResult<Option<Epoch>> {
if let Some(u) = epoch_id {
let ret = block_on(self.storage.get_epoch_by_epoch_id(u))
.map_err(|_| ChainQueryError::AsyncStorage)?;

Ok(Some(ret))
} else {
let ret = block_on(self.storage.get_latest_epoch())
.map_err(|_| ChainQueryError::AsyncStorage)?;

Ok(Some(ret))
}
}

fn get_receipt_by_hash(&self, tx_hash: &Hash) -> ProtocolResult<Option<Receipt>> {
let ret = block_on(self.storage.get_receipt(tx_hash.clone()))
.map_err(|_| ChainQueryError::AsyncStorage)?;

Ok(Some(ret))
}
}

#[derive(Debug, Display, From)]
pub enum ChainQueryError {
#[display(fmt = "get error when call async method of storage")]
AsyncStorage,
}

impl std::error::Error for ChainQueryError {}

impl From<ChainQueryError> for ProtocolError {
fn from(err: ChainQueryError) -> ProtocolError {
ProtocolError::new(ProtocolErrorKind::Binding, Box::new(err))
}
}
161 changes: 161 additions & 0 deletions core/binding/src/sdk/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
pub mod chain_querier;

use std::cell::RefCell;
use std::rc::Rc;

use protocol::fixed_codec::FixedCodec;
use protocol::traits::{
ChainQuerier, RequestContext, ServiceSDK, ServiceState, StoreArray, StoreBool, StoreMap,
StoreString, StoreUint64,
};
use protocol::types::{Address, Epoch, Hash, Receipt, SignedTransaction};
use protocol::ProtocolResult;

use crate::store::{
DefaultStoreArray, DefaultStoreBool, DefaultStoreMap, DefaultStoreString, DefaultStoreUint64,
};

pub struct DefalutServiceSDK<S: ServiceState, C: ChainQuerier, R: RequestContext> {
state: Rc<RefCell<S>>,
chain_querier: Rc<C>,
request_context: R,
}

impl<S: ServiceState, C: ChainQuerier, R: RequestContext> DefalutServiceSDK<S, C, R> {
pub fn new(state: Rc<RefCell<S>>, chain_querier: Rc<C>, request_context: R) -> Self {
Self {
state,
chain_querier,
request_context,
}
}
}

impl<S: 'static + ServiceState, C: ChainQuerier, R: RequestContext> ServiceSDK
for DefalutServiceSDK<S, C, R>
{
type ContextItem = R;
yejiayu marked this conversation as resolved.
Show resolved Hide resolved

// Alloc or recover a `Map` by` var_name`
fn alloc_or_recover_map<K: 'static + FixedCodec + PartialEq, V: 'static + FixedCodec>(
&mut self,
var_name: &str,
) -> ProtocolResult<Box<dyn StoreMap<K, V>>> {
Ok(Box::new(DefaultStoreMap::<S, K, V>::new(
Rc::clone(&self.state),
var_name,
)))
}

// Alloc or recover a `Array` by` var_name`
fn alloc_or_recover_array<E: 'static + FixedCodec>(
&mut self,
var_name: &str,
) -> ProtocolResult<Box<dyn StoreArray<E>>> {
Ok(Box::new(DefaultStoreArray::<S, E>::new(
Rc::clone(&self.state),
var_name,
)))
}

// Alloc or recover a `Uint64` by` var_name`
fn alloc_or_recover_uint64(&mut self, var_name: &str) -> ProtocolResult<Box<dyn StoreUint64>> {
Ok(Box::new(DefaultStoreUint64::new(
Rc::clone(&self.state),
var_name,
)))
}

// Alloc or recover a `String` by` var_name`
fn alloc_or_recover_string(&mut self, var_name: &str) -> ProtocolResult<Box<dyn StoreString>> {
Ok(Box::new(DefaultStoreString::new(
Rc::clone(&self.state),
var_name,
)))
}

// Alloc or recover a `Bool` by` var_name`
fn alloc_or_recover_bool(&mut self, var_name: &str) -> ProtocolResult<Box<dyn StoreBool>> {
Ok(Box::new(DefaultStoreBool::new(
Rc::clone(&self.state),
var_name,
)))
}

// Get a value from the service state by key
fn get_value<Key: FixedCodec, Ret: FixedCodec>(
&self,
key: &Key,
) -> ProtocolResult<Option<Ret>> {
self.state.borrow().get(key)
}

// Set a value to the service state by key
fn set_value<Key: FixedCodec, Val: FixedCodec>(
&mut self,
key: Key,
val: Val,
) -> ProtocolResult<()> {
self.state.borrow_mut().insert(key, val)
}

// Get a value from the specified address by key
fn get_account_value<Key: FixedCodec, Ret: FixedCodec>(
&self,
address: &Address,
key: &Key,
) -> ProtocolResult<Option<Ret>> {
self.state.borrow().get_account_value(address, key)
}

// Insert a pair of key / value to the specified address
fn set_account_value<Key: FixedCodec, Val: FixedCodec>(
&mut self,
address: &Address,
key: Key,
val: Val,
) -> ProtocolResult<()> {
self.state.borrow_mut().set_account_value(address, key, val)
}

// Get a signed transaction by `tx_hash`
// if not found on the chain, return None
fn get_transaction_by_hash(&self, tx_hash: &Hash) -> ProtocolResult<Option<SignedTransaction>> {
self.chain_querier.get_transaction_by_hash(tx_hash)
}

// Get a epoch by `epoch_id`
// if not found on the chain, return None
// When the parameter `epoch_id` is None, get the latest (executing)` epoch`
fn get_epoch_by_epoch_id(&self, epoch_id: Option<u64>) -> ProtocolResult<Option<Epoch>> {
self.chain_querier.get_epoch_by_epoch_id(epoch_id)
}

// Get a receipt by `tx_hash`
// if not found on the chain, return None
fn get_receipt_by_hash(&self, tx_hash: &Hash) -> ProtocolResult<Option<Receipt>> {
self.chain_querier.get_receipt_by_hash(tx_hash)
}

fn get_request_context(&self) -> ProtocolResult<Self::ContextItem> {
Ok(self.request_context.clone())
}

// Call other read-only methods of `service` and return the results
// synchronously NOTE: You can use recursive calls, but the maximum call
// stack is 1024
fn read(&self, service: &str, method: &str, playload: &str) -> ProtocolResult<json::JsonValue> {
unimplemented!();
}

// Call other writable methods of `service` and return the results synchronously
// NOTE: You can use recursive calls, but the maximum call stack is 1024
fn write(
&mut self,
service: &str,
method: &str,
playload: &str,
) -> ProtocolResult<json::JsonValue> {
unimplemented!();
}
}
Loading