Files
grin-wallet/impls/src/client_utils/json_rpc.rs
T
9450d2a3b8 Compact slate merge (#404)
* Add support for sending compact slates (#366)

* WIP add support for sending compact slates

* add repopulate_tx function to internal API

* first pass at compacted slate working

* move slate compaction to separate function

* test fixes

* support compact slate inits in invoice workflow

* add compress flags to send and invoice

* attempting to remove is_compact and assume all V4 slates begin as compact

* attempting to calculate offsets when full tx data isn't available

* update calc_commit to use participant blind data

* update doctests for compact slates

* start to remove unneeded fields from serialization

* make num_participants optional

* remove other_version from slate

* use grin master branch

* remove message field

* lock height assumed to be 0 if it doesn't exist

* don't serialise receiver signature when null

* don't serialize payment_info if not needed

* remove participant id from participant info

* add note on id field

* fix finalize and receive doctests

* finalize_tx tests, init_send_tx tests

* doctests for process_invoice_tx, retrieve_tx, tx_lock_outputs

* finished test changes

* update from grin master

* rebuild PR from diff (#380)

* recreate PR from diff (#381)

* serialize tx struct into top level coms object (#382)

* remove height (#383)

* Add State Slate (#384)

* add state field to slate and SlateV4

* set slate state at each transaction stage, add check to tests

* serialize slate status properly

* V4 Slate field tweaks (#386)

* various tweaks to V4 slate

* field renaming

* serialize slate v4 ID as base64 (#387)

* remove amount and fee where not needed (#388)

* Final Changes for compact Slate (#389)

* add tests for all types of file output, remove message args

* default range proof serialization

* shorten output features serialization

* rename payment proof fields in slate v4

* v4 payment proof serialization

* Binary Slates (#385)

* start test implementation

* add experimental binary serialization to slate

* serialize id

* serialize fields that can be skipped as a separate struct

* factor out sigs serialization

* clean up sigs and coms serialization

* completed v4 bin serialization

* add manual de/ser traits for V4 bin slate

* add simple byte array serializer

* complete wiring in of bin slate serialization

* clarify comment

* clarify comment

* update version

* test output dir name fix

* update slate v4 change description

* add binary output to command line

* Remove unneeded signature data during S2 and I2 stages (#390)

* remove unneeded return signature data during S2

* remove unneeded sig data from I2

* Doctest Fixes for compact slate branch (#392)

* begin to fix doctests

* more doctest fixes

* fix receive_tx

* update get_stored_tx to accept an UUID instead of a tx object, and operate on a raw Transaction object (#394)

* Fixes to async transaction posting (#395)

* unstash post_tx changes

* add offset during S3 and I3

* Revert slate id serialization to hex-string uuid (#396)

* update from master (#397)

* v3.x.x - v4.0.0 wallet compatibility fixes (#398)

* changes to support http sending to v3 wallets

* sending via http/tor TO 3.0.0 wallet works

* receiving FROM 3.0.0 wallets works over http/tor

* output converted V3 slate when needed

* paying invoices from 3.0.0 wallets working

* handle all participant info in slate states

* sending and receiving standard file transactions between v3 and 4 wallets confirmed working

* all file-based workflows working

* fixes resulting from tests

* remove reminder warnings

* remove lock_height, add kernel_features + arguments (#399)

* grin-wallet master now building against grin master (#402) (#403)

Co-authored-by: Antioch Peverell <apeverell@protonmail.com>

* Enhanced offset creation (#407)

* initial tests reworking offset creation

* invoice flow fixing + tests

* further test fixes

* change offset name in v4 slate, base64 serialize

* logic optimisation

* changes based on review feedback

Co-authored-by: Antioch Peverell <apeverell@protonmail.com>
2020-05-19 11:19:03 +01:00

277 lines
8.0 KiB
Rust

// Copyright 2020 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Derived from https://github.com/apoelstra/rust-jsonrpc
//! JSON RPC Client functionality
use std::{error, fmt};
use hyper;
use serde_json;
/// Builds a request
pub fn build_request<'a, 'b>(name: &'a str, params: &'b serde_json::Value) -> Request<'a, 'b> {
Request {
method: name,
params: params,
id: From::from(1),
jsonrpc: Some("2.0"),
}
}
#[derive(Debug, Clone, PartialEq, Serialize)]
/// A JSONRPC request object
pub struct Request<'a, 'b> {
/// The name of the RPC call
pub method: &'a str,
/// Parameters to the RPC call
pub params: &'b serde_json::Value,
/// Identifier for this Request, which should appear in the response
pub id: serde_json::Value,
/// jsonrpc field, MUST be "2.0"
pub jsonrpc: Option<&'a str>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
/// A JSONRPC response object
pub struct Response {
/// A result if there is one, or null
pub result: Option<serde_json::Value>,
/// An error if there is one, or null
pub error: Option<RpcError>,
/// Identifier for this Request, which should match that of the request
pub id: serde_json::Value,
/// jsonrpc field, MUST be "2.0"
pub jsonrpc: Option<String>,
}
impl Response {
/// Extract the result from a response
pub fn result<T: serde::de::DeserializeOwned>(&self) -> Result<T, Error> {
if let Some(ref e) = self.error {
return Err(Error::Rpc(e.clone()));
}
let result = match self.result.clone() {
Some(r) => serde_json::from_value(r["Ok"].clone()).map_err(Error::Json),
None => serde_json::from_value(serde_json::Value::Null).map_err(Error::Json),
}?;
Ok(result)
}
/// Extract the result from a response, consuming the response
pub fn into_result<T: serde::de::DeserializeOwned>(self) -> Result<T, Error> {
if let Some(e) = self.error {
return Err(Error::Rpc(e));
}
self.result()
}
/// Return the RPC error, if there was one, but do not check the result
pub fn _check_error(self) -> Result<(), Error> {
if let Some(e) = self.error {
Err(Error::Rpc(e))
} else {
Ok(())
}
}
/// Returns whether or not the `result` field is empty
pub fn _is_none(&self) -> bool {
self.result.is_none()
}
}
/// A library error
#[derive(Debug)]
pub enum Error {
/// Json error
Json(serde_json::Error),
/// Client error
Hyper(hyper::error::Error),
/// Error response
Rpc(RpcError),
/// Response to a request did not have the expected nonce
_NonceMismatch,
/// Response to a request had a jsonrpc field other than "2.0"
_VersionMismatch,
/// Batches can't be empty
_EmptyBatch,
/// Too many responses returned in batch
_WrongBatchResponseSize,
/// Batch response contained a duplicate ID
_BatchDuplicateResponseId(serde_json::Value),
/// Batch response contained an ID that didn't correspond to any request ID
_WrongBatchResponseId(serde_json::Value),
}
impl From<serde_json::Error> for Error {
fn from(e: serde_json::Error) -> Error {
Error::Json(e)
}
}
impl From<hyper::error::Error> for Error {
fn from(e: hyper::error::Error) -> Error {
Error::Hyper(e)
}
}
impl From<RpcError> for Error {
fn from(e: RpcError) -> Error {
Error::Rpc(e)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Json(ref e) => write!(f, "JSON decode error: {}", e),
Error::Hyper(ref e) => write!(f, "Hyper error: {}", e),
Error::Rpc(ref r) => write!(f, "RPC error response: {:?}", r),
Error::_BatchDuplicateResponseId(ref v) => {
write!(f, "duplicate RPC batch response ID: {}", v)
}
Error::_WrongBatchResponseId(ref v) => write!(f, "wrong RPC batch response ID: {}", v),
ref e => f.write_str(&format!("{}", e)),
}
}
}
impl std::error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::Json(_) => "JSON decode error",
Error::Hyper(_) => "Hyper error",
Error::Rpc(_) => "RPC error response",
Error::_NonceMismatch => "Nonce of response did not match nonce of request",
Error::_VersionMismatch => "`jsonrpc` field set to non-\"2.0\"",
Error::_EmptyBatch => "batches can't be empty",
Error::_WrongBatchResponseSize => "too many responses returned in batch",
Error::_BatchDuplicateResponseId(_) => "batch response contained a duplicate ID",
Error::_WrongBatchResponseId(_) => {
"batch response contained an ID that didn't correspond to any request ID"
}
}
}
fn cause(&self) -> Option<&dyn error::Error> {
match *self {
Error::Json(ref e) => Some(e),
Error::Hyper(ref e) => Some(e),
_ => None,
}
}
}
/// Standard error responses, as described at at
/// http://www.jsonrpc.org/specification#error_object
///
/// # Documentation Copyright
/// Copyright (C) 2007-2010 by the JSON-RPC Working Group
///
/// This document and translations of it may be used to implement JSON-RPC, it
/// may be copied and furnished to others, and derivative works that comment
/// on or otherwise explain it or assist in its implementation may be prepared,
/// copied, published and distributed, in whole or in part, without restriction
/// of any kind, provided that the above copyright notice and this paragraph
/// are included on all such copies and derivative works. However, this document
/// itself may not be modified in any way.
///
/// The limited permissions granted above are perpetual and will not be revoked.
///
/// This document and the information contained herein is provided "AS IS" and
/// ALL WARRANTIES, EXPRESS OR IMPLIED are DISCLAIMED, INCLUDING BUT NOT LIMITED
/// TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY
/// RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A
/// PARTICULAR PURPOSE.
///
#[allow(dead_code)]
#[derive(Debug)]
pub enum StandardError {
/// Invalid JSON was received by the server.
/// An error occurred on the server while parsing the JSON text.
ParseError,
/// The JSON sent is not a valid Request object.
InvalidRequest,
/// The method does not exist / is not available.
MethodNotFound,
/// Invalid method parameter(s).
InvalidParams,
/// Internal JSON-RPC error.
InternalError,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
/// A JSONRPC error object
pub struct RpcError {
/// The integer identifier of the error
pub code: i32,
/// A string describing the error
pub message: String,
/// Additional data specific to the error
pub data: Option<serde_json::Value>,
}
/// Create a standard error responses
pub fn _standard_error(code: StandardError, data: Option<serde_json::Value>) -> RpcError {
match code {
StandardError::ParseError => RpcError {
code: -32700,
message: "Parse error".to_string(),
data: data,
},
StandardError::InvalidRequest => RpcError {
code: -32600,
message: "Invalid Request".to_string(),
data: data,
},
StandardError::MethodNotFound => RpcError {
code: -32601,
message: "Method not found".to_string(),
data: data,
},
StandardError::InvalidParams => RpcError {
code: -32602,
message: "Invalid params".to_string(),
data: data,
},
StandardError::InternalError => RpcError {
code: -32603,
message: "Internal error".to_string(),
data: data,
},
}
}
/// Converts a Rust `Result` to a JSONRPC response object
pub fn _result_to_response(
result: Result<serde_json::Value, RpcError>,
id: serde_json::Value,
) -> Response {
match result {
Ok(data) => Response {
result: Some(data),
error: None,
id: id,
jsonrpc: Some(String::from("2.0")),
},
Err(err) => Response {
result: None,
error: Some(err),
id: id,
jsonrpc: Some(String::from("2.0")),
},
}
}