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:
Ignotus Peverell
2017-05-24 16:08:39 -07:00
parent de4ebdde71
commit ac553493f1
16 changed files with 846 additions and 282 deletions
+2
View File
@@ -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]
+1
View File
@@ -25,3 +25,4 @@ mod endpoints;
mod rest;
pub use endpoints::start_rest_apis;
pub use rest::*;
+46 -15
View File
@@ -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