From c560be83f11783247167202e0f835557e77f78db Mon Sep 17 00:00:00 2001 From: Adele Lynn Date: Sat, 17 Apr 2021 16:07:15 -0700 Subject: [PATCH] Implement register and unregister on wallet API, add tests --- wallet/api/v6_0/wallet_api.h | 5 ++ wallet/api/v6_0/wallet_api_defs.h | 28 +++++++++ wallet/api/v6_0/wallet_api_handle.cpp | 42 +++++++++++++ wallet/api/v6_0/wallet_api_parse.cpp | 86 +++++++++++++++++++++++++++ wallet/core/assets_utils.cpp | 2 +- wallet/unittests/wallet_api_test.cpp | 84 ++++++++++++++++++++++++++ 6 files changed, 246 insertions(+), 1 deletion(-) diff --git a/wallet/api/v6_0/wallet_api.h b/wallet/api/v6_0/wallet_api.h index b37449c297..05739ae4df 100644 --- a/wallet/api/v6_0/wallet_api.h +++ b/wallet/api/v6_0/wallet_api.h @@ -117,6 +117,9 @@ namespace beam::wallet } } + template + void onHandleRegisterUnregister(bool doRegister, const JsonRpcId &id, const T& data); + template void onHandleIssueConsume(bool issue, const JsonRpcId& id, const T& data); @@ -135,6 +138,8 @@ namespace beam::wallet WALLET_API_METHODS(PARSE_FUNC) #undef PARSE_FUNC + template + std::pair onParseRegisterUnregister(bool doRegister, const JsonRpcId& id, const json& params); template std::pair onParseIssueConsume(bool issue, const JsonRpcId& id, const json& params); diff --git a/wallet/api/v6_0/wallet_api_defs.h b/wallet/api/v6_0/wallet_api_defs.h index 33f392c75e..3831fd200b 100644 --- a/wallet/api/v6_0/wallet_api_defs.h +++ b/wallet/api/v6_0/wallet_api_defs.h @@ -56,6 +56,8 @@ namespace beam::wallet macro(AddrList, "addr_list", API_READ_ACCESS, API_SYNC, APPS_ALLOWED) \ macro(ValidateAddress, "validate_address", API_READ_ACCESS, API_SYNC, APPS_ALLOWED) \ macro(Send, "tx_send", API_WRITE_ACCESS, API_SYNC, APPS_ALLOWED) \ + macro(Register, "tx_asset_register", API_WRITE_ACCESS, API_SYNC, APPS_ALLOWED /* TODO: I feel like apps shouldn't be allowed to do this, but I also don't know what this refers to in "apps". */) \ + macro(Unregister, "tx_asset_unregister", API_WRITE_ACCESS, API_SYNC, APPS_ALLOWED) \ macro(Issue, "tx_asset_issue", API_WRITE_ACCESS, API_SYNC, APPS_BLOCKED) \ macro(Consume, "tx_asset_consume", API_WRITE_ACCESS, API_SYNC, APPS_BLOCKED) \ macro(TxAssetInfo, "tx_asset_info", API_WRITE_ACCESS, API_SYNC, APPS_ALLOWED) \ @@ -172,6 +174,32 @@ namespace beam::wallet }; }; + struct Register + { + Amount fee = 0; + std::string asset_meta; + boost::optional coins; + boost::optional txId; + + struct Response + { + TxID txId; + }; + }; + + struct Unregister + { + Amount fee = 0; + std::string asset_meta; + boost::optional coins; + boost::optional txId; + + struct Response + { + TxID txId; + }; + }; + struct Issue { Amount value = 0; diff --git a/wallet/api/v6_0/wallet_api_handle.cpp b/wallet/api/v6_0/wallet_api_handle.cpp index 05d49366ff..562648e986 100644 --- a/wallet/api/v6_0/wallet_api_handle.cpp +++ b/wallet/api/v6_0/wallet_api_handle.cpp @@ -352,6 +352,48 @@ namespace beam::wallet template void WalletApi::setTxAssetParams(const JsonRpcId& id, TxParameters& params, const Issue& data); template void WalletApi::setTxAssetParams(const JsonRpcId& id, TxParameters& params, const Consume& data); + void WalletApi::onHandleRegister(const JsonRpcId &id, const Register &data) + { + WalletApi::onHandleRegisterUnregister(false, id, data); + } + + void WalletApi::onHandleUnregister(const JsonRpcId &id, const Unregister &data) + { + WalletApi::onHandleRegisterUnregister(false, id, data); + } + + template + void WalletApi::onHandleRegisterUnregister(bool doRegister, const JsonRpcId& id, const T& data) + { + try { + auto walletDB = getWalletDB(); + auto wallet = getWallet(); + CoinIDList coins = data.coins ? *data.coins : CoinIDList(); + + WalletAssetMeta meta(data.asset_meta); + + if (data.txId && walletDB->getTx(*data.txId)) { + doTxAlreadyExistsError(id); + return; + } + + auto params = CreateTransactionParameters(doRegister ? TxType::AssetReg : TxType::AssetUnreg) + .SetParameter(TxParameterID::Amount, Rules::get().CA.DepositForList) + .SetParameter(TxParameterID::Fee, data.fee) + .SetParameter(TxParameterID::PreselectedCoins, coins) + .SetParameter(TxParameterID::AssetMetadata, data.asset_meta); + + const auto txId = wallet->StartTransaction(params); + doResponse(id, Register::Response{txId}); + } catch(const jsonrpc_exception&) { + throw; + } + catch (...) + { + throw jsonrpc_exception(ApiError::InternalErrorJsonRpc, "Transaction could not be created. Please look at logs."); + } + } + void WalletApi::onHandleIssue(const JsonRpcId& id, const Issue& data) { onHandleIssueConsume(true, id, data); diff --git a/wallet/api/v6_0/wallet_api_parse.cpp b/wallet/api/v6_0/wallet_api_parse.cpp index 23f4a8bbe9..53dc1aea07 100644 --- a/wallet/api/v6_0/wallet_api_parse.cpp +++ b/wallet/api/v6_0/wallet_api_parse.cpp @@ -617,6 +617,64 @@ namespace beam::wallet return std::make_pair(txDelete, MethodInfo()); } + std::pair WalletApi::onParseRegister(const JsonRpcId& id, const json& params) + { + return onParseRegisterUnregister(false, id, params); + } + + std::pair WalletApi::onParseUnregister(const JsonRpcId& id, const json& params) + { + return onParseRegisterUnregister(false, id, params); + } + + template + std::pair WalletApi::onParseRegisterUnregister(bool doRegister, const JsonRpcId& id, const json& params) + { + T data; + data.asset_meta = getMandatoryParam(params, "asset_meta"); + + WalletAssetMeta meta(data.asset_meta); + // ensure that asset meta has all required parameters at a minimum + const auto chkMetadata = [&](bool cond, const std::string& param) -> auto { + if (cond) { + throw jsonrpc_exception(ApiError::InvalidParamsJsonRpc, param + " is a required asset descriptor, but was not included."); + } + }; + + chkMetadata(meta.GetSchemaVersion() == 0, "SCH_VER"); + chkMetadata(meta.GetName().empty(), "N"); + chkMetadata(meta.GetShortName().empty(), "SN"); + chkMetadata(meta.GetUnitName().empty(), "UN"); + chkMetadata(meta.GetNthUnitName().empty(), "NTHUN"); + + if (hasParam(params, "coins")) + { + data.coins = readCoinsParameter(id, params); + } + + data.fee = getBeamFeeParam(params, "fee"); + data.txId = getOptionalParam(params, "txId"); + + MethodInfo info; + info.fee = data.fee; + + if (doRegister) + { + // is there an actual Beam to Groth value somewhere in wallet API source? + // additionally, is this correct behaviour? + info.spend[Asset::s_BeamID] = (Amount) 3000 * 100000000; + } + else + { + info.receive[Asset::s_BeamID] = (Amount) 3000 * 100000000; + } + + return std::make_pair(data, info); + } + + template std::pair WalletApi::onParseRegisterUnregister(bool doRegister, const JsonRpcId& id, const json& params); + template std::pair WalletApi::onParseRegisterUnregister(bool doRegister, const JsonRpcId& id, const json& params); + std::pair WalletApi::onParseIssue(const JsonRpcId& id, const json& params) { return onParseIssueConsume(true, id, params); @@ -1085,6 +1143,34 @@ namespace beam::wallet }; } + void WalletApi::getResponse(const JsonRpcId& id, const Register::Response& res, json& msg) + { + msg = json + { + {JsonRpcHeader, JsonRpcVersion}, + {"id", id}, + {"result", + { + {"txId", std::to_string(res.txId)} + } + } + }; + } + + void WalletApi::getResponse(const JsonRpcId& id, const Unregister::Response& res, json& msg) + { + msg = json + { + {JsonRpcHeader, JsonRpcVersion}, + {"id", id}, + {"result", + { + {"txId", std::to_string(res.txId)} + } + } + }; + } + void WalletApi::getResponse(const JsonRpcId& id, const Issue::Response& res, json& msg) { msg = json diff --git a/wallet/core/assets_utils.cpp b/wallet/core/assets_utils.cpp index 1977ee9d3a..7af25433bc 100644 --- a/wallet/core/assets_utils.cpp +++ b/wallet/core/assets_utils.cpp @@ -218,7 +218,7 @@ namespace beam::wallet { unsigned WalletAssetMeta::GetSchemaVersion() const { - const auto it = _values.find(SHORT_NAME_KEY); + const auto it = _values.find(VERSION_KEY); return it != _values.end() ? std::to_unsigned(it->second, false) : 0; } diff --git a/wallet/unittests/wallet_api_test.cpp b/wallet/unittests/wallet_api_test.cpp index 8bc4962349..8bab5ed6a7 100644 --- a/wallet/unittests/wallet_api_test.cpp +++ b/wallet/unittests/wallet_api_test.cpp @@ -344,6 +344,42 @@ namespace WALLET_CHECK(ApiSyncMode::DoneSync == api.executeAPIRequest(msg.data(), msg.size())); } + // confidential assets register/unregister test + template + void testCA_R_UR(const std::string& msg) { + class ApiTest : public WalletApiTest + { + public: + void onAPIError(const json& msg) override { + cout << msg["error"] << endl; + WALLET_CHECK(!"invalid register/unregister api json!!!"); + } + + void onHandleRegister(const JsonRpcId& id, const Register& data) override { + WALLET_CHECK(id > 0); + WALLET_CHECK(!data.asset_meta.empty()); + } + + void onHandleUnregister(const JsonRpcId& id, const Unregister& data) override { + WALLET_CHECK(id > 0); + WALLET_CHECK(!data.asset_meta.empty()); + } + }; + + ApiTest api; + WALLET_CHECK(ApiSyncMode::DoneSync == api.executeAPIRequest(msg.data(), msg.size())); + + { + json res; + typename T::Response status; + status.txId = {1,2,3}; + api.getResponse(12345, status, res); + testResultHeader(res); + + WALLET_CHECK(res["id"] == 12345); + } + } + template void testICJsonRpc(const std::string& msg) { @@ -924,6 +960,52 @@ namespace #endif // BEAM_ATOMIC_SWAP_SUPPORT } +template +void TestCA_R_URTx(const char *method) { + const auto exp = [&](std::string str) -> auto { + const char* what = "METHOD"; + const auto index = str.find(what); + if (index != std::string::npos) { + const std::string mname = std::string("\"") + method + "\""; + str.replace(index, strlen(what), mname); + } + return str; + }; + + testInvalidAssetJsonRpc(exp(JSON_CODE( + { + "jsonrpc" : "2.0", + "id" : 12345, + "method" : METHOD, + "params" : + { + "asset_meta": "" + } + } + ))); + + testInvalidAssetJsonRpc(exp(JSON_CODE( + { + "jsonrpc" : "2.0", + "id" : 12345, + "method" : METHOD, + "params" : {} + } + ))); + + testCA_R_UR(exp(JSON_CODE( + { + "jsonrpc" : "2.0", + "id" : 12345, + "method" : METHOD, + "params" : + { + "asset_meta": "STD:SCH_VER=1;N=Test coin;SN=TEST;UN=Test;NTHUN=Groth" + } + } + ))); +} + template void TestICTx(const char* method) { @@ -1295,6 +1377,8 @@ void TestAssetsAPI() Rules::get().CA.Enabled = true; Rules::get().UpdateChecksum(); + TestCA_R_URTx("tx_asset_register"); + TestCA_R_URTx("tx_asset_unregister"); TestICTx("tx_asset_issue"); TestICTx("tx_asset_consume"); TestAITx();