diff options
Diffstat (limited to 'src/wallet')
-rw-r--r-- | src/wallet/api/wallet.cpp | 4 | ||||
-rw-r--r-- | src/wallet/api/wallet.h | 2 | ||||
-rw-r--r-- | src/wallet/wallet2.cpp | 15 | ||||
-rw-r--r-- | src/wallet/wallet2.h | 29 | ||||
-rw-r--r-- | src/wallet/wallet2_api.h | 2 | ||||
-rw-r--r-- | src/wallet/wallet_rpc_server.cpp | 90 | ||||
-rw-r--r-- | src/wallet/wallet_rpc_server.h | 2 | ||||
-rw-r--r-- | src/wallet/wallet_rpc_server_commands_defs.h | 47 | ||||
-rw-r--r-- | src/wallet/wallet_rpc_server_error_codes.h | 2 |
9 files changed, 167 insertions, 26 deletions
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index e0326db84..fd0b65866 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1467,7 +1467,7 @@ bool WalletImpl::checkTxKey(const std::string &txid_str, std::string tx_key_str, } } -std::string WalletImpl::getTxProof(const std::string &txid_str, const std::string &address_str, const std::string &message, std::string &error_str) const +std::string WalletImpl::getTxProof(const std::string &txid_str, const std::string &address_str, const std::string &message) const { crypto::hash txid; if (!epee::string_tools::hex_to_pod(txid_str, txid)) @@ -1488,7 +1488,7 @@ std::string WalletImpl::getTxProof(const std::string &txid_str, const std::strin try { m_status = Status_Ok; - return m_wallet->get_tx_proof(txid, info.address, info.is_subaddress, message, error_str); + return m_wallet->get_tx_proof(txid, info.address, info.is_subaddress, message); } catch (const std::exception &e) { diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 38e167516..8ceabc843 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -138,7 +138,7 @@ public: virtual std::string getUserNote(const std::string &txid) const; virtual std::string getTxKey(const std::string &txid) const; virtual bool checkTxKey(const std::string &txid, std::string tx_key, const std::string &address, uint64_t &received, bool &in_pool, uint64_t &confirmations); - virtual std::string getTxProof(const std::string &txid, const std::string &address, const std::string &message, std::string &error_str) const; + virtual std::string getTxProof(const std::string &txid, const std::string &address, const std::string &message) const; virtual bool checkTxProof(const std::string &txid, const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &received, bool &in_pool, uint64_t &confirmations); virtual std::string getSpendProof(const std::string &txid, const std::string &message) const; virtual bool checkSpendProof(const std::string &txid, const std::string &message, const std::string &signature, bool &good) const; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index bfbb653a3..2c8513fa4 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -6536,9 +6536,8 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de } } -std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message, std::string &error_str) +std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message) { - error_str = ""; // determine if the address is found in the subaddress hash table (i.e. whether the proof is outbound or inbound) const bool is_out = m_subaddresses.count(address.m_spend_public_key) == 0; @@ -6554,11 +6553,7 @@ std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::ac { crypto::secret_key tx_key; std::vector<crypto::secret_key> additional_tx_keys; - if (!get_tx_key(txid, tx_key, additional_tx_keys)) - { - error_str = tr("Tx secret key wasn't found in the wallet file."); - return {}; - } + THROW_WALLET_EXCEPTION_IF(!get_tx_key(txid, tx_key, additional_tx_keys), error::wallet_internal_error, "Tx secret key wasn't found in the wallet file."); const size_t num_sigs = 1 + additional_tx_keys.size(); shared_secret.resize(num_sigs); @@ -6661,11 +6656,7 @@ std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::ac bool in_pool; uint64_t confirmations; check_tx_key_helper(txid, derivation, additional_derivations, address, received, in_pool, confirmations); - if (!received) - { - error_str = tr("No funds received in this tx."); - return {}; - } + THROW_WALLET_EXCEPTION_IF(!received, error::wallet_internal_error, tr("No funds received in this tx.")); // concatenate all signature strings for (size_t i = 0; i < num_sigs; ++i) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 7807f7f33..ce0c67fc3 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -292,6 +292,19 @@ namespace tools std::vector<cryptonote::tx_destination_entry> dests; // original setup, does not include change uint32_t subaddr_account; // subaddress account of your wallet to be used in this transfer std::set<uint32_t> subaddr_indices; // set of address indices used as inputs in this transfer + + BEGIN_SERIALIZE_OBJECT() + FIELD(sources) + FIELD(change_dts) + FIELD(splitted_dsts) + FIELD(selected_transfers) + FIELD(extra) + FIELD(unlock_time) + FIELD(use_rct) + FIELD(dests) + FIELD(subaddr_account) + FIELD(subaddr_indices) + END_SERIALIZE() }; typedef std::vector<transfer_details> transfer_container; @@ -313,6 +326,20 @@ namespace tools std::vector<cryptonote::tx_destination_entry> dests; tx_construction_data construction_data; + + BEGIN_SERIALIZE_OBJECT() + FIELD(tx) + FIELD(dust) + FIELD(fee) + FIELD(dust_added_to_fee) + FIELD(change_dts) + FIELD(selected_transfers) + FIELD(key_images) + FIELD(tx_key) + FIELD(additional_tx_keys) + FIELD(dests) + FIELD(construction_data) + END_SERIALIZE() }; // The term "Unsigned tx" is not really a tx since it's not signed yet. @@ -686,7 +713,7 @@ namespace tools bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const; void check_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations); void check_tx_key_helper(const crypto::hash &txid, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations); - std::string get_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message, std::string &error_str); + std::string get_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message); bool check_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message, const std::string &sig_str, uint64_t &received, bool &in_pool, uint64_t &confirmations); std::string get_spend_proof(const crypto::hash &txid, const std::string &message); diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h index 101fceebb..b1f8369a3 100644 --- a/src/wallet/wallet2_api.h +++ b/src/wallet/wallet2_api.h @@ -701,7 +701,7 @@ struct Wallet virtual std::string getUserNote(const std::string &txid) const = 0; virtual std::string getTxKey(const std::string &txid) const = 0; virtual bool checkTxKey(const std::string &txid, std::string tx_key, const std::string &address, uint64_t &received, bool &in_pool, uint64_t &confirmations) = 0; - virtual std::string getTxProof(const std::string &txid, const std::string &address, const std::string &message, std::string &error_str) const = 0; + virtual std::string getTxProof(const std::string &txid, const std::string &address, const std::string &message) const = 0; virtual bool checkTxProof(const std::string &txid, const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &received, bool &in_pool, uint64_t &confirmations) = 0; virtual std::string getSpendProof(const std::string &txid, const std::string &message) const = 0; virtual bool checkSpendProof(const std::string &txid, const std::string &message, const std::string &signature, bool &good) const = 0; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index f540ba67a..fe7f51617 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -30,6 +30,7 @@ #include <boost/format.hpp> #include <boost/asio/ip/address.hpp> #include <boost/filesystem/operations.hpp> +#include <boost/algorithm/string.hpp> #include <cstdint> #include "include_base_utils.h" using namespace epee; @@ -635,6 +636,13 @@ namespace tools tx_to_blob(ptx_vector.back().tx, blob); res.tx_blob = epee::string_tools::buff_to_hex_nodelimer(blob); } + if (req.get_tx_metadata) + { + std::ostringstream oss; + binary_archive<true> ar(oss); + ::serialization::serialize(ar, ptx_vector.back()); + res.tx_metadata = epee::string_tools::buff_to_hex_nodelimer(oss.str()); + } return true; } catch (const std::exception& e) @@ -682,7 +690,7 @@ namespace tools } // populate response with tx hashes - for (auto & ptx : ptx_vector) + for (const auto & ptx : ptx_vector) { res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx))); if (req.get_tx_keys) @@ -705,6 +713,13 @@ namespace tools tx_to_blob(ptx.tx, blob); res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob)); } + if (req.get_tx_metadata) + { + std::ostringstream oss; + binary_archive<true> ar(oss); + ::serialization::serialize(ar, const_cast<tools::wallet2::pending_tx&>(ptx)); + res.tx_metadata_list.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str())); + } } return true; @@ -735,7 +750,7 @@ namespace tools m_wallet->commit_tx(ptx_vector); // populate response with tx hashes - for (auto & ptx : ptx_vector) + for (const auto & ptx : ptx_vector) { res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx))); if (req.get_tx_keys) @@ -749,6 +764,13 @@ namespace tools tx_to_blob(ptx.tx, blob); res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob)); } + if (req.get_tx_metadata) + { + std::ostringstream oss; + binary_archive<true> ar(oss); + ::serialization::serialize(ar, const_cast<tools::wallet2::pending_tx&>(ptx)); + res.tx_metadata_list.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str())); + } } return true; @@ -793,7 +815,7 @@ namespace tools m_wallet->commit_tx(ptx_vector); // populate response with tx hashes - for (auto & ptx : ptx_vector) + for (const auto & ptx : ptx_vector) { res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx))); if (req.get_tx_keys) @@ -806,6 +828,13 @@ namespace tools tx_to_blob(ptx.tx, blob); res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob)); } + if (req.get_tx_metadata) + { + std::ostringstream oss; + binary_archive<true> ar(oss); + ::serialization::serialize(ar, const_cast<tools::wallet2::pending_tx&>(ptx)); + res.tx_metadata_list.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str())); + } } return true; @@ -889,6 +918,13 @@ namespace tools tx_to_blob(ptx.tx, blob); res.tx_blob = epee::string_tools::buff_to_hex_nodelimer(blob); } + if (req.get_tx_metadata) + { + std::ostringstream oss; + binary_archive<true> ar(oss); + ::serialization::serialize(ar, const_cast<tools::wallet2::pending_tx&>(ptx)); + res.tx_metadata = epee::string_tools::buff_to_hex_nodelimer(oss.str()); + } return true; } @@ -913,6 +949,47 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_relay_tx(const wallet_rpc::COMMAND_RPC_RELAY_TX::request& req, wallet_rpc::COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& er) + { + if (!m_wallet) return not_open(er); + + cryptonote::blobdata blob; + if (!epee::string_tools::parse_hexstr_to_binbuff(req.hex, blob)) + { + er.code = WALLET_RPC_ERROR_CODE_BAD_HEX; + er.message = "Failed to parse hex."; + return false; + } + + std::stringstream ss; + ss << blob; + binary_archive<false> ba(ss); + + tools::wallet2::pending_tx ptx; + bool r = ::serialization::serialize(ba, ptx); + if (!r) + { + er.code = WALLET_RPC_ERROR_CODE_BAD_TX_METADATA; + er.message = "Failed to parse tx metadata."; + return false; + } + + try + { + m_wallet->commit_tx(ptx); + } + catch(const std::exception &e) + { + er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR; + er.message = "Failed to commit tx."; + return false; + } + + res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)); + + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_make_integrated_address(const wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); @@ -1507,12 +1584,7 @@ namespace tools try { - res.signature = m_wallet->get_tx_proof(txid, info.address, info.is_subaddress, req.message, er.message); - if (res.signature.empty()) - { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - return false; - } + res.signature = m_wallet->get_tx_proof(txid, info.address, info.is_subaddress, req.message); } catch (const std::exception &e) { diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index beeadba6b..9455c4769 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -80,6 +80,7 @@ namespace tools MAP_JON_RPC_WE("sweep_dust", on_sweep_dust, wallet_rpc::COMMAND_RPC_SWEEP_DUST) MAP_JON_RPC_WE("sweep_all", on_sweep_all, wallet_rpc::COMMAND_RPC_SWEEP_ALL) MAP_JON_RPC_WE("sweep_single", on_sweep_single, wallet_rpc::COMMAND_RPC_SWEEP_SINGLE) + MAP_JON_RPC_WE("relay_tx", on_relay_tx, wallet_rpc::COMMAND_RPC_RELAY_TX) MAP_JON_RPC_WE("store", on_store, wallet_rpc::COMMAND_RPC_STORE) MAP_JON_RPC_WE("get_payments", on_get_payments, wallet_rpc::COMMAND_RPC_GET_PAYMENTS) MAP_JON_RPC_WE("get_bulk_payments", on_get_bulk_payments, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS) @@ -134,6 +135,7 @@ namespace tools bool on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er); bool on_sweep_all(const wallet_rpc::COMMAND_RPC_SWEEP_ALL::request& req, wallet_rpc::COMMAND_RPC_SWEEP_ALL::response& res, epee::json_rpc::error& er); bool on_sweep_single(const wallet_rpc::COMMAND_RPC_SWEEP_SINGLE::request& req, wallet_rpc::COMMAND_RPC_SWEEP_SINGLE::response& res, epee::json_rpc::error& er); + bool on_relay_tx(const wallet_rpc::COMMAND_RPC_RELAY_TX::request& req, wallet_rpc::COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& er); bool on_make_integrated_address(const wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er); bool on_split_integrated_address(const wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er); bool on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 1b15c0a99..e084d9e6d 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -291,6 +291,7 @@ namespace wallet_rpc bool get_tx_key; bool do_not_relay; bool get_tx_hex; + bool get_tx_metadata; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(destinations) @@ -303,6 +304,7 @@ namespace wallet_rpc KV_SERIALIZE(get_tx_key) KV_SERIALIZE_OPT(do_not_relay, false) KV_SERIALIZE_OPT(get_tx_hex, false) + KV_SERIALIZE_OPT(get_tx_metadata, false) END_KV_SERIALIZE_MAP() }; @@ -313,6 +315,7 @@ namespace wallet_rpc std::list<std::string> amount_keys; uint64_t fee; std::string tx_blob; + std::string tx_metadata; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_hash) @@ -320,6 +323,7 @@ namespace wallet_rpc KV_SERIALIZE(amount_keys) KV_SERIALIZE(fee) KV_SERIALIZE(tx_blob) + KV_SERIALIZE(tx_metadata) END_KV_SERIALIZE_MAP() }; }; @@ -338,6 +342,7 @@ namespace wallet_rpc bool get_tx_keys; bool do_not_relay; bool get_tx_hex; + bool get_tx_metadata; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(destinations) @@ -350,6 +355,7 @@ namespace wallet_rpc KV_SERIALIZE(get_tx_keys) KV_SERIALIZE_OPT(do_not_relay, false) KV_SERIALIZE_OPT(get_tx_hex, false) + KV_SERIALIZE_OPT(get_tx_metadata, false) END_KV_SERIALIZE_MAP() }; @@ -369,6 +375,7 @@ namespace wallet_rpc std::list<uint64_t> amount_list; std::list<uint64_t> fee_list; std::list<std::string> tx_blob_list; + std::list<std::string> tx_metadata_list; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_hash_list) @@ -376,6 +383,7 @@ namespace wallet_rpc KV_SERIALIZE(amount_list) KV_SERIALIZE(fee_list) KV_SERIALIZE(tx_blob_list) + KV_SERIALIZE(tx_metadata_list) END_KV_SERIALIZE_MAP() }; }; @@ -387,11 +395,13 @@ namespace wallet_rpc bool get_tx_keys; bool do_not_relay; bool get_tx_hex; + bool get_tx_metadata; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(get_tx_keys) KV_SERIALIZE_OPT(do_not_relay, false) KV_SERIALIZE_OPT(get_tx_hex, false) + KV_SERIALIZE_OPT(get_tx_metadata, false) END_KV_SERIALIZE_MAP() }; @@ -410,12 +420,14 @@ namespace wallet_rpc std::list<std::string> tx_key_list; std::list<uint64_t> fee_list; std::list<std::string> tx_blob_list; + std::list<std::string> tx_metadata_list; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_hash_list) KV_SERIALIZE(tx_key_list) KV_SERIALIZE(fee_list) KV_SERIALIZE(tx_blob_list) + KV_SERIALIZE(tx_metadata_list) END_KV_SERIALIZE_MAP() }; }; @@ -435,6 +447,7 @@ namespace wallet_rpc uint64_t below_amount; bool do_not_relay; bool get_tx_hex; + bool get_tx_metadata; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(address) @@ -448,6 +461,7 @@ namespace wallet_rpc KV_SERIALIZE(below_amount) KV_SERIALIZE_OPT(do_not_relay, false) KV_SERIALIZE_OPT(get_tx_hex, false) + KV_SERIALIZE_OPT(get_tx_metadata, false) END_KV_SERIALIZE_MAP() }; @@ -466,12 +480,14 @@ namespace wallet_rpc std::list<std::string> tx_key_list; std::list<uint64_t> fee_list; std::list<std::string> tx_blob_list; + std::list<std::string> tx_metadata_list; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_hash_list) KV_SERIALIZE(tx_key_list) KV_SERIALIZE(fee_list) KV_SERIALIZE(tx_blob_list) + KV_SERIALIZE(tx_metadata_list) END_KV_SERIALIZE_MAP() }; }; @@ -489,6 +505,7 @@ namespace wallet_rpc std::string key_image; bool do_not_relay; bool get_tx_hex; + bool get_tx_metadata; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(address) @@ -500,6 +517,36 @@ namespace wallet_rpc KV_SERIALIZE(key_image) KV_SERIALIZE_OPT(do_not_relay, false) KV_SERIALIZE_OPT(get_tx_hex, false) + KV_SERIALIZE_OPT(get_tx_metadata, false) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string tx_hash; + std::string tx_key; + uint64_t fee; + std::string tx_blob; + std::string tx_metadata; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(tx_hash) + KV_SERIALIZE(tx_key) + KV_SERIALIZE(fee) + KV_SERIALIZE(tx_blob) + KV_SERIALIZE(tx_metadata) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_RELAY_TX + { + struct request + { + std::string hex; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(hex) END_KV_SERIALIZE_MAP() }; diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h index 74f2999b6..c3f3e20d1 100644 --- a/src/wallet/wallet_rpc_server_error_codes.h +++ b/src/wallet/wallet_rpc_server_error_codes.h @@ -56,3 +56,5 @@ #define WALLET_RPC_ERROR_CODE_NO_WALLET_DIR -23 #define WALLET_RPC_ERROR_CODE_NO_TXKEY -24 #define WALLET_RPC_ERROR_CODE_WRONG_KEY -25 +#define WALLET_RPC_ERROR_CODE_BAD_HEX -26 +#define WALLET_RPC_ERROR_CODE_BAD_TX_METADATA -27 |