From 0e9ccef3e5b2ce4217b9b52f3039beb4a1dd5b9f Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Tue, 2 Apr 2019 15:42:28 +0100 Subject: [PATCH] Slate compatibility between versions (#35) * wallet compatibilities * rustfmt --- controller/src/controller.rs | 57 +++++++++++++++++++++++++----- impls/src/adapters/http.rs | 67 +++++++++++++++++++++++++++++++----- libwallet/src/slate.rs | 9 ++--- src/bin/cmd/wallet_args.rs | 6 ++-- 4 files changed, 115 insertions(+), 24 deletions(-) diff --git a/controller/src/controller.rs b/controller/src/controller.rs index 8f77bc47..a2131fe7 100644 --- a/controller/src/controller.rs +++ b/controller/src/controller.rs @@ -763,14 +763,14 @@ where Box::new(parse_body(req).and_then( //TODO: No way to insert a message from the params move |slate_str: String| { - let mut slate: Slate = Slate::deserialize_upgrade(&slate_str).unwrap(); + let slate: Slate = Slate::deserialize_upgrade(&slate_str).unwrap(); if let Err(e) = api.verify_slate_messages(&slate) { error!("Error validating participant messages: {}", e); err(e) } else { - match api.receive_tx(&mut slate, None, None) { - Ok(_) => ok(slate - .serialize_to_version(Some(slate.version_info.orig_version)) + match api.receive_tx(&slate, None, None) { + Ok(s) => ok(s + .serialize_to_version(Some(s.version_info.orig_version)) .unwrap()), Err(e) => { error!("receive_tx: failed with error: {}", e); @@ -798,7 +798,7 @@ where ), "receive_tx" => Box::new( self.receive_tx(req, api) - .and_then(|res| ok(json_response(&res))), + .and_then(|res| ok(json_response_slate(&res))), ), _ => Box::new(ok(response(StatusCode::BAD_REQUEST, "unknown action"))), } @@ -909,6 +909,29 @@ where } } +// As above, dealing with stringified slate output +// from older versions. +// Older versions are expecting a slate objects, anything from +// 1.1.0 up is expecting a string +fn json_response_slate(s: &T) -> Response +where + T: Serialize, +{ + match serde_json::to_string(s) { + Ok(mut json) => { + if let None = json.find("version_info") { + let mut r = json.clone(); + r.pop(); + r.remove(0); + // again, for backwards slate compat + json = r.replace("\\\"", "\"") + } + response(StatusCode::OK, json) + } + Err(_) => response(StatusCode::INTERNAL_SERVER_ERROR, ""), + } +} + // pretty-printed version of above fn json_response_pretty(s: &T) -> Response where @@ -987,10 +1010,26 @@ where req.into_body() .concat2() .map_err(|_| ErrorKind::GenericError("Failed to read request".to_owned()).into()) - .and_then(|body| match serde_json::from_reader(&body.to_vec()[..]) { - Ok(obj) => ok(obj), - Err(e) => { - err(ErrorKind::GenericError(format!("Invalid request body: {}", e)).into()) + .and_then(|body| { + match serde_json::from_reader(&body.to_vec()[..]) { + Ok(obj) => ok(obj), + Err(_) => { + // try to parse as string instead, for backwards compatibility + let replaced_str = String::from_utf8(body.to_vec().clone()) + .unwrap() + .replace("\"", "\\\""); + let mut str_vec = replaced_str.as_bytes().to_vec(); + str_vec.push(0x22); + str_vec.insert(0, 0x22); + match serde_json::from_reader(&str_vec[..]) { + Ok(obj) => ok(obj), + Err(e) => err(ErrorKind::GenericError(format!( + "Invalid request body: {}", + e + )) + .into()), + } + } } }), ) diff --git a/impls/src/adapters/http.rs b/impls/src/adapters/http.rs index c7b2034f..5d93ae52 100644 --- a/impls/src/adapters/http.rs +++ b/impls/src/adapters/http.rs @@ -15,9 +15,11 @@ /// HTTP Wallet 'plugin' implementation use crate::api; use crate::libwallet::slate::Slate; +use crate::libwallet::slate_versions::{v0, v1}; use crate::libwallet::{Error, ErrorKind}; use crate::WalletCommAdapter; use config::WalletConfig; +use failure::ResultExt; use std::collections::HashMap; #[derive(Clone)] @@ -47,15 +49,64 @@ impl WalletCommAdapter for HTTPWalletCommAdapter { let url = format!("{}/v1/wallet/foreign/receive_tx", dest); debug!("Posting transaction slate to {}", url); let slate = slate.serialize_to_version(Some(slate.version_info.orig_version))?; - let res: Result = api::client::post(url.as_str(), None, &slate); - match res { - Err(e) => { - let report = format!("Posting transaction slate (is recipient listening?): {}", e); - error!("{}", report); - Err(ErrorKind::ClientCallback(report).into()) + // For compatibility with older clients + let res: Slate = { + if let None = slate.find("version_info") { + let version = Slate::parse_slate_version(&slate)?; + match version { + 1 => { + let ver1: v1::SlateV1 = + serde_json::from_str(&slate).context(ErrorKind::SlateDeser)?; + let r: Result = + api::client::post(url.as_str(), None, &ver1); + match r { + Err(e) => { + let report = format!( + "Posting transaction slate (is recipient listening?): {}", + e + ); + error!("{}", report); + return Err(ErrorKind::ClientCallback(report).into()); + } + Ok(s) => Slate::deserialize_upgrade( + &serde_json::to_string(&s).context(ErrorKind::SlateDeser)?, + )?, + } + } + _ => { + let ver0: v0::SlateV0 = + serde_json::from_str(&slate).context(ErrorKind::SlateDeser)?; + let r: Result = + api::client::post(url.as_str(), None, &ver0); + match r { + Err(e) => { + let report = format!( + "Posting transaction slate (is recipient listening?): {}", + e + ); + error!("{}", report); + return Err(ErrorKind::ClientCallback(report).into()); + } + Ok(s) => Slate::deserialize_upgrade( + &serde_json::to_string(&s).context(ErrorKind::SlateDeser)?, + )?, + } + } + } + } else { + let res: Result = api::client::post(url.as_str(), None, &slate); + match res { + Err(e) => { + let report = + format!("Posting transaction slate (is recipient listening?): {}", e); + error!("{}", report); + return Err(ErrorKind::ClientCallback(report).into()); + } + Ok(r) => Slate::deserialize_upgrade(&r)?, + } } - Ok(r) => Ok(Slate::deserialize_upgrade(&r)?), - } + }; + Ok(res) } fn send_tx_async(&self, _dest: &str, _slate: &Slate) -> Result<(), Error> { diff --git a/libwallet/src/slate.rs b/libwallet/src/slate.rs index 223b60c8..4f882ce4 100644 --- a/libwallet/src/slate.rs +++ b/libwallet/src/slate.rs @@ -163,9 +163,9 @@ pub struct ParticipantMessages { } impl Slate { - // TODO: Reduce the number of changes that need to occur below for each new - // slate version - fn parse_slate_version(slate_json: &str) -> Result { + /// TODO: Reduce the number of changes that need to occur below for each new + /// slate version + pub fn parse_slate_version(slate_json: &str) -> Result { // keep attempting to deser, working through known versions until we have // enough to get the version out let res: Result = serde_json::from_str(slate_json); @@ -218,7 +218,8 @@ impl Slate { 1 => { let v2: SlateV2 = serde_json::from_str(&ser_self).context(ErrorKind::SlateDeser)?; let v1 = SlateV1::from(v2); - Ok(serde_json::to_string(&v1).context(ErrorKind::SlateDeser)?) + let slate = serde_json::to_string(&v1).context(ErrorKind::SlateDeser)?; + Ok(slate) } 0 => { let v2: SlateV2 = serde_json::from_str(&ser_self).context(ErrorKind::SlateDeser)?; diff --git a/src/bin/cmd/wallet_args.rs b/src/bin/cmd/wallet_args.rs index b8500e01..43f95216 100644 --- a/src/bin/cmd/wallet_args.rs +++ b/src/bin/cmd/wallet_args.rs @@ -397,10 +397,10 @@ pub fn parse_send_args(args: &ArgMatches) -> Result { - let v = parse_required(args, "target_slate_version")?; - Some(parse_u64(v, "target_slate_version")? as u16) + let v = parse_required(args, "slate_version")?; + Some(parse_u64(v, "slate_version")? as u16) } false => None, }