From b53a6bc14d3f6efff05270d0151b4a4ba4bef083 Mon Sep 17 00:00:00 2001 From: gulliver Date: Mon, 28 Nov 2022 22:58:55 +0100 Subject: [PATCH] added json-de-/serialisation and tests --- src/model.cpp | 242 ++++++++++++++++++++++++++++------ src/model.hpp | 22 ++-- test/test.cpp | 354 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 571 insertions(+), 47 deletions(-) diff --git a/src/model.cpp b/src/model.cpp index 0ccbcd1..f80f6e1 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -122,9 +122,11 @@ RequestCDDSerial::from_string(const std::string& str) auto json = crow::json::load(str); if (!json) { return tl::make_unexpected(eError::JSON_PARSE_ERROR); - } else if (!json.has("message_reference")) { + } else if ( !json.has("type") || !json.has("message_reference")) { return tl::make_unexpected(eError::JSON_ERROR); - } else { + } else if ( json["type"]!="request cdd serial") { + return tl::make_unexpected(eError::JSON_ERROR); + } else { RequestCDDSerial r; r.message_reference= json["message_reference"].u(); return r; @@ -145,9 +147,14 @@ RequestCDDC::from_string(const std::string& str) auto json = crow::json::load(str); if (!json) { return tl::make_unexpected(eError::JSON_PARSE_ERROR); - } else if (!(json.has("cdd_serial")&&json.has("message_reference"))) { - return tl::make_unexpected(eError::JSON_ERROR); - } else { + } else if ( !( json.has("type") + && json.has("message_reference") + && json.has("cdd_serial") + ) ) { + return tl::make_unexpected(eError::JSON_MISSING_KEY); + } else if ( json["type"]!="request cddc" ) { + return tl::make_unexpected(eError::JSON_WRONG_REQUEST_TYPE); + } else { RequestCDDC r; r.cdd_serial=json["cdd_serial"].u(); r.message_reference= json["message_reference"].u(); @@ -157,17 +164,38 @@ RequestCDDC::from_string(const std::string& str) tl::expected RequestMKCs::from_string(const std::string& str) { - std::vector denominations; - unsigned int message_reference; /// Client internal message reference. - /// (Integer) - std::vector mint_key_ids; - // "type": "request mint key certificates" - auto json = crow::json::load(str); if (!json) { return tl::make_unexpected(eError::JSON_PARSE_ERROR); - } else { - return tl::make_unexpected(eError::NOT_IMPLEMENTED); + } else if ( !(json.has("denominations") + && json.has("message_reference") + && json.has("mint_key_ids") + && json.has("type") + ) ) { + return tl::make_unexpected(eError::JSON_MISSING_KEY); + } else if ( json["type"]!="request mint key certificates" ) { + return tl::make_unexpected(eError::JSON_WRONG_REQUEST_TYPE); + } else { + RequestMKCs r; + r.message_reference= json["message_reference"].u(); + + auto denominations = json["denominations"]; + if ( denominations.t()!=crow::json::type::List) { + return tl::make_unexpected(eError::JSON_WRONG_REQUEST_TYPE); + } else { + for (auto d: denominations.lo()) { + r.denominations.push_back(d.u()); + } + } + auto mint_key_ids = json["mint_key_ids"]; + if ( mint_key_ids.t()!=crow::json::type::List) { + return tl::make_unexpected(eError::JSON_WRONG_REQUEST_TYPE); + } else { + for (auto k: mint_key_ids.lo()) { + r.mint_key_ids.push_back(k.u()); + } + } + return r; } } @@ -188,6 +216,25 @@ crow::json::wvalue Blind::to_json() const { return r; } +tl::expected Blind::from_json(const crow::json::rvalue& json) +{ + if ( !( json.has("type") + && json.has("blinded_payload_hash") + && json.has("mint_key_id") + && json.has("reference") + ) ) { + return tl::make_unexpected(eError::JSON_ERROR); + } else if ( json["type"]!="blinded payload hash" ) { + return tl::make_unexpected(eError::JSON_ERROR); + } else { + Blind r; + r.blinded_payload_hash = json["blinded_payload_hash"].s(); + r.mint_key_id = json["mint_key_id"].s(); + r.reference = json["reference"].s(); + return r; + } +} + crow::json::wvalue BlindSignature::to_json() const { crow::json::wvalue r; TO_JSON(blind_signature); @@ -200,14 +247,35 @@ crow::json::wvalue BlindSignature::to_json() const { tl::expected RequestMint::from_string(const std::string& str){ std::vector blinds; - unsigned int message_reference; /// Client internal message reference. - /// (Integer) // "type": "request mint" auto json = crow::json::load(str); if (!json) { return tl::make_unexpected(eError::JSON_PARSE_ERROR); - } else { - return tl::make_unexpected(eError::NOT_IMPLEMENTED); + } else if ( !( json.has("type") + && json.has("message_reference") + && json.has("transaction_reference") + && json.has("blinds") + ) ) { + return tl::make_unexpected(eError::JSON_ERROR); + } else if ( json["type"]!="request mint" ) { + return tl::make_unexpected(eError::JSON_ERROR); + } else { + RequestMint r; + r.message_reference= json["message_reference"].u(); + r.transaction_reference= json["transaction_reference"].s(); + if (json["blinds"].t()!=crow::json::type::List) { + return tl::make_unexpected(eError::JSON_WRONG_VALUE_TYPE); + } + + for (auto item: json["blinds"]) { + auto b = Blind::from_json(item); + if (!b.has_value()) { + return tl::make_unexpected(b.error()); + } else { + r.blinds.push_back(b.value()); + } + } + return r; } } @@ -240,6 +308,50 @@ crow::json::wvalue Coin::to_json() const return r; } +tl::expected Coin::Payload::from_json(const crow::json::rvalue& json) { + if ( !( json.has("cdd_location") + && json.has("denomination") + && json.has("issuer_id") + && json.has("mint_key_id") + && json.has("protocol_version") + && json.has("serial") + && json.has("type"))) { + return tl::make_unexpected(eError::JSON_ERROR); + } else if ( json["type"]!="payload" ) { + return tl::make_unexpected(eError::JSON_ERROR); + } else { + Coin::Payload payload; + payload.cdd_location = json["cdd_location"].s(); + payload.denomination = json["denomination"].u(); + payload.issuer_id = json["issuer_id"].s(); + payload.mint_key_id = json["mint_key_id"].s(); + payload.protocol_version = json["protocol_version"].s(); + payload.serial = json["serial"].s(); + return payload; + } +} + +tl::expected Coin::from_json(const crow::json::rvalue& json) { + if ( !( json.has("type") + && json.has("payload") + && json.has("signature") + ) ) { + return tl::make_unexpected(eError::JSON_ERROR); + } else if ( json["type"]!="coin" ) { + return tl::make_unexpected(eError::JSON_ERROR); + } else { + auto pl = Payload::from_json(json["payload"]); + if (!pl.has_value()) { + return tl::make_unexpected(pl.error()); + } else { + Coin c; + c.payload = pl.value(); + c.signature = json["signature"].s(); + return c; + } + } +} + crow::json::wvalue CoinStack::to_json() const { crow::json::wvalue r; TO_JSON_ARRAY(coins); @@ -251,17 +363,46 @@ crow::json::wvalue CoinStack::to_json() const { tl::expected RequestRenew::from_string(const std::string& str) { - std::vector blinds; - std::vector coins; - unsigned int message_reference; /// Client internal message reference. - /// (Integer) - unsigned int transaction_reference; - // "type": "request renew" auto json = crow::json::load(str); + if (!json) { return tl::make_unexpected(eError::JSON_PARSE_ERROR); - } else { - return tl::make_unexpected(eError::NOT_IMPLEMENTED); + } else if ( !( json.has("blinds") + && json.has("coins") + && json.has("transaction_reference") + && json.has("message_reference") + && json.has("type") + ) ) { + return tl::make_unexpected(eError::JSON_MISSING_KEY); + } else if ( json["type"]!="request renew" ) { + return tl::make_unexpected(eError::JSON_WRONG_REQUEST_TYPE); + } else if ( (json["coins"].t()!=crow::json::type::List) + || (json["blinds"].t()!=crow::json::type::List) ) { + return tl::make_unexpected(eError::JSON_WRONG_VALUE_TYPE); + } else { + RequestRenew r; + + for (auto item: json["coins"]) { + auto coin = Coin::from_json(item); + if (!coin.has_value()) { + return tl::make_unexpected(coin.error()); + } else { + r.coins.push_back(coin.value()); + } + } + + for (auto item: json["blinds"]) { + auto blind = Blind::from_json(item); + if (!blind.has_value()) { + return tl::make_unexpected(blind.error()); + } else { + r.blinds.push_back(blind.value()); + } + } + + r.message_reference = json["message_reference"].u(); + r.transaction_reference = json["transaction_reference"].s(); + return r; } } @@ -273,30 +414,53 @@ crow::json::wvalue ResponseDelay::to_json() const { tl::expected RequestResume::from_string(const std::string& str) { - unsigned int message_reference; /// Client internal message reference. - /// (Integer) - unsigned int transaction_reference; - // "type": "request resume" auto json = crow::json::load(str); if (!json) { return tl::make_unexpected(eError::JSON_PARSE_ERROR); - } else { - return tl::make_unexpected(eError::NOT_IMPLEMENTED); + } else if ( !( json.has("transaction_reference") + && json.has("message_reference") + && json.has("type") + ) ) { + return tl::make_unexpected(eError::JSON_MISSING_KEY); + } else if ( json["type"]!="request resume" ) { + return tl::make_unexpected(eError::JSON_WRONG_REQUEST_TYPE); + } else { + RequestResume r; + r.message_reference = json["message_reference"].u(); + r.transaction_reference = json["transaction_reference"].s(); + return r; } } tl::expected RequestRedeem::from_string(const std::string& str) { - std::vector coins; - unsigned int message_reference; /// Client internal message reference. - /// (Integer) - unsigned int transaction_reference; - // "type": "request redeem" - auto json = crow::json::load(str); + // "type": +auto json = crow::json::load(str); if (!json) { return tl::make_unexpected(eError::JSON_PARSE_ERROR); - } else { - return tl::make_unexpected(eError::NOT_IMPLEMENTED); + } else if ( !( json.has("coins") + && json.has("message_reference") + && json.has("type") + ) ) { + return tl::make_unexpected(eError::JSON_MISSING_KEY); + } else if ( json["type"]!="request redeem" ) { + return tl::make_unexpected(eError::JSON_WRONG_REQUEST_TYPE); + } else { + RequestRedeem r; + r.message_reference = json["message_reference"].u(); + if (json["coins"].t()!=crow::json::type::List) { + return tl::make_unexpected(eError::JSON_WRONG_VALUE_TYPE); + } + + for (auto item: json["coins"]) { + auto coin = Coin::from_json(item); + if (!coin.has_value()) { + return tl::make_unexpected(coin.error()); + } else { + r.coins.push_back(coin.value()); + } + } + return r; } } diff --git a/src/model.hpp b/src/model.hpp index 4067575..fceea2d 100644 --- a/src/model.hpp +++ b/src/model.hpp @@ -82,6 +82,9 @@ struct MintKeyCert { enum class eError { JSON_PARSE_ERROR, + JSON_MISSING_KEY, + JSON_WRONG_REQUEST_TYPE, + JSON_WRONG_VALUE_TYPE, JSON_ERROR, NOT_IMPLEMENTED }; @@ -135,10 +138,11 @@ struct ResponseMKCs: Response { }; struct Blind { - std::string blinded_payload_hash; - std::string mint_key_id; + std::string blinded_payload_hash; //bigint + std::string mint_key_id; //bigint std::string reference; - crow::json::wvalue to_json() const; + crow::json::wvalue to_json() const; + static tl::expected from_json(const crow::json::rvalue& json); }; struct BlindSignature { @@ -148,9 +152,10 @@ struct BlindSignature { }; struct RequestMint { - std::vector blinds; unsigned int message_reference; /// Client internal message reference. /// (Integer) + std::string transaction_reference; + std::vector blinds; // "type": "request mint" static tl::expected from_string(const std::string& str); }; @@ -172,12 +177,14 @@ struct Coin { std::string serial; crow::json::wvalue to_json() const; -}; + static tl::expected from_json(const crow::json::rvalue& json); + }; Payload payload; std::string signature; crow::json::wvalue to_json() const; + static tl::expected from_json(const crow::json::rvalue& json); }; struct CoinStack { @@ -192,7 +199,7 @@ struct RequestRenew { std::vector coins; unsigned int message_reference; /// Client internal message reference. /// (Integer) - unsigned int transaction_reference; + std::string transaction_reference; // "type": "request renew" static tl::expected from_string(const std::string& str); }; @@ -204,7 +211,7 @@ struct ResponseDelay : Response { struct RequestResume { unsigned int message_reference; /// Client internal message reference. /// (Integer) - unsigned int transaction_reference; + std::string transaction_reference; // "type": "request resume" static tl::expected from_string(const std::string& str); }; @@ -213,7 +220,6 @@ struct RequestRedeem { std::vector coins; unsigned int message_reference; /// Client internal message reference. /// (Integer) - unsigned int transaction_reference; // "type": "request redeem" static tl::expected from_string(const std::string& str); }; diff --git a/test/test.cpp b/test/test.cpp index 601f7f2..111f03e 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -1,4 +1,5 @@ #include +#include "crow/json.h" #include "model.hpp" TEST_CASE( "PublicKey::to_json", "[to_json]" ) { @@ -12,3 +13,356 @@ TEST_CASE( "PublicKey::to_json", "[to_json]" ) { REQUIRE( json["type"].dump() == "\"rsa public key\"" ); REQUIRE( json.keys().size() == 3 ); } + +TEST_CASE( "RequestCDDSerial::from_string", "[from_string]" ) { + // good case + std::string good = + "{" + "\"message_reference\": 100000," + "\"type\": \"request cdd serial\"" + "}"; + + auto res = RequestCDDSerial::from_string(good); + REQUIRE(res.has_value()==true); + REQUIRE(res->message_reference == 100000); + + // bad cases + res = RequestCDDSerial::from_string(""); + REQUIRE(res.has_value()==false); + + // invalid type + res = RequestCDDSerial::from_string( "{" + "\"message_reference\": 100000," + "\"type\": \"request something wrong\"" + "}"); + REQUIRE(res.has_value()==false); + + // invalid attribute name + res = RequestCDDSerial::from_string("{" + "\"x_message_reference\": 100000," + "}"); + REQUIRE(res.has_value()==false); +} + +TEST_CASE( "RequestCDDC::from_string", "[from_string]" ) { + // good case + auto res = RequestCDDC::from_string("{" + "\"cdd_serial\": 1," + "\"message_reference\": 100001," + "\"type\": \"request cddc\"" + "}"); + REQUIRE(res.has_value()==true); + REQUIRE(res->cdd_serial == 1); + REQUIRE(res->message_reference == 100001); + + // bad cases + res = RequestCDDC::from_string(""); + REQUIRE(res.has_value()==false); + + // invalid type + res = RequestCDDC::from_string( "{" + "\"cdd_serial\": 1," + "\"message_reference\": 100001," + "\"type\": \"wrong type\"," + "}"); + REQUIRE(res.has_value()==false); + + // invalid attribute name + res = RequestCDDC::from_string("{" + "\"cdd_serial\": 1," + "\"x_ message_reference\": 100001," + "\"type\": \"request cddc\"," + "}"); + REQUIRE(res.has_value()==false); +} + +TEST_CASE( "RequestMKCs::from_string", "[from_string]" ) { + // good case + auto res = RequestMKCs::from_string( "{" + "\"denominations\": [1, 2, 5]," + "\"message_reference\": 100002," + "\"mint_key_ids\": []," + "\"type\": \"request mint key certificates\"" + "}"); + const std::vector EXPECTED_DENOMINATIONS = {1,2,5}; + const std::vector EXPECTED_MINT_KEY_IDS = {}; + + REQUIRE(res.has_value()==true); + REQUIRE(res->denominations == EXPECTED_DENOMINATIONS); + REQUIRE(res->message_reference == 100002); + REQUIRE(res->mint_key_ids == EXPECTED_MINT_KEY_IDS); + + // bad cases + res = RequestMKCs::from_string(""); + REQUIRE(res.has_value()==false); + + // invalid type + res = RequestMKCs::from_string( "{" + "\"denominations\": [1, 2, 5]," + "\"message_reference\": 100002," + "\"mint_key_ids\": []," + "\"type\": \"request wrong type\"" + "}"); + REQUIRE(res.has_value()==false); + + // invalid attribute name + res = RequestMKCs::from_string("{" + "\"denominations\": [1, 2, 5]," + "\"x_message_reference\": 100002," // WRONG NAME + "\"mint_key_ids\": []," + "\"type\": \"request wrong type\"" + "}"); + REQUIRE(res.has_value()==false); + + // invalid attribute type (not an array as expected) + res = RequestMKCs::from_string("{" + "\"denominations\": 1," + "\"message_reference\": 100002," // WRONG NAME + "\"mint_key_ids\": []," + "\"type\": \"request wrong type\"" + "}"); + REQUIRE(res.has_value()==false); + +} + +TEST_CASE( "Blind::from_json", "[from_string]" ) { + + auto good = crow::json::load("{" + "\"blinded_payload_hash\": \"924edb672c3345492f38341ff86b57181da4c673ef...\"," + "\"mint_key_id\": \"1ceb977bb531c65f133ab8b0d60862b17369d96\"," + "\"reference\": \"a0\"," + "\"type\": \"blinded payload hash\"" + "}"); + + auto res = Blind::from_json(good); + REQUIRE(res.has_value()==true); + REQUIRE(res->blinded_payload_hash== + "924edb672c3345492f38341ff86b57181da4c673ef..."); + REQUIRE(res->mint_key_id=="1ceb977bb531c65f133ab8b0d60862b17369d96"); + REQUIRE(res->reference=="a0"); + // bad cases + // wrong_type["blinded_payload_hash"]= "924edb672c3345492f38341ff86b57181da4c673ef..."; + // wrong_type["mint_key_id"]= "1ceb977bb531c65f133ab8b0d60862b17369d96"; + // wrong_type["reference"] = "a0"; + // wrong_type["type"]= "wrong type"; + + // res = Blind::from_json(wrong_type); +} + +TEST_CASE( "RequestMint::from_string", "[from_string]" ) { + // good case + auto res = RequestMint::from_string( "{" + "\"blinds\": [" + "{" + "\"blinded_payload_hash\": \"924edb672c3345492f38341ff86b57181da4c673ef...\"," + "\"mint_key_id\": \"1ceb977bb531c65f133ab8b0d60862b17369d96\"," + "\"reference\": \"a0\"," + "\"type\": \"blinded payload hash\"" + "}," + "{" + "\"blinded_payload_hash\": \"95db92e1c46ebea5edec5e508a831263de6fb78b4c...\"," + "\"mint_key_id\": \"f2864e5cd937dbaa4825e73a81062de162143682\"," + "\"reference\": \"a1\"," + "\"type\": \"blinded payload hash\"" + "}," + "{" + "\"blinded_payload_hash\": \"10afac98ac43eb40e996c621d5db4d2238348e3f74...\"," + "\"mint_key_id\": \"897a16bf12bd9ba474ef7be0e3a53553a7b4ece8\"," + "\"reference\": \"a2\"," + "\"type\": \"blinded payload hash\"" + "}" + "]," + "\"message_reference\": 100003," + "\"transaction_reference\": \"b2221a58008a05a6c4647159c324c985\"," + "\"type\": \"request mint\"" + "}"); + + REQUIRE(res.has_value()==true); + REQUIRE(res->message_reference==100003); + REQUIRE(res->transaction_reference == "b2221a58008a05a6c4647159c324c985"); + /// \todo check blinds + + // bad cases + res = RequestMint::from_string(""); + REQUIRE(res.has_value()==false); + + // invalid type + res = RequestMint::from_string( "{" + "\"blinds\": []," + "\"message_reference\": 100003," + "\"transaction_reference\": \"b2221a58008a05a6c4647159c324c985\"," + "\"type\": \"request wrong type\"" + "}"); + REQUIRE(res.has_value()==false); + + // invalid attribute name + res = RequestMint::from_string("{" + "\"blinds\": []," + "\"x_message_reference\": 100003," + "\"transaction_reference\": \"b2221a58008a05a6c4647159c324c985\"," + "\"type\": \"request mint\"" + "}"); + REQUIRE(res.has_value()==false); + + // invalid attribute type (not an array as expected) + res = RequestMint::from_string("{" + "\"blinds\": 1," + "\"message_reference\": 100003," + "\"transaction_reference\": \"b2221a58008a05a6c4647159c324c985\"," + "\"type\": \"request mint\"" + "}"); + REQUIRE(res.has_value()==false); +} + +TEST_CASE( "RequestRenew::from_string", "[from_string]" ) { + auto res = RequestRenew::from_string( + "{" + " \"blinds\": [" + " {" + " \"blinded_payload_hash\": \"7ed0cda1c1b36f544514b12848b8436974b7b9f6c7...\"," + " \"mint_key_id\": \"f2864e5cd937dbaa4825e73a81062de162143682\"," + " \"reference\": \"b0\"," + " \"type\": \"blinded payload hash\"" + " }" + " ]," + " \"coins\": [" + " {" + " \"payload\": {" + " \"cdd_location\": \"https://opencent.org\"," + " \"denomination\": 1," + " \"issuer_id\": \"23ed956e629ba35f0002eaf833ea436aea7db5c2\"," + " \"mint_key_id\": \"1ceb977bb531c65f133ab8b0d60862b17369d96\"," + " \"protocol_version\": \"https://opencoin.org/1.0\"," + " \"serial\": \"cd613e30d8f16adf91b7584a2265b1f5\"," + " \"type\": \"payload\"" + " }," + " \"signature\": \"2ec0af339566b19fb9867b491ce58025dcefcab649...\"," + " \"type\": \"coin\"" + " }" + " ]," + " \"message_reference\": 100004," + " \"transaction_reference\": \"ad45f23d3b1a11df587fd2803bab6c39\"," + " \"type\": \"request renew\"" + "}"); + REQUIRE(res.has_value()==true); + + // invalid attribute name + res = RequestRenew::from_string("{" + "\"blinds\": []," + "\"coins\": []," + "\"x_message_reference\": 100003," + "\"transaction_reference\": \"b2221a58008a05a6c4647159c324c985\"," + "\"type\": \"request mint\"" + "}"); + REQUIRE(res.has_value()==false); + + // invalid attribute type (not an array as expected) + res = RequestRenew::from_string("{" + "\"blinds\": 1," + "\"coins\": []," + "\"message_reference\": 100003," + "\"transaction_reference\": \"b2221a58008a05a6c4647159c324c985\"," + "\"type\": \"request mint\"" + "}"); + REQUIRE(res.has_value()==false); +} + +TEST_CASE( "RequestResume::from_string", "[from_string]" ) { + // good case + auto res = RequestResume::from_string( "{" + "\"message_reference\": 100005," + "\"transaction_reference\": \"ad45f23d3b1a11df587fd2803bab6c39\"," + "\"type\": \"request resume\"" + "}"); + + REQUIRE(res.has_value()==true); + REQUIRE(res->message_reference==100005); + REQUIRE(res->transaction_reference=="ad45f23d3b1a11df587fd2803bab6c39"); + + // bad cases + res = RequestResume::from_string(""); + REQUIRE(res.has_value()==false); + + // invalid type + res = RequestResume::from_string( "{" + "\"message_reference\": 100005," + "\"transaction_reference\": \"ad45f23d3b1a11df587fd2803bab6c39\"," + "\"type\": \"Wrong type\"" + "}"); + REQUIRE(res.has_value()==false); + + // invalid attribute name + res = RequestResume::from_string("{" + "\"message_reference\": 100005," + "\"WRONG_ATTR_transaction_reference\": \"ad45f23d3b1a11df587fd2803bab6c39\"," + "\"type\": \"request resume\"" + "}"); + REQUIRE(res.has_value()==false); +} + +TEST_CASE( "RequestRedeem::from_string", "[from_string]" ) { + + auto res = RequestRedeem::from_string( "{" + "\"coins\": [" + "{" + "\"payload\": {" + "\"cdd_location\": \"https://opencent.org\"," + "\"denomination\": 2," + "\"issuer_id\": \"23ed956e629ba35f0002eaf833ea436aea7db5c2\"," + "\"mint_key_id\": \"f2864e5cd937dbaa4825e73a81062de162143682\"," + "\"protocol_version\": \"https://opencoin.org/1.0\"," + "\"serial\": \"cd447e35b8b6d8fe442e3d437204e52d\"," + "\"type\": \"payload\"" + "}," + "\"signature\": \"11b6bfa18134c300f4440df1db17a08fa71a071b71...\"," + "\"type\": \"coin\"" + "}," + "{" + "\"payload\": {" + "\"cdd_location\": \"https://opencent.org\"," + "\"denomination\": 2," + "\"issuer_id\": \"23ed956e629ba35f0002eaf833ea436aea7db5c2\"," + "\"mint_key_id\": \"f2864e5cd937dbaa4825e73a81062de162143682\"," + "\"protocol_version\": \"https://opencoin.org/1.0\"," + "\"serial\": \"5b6e6e307d4bedc51431193e6c3f339\"," + "\"type\": \"payload\"" + "}," + "\"signature\": \"a6dd7b7f1f12c4e411289e8ea0355f24a8597bbc38...\"," + "\"type\": \"coin\"" + "}" + "]," + "\"message_reference\": 100006," + "\"type\": \"request redeem\"" + "}"); + + REQUIRE(res.has_value()==true); + REQUIRE(res->message_reference==100006); + + // bad cases + res = RequestRedeem::from_string(""); + REQUIRE(res.has_value()==false); + + // invalid type + res = RequestRedeem::from_string( "{" + "\"coins\": []," + "\"message_reference\": 100006," + "\"type\": \"WRONG TYPE\"" + "}"); + REQUIRE(res.has_value()==false); + + // invalid attribute name + res = RequestRedeem::from_string("{" + "\"INVALID_coins\": []," + "\"message_reference\": 100006," + "\"type\": \"request redeem\"" + "}"); + REQUIRE(res.has_value()==false); + // invalid coin type + res = RequestRedeem::from_string("{" + "\"coins\": 1," + "\"message_reference\": 100006," + "\"type\": \"request redeem\"" + "}"); + REQUIRE(res.has_value()==false); +} +