WIP: Wallet functionality to receive and send coins
Beginning of a first pass at simple wallet functionalities so Grin can be used to author transactions. We introduce a receiving server, to be at least able to build coinbase outputs that can be used by the mining daemon. Present: * Coinbase receiving API. * Command to start the receiving server. * Beginning of a transaction sending command. * Improvements to the REST API abstractions to support the above. Still to do: * Change to the miner daemon to use the receiving server. * A command line sender. * API to receive any transaction (not just coinbase). * A command line receiver. Beyond that, HD derivation and seed generation are very simple so far and almost certainly insecure. Just for testing for now.
This commit is contained in:
@@ -38,6 +38,8 @@ pub struct ChainApi {
|
||||
impl ApiEndpoint for ChainApi {
|
||||
type ID = String;
|
||||
type T = Tip;
|
||||
type OP_IN = ();
|
||||
type OP_OUT = ();
|
||||
|
||||
fn operations(&self) -> Vec<Operation> {
|
||||
vec![Operation::Get]
|
||||
|
||||
@@ -25,3 +25,4 @@ mod endpoints;
|
||||
mod rest;
|
||||
|
||||
pub use endpoints::start_rest_apis;
|
||||
pub use rest::*;
|
||||
|
||||
+46
-15
@@ -110,6 +110,8 @@ pub type ApiResult<T> = ::std::result::Result<T, ApiError>;
|
||||
pub trait ApiEndpoint: Clone + Send + Sync + 'static {
|
||||
type ID: ToString + FromStr;
|
||||
type T: Serialize + Deserialize;
|
||||
type OP_IN: Serialize + Deserialize;
|
||||
type OP_OUT: Serialize + Deserialize;
|
||||
|
||||
fn operations(&self) -> Vec<Operation>;
|
||||
|
||||
@@ -134,10 +136,7 @@ pub trait ApiEndpoint: Clone + Send + Sync + 'static {
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn operation<IN, OUT>(&self, op: String, input: IN) -> ApiResult<OUT>
|
||||
where IN: Serialize + Deserialize,
|
||||
OUT: Serialize + Deserialize
|
||||
{
|
||||
fn operation(&self, op: String, input: Self::OP_IN) -> ApiResult<Self::OP_OUT> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
@@ -181,6 +180,24 @@ impl<E> Handler for ApiWrapper<E>
|
||||
}
|
||||
}
|
||||
|
||||
struct OpWrapper<E> {
|
||||
operation: String,
|
||||
endpoint: E,
|
||||
}
|
||||
|
||||
impl<E> Handler for OpWrapper<E>
|
||||
where E: ApiEndpoint
|
||||
{
|
||||
fn handle(&self, req: &mut Request) -> IronResult<Response> {
|
||||
let t: E::OP_IN = serde_json::from_reader(req.body.by_ref())
|
||||
.map_err(|e| IronError::new(e, status::BadRequest))?;
|
||||
let res = self.endpoint.operation(self.operation.clone(), t)?;
|
||||
let res_json = serde_json::to_string(&res)
|
||||
.map_err(|e| IronError::new(e, status::InternalServerError))?;
|
||||
Ok(Response::with((status::Ok, res_json)))
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_param<ID>(req: &mut Request, param: &'static str) -> IronResult<ID>
|
||||
where ID: ToString + FromStr,
|
||||
<ID as FromStr>::Err: Debug + Send + Error + 'static
|
||||
@@ -224,17 +241,31 @@ impl ApiServer {
|
||||
let route_postfix = &subpath[1..];
|
||||
let root = self.root.clone() + &subpath;
|
||||
for op in endpoint.operations() {
|
||||
let full_path = match op.clone() {
|
||||
Operation::Get => root.clone() + "/:id",
|
||||
Operation::Update => root.clone() + "/:id",
|
||||
Operation::Delete => root.clone() + "/:id",
|
||||
Operation::Create => root.clone(),
|
||||
Operation::Custom(op_s) => format!("{}/:{}", root.clone(), op_s),
|
||||
};
|
||||
self.router.route(op.to_method(),
|
||||
full_path,
|
||||
ApiWrapper(endpoint.clone()),
|
||||
format!("{:?}_{}", op, route_postfix));
|
||||
let route_name = format!("{:?}_{}", op, route_postfix);
|
||||
|
||||
// special case of custom operations
|
||||
if let Operation::Custom(op_s) = op.clone() {
|
||||
let wrapper = OpWrapper {
|
||||
operation: op_s.clone(),
|
||||
endpoint: endpoint.clone(),
|
||||
};
|
||||
let full_path = format!("{}", root.clone());
|
||||
self.router.route(op.to_method(), full_path.clone(), wrapper, route_name);
|
||||
info!("POST {}", full_path);
|
||||
} else {
|
||||
|
||||
// regular REST operations
|
||||
let full_path = match op.clone() {
|
||||
Operation::Get => root.clone() + "/:id",
|
||||
Operation::Update => root.clone() + "/:id",
|
||||
Operation::Delete => root.clone() + "/:id",
|
||||
Operation::Create => root.clone(),
|
||||
_ => panic!("unreachable"),
|
||||
};
|
||||
let wrapper = ApiWrapper(endpoint.clone());
|
||||
self.router.route(op.to_method(), full_path.clone(), wrapper, route_name);
|
||||
info!("{} {}", op.to_method(), full_path);
|
||||
}
|
||||
}
|
||||
|
||||
// support for the HTTP Options method by differentiating what's on the
|
||||
|
||||
Reference in New Issue
Block a user