Raven Core  3.0.0
P2P Digital Currency
messages.cpp
Go to the documentation of this file.
1 // Copyright (c) 2017-2019 The Raven Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #include "assets/assets.h"
6 #include "assets/assetdb.h"
7 #include "assets/messages.h"
8 #include "assets/messagedb.h"
9 #include <map>
10 #include "tinyformat.h"
11 
12 #include "amount.h"
13 #include "base58.h"
14 #include "chain.h"
15 #include "consensus/validation.h"
16 #include "core_io.h"
17 #include "httpserver.h"
18 #include "validation.h"
19 #include "net.h"
20 #include "policy/feerate.h"
21 #include "policy/fees.h"
22 #include "policy/policy.h"
23 #include "policy/rbf.h"
24 #include "rpc/mining.h"
25 #include "rpc/safemode.h"
26 #include "rpc/server.h"
27 #include "script/sign.h"
28 #include "timedata.h"
29 #include "util.h"
30 #include "utilmoneystr.h"
31 #include "wallet/coincontrol.h"
32 #include "wallet/feebumper.h"
33 #include "wallet/wallet.h"
34 #include "wallet/walletdb.h"
35 
37 {
38  return AreMessagingDeployed() ? "" : "\nTHIS COMMAND IS NOT YET ACTIVE!\nhttps://github.com/RavenProject/rips/blob/master/rip-0005.mediawiki\n";
39 }
40 
42  if (request.fHelp || !AreMessagingDeployed() || request.params.size() != 0)
43  throw std::runtime_error(
44  "viewallmessages \n"
46  "\nView all messages that the wallet contains\n"
47 
48  "\nResult:\n"
49  "\"Asset Name:\" (string) The name of the asset the message was sent on\n"
50  "\"Message:\" (string) The IPFS hash of the message\n"
51  "\"Time:\" (Date) The time as a date in the format (YY-mm-dd Hour-minute-second)\n"
52  "\"Block Height:\" (number) The height of the block the message was included in\n"
53  "\"Status:\" (string) Status of the message (READ, UNREAD, ORPHAN, EXPIRED, SPAM, HIDDEN, ERROR)\n"
54  "\"Expire Time:\" (Date, optional) If the message had an expiration date assigned, it will be shown here in the format (YY-mm-dd Hour-minute-second)\n"
55  "\"Expire UTC Time:\" (Date, optional) If the message contains an expire date that is too large, the UTC number will be displayed\n"
56 
57 
58  "\nExamples:\n"
59  + HelpExampleCli("viewallmessages", "")
60  + HelpExampleRpc("viewallmessages", "")
61  );
62 
63  if (!fMessaging) {
65  ret.push_back("Messaging is disabled. To enable messaging, run the wallet without -disablemessaging or remove disablemessaging from your raven.conf");
66  return ret;
67  }
68 
69  if (!pMessagesCache || !pmessagedb) {
71  ret.push_back("Messaging database and cache are having problems (a wallet restart might fix this issue)");
72  return ret;
73  }
74 
75  std::set<CMessage> setMessages;
76 
77  pmessagedb->LoadMessages(setMessages);
78 
79  for (auto pair : mapDirtyMessagesOrphaned) {
80  CMessage message = pair.second;
81  message.status = MessageStatus::ORPHAN;
82  if (setMessages.count(message))
83  setMessages.erase(message);
84  setMessages.insert(message);
85  }
86 
87  for (auto out : setDirtyMessagesRemove) {
88  CMessage message;
89  message.out = out;
90  setMessages.erase(message);
91  }
92 
93  for (auto pair : mapDirtyMessagesAdd) {
94  setMessages.erase(pair.second);
95  setMessages.insert(pair.second);
96  }
97 
98  UniValue messages(UniValue::VARR);
99 
100  for (auto message : setMessages) {
102 
103  obj.push_back(Pair("Asset Name", message.strName));
104  obj.push_back(Pair("Message", EncodeAssetData(message.ipfsHash)));
105  obj.push_back(Pair("Time", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", message.time)));
106  obj.push_back(Pair("Block Height", message.nBlockHeight));
107  obj.push_back(Pair("Status", MessageStatusToString(message.status)));
108  try {
109  std::string date = DateTimeStrFormat("%Y-%m-%d %H:%M:%S", message.nExpiredTime);
110  if (message.nExpiredTime)
111  obj.push_back(Pair("Expire Time", date));
112  } catch (...) {
113  obj.push_back(Pair("Expire UTC Time", message.nExpiredTime));
114  }
115 
116  messages.push_back(obj);
117  }
118 
119 
120  return messages;
121 }
122 
124  if (request.fHelp || request.params.size() != 0)
125  throw std::runtime_error(
126  "viewallmessagechannels \n"
128  "\nView all message channels the wallet is subscribed to\n"
129 
130  "\nResult:[\n"
131  "\"Asset Name\" (string) The asset channel name\n"
132  "\n]\n"
133  "\nExamples:\n"
134  + HelpExampleCli("viewallmessagechannels", "")
135  + HelpExampleRpc("viewallmessagechannels", "")
136  );
137 
138  if (!fMessaging) {
140  ret.push_back("Messaging is disabled. To enable messaging, run the wallet without -disablemessaging or remove disablemessaging from your raven.conf");
141  return ret;
142  }
143 
146  ret.push_back("Messaging channel database and cache are having problems (a wallet restart might fix this issue)");
147  return ret;
148  }
149 
150  std::set<std::string> setChannels;
151 
153 
154  LogPrintf("%s: Checking caches removeSize:%u, addSize:%u\n", __func__, setDirtyChannelsRemove.size(), setDirtyChannelsAdd.size());
155 
156  for (auto name : setDirtyChannelsRemove) {
157  setChannels.erase(name);
158  }
159 
160  for (auto name : setDirtyChannelsAdd) {
161  setChannels.insert(name);
162  }
163 
164  UniValue channels(UniValue::VARR);
165 
166  for (auto name : setChannels) {
167  channels.push_back(name);
168  }
169 
170  return channels;
171 }
172 
174  if (request.fHelp || request.params.size() != 1)
175  throw std::runtime_error(
176  "subscribetochannel \n"
178  "\nSubscribe to a certain message channel\n"
179 
180  "\nArguments:\n"
181  "1. \"channel_name\" (string, required) The channel name to subscribe to, it must end with '!' or have an '~' in the name\n"
182 
183  "\nResult:[\n"
184  "\n]\n"
185  "\nExamples:\n"
186  + HelpExampleCli("subscribetochannel", "\"ASSET_NAME!\"")
187  + HelpExampleRpc("subscribetochannel", "\"ASSET_NAME!\"")
188  );
189 
190  if (!fMessaging) {
191  throw JSONRPCError(RPC_DATABASE_ERROR, "Messaging is disabled. To enable messaging, run the wallet without -disablemessaging or remove disablemessaging from your raven.conf");
192  }
193 
195  throw JSONRPCError(RPC_DATABASE_ERROR, "Message database isn't setup");
196  }
197 
198  std::string channel_name = request.params[0].get_str();
199 
200  AssetType type;
201  if (!IsAssetNameValid(channel_name, type))
202  throw JSONRPCError(
203  RPC_INVALID_PARAMETER, "Channel Name is not valid.");
204 
205  // if the given asset name is a root of sub asset, subscribe to that assets owner token
206  if (type == AssetType::ROOT || type == AssetType::SUB) {
207  channel_name += "!";
208  if (!IsAssetNameValid(channel_name, type))
209  throw JSONRPCError(
210  RPC_INVALID_PARAMETER, "Channel Name is not valid.");
211  }
212 
213  if (type != AssetType::OWNER && type != AssetType::MSGCHANNEL)
214  throw JSONRPCError(
215  RPC_INVALID_PARAMETER, "Channel Name must be a owner asset, or a message channel asset e.g OWNER!, MSG_CHANNEL~123.");
216 
217  AddChannel(channel_name);
218 
219  return "Subscribed to channel: " + channel_name;
220 }
221 
222 
224  if (request.fHelp || !AreMessagingDeployed() || request.params.size() != 1)
225  throw std::runtime_error(
226  "unsubscribefromchannel \n"
228  "\nUnsubscribe from a certain message channel\n"
229 
230  "\nArguments:\n"
231  "1. \"channel_name\" (string, required) The channel name to unsubscribe from, must end with '!' or have an '~' in the name\n"
232 
233  "\nResult:[\n"
234  "\n]\n"
235  "\nExamples:\n"
236  + HelpExampleCli("unsubscribefromchannel", "\"ASSET_NAME!\"")
237  + HelpExampleRpc("unsubscribefromchannel", "\"ASSET_NAME!\"")
238  );
239 
240  if (!fMessaging) {
241  throw JSONRPCError(RPC_DATABASE_ERROR, "Messaging is disabled. To enable messaging, run the wallet without -disablemessaging or remove disablemessaging from your raven.conf");
242  }
243 
245  throw JSONRPCError(RPC_DATABASE_ERROR, "Message database isn't setup");
246  }
247 
248  std::string channel_name = request.params[0].get_str();
249 
250  AssetType type;
251  if (!IsAssetNameValid(channel_name, type))
252  throw JSONRPCError(
253  RPC_INVALID_PARAMETER, "Channel Name is not valid.");
254 
255  // if the given asset name is a root of sub asset, subscribe to that assets owner token
256  if (type == AssetType::ROOT || type == AssetType::SUB) {
257  channel_name += "!";
258 
259  if (!IsAssetNameValid(channel_name, type))
260  throw JSONRPCError(
261  RPC_INVALID_PARAMETER, "Channel Name is not valid.");
262  }
263 
264  if (type != AssetType::OWNER && type != AssetType::MSGCHANNEL)
265  throw JSONRPCError(
266  RPC_INVALID_PARAMETER, "Channel Name must be a owner asset, or a message channel asset e.g OWNER!, MSG_CHANNEL~123.");
267 
268  RemoveChannel(channel_name);
269 
270  return "Unsubscribed from channel: " + channel_name;
271 }
272 
274  if (request.fHelp || !AreMessagingDeployed() || request.params.size() != 0)
275  throw std::runtime_error(
276  "clearmessages \n"
278  "\nDelete current database of messages\n"
279 
280  "\nResult:[\n"
281  "\n]\n"
282  "\nExamples:\n"
283  + HelpExampleCli("clearmessages", "")
284  + HelpExampleRpc("clearmessages", "")
285  );
286 
287  if (!fMessaging) {
288  throw JSONRPCError(RPC_DATABASE_ERROR, "Messaging is disabled. To enable messaging, run the wallet without -disablemessaging or remove disablemessaging from your raven.conf");
289  }
290 
291  if (!pMessagesCache || !pmessagedb) {
292  throw JSONRPCError(RPC_DATABASE_ERROR, "Message database isn't setup");
293  }
294 
295  int count = 0;
296  count += mapDirtyMessagesAdd.size();
297 
298  pMessagesCache->Clear();
299  setDirtyMessagesRemove.clear();
300  mapDirtyMessagesAdd.clear();
301  mapDirtyMessagesOrphaned.clear();
303 
304  return "Erased " + std::to_string(count) + " Messages from the database and cache";
305 }
306 
308  if (request.fHelp || !AreMessagingDeployed() || request.params.size() < 2 || request.params.size() > 3)
309  throw std::runtime_error(
310  "sendmessage \"channel_name\" \"ipfs_hash\" (expire_time)\n"
312  "\nCreates and broadcasts a message transaction to the network for a channel this wallet owns"
313 
314  "\nArguments:\n"
315  "1. \"channel_name\" (string, required) Name of the channel that you want to send a message with (message channel, administrator asset), if a non administrator asset name is given, the administrator '!' will be added to it\n"
316  "2. \"ipfs_hash\" (string, required) The IPFS hash of the message\n"
317  "3. \"expire_time\" (numeric, optional) UTC timestamp of when the message expires\n"
318 
319  "\nResult:[\n"
320  "txid\n"
321  "]\n"
322 
323  "\nExamples:\n"
324  + HelpExampleCli("sendmessage", "\"ASSET_NAME!\" \"QmTqu3Lk3gmTsQVtjU7rYYM37EAW4xNmbuEAp2Mjr4AV7E\" 15863654")
325  + HelpExampleCli("sendmessage", "\"ASSET_NAME!\" \"QmTqu3Lk3gmTsQVtjU7rYYM37EAW4xNmbuEAp2Mjr4AV7E\" 15863654")
326  );
327 
328  CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
329  if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
330  return NullUniValue;
331  }
332 
333  ObserveSafeMode();
334  LOCK2(cs_main, pwallet->cs_wallet);
335 
336  EnsureWalletIsUnlocked(pwallet);
337 
338  std::string asset_name = request.params[0].get_str();
339  std::string ipfs_hash = request.params[1].get_str();
340 
341  if (ipfs_hash.length() != 46)
342  throw JSONRPCError(RPC_INVALID_PARAMS, std::string("Invalid IPFS hash (must be 46 characters)"));
343  if (ipfs_hash.substr(0, 2) != "Qm")
344  throw JSONRPCError(RPC_INVALID_PARAMS, std::string("Invalid IPFS hash (doesn't start with 'Qm')"));
345 
346  int64_t expire_time = 0;
347  if (request.params.size() > 2) {
348  expire_time = request.params[2].get_int64();
349  }
350 
351  AssetType type;
352  std::string strNameError;
353  if (!IsAssetNameValid(asset_name, type, strNameError))
354  throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid asset_name: ") + strNameError);
355 
356  if (type != AssetType::MSGCHANNEL && type != AssetType::OWNER && type != AssetType::ROOT && type != AssetType::SUB && type != AssetType::RESTRICTED) {
357  throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid asset_name: Only message channels, root, sub, restricted, and owner assets are allowed"));
358  }
359 
360  if (type == AssetType::ROOT || type == AssetType::SUB || type == AssetType::RESTRICTED)
361  asset_name += OWNER_TAG;
362 
363  std::pair<int, std::string> error;
364  std::vector< std::pair<CAssetTransfer, std::string> >vTransfers;
365 
366  std::map<std::string, std::vector<COutput> > mapAssetCoins;
367  pwallet->AvailableAssets(mapAssetCoins);
368 
369  if (!mapAssetCoins.count(asset_name)) {
370  throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Wallet doesn't own the asset_name: " + asset_name));
371  }
372 
373  // Get the address that the coin resides in, because to send a valid message. You need to send it to the same address that it currently resides in.
374  CTxDestination dest;
375  ExtractDestination(mapAssetCoins.at(asset_name)[0].tx->tx->vout[mapAssetCoins.at(asset_name)[0].i].scriptPubKey, dest);
376  std::string address = EncodeDestination(dest);
377 
378  vTransfers.emplace_back(std::make_pair(CAssetTransfer(asset_name, OWNER_ASSET_AMOUNT, DecodeAssetData(ipfs_hash), expire_time), address));
379  CReserveKey reservekey(pwallet);
380  CWalletTx transaction;
381  CAmount nRequiredFee;
382 
383  CCoinControl ctrl;
384 
385  // Create the Transaction
386  if (!CreateTransferAssetTransaction(pwallet, ctrl, vTransfers, "", error, transaction, reservekey, nRequiredFee))
387  throw JSONRPCError(error.first, error.second);
388 
389  // Send the Transaction to the network
390  std::string txid;
391  if (!SendAssetTransaction(pwallet, transaction, reservekey, error, txid))
392  throw JSONRPCError(error.first, error.second);
393 
394  // Display the transaction id
395  UniValue result(UniValue::VARR);
396  result.push_back(txid);
397  return result;
398 }
399 
400 static const CRPCCommand commands[] =
401  { // category name actor (function) argNames
402  // ----------- ------------------------ ----------------------- ----------
403  { "messages", "viewallmessages", &viewallmessages, {}},
404  { "messages", "viewallmessagechannels", &viewallmessagechannels, {}},
405  { "messages", "subscribetochannel", &subscribetochannel, {"channel_name"}},
406  { "messages", "unsubscribefromchannel", &unsubscribefromchannel, {"channel_name"}},
407  { "messages", "sendmessage", &sendmessage, {"channel", "ipfs_hash", "expire_time"}},
408  { "messages", "clearmessages", &clearmessages, {}},
409  };
410 
412 {
413  for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
414  t.appendCommand(commands[vcidx].name, &commands[vcidx]);
415 }
std::set< COutPoint > setDirtyMessagesRemove
Definition: messages.cpp:15
#define OWNER_ASSET_AMOUNT
Definition: assets.h:35
void AddChannel(const std::string &name)
Definition: messages.cpp:130
boost::variant< CNoDestination, CKeyID, CScriptID > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:89
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
Parse a standard scriptPubKey for the destination address.
Definition: standard.cpp:210
Raven RPC command dispatcher.
Definition: server.h:143
bool LoadMessages(std::set< CMessage > &setMessages)
Definition: messagedb.cpp:32
CCriticalSection cs_wallet
Definition: wallet.h:751
bool CreateTransferAssetTransaction(CWallet *pwallet, const CCoinControl &coinControl, const std::vector< std::pair< CAssetTransfer, std::string > >vTransfers, const std::string &changeAddress, std::pair< int, std::string > &error, CWalletTx &wtxNew, CReserveKey &reservekey, CAmount &nFeeRequired, std::vector< std::pair< CNullAssetTxData, std::string > > *nullAssetTxData, std::vector< CNullAssetTxData > *nullGlobalRestrictionData)
Create a transfer asset transaction.
Definition: assets.cpp:4094
bool EnsureWalletIsAvailable(CWallet *const pwallet, bool avoidException)
Definition: rpcwallet.cpp:63
std::string DateTimeStrFormat(const char *pszFormat, int64_t nTime)
Definition: utiltime.cpp:79
CMessageChannelDB * pmessagechanneldb
Global variable that points to the message channel database (protected by cs_main) ...
Definition: validation.cpp:233
CCriticalSection cs_main
Global state.
Definition: validation.cpp:72
const std::string & get_str() const
std::string HelpExampleRpc(const std::string &methodname, const std::string &args)
Definition: server.cpp:527
UniValue clearmessages(const JSONRPCRequest &request)
Definition: messages.cpp:273
int64_t get_int64() const
UniValue viewallmessagechannels(const JSONRPCRequest &request)
Definition: messages.cpp:123
bool EraseAllMessages(int &count)
Definition: messagedb.cpp:58
bool appendCommand(const std::string &name, const CRPCCommand *pcmd)
Appends a CRPCCommand to the dispatch table.
Definition: server.cpp:299
MessageStatus status
Definition: messages.h:76
CLRUCache< std::string, int > * pMessageSubscribedChannelsCache
Global variable that points to the subscribed channel LRU Cache (protected by cs_main) ...
Definition: validation.cpp:230
Coin Control Features.
Definition: coincontrol.h:17
Invalid, missing or duplicate parameter.
Definition: protocol.h:54
int64_t CAmount
Amount in corbies (Can be negative)
Definition: amount.h:13
CLRUCache< std::string, CMessage > * pMessagesCache
Global variable that points to the subscribed channel LRU Cache (protected by cs_main) ...
Definition: validation.cpp:229
bool AreMessagingDeployed()
std::set< std::string > setDirtyChannelsRemove
Definition: messages.cpp:20
#define LOCK2(cs1, cs2)
Definition: sync.h:177
std::string name
Definition: server.h:135
bool IsAssetNameValid(const std::string &name, AssetType &assetType, std::string &error)
Definition: assets.cpp:207
std::string DecodeAssetData(std::string encoded)
Decode and Encode IPFS hashes, or OIP hashes.
Definition: assets.cpp:3699
bool push_back(const UniValue &val)
Definition: univalue.cpp:110
#define LogPrintf(...)
Definition: util.h:149
COutPoint out
Definition: messages.h:71
UniValue params
Definition: server.h:45
#define OWNER_TAG
Definition: assets.h:32
CMessageDB * pmessagedb
Global variable that points to the messages database (protected by cs_main)
Definition: validation.cpp:232
std::map< COutPoint, CMessage > mapDirtyMessagesOrphaned
Definition: messages.cpp:17
std::string MessageActivationWarning()
Definition: messages.cpp:36
UniValue sendmessage(const JSONRPCRequest &request)
Definition: messages.cpp:307
bool LoadMyMessageChannels(std::set< std::string > &setChannels)
Definition: messagedb.cpp:141
std::string HelpExampleCli(const std::string &methodname, const std::string &args)
Definition: server.cpp:522
void RegisterMessageRPCCommands(CRPCTable &t)
Register message RPC commands.
Definition: messages.cpp:411
bool SendAssetTransaction(CWallet *pwallet, CWalletTx &transaction, CReserveKey &reserveKey, std::pair< int, std::string > &error, std::string &txid)
Send any type of asset transaction to the network.
Definition: assets.cpp:4243
std::map< COutPoint, CMessage > mapDirtyMessagesAdd
Definition: messages.cpp:16
A transaction with a bunch of additional info that only the owner cares about.
Definition: wallet.h:285
AssetType
Definition: assettypes.h:21
Database error.
Definition: protocol.h:55
bool fHelp
Definition: server.h:46
void AvailableAssets(std::map< std::string, std::vector< COutput > > &mapAssetCoins, bool fOnlySafe=true, const CCoinControl *coinControl=nullptr, const CAmount &nMinimumAmount=1, const CAmount &nMaximumAmount=MAX_MONEY, const CAmount &nMinimumSumAmount=MAX_MONEY, const uint64_t &nMaximumCount=0, const int &nMinDepth=0, const int &nMaxDepth=9999999) const
Helper function that calls AvailableCoinsAll, used for transfering assets.
Definition: wallet.cpp:2165
#define ARRAYLEN(array)
std::string EncodeDestination(const CTxDestination &dest)
Definition: base58.cpp:326
A key allocated from the key pool.
Definition: wallet.h:1193
std::string MessageStatusToString(MessageStatus status)
Definition: messages.cpp:39
bool fMessaging
Definition: validation.cpp:82
UniValue viewallmessages(const JSONRPCRequest &request)
Definition: messages.cpp:41
UniValue subscribetochannel(const JSONRPCRequest &request)
Definition: messages.cpp:173
A CWallet is an extension of a keystore, which also maintains a set of transactions and balances...
Definition: wallet.h:673
const UniValue NullUniValue
Definition: univalue.cpp:15
CWallet * GetWalletForJSONRPCRequest(const JSONRPCRequest &request)
Figures out what wallet, if any, to use for a JSONRPCRequest.
Definition: rpcwallet.cpp:40
bool error(const char *fmt, const Args &... args)
Definition: util.h:168
void ObserveSafeMode()
Definition: safemode.cpp:7
std::set< std::string > setDirtyChannelsAdd
Definition: messages.cpp:19
UniValue JSONRPCError(int code, const std::string &message)
Definition: protocol.cpp:55
size_t size() const
Definition: univalue.h:70
void RemoveChannel(const std::string &name)
Definition: messages.cpp:140
std::string EncodeAssetData(std::string decoded)
Definition: assets.cpp:3716
void EnsureWalletIsUnlocked(CWallet *const pwallet)
Definition: rpcwallet.cpp:80
UniValue unsubscribefromchannel(const JSONRPCRequest &request)
Definition: messages.cpp:223