Master Core  v0.0.9 - 2abfd2849db8ba7a83957c64eb976b406713c123
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
mastercore_rpc.cpp
Go to the documentation of this file.
1 // RPC calls
2 
3 #include "rpcserver.h"
4 #include "util.h"
5 #include "init.h"
6 #include "wallet.h"
7 #include "clientversion.h"
8 
9 #include <stdint.h>
10 #include <string.h>
11 #include <map>
12 
13 #include <fstream>
14 #include <vector>
15 #include <string>
16 
17 #include <boost/algorithm/string.hpp>
18 #include <boost/algorithm/string/find.hpp>
19 #include <boost/lexical_cast.hpp>
20 #include <boost/format.hpp>
21 
22 #include "json/json_spirit_utils.h"
23 #include "json/json_spirit_value.h"
24 
25 #include "leveldb/db.h"
26 #include "leveldb/write_batch.h"
27 
28 #include <openssl/sha.h>
29 
30 using namespace std;
31 using namespace boost;
32 using namespace json_spirit;
33 
34 #include "mastercore.h"
35 
36 using namespace mastercore;
37 
38 #include "mastercore_convert.h"
39 #include "mastercore_dex.h"
41 #include "mastercore_tx.h"
42 #include "mastercore_sp.h"
43 #include "mastercore_rpc.h"
44 #include "mastercore_errors.h"
45 #include "mastercore_version.h"
46 
47 void PropertyToJSON(const CMPSPInfo::Entry& sProperty, Object& property_obj)
48 {
49  property_obj.push_back(Pair("name", sProperty.name));
50  property_obj.push_back(Pair("category", sProperty.category));
51  property_obj.push_back(Pair("subcategory", sProperty.subcategory));
52  property_obj.push_back(Pair("data", sProperty.data));
53  property_obj.push_back(Pair("url", sProperty.url));
54  property_obj.push_back(Pair("divisible", sProperty.isDivisible()));
55 }
56 
57 void MetaDexObjectToJSON(const CMPMetaDEx& obj, Object& metadex_obj)
58 {
59  CMPSPInfo::Entry spProperty;
60  CMPSPInfo::Entry spDesProperty;
61 
62  _my_sps->getSP(obj.getProperty(), spProperty);
63  _my_sps->getSP(obj.getDesProperty(), spDesProperty);
64 
65  std::string strAmountOriginal = FormatMP(obj.getProperty(), obj.getAmountForSale());
66  std::string strAmountDesired = FormatMP(obj.getDesProperty(), obj.getAmountDesired());
67  std::string strEcosystem = isTestEcosystemProperty(obj.getProperty()) ? "Test" : "Main";
68 
69  // add data to JSON object
70  metadex_obj.push_back(Pair("address", obj.getAddr()));
71  metadex_obj.push_back(Pair("txid", obj.getHash().GetHex()));
72  metadex_obj.push_back(Pair("ecosystem", strEcosystem));
73  metadex_obj.push_back(Pair("property_owned", (uint64_t) obj.getProperty()));
74  metadex_obj.push_back(Pair("property_desired", (uint64_t) obj.getDesProperty()));
75  metadex_obj.push_back(Pair("property_owned_divisible", spProperty.isDivisible()));
76  metadex_obj.push_back(Pair("property_desired_divisible", spDesProperty.isDivisible()));
77  metadex_obj.push_back(Pair("amount_original", strAmountOriginal));
78  metadex_obj.push_back(Pair("amount_desired", strAmountDesired));
79  metadex_obj.push_back(Pair("action", (int) obj.getAction()));
80  metadex_obj.push_back(Pair("block", obj.getBlock()));
81  metadex_obj.push_back(Pair("blocktime", obj.getBlockTime()));
82 }
83 
84 void MetaDexObjectsToJSON(std::vector<CMPMetaDEx>& vMetaDexObjs, Array& response)
85 {
86  MetaDEx_compare compareByHeight;
87 
88  // sorts metadex objects based on block height and position in block
89  std::sort (vMetaDexObjs.begin(), vMetaDexObjs.end(), compareByHeight);
90 
91  for (std::vector<CMPMetaDEx>::const_iterator it = vMetaDexObjs.begin(); it != vMetaDexObjs.end(); ++it) {
92  Object metadex_obj;
93  MetaDexObjectToJSON(*it, metadex_obj);
94 
95  response.push_back(metadex_obj);
96  }
97 }
98 
99 void BalanceToJSON(const std::string& address, uint32_t property, Object& balance_obj)
100 {
101  // confirmed balance minus unconfirmed, spent amounts
102  int64_t nAvailable = getUserAvailableMPbalance(address, property);
103 
104  int64_t nReserved = 0;
105  nReserved += getMPbalance(address, property, ACCEPT_RESERVE);
106  nReserved += getMPbalance(address, property, METADEX_RESERVE);
107  nReserved += getMPbalance(address, property, SELLOFFER_RESERVE);
108 
109  balance_obj.push_back(Pair("balance", FormatMP(property, nAvailable)));
110  balance_obj.push_back(Pair("reserved", FormatMP(property, nReserved)));
111 }
112 
113 // display the tally map & the offer/accept list(s)
114 Value mscrpc(const Array& params, bool fHelp)
115 {
116 int extra = 0;
117 int extra2 = 0, extra3 = 0;
118 
119  if (fHelp || params.size() > 3)
120  throw runtime_error(
121  "mscrpc\n"
122  "\nReturns the number of blocks in the longest block chain.\n"
123  "\nResult:\n"
124  "n (numeric) The current block count\n"
125  "\nExamples:\n"
126  + HelpExampleCli("mscrpc", "")
127  + HelpExampleRpc("mscrpc", "")
128  );
129 
130  if (0 < params.size()) extra = atoi(params[0].get_str());
131  if (1 < params.size()) extra2 = atoi(params[1].get_str());
132  if (2 < params.size()) extra3 = atoi(params[2].get_str());
133 
134  printf("%s(extra=%d,extra2=%d,extra3=%d)\n", __FUNCTION__, extra, extra2, extra3);
135 
136  bool bDivisible = isPropertyDivisible(extra2);
137 
138  // various extra tests
139  switch (extra)
140  {
141  case 0: // the old output
142  {
143  uint64_t total = 0;
144 
145  // display all balances
146  for(map<string, CMPTally>::iterator my_it = mp_tally_map.begin(); my_it != mp_tally_map.end(); ++my_it)
147  {
148  // my_it->first = key
149  // my_it->second = value
150 
151  printf("%34s => ", (my_it->first).c_str());
152  total += (my_it->second).print(extra2, bDivisible);
153  }
154 
155  printf("total for property %d = %X is %s\n", extra2, extra2, FormatDivisibleMP(total).c_str());
156  }
157  break;
158 
159  case 1:
160  // display the whole CMPTxList (leveldb)
161  p_txlistdb->printAll();
163  break;
164 
165  case 2:
166  // display smart properties
167  _my_sps->printAll();
168  break;
169 
170  case 3:
171  unsigned int id;
172  // for each address display all currencies it holds
173  for(map<string, CMPTally>::iterator my_it = mp_tally_map.begin(); my_it != mp_tally_map.end(); ++my_it)
174  {
175  // my_it->first = key
176  // my_it->second = value
177 
178  printf("%34s => ", (my_it->first).c_str());
179  (my_it->second).print(extra2);
180 
181  (my_it->second).init();
182  while (0 != (id = (my_it->second).next()))
183  {
184  printf("Id: %u=0x%X ", id, id);
185  }
186  printf("\n");
187  }
188  break;
189 
190  case 4:
191  for(CrowdMap::const_iterator it = my_crowds.begin(); it != my_crowds.end(); ++it)
192  {
193  (it->second).print(it->first);
194  }
195  break;
196 
197  case 5:
198  printf("isMPinBlockRange(%d,%d)=%s\n", extra2, extra3, isMPinBlockRange(extra2, extra3, false) ? "YES":"NO");
199  break;
200 
201  case 6:
202  MetaDEx_debug_print(true, true);
203  break;
204 
205  case 7:
206  // display the whole CMPTradeList (leveldb)
209  break;
210 
211  case 8:
212  // display the STO receive list
215  break;
216  }
217  return GetHeight();
218 }
219 
220 // display an MP balance via RPC
221 Value getbalance_MP(const Array& params, bool fHelp)
222 {
223  if (fHelp || params.size() != 2)
224  throw runtime_error(
225  "getbalance_MP \"address\" propertyid\n"
226  "\nReturns the Master Protocol balance for a given address and currency/property.\n"
227  "\nResult:\n"
228  "n (numeric) The applicable balance for address:currency/propertyID pair\n"
229  "\nExamples:\n"
230  ">mastercored getbalance_MP 1EXoDusjGwvnjZUyKkxZ4UHEf77z6A5S4P 1\n"
231  );
232  std::string address = params[0].get_str();
233  int64_t tmpPropertyId = params[1].get_int64();
234  if ((1 > tmpPropertyId) || (4294967295 < tmpPropertyId)) // not safe to do conversion
235  throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid property identifier");
236 
237  unsigned int propertyId = int(tmpPropertyId);
238  CMPSPInfo::Entry sp;
239  if (false == _my_sps->getSP(propertyId, sp)) {
240  throw JSONRPCError(RPC_INVALID_PARAMETER, "Property identifier does not exist");
241  }
242 
243  Object balance_obj;
244  BalanceToJSON(address, propertyId, balance_obj);
245 
246  return balance_obj;
247 }
248 
249 // send a MP transaction via RPC - simple send
250 Value send_MP(const Array& params, bool fHelp)
251 {
252 if (fHelp || params.size() < 4 || params.size() > 6)
253  throw runtime_error(
254  "send_MP \"fromaddress\" \"toaddress\" propertyid \"amount\" ( \"redeemaddress\" \"referenceamount\" )\n"
255  "\nCreates and broadcasts a simple send for a given amount and currency/property ID.\n"
256  "\nParameters:\n"
257  "FromAddress : the address to send from\n"
258  "ToAddress : the address to send to\n"
259  "PropertyID : the id of the smart property to send\n"
260  "Amount : the amount to send\n"
261  "RedeemAddress : (optional) the address that can redeem the bitcoin outputs. Defaults to FromAddress\n"
262  "ReferenceAmount:(optional)\n"
263  "Result:\n"
264  "txid (string) The transaction ID of the sent transaction\n"
265  "\nExamples:\n"
266  ">mastercored send_MP 1FromAddress 1ToAddress PropertyID Amount 1RedeemAddress\n"
267  );
268 
269  std::string FromAddress = (params[0].get_str());
270  std::string ToAddress = (params[1].get_str());
271  std::string RedeemAddress = (params.size() > 4) ? (params[4].get_str()): "";
272 
273  int64_t tmpPropertyId = params[2].get_int64();
274  if ((1 > tmpPropertyId) || (4294967295 < tmpPropertyId)) // not safe to do conversion
275  throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid property identifier");
276  unsigned int propertyId = int(tmpPropertyId);
277 
278  CMPSPInfo::Entry sp;
279  if (false == _my_sps->getSP(propertyId, sp)) {
280  throw JSONRPCError(RPC_INVALID_PARAMETER, "Property identifier does not exist");
281  }
282 
283  bool divisible = sp.isDivisible();
284 
285  string strAmount = params[3].get_str();
286  int64_t Amount = 0, additional = 0;
287  Amount = StrToInt64(strAmount, divisible);
288 
289  if (0 >= Amount)
290  throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
291 
292  std::string strAdditional = (params.size() > 5) ? (params[5].get_str()): "0";
293  additional = StrToInt64(strAdditional, true);
294 
295  int n = params.size();
296  printf("#: %d, additional= %ld\n", n, additional);
297 
298  if ((0.01 * COIN) < additional)
299  throw JSONRPCError(RPC_TYPE_ERROR, "Invalid reference amount");
300 
301  //some sanity checking of the data supplied?
302  int code = 0;
303  uint256 newTX = send_INTERNAL_1packet(FromAddress, ToAddress, RedeemAddress, propertyId, Amount, 0, 0, MSC_TYPE_SIMPLE_SEND, additional, &code);
304 
305  if (0 != code) throw JSONRPCError(code, error_str(code));
306 
307  //we need to do better than just returning a string of 0000000 here if we can't send the TX
308  return newTX.GetHex();
309 }
310 
311 // send a MP transaction via RPC - simple send
312 Value sendtoowners_MP(const Array& params, bool fHelp)
313 {
314 if (fHelp || params.size() < 3 || params.size() > 4)
315  throw runtime_error(
316  "sendtoowners_MP \"fromaddress\" propertyid \"amount\" ( \"redeemaddress\" )\n"
317  "\nCreates and broadcasts a send-to-owners transaction for a given amount and currency/property ID.\n"
318  "\nParameters:\n"
319  "FromAddress : the address to send from\n"
320  "PropertyID : the id of the smart property to send\n"
321  "Amount (string): the amount to send\n"
322  "RedeemAddress : (optional) the address that can redeem the bitcoin outputs. Defaults to FromAddress\n"
323  "\nResult:\n"
324  "txid (string) The transaction ID of the sent transaction\n"
325  "\nExamples:\n"
326  ">mastercored send_MP 1FromAddress PropertyID Amount 1RedeemAddress\n"
327  );
328 
329  std::string FromAddress = (params[0].get_str());
330  std::string RedeemAddress = (params.size() > 3) ? (params[3].get_str()): "";
331 
332  int64_t tmpPropertyId = params[1].get_int64();
333  if ((1 > tmpPropertyId) || (4294967295 < tmpPropertyId)) // not safe to do conversion
334  throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid property identifier");
335 
336  unsigned int propertyId = int(tmpPropertyId);
337 
338  CMPSPInfo::Entry sp;
339  if (false == _my_sps->getSP(propertyId, sp)) {
340  throw JSONRPCError(RPC_INVALID_PARAMETER, "Property identifier does not exist");
341  }
342 
343  bool divisible = sp.isDivisible();
344 
345 // printf("%s(), params3='%s' line %d, file: %s\n", __FUNCTION__, params[3].get_str().c_str(), __LINE__, __FILE__);
346 
347  string strAmount = params[2].get_str();
348  int64_t Amount = 0;
349  Amount = StrToInt64(strAmount, divisible);
350 
351  if (0 >= Amount)
352  throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
353 
354  // printf("%s() %40.25lf, %lu, line %d, file: %s\n", __FUNCTION__, tmpAmount, Amount, __LINE__, __FILE__);
355 
356  //some sanity checking of the data supplied?
357  int code = 0;
358  uint256 newTX = send_INTERNAL_1packet(FromAddress, "", RedeemAddress, propertyId, Amount, 0, 0, MSC_TYPE_SEND_TO_OWNERS, 0, &code);
359 
360  if (0 != code)
361  throw JSONRPCError(code, error_str(code));
362 
363  //we need to do better than just returning a string of 0000000 here if we can't send the TX
364  return newTX.GetHex();
365 }
366 
367 Value sendrawtx_MP(const Array& params, bool fHelp)
368 {
369 if (fHelp || params.size() < 2 || params.size() > 5)
370  throw runtime_error(
371  "sendrawtx_MP \"fromaddress\" \"hexstring\" ( \"toaddress\" \"redeemaddress\" \"referenceamount\" )\n"
372  "\nCreates and broadcasts a raw Master protocol transaction.\n"
373  "\nParameters:\n"
374  "FromAddress : the address to send from\n"
375  "RawTX : the hex-encoded raw transaction\n"
376  "ToAddress : the address to send to. This should be empty: (\"\") for transaction\n"
377  " types that do not use a reference/to address\n"
378  "RedeemAddress : (optional) the address that can redeem the bitcoin outputs. Defaults to FromAddress\n"
379  "ReferenceAmount:(optional)\n"
380  "\nResult:\n"
381  "txid (string) The transaction ID of the sent transaction\n"
382  "\nExamples:\n"
383  ">mastercored sendrawtx_MP 1FromAddress <tx bytes hex> 1ToAddress 1RedeemAddress\n"
384  );
385 
386  std::string fromAddress = (params[0].get_str());
387  std::string hexTransaction = (params[1].get_str());
388  std::string toAddress = (params.size() > 2) ? (params[2].get_str()): "";
389  std::string redeemAddress = (params.size() > 3) ? (params[3].get_str()): "";
390 
391  int64_t referenceAmount = 0;
392 
393  if (params.size() > 4)
394  referenceAmount = StrToInt64(params[4].get_str(), true);
395 
396  //some sanity checking of the data supplied?
397  uint256 newTX;
398  vector<unsigned char> data = ParseHex(hexTransaction);
399  int rc = ClassB_send(fromAddress, toAddress, redeemAddress, data, newTX, referenceAmount);
400 
401  if (0 != rc)
402  throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("error code= %i", rc));
403 
404  //we need to do better than just returning a string of 0000000 here if we can't send the TX
405  return newTX.GetHex();
406 }
407 
408 Value getallbalancesforid_MP(const Array& params, bool fHelp)
409 {
410  if (fHelp || params.size() != 1)
411  throw runtime_error(
412  "getallbalancesforid_MP propertyid\n"
413  "\nGet a list of balances for a given currency or property identifier.\n"
414  "\nArguments:\n"
415  "1. propertyid (int, required) The property identifier\n"
416  "\nResult:\n"
417  "{\n"
418  " \"address\" : \"1Address\", (string) The address\n"
419  " \"balance\" : \"x.xxx\", (string) The available balance of the address\n"
420  " \"reserved\" : \"x.xxx\", (string) The amount reserved by sell offers and accepts\n"
421  "}\n"
422 
423  "\nbExamples\n"
424  + HelpExampleCli("getallbalancesforid_MP", "1")
425  + HelpExampleRpc("getallbalancesforid_MP", "1")
426  );
427 
428  int64_t tmpPropertyId = params[0].get_int64();
429  if ((1 > tmpPropertyId) || (4294967295 < tmpPropertyId)) // not safe to do conversion
430  throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid property identifier");
431 
432  unsigned int propertyId = int(tmpPropertyId);
433  CMPSPInfo::Entry sp;
434  if (false == _my_sps->getSP(propertyId, sp)) {
435  throw JSONRPCError(RPC_INVALID_PARAMETER, "Property identifier does not exist");
436  }
437 
438  Array response;
439 
440  for(map<string, CMPTally>::iterator my_it = mp_tally_map.begin(); my_it != mp_tally_map.end(); ++my_it)
441  {
442  unsigned int id;
443  bool includeAddress=false;
444  string address = (my_it->first).c_str();
445  (my_it->second).init();
446  while (0 != (id = (my_it->second).next()))
447  {
448  if(id==propertyId) { includeAddress=true; break; }
449  }
450 
451  if (!includeAddress) continue; //ignore this address, has never transacted in this propertyId
452 
453  Object balance_obj;
454  balance_obj.push_back(Pair("address", address));
455  BalanceToJSON(address, propertyId, balance_obj);
456 
457  response.push_back(balance_obj);
458  }
459 return response;
460 }
461 
462 Value getallbalancesforaddress_MP(const Array& params, bool fHelp)
463 {
464  string address;
465 
466  if (fHelp || params.size() != 1)
467  throw runtime_error(
468  "getallbalancesforaddress_MP \"address\"\n"
469  "\nGet a list of all balances for a given address.\n"
470  "\nArguments:\n"
471  "1. address (string, required) The address\n"
472  "\nResult:\n"
473  "{\n"
474  " \"propertyid\" : x, (numeric) the property id\n"
475  " \"balance\" : \"x.xxx\", (string) The available balance of the address\n"
476  " \"reserved\" : \"x.xxx\", (string) The amount reserved by sell offers and accepts\n"
477  "}\n"
478 
479  "\nbExamples\n"
480  + HelpExampleCli("getallbalancesforaddress_MP", "address")
481  + HelpExampleRpc("getallbalancesforaddress_MP", "address")
482  );
483 
484  address = params[0].get_str();
485  if (address.empty())
486  throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid address");
487 
488  Array response;
489 
490  CMPTally *addressTally=getTally(address);
491 
492  if (NULL == addressTally) // addressTally object does not exist
493  throw JSONRPCError(RPC_INVALID_PARAMETER, "Address not found");
494 
495  addressTally->init();
496 
497  uint64_t propertyId; // avoid issues with json spirit at uint32
498  while (0 != (propertyId = addressTally->next()))
499  {
500  Object balance_obj;
501  balance_obj.push_back(Pair("propertyid", propertyId));
502  BalanceToJSON(address, propertyId, balance_obj);
503 
504  response.push_back(balance_obj);
505  }
506 
507  return response;
508 }
509 
510 Value getproperty_MP(const Array& params, bool fHelp)
511 {
512  if (fHelp || params.size() != 1)
513  throw runtime_error(
514  "getproperty_MP propertyid\n"
515  "\nGet details for a property identifier.\n"
516  "\nArguments:\n"
517  "1. propertyid (int, required) The property identifier\n"
518  "\nResult:\n"
519  "{\n"
520  " \"name\" : \"PropertyName\", (string) the property name\n"
521  " \"category\" : \"PropertyCategory\", (string) the property category\n"
522  " \"subcategory\" : \"PropertySubCategory\", (string) the property subcategory\n"
523  " \"data\" : \"PropertyData\", (string) the property data\n"
524  " \"url\" : \"PropertyURL\", (string) the property URL\n"
525  " \"divisible\" : false, (boolean) whether the property is divisible\n"
526  " \"issuer\" : \"1Address\", (string) the property issuer address\n"
527  " \"issueancetype\" : \"Fixed\", (string) the property method of issuance\n"
528  " \"totaltokens\" : x (string) the total number of tokens in existence\n"
529  "}\n"
530 
531  "\nbExamples\n"
532  + HelpExampleCli("getproperty_MP", "3")
533  + HelpExampleRpc("getproperty_MP", "3")
534  );
535 
536  int64_t tmpPropertyId = params[0].get_int64();
537  if ((1 > tmpPropertyId) || (4294967295 < tmpPropertyId)) // not safe to do conversion
538  throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid property identifier");
539 
540  unsigned int propertyId = int(tmpPropertyId);
541  CMPSPInfo::Entry sp;
542  if (false == _my_sps->getSP(propertyId, sp)) {
543  throw JSONRPCError(RPC_INVALID_PARAMETER, "Property identifier does not exist");
544  }
545 
546  int64_t nTotalTokens = getTotalTokens(propertyId);
547  std::string strCreationHash = sp.txid.GetHex();
548  std::string strTotalTokens = FormatMP(propertyId, nTotalTokens);
549 
550  Object response;
551  response.push_back(Pair("propertyid", (uint64_t) propertyId));
552  PropertyToJSON(sp, response); // name, category, subcategory, data, url, divisible
553  response.push_back(Pair("issuer", sp.issuer));
554  response.push_back(Pair("creationtxid", strCreationHash));
555  response.push_back(Pair("fixedissuance", sp.fixed));
556  response.push_back(Pair("totaltokens", strTotalTokens));
557 
558 return response;
559 }
560 
561 Value listproperties_MP(const Array& params, bool fHelp)
562 {
563  if (fHelp)
564  throw runtime_error(
565  "listproperties_MP\n"
566  "\nLists all smart properties.\n"
567  "\nResult:\n"
568  "{\n"
569  " \"name\" : \"PropertyName\", (string) the property name\n"
570  " \"category\" : \"PropertyCategory\", (string) the property category\n"
571  " \"subcategory\" : \"PropertySubCategory\", (string) the property subcategory\n"
572  " \"data\" : \"PropertyData\", (string) the property data\n"
573  " \"url\" : \"PropertyURL\", (string) the property URL\n"
574  " \"divisible\" : false, (boolean) whether the property is divisible\n"
575  "}\n"
576 
577  "\nbExamples\n"
578  + HelpExampleCli("listproperties_MP", "")
579  + HelpExampleRpc("listproperties_MP", "")
580  );
581 
582  Array response;
583 
584  int64_t propertyId;
585  unsigned int nextSPID = _my_sps->peekNextSPID(1);
586  for (propertyId = 1; propertyId<nextSPID; propertyId++)
587  {
588  CMPSPInfo::Entry sp;
589  if (false != _my_sps->getSP(propertyId, sp))
590  {
591  Object property_obj;
592  property_obj.push_back(Pair("propertyid", propertyId));
593  PropertyToJSON(sp, property_obj); // name, category, subcategory, data, url, divisible
594 
595  response.push_back(property_obj);
596  }
597  }
598 
599  unsigned int nextTestSPID = _my_sps->peekNextSPID(2);
600  for (propertyId = TEST_ECO_PROPERTY_1; propertyId<nextTestSPID; propertyId++)
601  {
602  CMPSPInfo::Entry sp;
603  if (false != _my_sps->getSP(propertyId, sp))
604  {
605  Object property_obj;
606  property_obj.push_back(Pair("propertyid", propertyId));
607  PropertyToJSON(sp, property_obj); // name, category, subcategory, data, url, divisible
608 
609  response.push_back(property_obj);
610  }
611  }
612 return response;
613 }
614 
615 Value getcrowdsale_MP(const Array& params, bool fHelp)
616 {
617  if (fHelp || params.size() < 1 )
618  throw runtime_error(
619  "getcrowdsale_MP propertyid\n"
620  "\nGet information about a crowdsale for a property identifier.\n"
621  "\nArguments:\n"
622  "1. propertyid (int, required) The property identifier\n"
623  "\nResult:\n"
624  "{\n"
625  " \"name\" : \"PropertyName\", (string) the property name\n"
626  " \"active\" : false, (boolean) whether the crowdsale is active\n"
627  " \"issuer\" : \"1Address\", (string) the issuer address\n"
628  " \"creationtxid\" : \"txid\", (string) the transaction that created the crowdsale\n"
629  " \"propertyiddesired\" : x, (numeric) the property ID desired\n"
630  " \"tokensperunit\" : x, (numeric) the number of tokens awarded per unit\n"
631  " \"earlybonus\" : x, (numeric) the percentage per week early bonus applied\n"
632  " \"percenttoissuer\" : x, (numeric) the percentage awarded to the issuer\n"
633  " \"starttime\" : xxx, (numeric) the start time of the crowdsale\n"
634  " \"deadline\" : xxx, (numeric) the time the crowdsale will automatically end\n"
635  " \"closedearly\" : false, (boolean) whether the crowdsale was ended early\n"
636  " \"maxtokens\" : false, (boolean) whether the crowdsale was ended early due to maxing the token count\n"
637  " \"endedtime\" : xxx, (numeric) the time the crowdsale ended\n"
638  " \"closetx\" : \"txid\", (string) the transaction that closed the crowdsale\n"
639  "}\n"
640 
641  "\nbExamples\n"
642  + HelpExampleCli("getcrowdsale_MP", "3")
643  + HelpExampleRpc("getcrowdsale_MP", "3")
644  );
645 
646  int64_t tmpPropertyId = params[0].get_int64();
647  if ((1 > tmpPropertyId) || (4294967295 < tmpPropertyId)) // not safe to do conversion
648  throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid property identifier");
649 
650  unsigned int propertyId = int(tmpPropertyId);
651  CMPSPInfo::Entry sp;
652  if (false == _my_sps->getSP(propertyId, sp)) {
653  throw JSONRPCError(RPC_INVALID_PARAMETER, "Property identifier does not exist");
654  }
655 
656  bool showVerbose = false;
657  if (params.size() > 1) showVerbose = params[1].get_bool();
658 
659  bool fixedIssuance = sp.fixed;
660  bool manualIssuance = sp.manual;
661  if (fixedIssuance || manualIssuance) // property was not a variable issuance
662  throw JSONRPCError(RPC_INVALID_PARAMETER, "Property was not created with a crowdsale");
663 
664  uint256 creationHash = sp.txid;
665 
666  CTransaction tx;
667  uint256 hashBlock = 0;
668  if (!GetTransaction(creationHash, tx, hashBlock, true))
669  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
670 
671  if ((0 == hashBlock) || (NULL == mapBlockIndex[hashBlock]))
672  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unconfirmed transactions are not supported");
673 
674  Object response;
675 
676  bool active = false;
677  active = isCrowdsaleActive(propertyId);
678  string propertyName = sp.name;
679  int64_t startTime = mapBlockIndex[hashBlock]->nTime;
680  int64_t deadline = sp.deadline;
681  uint8_t earlyBonus = sp.early_bird;
682  uint8_t percentToIssuer = sp.percentage;
683  bool closeEarly = sp.close_early;
684  bool maxTokens = sp.max_tokens;
685  int64_t timeClosed = sp.timeclosed;
686  string txidClosed = sp.txid_close.GetHex();
687  string issuer = sp.issuer;
688  int64_t amountRaised = 0;
689  int64_t tokensIssued = getTotalTokens(propertyId);
690  int64_t missedTokens = sp.missedTokens;
691  int64_t tokensPerUnit = sp.num_tokens;
692  int64_t propertyIdDesired = sp.property_desired;
693  std::map<std::string, std::vector<uint64_t> > database;
694 
695  if (active)
696  {
697  bool crowdFound = false;
698  for(CrowdMap::const_iterator it = my_crowds.begin(); it != my_crowds.end(); ++it)
699  {
700  CMPCrowd crowd = it->second;
701  int64_t tmpPropertyId = crowd.getPropertyId();
702  if (tmpPropertyId == propertyId)
703  {
704  crowdFound = true;
705  database = crowd.getDatabase();
706  }
707  }
708  if (!crowdFound)
709  throw JSONRPCError(RPC_INTERNAL_ERROR, "Crowdsale is flagged active but cannot be retrieved");
710  }
711  else
712  {
713  database = sp.historicalData;
714  }
715 
716  file_log("SIZE OF DB %lu\n", sp.historicalData.size() );
717  //bool closedEarly = false; //this needs to wait for dead crowdsale persistence
718  //int64_t endedTime = 0; //this needs to wait for dead crowdsale persistence
719 
720  Array participanttxs;
721  std::map<std::string, std::vector<uint64_t> >::const_iterator it;
722  for(it = database.begin(); it != database.end(); it++)
723  {
724  Object participanttx;
725 
726  string txid = it->first; //uint256 txid = it->first;
727  int64_t userTokens = it->second.at(2);
728  int64_t issuerTokens = it->second.at(3);
729  int64_t amountSent = it->second.at(0);
730 
731  amountRaised += amountSent;
732  participanttx.push_back(Pair("txid", txid)); //.GetHex()).c_str();
733  participanttx.push_back(Pair("amountsent", FormatMP(propertyIdDesired, amountSent)));
734  participanttx.push_back(Pair("participanttokens", FormatMP(propertyId, userTokens)));
735  participanttx.push_back(Pair("issuertokens", FormatMP(propertyId, issuerTokens)));
736  participanttxs.push_back(participanttx);
737  }
738 
739  response.push_back(Pair("name", propertyName));
740  response.push_back(Pair("active", active));
741  response.push_back(Pair("issuer", issuer));
742  response.push_back(Pair("propertyiddesired", propertyIdDesired));
743  response.push_back(Pair("tokensperunit", FormatMP(propertyId, tokensPerUnit)));
744  response.push_back(Pair("earlybonus", earlyBonus));
745  response.push_back(Pair("percenttoissuer", percentToIssuer));
746  response.push_back(Pair("starttime", startTime));
747  response.push_back(Pair("deadline", deadline));
748  response.push_back(Pair("amountraised", FormatMP(propertyIdDesired, amountRaised)));
749  response.push_back(Pair("tokensissued", FormatMP(propertyId, tokensIssued)));
750  response.push_back(Pair("addedissuertokens", FormatMP(propertyId, missedTokens)));
751 
752  if (!active) response.push_back(Pair("closedearly", closeEarly));
753  if (!active) response.push_back(Pair("maxtokens", maxTokens));
754  if (closeEarly) response.push_back(Pair("endedtime", timeClosed));
755  if (closeEarly && !maxTokens) response.push_back(Pair("closetx", txidClosed));
756 
757  // array of txids contributing to crowdsale here if needed
758  if (showVerbose)
759  {
760  response.push_back(Pair("participanttransactions", participanttxs));
761  }
762 return response;
763 }
764 
765 Value getactivecrowdsales_MP(const Array& params, bool fHelp)
766 {
767  if (fHelp)
768  throw runtime_error(
769  "getactivecrowdsales_MP\n"
770  "\nGet active crowdsales.\n"
771  "\nResult:\n"
772  "{\n"
773  " \"name\" : \"PropertyName\", (string) the property name\n"
774  " \"issuer\" : \"1Address\", (string) the issuer address\n"
775  " \"creationtxid\" : \"txid\", (string) the transaction that created the crowdsale\n"
776  " \"propertyiddesired\" : x, (numeric) the property ID desired\n"
777  " \"tokensperunit\" : x, (numeric) the number of tokens awarded per unit\n"
778  " \"earlybonus\" : x, (numeric) the percentage per week early bonus applied\n"
779  " \"percenttoissuer\" : x, (numeric) the percentage awarded to the issuer\n"
780  " \"starttime\" : xxx, (numeric) the start time of the crowdsale\n"
781  " \"deadline\" : xxx, (numeric) the time the crowdsale will automatically end\n"
782  "}\n"
783 
784  "\nbExamples\n"
785  + HelpExampleCli("getactivecrowdsales_MP", "")
786  + HelpExampleRpc("getactivecrowdsales_MP", "")
787  );
788 
789  Array response;
790 
791  for(CrowdMap::const_iterator it = my_crowds.begin(); it != my_crowds.end(); ++it)
792  {
793  CMPCrowd crowd = it->second;
794  CMPSPInfo::Entry sp;
795  bool spFound = _my_sps->getSP(crowd.getPropertyId(), sp);
796  int64_t propertyId = crowd.getPropertyId();
797  if (spFound)
798  {
799  Object responseObj;
800 
801  uint256 creationHash = sp.txid;
802 
803  CTransaction tx;
804  uint256 hashBlock = 0;
805  if (!GetTransaction(creationHash, tx, hashBlock, true))
806  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
807 
808  if ((0 == hashBlock) || (NULL == mapBlockIndex[hashBlock]))
809  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unconfirmed transactions are not supported");
810 
811  string propertyName = sp.name;
812  int64_t startTime = mapBlockIndex[hashBlock]->nTime;
813  int64_t deadline = sp.deadline;
814  uint8_t earlyBonus = sp.early_bird;
815  uint8_t percentToIssuer = sp.percentage;
816  string issuer = sp.issuer;
817  int64_t tokensPerUnit = sp.num_tokens;
818  int64_t propertyIdDesired = sp.property_desired;
819 
820  responseObj.push_back(Pair("propertyid", propertyId));
821  responseObj.push_back(Pair("name", propertyName));
822  responseObj.push_back(Pair("issuer", issuer));
823  responseObj.push_back(Pair("propertyiddesired", propertyIdDesired));
824  responseObj.push_back(Pair("tokensperunit", FormatMP(propertyId, tokensPerUnit)));
825  responseObj.push_back(Pair("earlybonus", earlyBonus));
826  responseObj.push_back(Pair("percenttoissuer", percentToIssuer));
827  responseObj.push_back(Pair("starttime", startTime));
828  responseObj.push_back(Pair("deadline", deadline));
829 
830  response.push_back(responseObj);
831  }
832  }
833 
834 return response;
835 }
836 
837 Value getgrants_MP(const Array& params, bool fHelp)
838 {
839  if (fHelp || params.size() != 1 )
840  throw runtime_error(
841  "getgrants_MP propertyid\n"
842  "\nGet information about grants and revokes for a property identifier.\n"
843  "\nArguments:\n"
844  "1. propertyid (int, required) The property identifier\n"
845  "\nResult:\n"
846  "{\n"
847  " \"name\" : \"PropertyName\", (string) the property name\n"
848  " \"issuer\" : \"1Address\", (string) the issuer address\n"
849  " \"creationtxid\" : \"txid\", (string) the transaction that created the crowdsale\n"
850  " \"totaltokens\" : \"1234\", (string) the number of tokens in circulation\n"
851  " \"issuances\" : [\n"
852  " {\n"
853  " \"txid\" : \"txid\", (string) the transaction that granted/revoked tokens\n"
854  " \"grant\" : \"1234\", (string) the number of tokens granted\n"
855  " },\n"
856  " {\n"
857  " \"txid\" : \"txid\", (string) the transaction that granted/revoked tokens\n"
858  " \"revoke\" : \"1234\", (string) the number of tokens revoked\n"
859  " },\n"
860  " ...\n"
861  " ]\n"
862  "}\n"
863 
864  "\nbExamples\n"
865  + HelpExampleCli("getgrants_MP", "3")
866  + HelpExampleRpc("getgrants_MP", "3")
867  );
868 
869  int64_t tmpPropertyId = params[0].get_int64();
870  if ((1 > tmpPropertyId) || (4294967295 < tmpPropertyId)) // not safe to do conversion
871  throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid property identifier");
872 
873  unsigned int propertyId = int(tmpPropertyId);
874  CMPSPInfo::Entry sp;
875  if (false == _my_sps->getSP(propertyId, sp)) {
876  throw JSONRPCError(RPC_INVALID_PARAMETER, "Property identifier does not exist");
877  }
878 
879  bool fixedIssuance = sp.fixed;
880  bool manualIssuance = sp.manual;
881  if (fixedIssuance || !manualIssuance) // property was not a manual issuance
882  throw JSONRPCError(RPC_INVALID_PARAMETER, "Property was not created with a manual issuance");
883 
884  uint256 creationHash = sp.txid;
885 
886  Object response;
887 
888 // bool active = isCrowdsaleActive(propertyId); // UNUSED WARNING
889 // bool divisible = sp.isDivisible(); // UNUSED WARNING
890  string propertyName = sp.name;
891  string issuer = sp.issuer;
892  int64_t totalTokens = getTotalTokens(propertyId);
893 
894  Array issuancetxs;
895  std::map<std::string, std::vector<uint64_t> >::const_iterator it;
896  for(it = sp.historicalData.begin(); it != sp.historicalData.end(); it++)
897  {
898 
899  string txid = it->first; //uint256 txid = it->first;
900  int64_t grantedTokens = it->second.at(0);
901  int64_t revokedTokens = it->second.at(1);
902  if (grantedTokens > 0){
903  Object granttx;
904  granttx.push_back(Pair("txid", txid));
905  granttx.push_back(Pair("grant", FormatMP(propertyId, grantedTokens)));
906  issuancetxs.push_back(granttx);
907  }
908 
909  if (revokedTokens > 0){
910  Object revoketx;
911  revoketx.push_back(Pair("txid", txid));
912  revoketx.push_back(Pair("revoke", FormatMP(propertyId, revokedTokens)));
913  issuancetxs.push_back(revoketx);
914  }
915  }
916 
917  response.push_back(Pair("name", propertyName));
918  response.push_back(Pair("issuer", issuer));
919  response.push_back(Pair("creationtxid", creationHash.GetHex()));
920  response.push_back(Pair("totaltokens", FormatMP(propertyId, totalTokens)));
921  response.push_back(Pair("issuances", issuancetxs));
922  return response;
923 }
924 
925 int check_prop_valid(int64_t tmpPropId, string error, string exist_error ) {
926  CMPSPInfo::Entry sp;
927 
928  if ((1 > tmpPropId) || (4294967295 < tmpPropId))
929  throw JSONRPCError(RPC_INVALID_PARAMETER, error);
930  if (false == _my_sps->getSP(tmpPropId, sp))
931  throw JSONRPCError(RPC_INVALID_PARAMETER, exist_error);
932 
933  return tmpPropId;
934 }
935 
936 #ifndef DISABLE_METADEX
937 Value trade_MP(const Array& params, bool fHelp)
938 {
939  if (fHelp || params.size() < 6)
940  throw runtime_error(
941  "trade_MP \"address\" \"amountforsale\" propertyid1 \"amountdesired\" propertid2 action ( \"redeemaddress\" )\n"
942  "\nPlace or cancel an offer on the distributed token exchange.\n"
943 
944  "\nArguments:\n"
945  "1. address (string, required) address that is making the sale\n"
946  "2. amount_for_sale (string, required) amount owned to put up on sale\n"
947  "3. property_id1 (int, required) property owned to put up on sale\n"
948  "4. amount_desired (string, required) amount wanted/willing to purchase\n"
949  "5. property_id2 (int, required) property wanted/willing to purchase\n"
950  "6. action (int, required) decision to either start a new (1), cancel_price (2), cancel_pair (3), or cancel_all (4) for an offer\n"
951  "7. redeem_address (optional) the address that can redeem the bitcoin outputs, defaults to sender\n"
952  );
953 
954  std::string fromAddress = params[0].get_str();
955  std::string redeemAddress = (params.size() > 6) ? (params[6].get_str()) : "";
956 
957  uint32_t propertyIdSale = static_cast<uint32_t>(params[2].get_int64());
958  uint32_t propertyIdWant = static_cast<uint32_t>(params[4].get_int64());
959 
960  int64_t amountSale = 0;
961  int64_t amountWant = 0;
962  int64_t action = params[5].get_int64();
963 
964  if (action <= 0 || CMPTransaction::CANCEL_EVERYTHING < action)
965  throw JSONRPCError(RPC_TYPE_ERROR, "Invalid action (1, 2, 3, 4 only)");
966 
967  // CANCEL-EVERYTHING doesn't require additional checks or setup
968  // CANCEL-AT-PRICE and CANCEL-ALL-FOR-PAIR have property values
970  {
971  check_prop_valid(propertyIdSale,
972  "Invalid property identifier (Sale)",
973  "Property identifier does not exist (Sale)");
974 
975  check_prop_valid(propertyIdWant,
976  "Invalid property identifier (Want)",
977  "Property identifier does not exist (Want)");
978 
979  if (isTestEcosystemProperty(propertyIdSale) != isTestEcosystemProperty(propertyIdWant))
980  throw JSONRPCError(RPC_INVALID_PARAMETER, "Properties must be in the same ecosystem (Main/Test)");
981 
982  if (propertyIdSale == propertyIdWant)
983  throw JSONRPCError(RPC_INVALID_PARAMETER, "Properties must be different");
984  }
985 
986  // CANCEL-AT-PRICE has also price information
987  if (action <= CMPTransaction::CANCEL_AT_PRICE)
988  {
989  CMPSPInfo::Entry sp;
990  bool divisible_sale = false, divisible_want = false;
991 
992  _my_sps->getSP(propertyIdSale, sp);
993  divisible_sale = sp.isDivisible();
994 
995  _my_sps->getSP(propertyIdWant, sp);
996  divisible_want = sp.isDivisible();
997 
998  std::string strAmountSale = params[1].get_str();
999  amountSale = StrToInt64(strAmountSale, divisible_sale);
1000 
1001  std::string strAmountWant = params[3].get_str();
1002  amountWant = StrToInt64(strAmountWant, divisible_want);
1003 
1004  if (0 >= amountSale)
1005  throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount (Sale)");
1006 
1007  if (0 >= amountWant)
1008  throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount (Want)");
1009  }
1010 
1011  int code = 0;
1012  uint256 newTX = send_INTERNAL_1packet(fromAddress, "", redeemAddress, propertyIdSale, amountSale, propertyIdWant, amountWant, MSC_TYPE_METADEX, action, &code);
1013  if (0 != code) throw JSONRPCError(code, error_str(code));
1014 
1015  // we need to do better than just returning a string of 0000000 here if we can't send the TX
1016  return newTX.GetHex();
1017 }
1018 
1019 Value getorderbook_MP(const Array& params, bool fHelp)
1020 {
1021  if (fHelp || params.size() < 1)
1022  throw runtime_error(
1023  "getorderbook_MP property_id1 ( property_id2 )\n"
1024  "\nAllows user to request active order information from the order book\n"
1025 
1026  "\nArguments:\n"
1027  "1. property_id1 (int, required) amount owned to up on sale\n"
1028  "2. property_id2 (int, optional) property owned to put up on sale\n"
1029  );
1030 
1031  unsigned int propertyIdSaleFilter = 0, propertyIdWantFilter = 0;
1032 
1033  bool filter_by_desired = (params.size() == 2) ? true : false;
1034 
1035  propertyIdSaleFilter = check_prop_valid( params[0].get_int64() , "Invalid property identifier (Sale)", "Property identifier does not exist (Sale)");
1036 
1037  if ( filter_by_desired ) {
1038  propertyIdWantFilter = check_prop_valid( params[1].get_int64() , "Invalid property identifier (Want)", "Property identifier does not exist (Want)");
1039  }
1040 
1041  //for each address
1042  //get property pair and total order amount at a price
1043  std::vector<CMPMetaDEx> vMetaDexObjects;
1044 
1045  for (md_PropertiesMap::iterator my_it = metadex.begin(); my_it != metadex.end(); ++my_it)
1046  {
1047  md_PricesMap & prices = my_it->second;
1048  for (md_PricesMap::iterator it = prices.begin(); it != prices.end(); ++it)
1049  {
1050  md_Set & indexes = (it->second);
1051  for (md_Set::iterator it = indexes.begin(); it != indexes.end(); ++it)
1052  {
1053  CMPMetaDEx obj = *it;
1054 
1055  //this filter, the first part is filtering by two currencies, the second part is filtering by the first only
1056  bool filter = ( filter_by_desired && ( obj.getProperty() == propertyIdSaleFilter ) && ( obj.getDesProperty() == propertyIdWantFilter ) ) || ( !filter_by_desired && ( obj.getProperty() == propertyIdSaleFilter ) );
1057 
1058  if ( filter ) {
1059  vMetaDexObjects.push_back(obj);
1060  }
1061  }
1062  }
1063  }
1064 
1065  Array response;
1066  MetaDexObjectsToJSON(vMetaDexObjects, response);
1067 
1068  return response;
1069 }
1070 
1071 Value gettradessince_MP(const Array& params, bool fHelp)
1072 {
1073  if (fHelp)
1074  throw runtime_error(
1075  "gettradessince_MP\n"
1076  "\nAllows user to request last known orders from order book\n"
1077 
1078  "\nArguments:\n"
1079  "1. timestamp (int, optional, default=[" + strprintf("%s",GetLatestBlockTime() - 1209600) + "]) starting from the timestamp, orders to show"
1080  "2. property_id1 (int, optional) filter orders by property_id1 on either side of the trade \n"
1081  "3. property_id2 (int, optional) filter orders by property_id1 and property_id2\n"
1082  );
1083 
1084  unsigned int propertyIdSaleFilter = 0, propertyIdWantFilter = 0;
1085 
1086  uint64_t timestamp = (params.size() > 0) ? params[0].get_int64() : GetLatestBlockTime() - 1209600; //2 weeks
1087 
1088  bool filter_by_one = (params.size() > 1) ? true : false;
1089  bool filter_by_both = (params.size() > 2) ? true : false;
1090 
1091  if( filter_by_one ) {
1092  propertyIdSaleFilter = check_prop_valid( params[1].get_int64() , "Invalid property identifier (Sale)", "Property identifier does not exist (Sale)");
1093  }
1094 
1095  if ( filter_by_both ) {
1096  propertyIdWantFilter = check_prop_valid( params[2].get_int64() , "Invalid property identifier (Want)", "Property identifier does not exist (Want)");
1097  }
1098 
1099  std::vector<CMPMetaDEx> vMetaDexObjects;
1100 
1101  for (md_PropertiesMap::iterator my_it = metadex.begin(); my_it != metadex.end(); ++my_it)
1102  {
1103  md_PricesMap & prices = my_it->second;
1104  for (md_PricesMap::iterator it = prices.begin(); it != prices.end(); ++it)
1105  {
1106  md_Set & indexes = (it->second);
1107  for (md_Set::iterator it = indexes.begin(); it != indexes.end(); ++it)
1108  {
1109  CMPMetaDEx obj = *it;
1110 
1111  bool filter = 1;
1112 
1113  if( filter_by_one || filter_by_both ) {
1114  //this filter, the first part is filtering by two currencies, the second part is filtering by the first only
1115  filter = ( filter_by_both && ( obj.getProperty() == propertyIdSaleFilter ) && ( obj.getDesProperty() == propertyIdWantFilter ) ) || ( !filter_by_both && ( obj.getProperty() == propertyIdSaleFilter ) );
1116  }
1117 
1118  if ( filter && (obj.getBlockTime() >= timestamp)) {
1119  vMetaDexObjects.push_back(obj);
1120  }
1121  }
1122  }
1123  }
1124 
1125  Array response;
1126  MetaDexObjectsToJSON(vMetaDexObjects, response);
1127 
1128  return response;
1129 }
1130 
1131 Value getopenorders_MP(const Array& params, bool fHelp)
1132 {
1133  if (fHelp)
1134  throw runtime_error(
1135  "getorderbook_MP\n"
1136  "\nAllows user to request active order information from the order book\n"
1137 
1138  "\nArguments:\n"
1139  "1. property_id1 (int, required) amount owned to up on sale\n"
1140  "2. property_id2 (int, optional) property owned to put up on sale\n"
1141 
1142  "\nResult:\n"
1143  "[ (array of string)\n"
1144  " \"hash\" (string) Transaction id\n"
1145  " ,...\n"
1146  "]\n"
1147 
1148  "\nbExamples\n"
1149  //+ HelpExampleCli("trade_MP", "50", "3", "500", "5" )
1150  //+ HelpExampleRpc("trade_MP", "50", "3", "500", "5" )
1151  );
1152  return "\nNot Implemented";
1153 }
1154 
1155 Value gettradehistory_MP(const Array& params, bool fHelp)
1156 {
1157  if (fHelp || params.size() < 1)
1158  throw runtime_error(
1159  "gettradehistory_MP\n"
1160  "\nAllows user to retreive trade history for some address on the Metadex\n"
1161 
1162  "\nArguments:\n"
1163  "1. address (string, required) address to query history on\n"
1164  "2. number (int, optional ) number of trades to retreive\n"
1165  "3. property_id (int, optional) filter by propertyid on one side\n"
1166  );
1167 
1168  string address = params[0].get_str();
1169  unsigned int number_trades = (params.size() == 2 ? (unsigned int) params[1].get_int64() : 512);
1170  unsigned int propertyIdFilter = 0;
1171 
1172  bool filter_by_one = (params.size() == 3) ? true : false;
1173 
1174  if( filter_by_one ) {
1175  propertyIdFilter = check_prop_valid( params[2].get_int64() , "Invalid property identifier (Sale)", "Property identifier does not exist (Sale)");
1176  }
1177 
1178  std::vector<CMPMetaDEx> vMetaDexObjects;
1179 
1180  for (md_PropertiesMap::iterator my_it = metadex.begin(); my_it != metadex.end(); ++my_it)
1181  {
1182  md_PricesMap & prices = my_it->second;
1183  for (md_PricesMap::iterator it = prices.begin(); it != prices.end(); ++it)
1184  {
1185  md_Set & indexes = (it->second);
1186  for (md_Set::iterator it = indexes.begin(); it != indexes.end(); ++it)
1187  {
1188  CMPMetaDEx obj = *it;
1189 
1190  bool filter = 1;
1191 
1192  //this filter, the first part is filtering by two currencies, the second part is filtering by the first only
1193  filter = (obj.getAddr() == address) &&
1194  ( ( ( filter_by_one && (obj.getProperty() == propertyIdFilter) == 1 ) ) || !filter_by_one ) &&
1195  (vMetaDexObjects.size() < number_trades);
1196  if ( filter ) {
1197  vMetaDexObjects.push_back(obj);
1198  }
1199  }
1200  }
1201  }
1202 
1203  Array response;
1204  MetaDexObjectsToJSON(vMetaDexObjects, response);
1205 
1206  return response;
1207 }
1208 #endif
1209 
1210 Value getactivedexsells_MP(const Array& params, bool fHelp)
1211 {
1212  if (fHelp)
1213  throw runtime_error(
1214  "getactivedexsells_MP\n"
1215  "\nGet currently active offers on the distributed exchange.\n"
1216  "\nResult:\n"
1217  "{\n"
1218  "}\n"
1219 
1220  "\nbExamples\n"
1221  + HelpExampleCli("getactivedexsells_MP", "")
1222  + HelpExampleRpc("getactivedexsells_MP", "")
1223  );
1224 
1225  //if 0 params list all sells, otherwise first param is filter address
1226  bool addressFilter = false;
1227  string addressParam;
1228 
1229  if (params.size() > 0)
1230  {
1231  addressParam = params[0].get_str();
1232  addressFilter = true;
1233  }
1234 
1235  Array response;
1236 
1237  for(OfferMap::iterator it = my_offers.begin(); it != my_offers.end(); ++it)
1238  {
1239  CMPOffer selloffer = it->second;
1240  string sellCombo = it->first;
1241  string seller = sellCombo.substr(0, sellCombo.size()-2);
1242 
1243  //filtering
1244  if ((addressFilter) && (seller != addressParam)) continue;
1245 
1246  uint256 sellHash = selloffer.getHash();
1247  string txid = sellHash.GetHex();
1248  uint64_t propertyId = selloffer.getProperty();
1249  uint64_t minFee = selloffer.getMinFee();
1250  unsigned char timeLimit = selloffer.getBlockTimeLimit();
1251  uint64_t sellOfferAmount = selloffer.getOfferAmountOriginal(); //badly named - "Original" implies off the wire, but is amended amount
1252  uint64_t sellBitcoinDesired = selloffer.getBTCDesiredOriginal(); //badly named - "Original" implies off the wire, but is amended amount
1253  uint64_t amountAvailable = getMPbalance(seller, propertyId, SELLOFFER_RESERVE);
1254  uint64_t amountAccepted = getMPbalance(seller, propertyId, ACCEPT_RESERVE);
1255 
1256  //unit price & updated bitcoin desired calcs
1257  double unitPriceFloat = 0;
1258  if ((sellOfferAmount>0) && (sellBitcoinDesired > 0)) unitPriceFloat = (double)sellBitcoinDesired/(double)sellOfferAmount; //divide by zero protection
1259  uint64_t unitPrice = rounduint64(unitPriceFloat * COIN);
1260  uint64_t bitcoinDesired = rounduint64(amountAvailable*unitPriceFloat);
1261 
1262  Object responseObj;
1263 
1264  responseObj.push_back(Pair("txid", txid));
1265  responseObj.push_back(Pair("propertyid", propertyId));
1266  responseObj.push_back(Pair("seller", seller));
1267  responseObj.push_back(Pair("amountavailable", FormatDivisibleMP(amountAvailable)));
1268  responseObj.push_back(Pair("bitcoindesired", FormatDivisibleMP(bitcoinDesired)));
1269  responseObj.push_back(Pair("unitprice", FormatDivisibleMP(unitPrice)));
1270  responseObj.push_back(Pair("timelimit", timeLimit));
1271  responseObj.push_back(Pair("minimumfee", FormatDivisibleMP(minFee)));
1272 
1273  // display info about accepts related to sell
1274  responseObj.push_back(Pair("amountaccepted", FormatDivisibleMP(amountAccepted)));
1275  Array acceptsMatched;
1276  for(AcceptMap::iterator ait = my_accepts.begin(); ait != my_accepts.end(); ++ait)
1277  {
1278  Object matchedAccept;
1279 
1280  CMPAccept accept = ait->second;
1281  string acceptCombo = ait->first;
1282  uint256 matchedHash = accept.getHash();
1283  // does this accept match the sell?
1284  if (matchedHash == sellHash)
1285  {
1286  //split acceptCombo out to get the buyer address
1287  string buyer = acceptCombo.substr((acceptCombo.find("+")+1),(acceptCombo.size()-(acceptCombo.find("+")+1)));
1288  uint64_t acceptBlock = accept.getAcceptBlock();
1289  uint64_t acceptAmount = accept.getAcceptAmountRemaining();
1290  matchedAccept.push_back(Pair("buyer", buyer));
1291  matchedAccept.push_back(Pair("block", acceptBlock));
1292  matchedAccept.push_back(Pair("amount", FormatDivisibleMP(acceptAmount)));
1293  acceptsMatched.push_back(matchedAccept);
1294  }
1295  }
1296  responseObj.push_back(Pair("accepts", acceptsMatched));
1297 
1298  // add sell object into response array
1299  response.push_back(responseObj);
1300  }
1301 
1302 return response;
1303 }
1304 
1305 Value listblocktransactions_MP(const Array& params, bool fHelp)
1306 {
1307  if (fHelp || params.size() != 1)
1308  throw runtime_error(
1309  "listblocktransactions_MP index\n"
1310  "\nReturns all Master Protocol transactions in a block.\n"
1311  "\nArguments:\n"
1312  "1. index (numeric, required) The block height or index\n"
1313  "\nResult:\n"
1314  "[ (array of string)\n"
1315  " \"hash\" (string) Transaction id\n"
1316  " ,...\n"
1317  "]\n"
1318 
1319  "\nExamples\n"
1320  + HelpExampleCli("listblocktransactions_MP", "279007")
1321  + HelpExampleRpc("listblocktransactions_MP", "279007")
1322  );
1323 
1324  // firstly let's get the block height given in the param
1325  int blockHeight = params[0].get_int();
1326  if (blockHeight < 0 || blockHeight > GetHeight())
1327  throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
1328 
1329  // next let's obtain the block for this height
1330  CBlockIndex* mpBlockIndex = chainActive[blockHeight];
1331  CBlock mpBlock;
1332 
1333  // now let's read this block in from disk so we can loop its transactions
1334  if(!ReadBlockFromDisk(mpBlock, mpBlockIndex))
1335  throw JSONRPCError(RPC_INTERNAL_ERROR, "Failed to read block from disk");
1336 
1337  // create an array to hold our response
1338  Array response;
1339 
1340  // now we want to loop through each of the transactions in the block and run against CMPTxList::exists
1341  // those that return positive add to our response array
1342  BOOST_FOREACH(const CTransaction&tx, mpBlock.vtx)
1343  {
1344  bool mptx = p_txlistdb->exists(tx.GetHash());
1345  if (mptx)
1346  {
1347  // later we can add a verbose flag to decode here, but for now callers can send returned txids into gettransaction_MP
1348  // add the txid into the response as it's an MP transaction
1349  response.push_back(tx.GetHash().GetHex());
1350  }
1351  }
1352  return response;
1353 }
1354 
1355 // this function standardizes the RPC output for gettransaction_MP and listtransaction_MP into a central function
1356 int populateRPCTransactionObject(uint256 txid, Object *txobj, string filterAddress = "")
1357 {
1358  //uint256 hash;
1359  //hash.SetHex(params[0].get_str());
1360 
1361  CTransaction wtx;
1362  uint256 blockHash = 0;
1363  if (!GetTransaction(txid, wtx, blockHash, true)) { return MP_TX_NOT_FOUND; }
1364 
1365  CMPTransaction mp_obj;
1366  uint256 wtxid = txid;
1367  bool bIsMine = false;
1368  bool isMPTx = false;
1369  uint64_t nFee = 0;
1370  string MPTxType;
1371  unsigned int MPTxTypeInt;
1372  string selectedAddress;
1373  string senderAddress;
1374  string refAddress;
1375  bool valid = false;
1376  bool showReference = false;
1377  uint64_t propertyId = 0; //using 64 instead of 32 here as json::sprint chokes on 32 - research why
1378  bool divisible = false;
1379  uint64_t amount = 0;
1380  string result;
1381  uint64_t sell_minfee = 0;
1382  unsigned char sell_timelimit = 0;
1383  unsigned char sell_subaction = 0;
1384  uint64_t sell_btcdesired = 0;
1385  int rc=0;
1386  bool crowdPurchase = false;
1387  int64_t crowdPropertyId = 0;
1388  int64_t crowdTokens = 0;
1389  int64_t issuerTokens = 0;
1390  bool crowdDivisible = false;
1391  string crowdName;
1392  string propertyName;
1393  bool mdex_propertyId_Div = false;
1394  uint64_t mdex_propertyWanted = 0;
1395  uint64_t mdex_amountWanted = 0;
1396  bool mdex_propertyWanted_Div = false;
1397  unsigned int mdex_action = 0;
1398  string mdex_actionStr;
1399 
1400  if ((0 == blockHash) || (NULL == mapBlockIndex[blockHash])) { return MP_TX_UNCONFIRMED; }
1401 
1402  CBlockIndex* pBlockIndex = mapBlockIndex[blockHash];
1403  if (NULL == pBlockIndex) { return MP_BLOCK_NOT_IN_CHAIN; }
1404 
1405  int blockHeight = pBlockIndex->nHeight;
1406  int confirmations = 1 + GetHeight() - pBlockIndex->nHeight;
1407  int64_t blockTime = mapBlockIndex[blockHash]->nTime;
1408 
1409  mp_obj.SetNull();
1410  CMPOffer temp_offer;
1411  CMPMetaDEx temp_metadexoffer;
1412 
1413  // replace initial MP detection with levelDB lookup instead of parse, this is much faster especially in calls like list/search
1414  if (p_txlistdb->exists(wtxid))
1415  {
1416  //transaction is in levelDB, so we can attempt to parse it
1417  int parseRC = parseTransaction(true, wtx, blockHeight, 0, &mp_obj);
1418  if (0 <= parseRC) //negative RC means no MP content/badly encoded TX, we shouldn't see this if TX in levelDB but check for sanity
1419  {
1420  // do we have a non-zero RC, if so it's a payment, handle differently
1421  if (0 < parseRC)
1422  {
1423  // handle as payment TX - this doesn't fit nicely into the kind of output for a MP tx so we'll do this seperately
1424  // add generic TX data to the output
1425  //Object txobj;
1426  txobj->push_back(Pair("txid", wtxid.GetHex()));
1427  txobj->push_back(Pair("confirmations", confirmations));
1428  txobj->push_back(Pair("blocktime", blockTime));
1429  txobj->push_back(Pair("type", "DEx Purchase"));
1430  // always min one subrecord, grab sender from first sub so we can display in parent as per Adam feedback
1431  string tmpBuyer;
1432  string tmpSeller;
1433  uint64_t tmpVout;
1434  uint64_t tmpNValue;
1435  uint64_t tmpPropertyId;
1436  p_txlistdb->getPurchaseDetails(wtxid,1,&tmpBuyer,&tmpSeller,&tmpVout,&tmpPropertyId,&tmpNValue);
1437  txobj->push_back(Pair("sendingaddress", tmpBuyer));
1438  // get the details of sub records for payment(s) in the tx and push into an array
1439  Array purchases;
1440  int numberOfPurchases=p_txlistdb->getNumberOfPurchases(wtxid);
1441  if (0<numberOfPurchases)
1442  {
1443  for(int purchaseNumber = 1; purchaseNumber <= numberOfPurchases; purchaseNumber++)
1444  {
1445  Object purchaseObj;
1446  string buyer;
1447  string seller;
1448  uint64_t vout;
1449  uint64_t nValue;
1450  p_txlistdb->getPurchaseDetails(wtxid,purchaseNumber,&buyer,&seller,&vout,&propertyId,&nValue);
1451  bIsMine = false;
1452  bIsMine = IsMyAddress(buyer);
1453  if (!bIsMine)
1454  {
1455  bIsMine = IsMyAddress(seller);
1456  }
1457  if (!filterAddress.empty()) if ((buyer != filterAddress) && (seller != filterAddress)) return -1; // return negative rc if filtering & no match
1458  uint64_t amountPaid = wtx.vout[vout].nValue;
1459  purchaseObj.push_back(Pair("vout", vout));
1460  purchaseObj.push_back(Pair("amountpaid", FormatDivisibleMP(amountPaid)));
1461  purchaseObj.push_back(Pair("ismine", bIsMine));
1462  purchaseObj.push_back(Pair("referenceaddress", seller));
1463  purchaseObj.push_back(Pair("propertyid", propertyId));
1464  purchaseObj.push_back(Pair("amountbought", FormatDivisibleMP(nValue)));
1465  purchaseObj.push_back(Pair("valid", true)); //only valid purchases are stored, anything else is regular BTC tx
1466  purchases.push_back(purchaseObj);
1467  }
1468  }
1469  txobj->push_back(Pair("purchases", purchases));
1470  // return the object
1471  return 0;
1472  }
1473  else
1474  {
1475  // otherwise RC was 0, a valid MP transaction so far
1476  if (0<=mp_obj.step1())
1477  {
1478  MPTxType = mp_obj.getTypeString();
1479  MPTxTypeInt = mp_obj.getType();
1480  senderAddress = mp_obj.getSender();
1481  refAddress = mp_obj.getReceiver();
1482  if (!filterAddress.empty()) if ((senderAddress != filterAddress) && (refAddress != filterAddress)) return -1; // return negative rc if filtering & no match
1483  isMPTx = true;
1484  nFee = mp_obj.getFeePaid();
1485  int tmpblock=0;
1486  uint32_t tmptype=0;
1487  uint64_t amountNew=0;
1488  valid=getValidMPTX(wtxid, &tmpblock, &tmptype, &amountNew);
1489  //populate based on type of tx
1490  switch (MPTxTypeInt)
1491  {
1492  case MSC_TYPE_METADEX:
1493  if (0 == mp_obj.step2_Value())
1494  {
1495  propertyId = mp_obj.getProperty();
1496  amount = mp_obj.getAmount();
1497  mdex_propertyId_Div = isPropertyDivisible(propertyId);
1498  if (0 <= mp_obj.interpretPacket(NULL,&temp_metadexoffer))
1499  {
1500  mdex_propertyWanted = temp_metadexoffer.getDesProperty();
1501  mdex_propertyWanted_Div = isPropertyDivisible(mdex_propertyWanted);
1502  mdex_amountWanted = temp_metadexoffer.getAmountDesired();
1503  mdex_action = temp_metadexoffer.getAction();
1504  if(1 == mdex_action) mdex_actionStr = "new sell";
1505  if(2 == mdex_action) mdex_actionStr = "cancel price";
1506  if(3 == mdex_action) mdex_actionStr = "cancel pair";
1507  if(4 == mdex_action) mdex_actionStr = "cancel all";
1508  }
1509  }
1510 
1511  break;
1513  if (0 == mp_obj.step2_Value())
1514  {
1515  showReference = true;
1516  propertyId = mp_obj.getProperty();
1517  amount = mp_obj.getAmount();
1518  }
1519  break;
1521  if (0 == mp_obj.step2_Value())
1522  {
1523  propertyId = mp_obj.getProperty();
1524  amount = mp_obj.getAmount();
1525  }
1526  break;
1528  mp_obj.step2_SmartProperty(rc);
1529  if (0 == rc)
1530  {
1531  propertyId = _my_sps->findSPByTX(wtxid); // propertyId of created property (if valid)
1532  amount = getTotalTokens(propertyId);
1533  propertyName = mp_obj.getSPName();
1534  }
1535  break;
1537  mp_obj.step2_SmartProperty(rc);
1538  if (0 == rc)
1539  {
1540  propertyId = _my_sps->findSPByTX(wtxid); // propertyId of created property (if valid)
1541  amount = 0; // crowdsale txs always create zero tokens
1542  propertyName = mp_obj.getSPName();
1543  }
1544  break;
1546  //propertyId = _my_sps->findSPByTX(wtxid); // propertyId of created property (if valid)
1547  //amount = 0; // issuance of a managed token does not create tokens
1548  mp_obj.step2_SmartProperty(rc);
1549  if (0 == rc)
1550  {
1551  propertyId = _my_sps->findSPByTX(wtxid); // propertyId of created property (if valid)
1552  amount = 0; // crowdsale txs always create zero tokens
1553  propertyName = mp_obj.getSPName();
1554  }
1555  break;
1556  case MSC_TYPE_SIMPLE_SEND:
1557  if (0 == mp_obj.step2_Value())
1558  {
1559  propertyId = mp_obj.getProperty();
1560  amount = mp_obj.getAmount();
1561  showReference = true;
1562  //check crowdsale invest?
1563  crowdPurchase = isCrowdsalePurchase(wtxid, refAddress, &crowdPropertyId, &crowdTokens, &issuerTokens);
1564  if (crowdPurchase)
1565  {
1566  MPTxType = "Crowdsale Purchase";
1567  CMPSPInfo::Entry sp;
1568  if (false == _my_sps->getSP(crowdPropertyId, sp)) { return MP_CROWDSALE_WITHOUT_PROPERTY; }
1569 
1570  crowdName = sp.name;
1571  crowdDivisible = sp.isDivisible();
1572  }
1573  }
1574  break;
1575  case MSC_TYPE_TRADE_OFFER:
1576  if (0 == mp_obj.step2_Value())
1577  {
1578  propertyId = mp_obj.getProperty();
1579  amount = mp_obj.getAmount();
1580  }
1581  if (0 <= mp_obj.interpretPacket(&temp_offer))
1582  {
1583  sell_minfee = temp_offer.getMinFee();
1584  sell_timelimit = temp_offer.getBlockTimeLimit();
1585  sell_subaction = temp_offer.getSubaction();
1586  sell_btcdesired = temp_offer.getBTCDesiredOriginal();
1587  if (3 < sell_subaction) sell_subaction=0; // case where subaction byte >3, to have been allowed must be a v0 sell, flip byte to 0
1588  // for now must mark all v0 sells as new until we can store subaction for v0 sells
1589  if ((0 == sell_subaction) && (0 < amount)) sell_subaction=1; // case where subaction byte=0, must be a v0 sell, infer action from amount
1590  if ((0 == sell_subaction) && (0 == amount)) sell_subaction=3; // case where subaction byte=0, must be a v0 sell, infer action from amount
1591  }
1592  if ((valid) && (amountNew>0)) amount=amountNew; //amount has been amended, update
1593  break;
1595  if (0 == mp_obj.step2_Value())
1596  {
1597  propertyId = mp_obj.getProperty();
1598  amount = mp_obj.getAmount();
1599  showReference = true;
1600  }
1601  if ((valid) && (amountNew>0)) amount=amountNew; //amount has been amended, update
1602  break;
1604  if (0 == mp_obj.step2_Value())
1605  {
1606  propertyId = mp_obj.getProperty();
1607  }
1608  break;
1610  if (0 == mp_obj.step2_Value())
1611  {
1612  propertyId = mp_obj.getProperty();
1613  amount = mp_obj.getAmount();
1614  }
1615  break;
1616  } // end switch
1617  divisible=isPropertyDivisible(propertyId);
1618  } // end step 1 if
1619  else
1620  {
1621  return -3336; // "Not a Master Protocol transaction"
1622  }
1623  } // end payment check if
1624  } //negative RC check
1625  else
1626  {
1628  }
1629  }
1630  else
1631  {
1633  }
1634 
1635  if (isMPTx)
1636  {
1637  // test sender and reference against ismine to determine which address is ours
1638  // if both ours (eg sending to another address in wallet) use reference
1639  bIsMine = IsMyAddress(senderAddress);
1640  if (!bIsMine)
1641  {
1642  bIsMine = IsMyAddress(refAddress);
1643  }
1644  txobj->push_back(Pair("txid", wtxid.GetHex()));
1645  txobj->push_back(Pair("sendingaddress", senderAddress));
1646  if (showReference) txobj->push_back(Pair("referenceaddress", refAddress));
1647  txobj->push_back(Pair("ismine", bIsMine));
1648  txobj->push_back(Pair("confirmations", confirmations));
1649  txobj->push_back(Pair("fee", ValueFromAmount(nFee)));
1650  txobj->push_back(Pair("blocktime", blockTime));
1651  txobj->push_back(Pair("version", (int64_t)mp_obj.getVersion()));
1652  txobj->push_back(Pair("type_int", (int64_t)mp_obj.getType()));
1653  txobj->push_back(Pair("type", MPTxType));
1654  if (MSC_TYPE_METADEX != MPTxTypeInt) txobj->push_back(Pair("propertyid", propertyId));
1655  if ((MSC_TYPE_CREATE_PROPERTY_VARIABLE == MPTxTypeInt) || (MSC_TYPE_CREATE_PROPERTY_FIXED == MPTxTypeInt) || (MSC_TYPE_CREATE_PROPERTY_MANUAL == MPTxTypeInt))
1656  {
1657  txobj->push_back(Pair("propertyname", propertyName));
1658  }
1659  if (MSC_TYPE_METADEX != MPTxTypeInt) txobj->push_back(Pair("divisible", divisible));
1660  if (MSC_TYPE_METADEX != MPTxTypeInt) txobj->push_back(Pair("amount", FormatMP(propertyId, amount)));
1661  if (crowdPurchase)
1662  {
1663  txobj->push_back(Pair("purchasedpropertyid", crowdPropertyId));
1664  txobj->push_back(Pair("purchasedpropertyname", crowdName));
1665  txobj->push_back(Pair("purchasedpropertydivisible", crowdDivisible));
1666  txobj->push_back(Pair("purchasedtokens", FormatMP(crowdPropertyId, crowdTokens)));
1667  txobj->push_back(Pair("issuertokens", FormatMP(crowdPropertyId, issuerTokens)));
1668  }
1669  if (MSC_TYPE_TRADE_OFFER == MPTxTypeInt)
1670  {
1671  txobj->push_back(Pair("feerequired", ValueFromAmount(sell_minfee)));
1672  txobj->push_back(Pair("timelimit", sell_timelimit));
1673  if (1 == sell_subaction) txobj->push_back(Pair("subaction", "New"));
1674  if (2 == sell_subaction) txobj->push_back(Pair("subaction", "Update"));
1675  if (3 == sell_subaction) txobj->push_back(Pair("subaction", "Cancel"));
1676  txobj->push_back(Pair("bitcoindesired", ValueFromAmount(sell_btcdesired)));
1677  }
1678  if (MSC_TYPE_METADEX == MPTxTypeInt)
1679  {
1680  txobj->push_back(Pair("amountoffered", FormatMP(propertyId, amount)));
1681  txobj->push_back(Pair("propertyoffered", propertyId));
1682  txobj->push_back(Pair("propertyofferedisdivisible", mdex_propertyId_Div));
1683  txobj->push_back(Pair("amountdesired", FormatMP(mdex_propertyWanted, mdex_amountWanted)));
1684  txobj->push_back(Pair("propertydesired", mdex_propertyWanted));
1685  txobj->push_back(Pair("propertydesiredisdivisible", mdex_propertyWanted_Div));
1686  txobj->push_back(Pair("action", mdex_actionStr));
1687  //txobj->push_back(Pair("unit_price", mdex_unitPrice ) );
1688  //txobj->push_back(Pair("inverse_unit_price", mdex_invUnitPrice ) );
1689  //active?
1690  //txobj->push_back(Pair("amount_original", FormatDivisibleMP(mdex_amt_orig_sale)));
1691  //txobj->push_back(Pair("amount_desired", FormatDivisibleMP(mdex_amt_des)));
1692  }
1693  txobj->push_back(Pair("valid", valid));
1694  }
1695  return 0;
1696 }
1697 
1698 Value gettransaction_MP(const Array& params, bool fHelp)
1699 {
1700  // note this call has been refactored to use the singular populateRPCTransactionObject function
1701 
1702  if (fHelp || params.size() != 1)
1703  throw runtime_error(
1704  "gettransaction_MP \"txid\"\n"
1705  "\nGet detailed information about a Master Protocol transaction <txid>\n"
1706  "\nArguments:\n"
1707  "1. \"txid\" (string, required) The transaction id\n"
1708  "\nResult:\n"
1709  "{\n"
1710  " \"txid\" : \"transactionid\", (string) The transaction id\n"
1711  " \"sendingaddress\" : \"sender\", (string) The sending address\n"
1712  " \"referenceaddress\" : \"receiving address\", (string) The receiving address (if applicable)\n"
1713  " \"ismine\" : true/false\", (boolean) Whether the transaction involes an address in the wallet\n"
1714  " \"confirmations\" : n, (numeric) The number of confirmations\n"
1715  " \"blocktime\" : ttt, (numeric) The time in seconds since epoch (1 Jan 1970 GMT)\n"
1716  " \"type\" : \"transaction type\", (string) The type of transaction\n"
1717  " \"propertyid\" : \"property id\", (numeric) The ID of the property transacted\n"
1718  " \"propertyname\" : \"property name\", (string) The name of the property (for creation transactions)\n"
1719  " \"divisible\" : true/false\", (boolean) Whether the property is divisible\n"
1720  " \"amount\" : \"x.xxx\", (string) The transaction amount\n"
1721  " \"valid\" : true/false\", (boolean) Whether the transaction is valid\n"
1722  "}\n"
1723 
1724  "\nbExamples\n"
1725  + HelpExampleCli("gettransaction_MP", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
1726  + HelpExampleRpc("gettransaction_MP", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
1727  );
1728 
1729  uint256 hash;
1730  hash.SetHex(params[0].get_str());
1731  Object txobj;
1732  // make a request to new RPC populator function to populate a transaction object
1733  int populateResult = populateRPCTransactionObject(hash, &txobj);
1734  // check the response, throw any error codes if false
1735  if (0>populateResult)
1736  {
1737  // TODO: consider throwing other error codes, check back with Bitcoin Core
1738  switch (populateResult)
1739  {
1740  case MP_TX_NOT_FOUND:
1741  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
1742 
1743  case MP_TX_UNCONFIRMED:
1744  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unconfirmed transactions are not supported");
1745 
1746  case MP_BLOCK_NOT_IN_CHAIN:
1747  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not part of the active chain");
1748 
1750  throw JSONRPCError(RPC_INTERNAL_ERROR, "Potential database corruption: \
1751  \"Crowdsale Purchase\" without valid property identifier");
1752 
1754  throw JSONRPCError(RPC_INTERNAL_ERROR, "Potential database corruption: Invalid transaction found");
1755 
1757  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Not a Master Protocol transaction");
1758  }
1759  }
1760  // everything seems ok, return the object
1761  return txobj;
1762 }
1763 
1764 Value listtransactions_MP(const Array& params, bool fHelp)
1765 {
1766  // note this call has been refactored to use the singular populateRPCTransactionObject function
1767  // note address filtering has been readded to this call, and searchtransactions_MP has been removed since performance hit no longer an issue
1768 
1769  if (fHelp || params.size() > 5)
1770  throw runtime_error(
1771  "listtransactions_MP ( \"address\" count skip startblock endblock )\n" //todo increase verbosity in help
1772  "\nList wallet transactions filtered on counts and block boundaries\n"
1773  + HelpExampleCli("listtransactions_MP", "")
1774  + HelpExampleRpc("listtransactions_MP", "")
1775  );
1776 
1777  CWallet *wallet = pwalletMain;
1778  string sAddress = "";
1779  string addressParam = "";
1780  bool addressFilter;
1781 
1782  //if 0 params consider all addresses in wallet, otherwise first param is filter address
1783  addressFilter = false;
1784  if (params.size() > 0)
1785  {
1786  // allow setting "" or "*" to use nCount and nFrom params with all addresses in wallet
1787  if ( ("*" != params[0].get_str()) && ("" != params[0].get_str()) )
1788  {
1789  addressParam = params[0].get_str();
1790  addressFilter = true;
1791  }
1792  }
1793 
1794  int64_t nCount = 10;
1795  if (params.size() > 1) nCount = params[1].get_int64();
1796  if (nCount < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
1797  int64_t nFrom = 0;
1798  if (params.size() > 2) nFrom = params[2].get_int64();
1799  if (nFrom < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
1800  int64_t nStartBlock = 0;
1801  if (params.size() > 3) nStartBlock = params[3].get_int64();
1802  if (nStartBlock < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative start block");
1803  int64_t nEndBlock = 999999;
1804  if (params.size() > 4) nEndBlock = params[4].get_int64();
1805  if (nEndBlock < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative end block");
1806 
1807  Array response; //prep an array to hold our output
1808 
1809  // STO has no inbound transaction, so we need to use an insert methodology here
1810  // get STO receipts affecting me
1811  string mySTOReceipts = s_stolistdb->getMySTOReceipts(addressParam);
1812  std::vector<std::string> vecReceipts;
1813  boost::split(vecReceipts, mySTOReceipts, boost::is_any_of(","), token_compress_on);
1814  int64_t lastTXBlock = 999999;
1815 
1816  // rewrite to use original listtransactions methodology from core
1817  LOCK(wallet->cs_wallet);
1818  std::list<CAccountingEntry> acentries;
1819  CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, "*");
1820 
1821  // iterate backwards until we have nCount items to return:
1822  for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
1823  {
1824  CWalletTx *const pwtx = (*it).second.first;
1825  if (pwtx != 0)
1826  {
1827  // get the height of the transaction and check it's within the chosen parameters
1828  uint256 blockHash = pwtx->hashBlock;
1829  if ((0 == blockHash) || (NULL == mapBlockIndex[blockHash])) continue;
1830  CBlockIndex* pBlockIndex = mapBlockIndex[blockHash];
1831  if (NULL == pBlockIndex) continue;
1832  int blockHeight = pBlockIndex->nHeight;
1833  if ((blockHeight < nStartBlock) || (blockHeight > nEndBlock)) continue; // ignore it if not within our range
1834 
1835  // look for an STO receipt to see if we need to insert it
1836  for(uint32_t i = 0; i<vecReceipts.size(); i++)
1837  {
1838  std::vector<std::string> svstr;
1839  boost::split(svstr, vecReceipts[i], boost::is_any_of(":"), token_compress_on);
1840  if(4 == svstr.size()) // make sure expected num items
1841  {
1842  if((atoi(svstr[1]) < lastTXBlock) && (atoi(svstr[1]) > blockHeight))
1843  {
1844  // STO receipt insert here - add STO receipt to response array
1845  uint256 hash;
1846  hash.SetHex(svstr[0]);
1847  Object txobj;
1848  int populateResult = -1;
1849  populateResult = populateRPCTransactionObject(hash, &txobj);
1850  if (0 == populateResult)
1851  {
1852  Array receiveArray;
1853  uint64_t tmpAmount = 0;
1854  uint64_t stoFee = 0;
1855  s_stolistdb->getRecipients(hash, addressParam, &receiveArray, &tmpAmount, &stoFee);
1856  // add matches array and stofee to object
1857  txobj.push_back(Pair("totalstofee", FormatDivisibleMP(stoFee))); // fee always MSC so always divisible
1858  txobj.push_back(Pair("recipients", receiveArray));
1859  response.push_back(txobj); // add the transaction object to the response array
1860  }
1861  // don't burn time doing more work than we need to
1862  if ((int)response.size() >= (nCount+nFrom)) break;
1863  }
1864  }
1865  }
1866 
1867  // populateRPCTransactionObject will throw us a non-0 rc if this isn't a MP transaction, speeds up search by orders of magnitude
1868  uint256 hash = pwtx->GetHash();
1869  Object txobj;
1870 
1871  // make a request to new RPC populator function to populate a transaction object (if it is a MP transaction)
1872  int populateResult = -1;
1873  if (addressFilter)
1874  {
1875  populateResult = populateRPCTransactionObject(hash, &txobj, addressParam); // pass in an address filter
1876  }
1877  else
1878  {
1879  populateResult = populateRPCTransactionObject(hash, &txobj); // no address filter
1880  }
1881  if (0 == populateResult) response.push_back(txobj); // add the transaction object to the response array if we get a 0 rc
1882  lastTXBlock = blockHeight;
1883 
1884  // don't burn time doing more work than we need to
1885  if ((int)response.size() >= (nCount+nFrom)) break;
1886  }
1887  }
1888  // sort array here and cut on nFrom and nCount
1889  if (nFrom > (int)response.size())
1890  nFrom = response.size();
1891  if ((nFrom + nCount) > (int)response.size())
1892  nCount = response.size() - nFrom;
1893  Array::iterator first = response.begin();
1894  std::advance(first, nFrom);
1895  Array::iterator last = response.begin();
1896  std::advance(last, nFrom+nCount);
1897 
1898  if (last != response.end()) response.erase(last, response.end());
1899  if (first != response.begin()) response.erase(response.begin(), first);
1900 
1901  std::reverse(response.begin(), response.end()); // return oldest to newest?
1902  return response; // return response array for JSON serialization
1903 }
1904 
1905 Value getinfo_MP(const Array& params, bool fHelp)
1906 {
1907  Object infoResponse;
1908  // other bits of info we want to report should be included here
1909 
1910  // provide the mastercore and bitcoin version and if available commit id
1911  infoResponse.push_back(Pair("mastercoreversion", "0.0." + boost::lexical_cast<string>((double)OMNICORE_VERSION_BASE/10) + OMNICORE_VERSION_TYPE ));
1912  infoResponse.push_back(Pair("bitcoincoreversion", "0." + boost::lexical_cast<string>((int)CLIENT_VERSION/100)));
1913  infoResponse.push_back(Pair("commitinfo", COMMIT_INFO));
1914 
1915  // provide the current block details
1916  uint64_t block = chainActive.Height();
1917  uint64_t blockTime = chainActive[chainActive.Height()]->GetBlockTime();
1918  int64_t blockMPTransactions = p_txlistdb->getMPTransactionCountBlock(block);
1919  int64_t totalMPTransactions = p_txlistdb->getMPTransactionCountTotal();
1920  int64_t totalMPTrades = t_tradelistdb->getMPTradeCountTotal();
1921  infoResponse.push_back(Pair("block", block));
1922  infoResponse.push_back(Pair("blocktime", blockTime));
1923  infoResponse.push_back(Pair("blocktransactions", blockMPTransactions));
1924 
1925  // provide the number of trades completed
1926  infoResponse.push_back(Pair("totaltrades", totalMPTrades));
1927  // provide the number of transactions parsed
1928  infoResponse.push_back(Pair("totaltransactions", totalMPTransactions));
1929 
1930  // handle alerts
1931  Object alertResponse;
1933  int32_t alertType = 0;
1934  uint64_t expiryValue = 0;
1935  uint32_t typeCheck = 0;
1936  uint32_t verCheck = 0;
1937  std::vector<std::string> vstr;
1938  string alertMessage;
1939 
1940  //split the global message string if it's not empty
1941  if(!global_alert_message.empty())
1942  {
1943  boost::split(vstr, global_alert_message, boost::is_any_of(":"), token_compress_on);
1944  // make sure there are 5 tokens and they convert ok
1945  if (5 == vstr.size())
1946  {
1947  try
1948  {
1949  alertType = boost::lexical_cast<int32_t>(vstr[0]);
1950  expiryValue = boost::lexical_cast<uint64_t>(vstr[1]);
1951  typeCheck = boost::lexical_cast<uint32_t>(vstr[2]);
1952  verCheck = boost::lexical_cast<uint32_t>(vstr[3]);
1953  alertMessage = vstr[4];
1954  } catch (const boost::bad_lexical_cast &e)
1955  {
1956  file_log("DEBUG ALERT - error in converting values from global alert string\n");
1957  alertType = 0;
1958  expiryValue = 0;
1959  alertMessage = "error";
1960  }
1961  string alertTypeStr;
1962  switch (alertType)
1963  {
1964  case 0: alertTypeStr = "error"; break;
1965  case 1: alertTypeStr = "textalertexpiringbyblock"; break;
1966  case 2: alertTypeStr = "textalertexpiringbytime"; break;
1967  case 3: alertTypeStr = "textalertexpiringbyversion"; break;
1968  case 4: alertTypeStr = "updatealerttxcheck"; break;
1969  }
1970  alertResponse.push_back(Pair("alerttype", alertTypeStr));
1971  alertResponse.push_back(Pair("expiryvalue", FormatIndivisibleMP(expiryValue)));
1972  if (alertType == 4) { alertResponse.push_back(Pair("typecheck", FormatIndivisibleMP(typeCheck))); alertResponse.push_back(Pair("vercheck", FormatIndivisibleMP(verCheck))); }
1973  alertResponse.push_back(Pair("alertmessage", alertMessage.c_str()));
1974  }
1975  else
1976  {
1977  file_log("DEBUG ALERT ERROR - Something went wrong decoding the global alert string.\n");
1978  throw JSONRPCError(RPC_INVALID_PARAMETER, "Debug Alert Error - Something went wrong decoding the global alert string."); //better RPC error code
1979  }
1980  }
1981  infoResponse.push_back(Pair("alert", alertResponse));
1982  return infoResponse;
1983 }
1984 
1985 Value getsto_MP(const Array& params, bool fHelp)
1986 {
1987  if (fHelp || params.size() < 1 || params.size() > 2)
1988  throw runtime_error(
1989  "getsto_MP \"txid\" \"recipientfilter\"\n"
1990  "\nGet information and recipients of send to owners transaction <txid>\n"
1991  "\nArguments:\n"
1992  "1. \"txid\" (string, required) The transaction id\n"
1993  "2. \"recipientfilter\" (string, optional) The recipient address filter (wallet by default, \"*\" for all)\n"
1994  "\nResult:\n"
1995  "{\n"
1996  " \"txid\" : \"transactionid\", (string) The transaction id\n"
1997  + HelpExampleCli("getsto_MP", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
1998  + HelpExampleRpc("getsto_MP", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
1999  );
2000 
2001  uint256 hash;
2002  hash.SetHex(params[0].get_str());
2003  string filterAddress = "";
2004  if (params.size() == 2) filterAddress=params[1].get_str();
2005  Object txobj;
2006  CTransaction wtx;
2007  uint256 blockHash = 0;
2008  if (!GetTransaction(hash, wtx, blockHash, true)) { return MP_TX_NOT_FOUND; }
2009  uint64_t propertyId = 0;
2010  CMPTransaction mp_obj;
2011  int parseRC = parseTransaction(true, wtx, 0, 0, &mp_obj);
2012  if (0 <= parseRC) //negative RC means no MP content/badly encoded TX, we shouldn't see this if TX in levelDB but check for safety
2013  {
2014  if (0<=mp_obj.step1())
2015  {
2016  if (0 == mp_obj.step2_Value())
2017  {
2018  propertyId = mp_obj.getProperty();
2019  }
2020  }
2021  if (propertyId == 0) // something went wrong, couldn't decode property ID - bad packet?
2022  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Not a Master Protocol transaction");
2023 
2024  // make a request to new RPC populator function to populate a transaction object
2025  int populateResult = populateRPCTransactionObject(hash, &txobj);
2026  // check the response, throw any error codes if false
2027  if (0>populateResult)
2028  {
2029  // TODO: consider throwing other error codes, check back with Bitcoin Core
2030  switch (populateResult)
2031  {
2032  case MP_TX_NOT_FOUND:
2033  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
2034  case MP_TX_UNCONFIRMED:
2035  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unconfirmed transactions are not supported");
2036  case MP_BLOCK_NOT_IN_CHAIN:
2037  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not part of the active chain");
2039  throw JSONRPCError(RPC_INTERNAL_ERROR, "Potential database corruption: \
2040  \"Crowdsale Purchase\" without valid property identifier");
2042  throw JSONRPCError(RPC_INTERNAL_ERROR, "Potential database corruption: Invalid transaction found");
2044  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Not a Master Protocol transaction");
2045  }
2046  }
2047  // create array of recipients
2048  Array receiveArray;
2049  uint64_t tmpAmount = 0;
2050  uint64_t stoFee = 0;
2051  s_stolistdb->getRecipients(hash, filterAddress, &receiveArray, &tmpAmount, &stoFee);
2052  // add matches array and stofee to object
2053  txobj.push_back(Pair("totalstofee", FormatDivisibleMP(stoFee))); // fee always MSC so always divisible
2054  txobj.push_back(Pair("recipients", receiveArray));
2055  }
2056  // return object
2057  return txobj;
2058 }
2059 
2060 Value gettrade_MP(const Array& params, bool fHelp)
2061 {
2062  if (fHelp || params.size() != 1)
2063  throw runtime_error(
2064  "gettrade_MP \"txid\"\n"
2065  "\nGet detailed information and trade matches for a Master Protocol MetaDEx trade offer <txid>\n"
2066  "\nArguments:\n"
2067  "1. \"txid\" (string, required) The transaction id\n"
2068  "\nResult:\n"
2069  "{\n"
2070  " \"txid\" : \"transactionid\", (string) The transaction id\n"
2071  + HelpExampleCli("gettrade_MP", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
2072  + HelpExampleRpc("gettrade_MP", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
2073  );
2074 
2075  uint256 hash;
2076  hash.SetHex(params[0].get_str());
2077  Object tradeobj;
2078  Object txobj;
2079  CMPMetaDEx temp_metadexoffer;
2080 
2081  //get sender & propId
2082  string senderAddress;
2083  unsigned int propertyId = 0;
2084  CTransaction wtx;
2085  uint256 blockHash = 0;
2086  if (!GetTransaction(hash, wtx, blockHash, true)) { return MP_TX_NOT_FOUND; }
2087  CMPTransaction mp_obj;
2088  int parseRC = parseTransaction(true, wtx, 0, 0, &mp_obj);
2089  if (0 <= parseRC) //negative RC means no MP content/badly encoded TX, we shouldn't see this if TX in levelDB but check for sa$
2090  {
2091  if (0<=mp_obj.step1())
2092  {
2093  senderAddress = mp_obj.getSender();
2094  if (0 == mp_obj.step2_Value())
2095  {
2096  propertyId = mp_obj.getProperty();
2097  }
2098  }
2099  }
2100  if (propertyId == 0) // something went wrong, couldn't decode property ID - bad packet?
2101  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Not a Master Protocol transaction");
2102  if (senderAddress.empty()) // something went wrong, couldn't decode transaction
2103  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Not a Master Protocol transaction");
2104 
2105  // make a request to new RPC populator function to populate a transaction object
2106  int populateResult = populateRPCTransactionObject(hash, &txobj);
2107 
2108  // check the response, throw any error codes if false
2109  if (0>populateResult)
2110  {
2111  // TODO: consider throwing other error codes, check back with Bitcoin Core
2112  switch (populateResult)
2113  {
2114  case MP_TX_NOT_FOUND:
2115  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
2116  case MP_TX_UNCONFIRMED:
2117  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unconfirmed transactions are not supported");
2118  case MP_BLOCK_NOT_IN_CHAIN:
2119  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not part of the active chain");
2121  throw JSONRPCError(RPC_INTERNAL_ERROR, "Potential database corruption: \
2122  \"Crowdsale Purchase\" without valid property identifier");
2124  throw JSONRPCError(RPC_INTERNAL_ERROR, "Potential database corruption: Invalid transaction found");
2126  throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Not a Master Protocol transaction");
2127  }
2128  }
2129 
2130  // get the amount for sale in this sell offer to see if filled
2131  uint64_t amountForSale = mp_obj.getAmount();
2132 
2133  // create array of matches
2134  Array tradeArray;
2135  uint64_t totalBought = 0;
2136  uint64_t totalSold = 0;
2137  t_tradelistdb->getMatchingTrades(hash, propertyId, &tradeArray, &totalSold, &totalBought);
2138 
2139  // get action byte
2140  int actionByte = 0;
2141  if (0 <= mp_obj.interpretPacket(NULL,&temp_metadexoffer)) { actionByte = (int)temp_metadexoffer.getAction(); }
2142 
2143  // everything seems ok, now add status and get an array of matches to add to the object
2144  // work out status
2145  bool orderOpen = isMetaDExOfferActive(hash, propertyId);
2146  bool partialFilled = false;
2147  bool filled = false;
2148  string statusText;
2149  if(totalSold>0) partialFilled = true;
2150  if(totalSold>=amountForSale) filled = true;
2151  statusText = "unknown";
2152  if((!orderOpen) && (!partialFilled)) statusText = "cancelled"; // offers that are closed but not filled must have been cancelled
2153  if((!orderOpen) && (partialFilled)) statusText = "cancelled part filled"; // offers that are closed but not filled must have been cancelled
2154  if((!orderOpen) && (filled)) statusText = "filled"; // filled offers are closed
2155  if((orderOpen) && (!partialFilled)) statusText = "open"; // offer exists but no matches yet
2156  if((orderOpen) && (partialFilled)) statusText = "open part filled"; // offer exists, some matches but not filled yet
2157  if(actionByte==1) txobj.push_back(Pair("status", statusText)); // no status for cancel txs
2158 
2159  // add cancels array to object and set status as cancelled only if cancel type
2160  if(actionByte != 1)
2161  {
2162  Array cancelArray;
2163  int numberOfCancels = p_txlistdb->getNumberOfMetaDExCancels(hash);
2164  if (0<numberOfCancels)
2165  {
2166  for(int refNumber = 1; refNumber <= numberOfCancels; refNumber++)
2167  {
2168  Object cancelTx;
2169  string strValue = p_txlistdb->getKeyValue(hash.ToString() + "-C" + to_string(refNumber));
2170  if (!strValue.empty())
2171  {
2172  std::vector<std::string> vstr;
2173  boost::split(vstr, strValue, boost::is_any_of(":"), token_compress_on);
2174  if (3 <= vstr.size())
2175  {
2176  uint64_t propId = boost::lexical_cast<uint64_t>(vstr[1]);
2177  uint64_t amountUnreserved = boost::lexical_cast<uint64_t>(vstr[2]);
2178  cancelTx.push_back(Pair("txid", vstr[0]));
2179  cancelTx.push_back(Pair("propertyid", propId));
2180  cancelTx.push_back(Pair("amountunreserved", FormatMP(propId, amountUnreserved)));
2181  cancelArray.push_back(cancelTx);
2182  }
2183  }
2184  }
2185  }
2186  txobj.push_back(Pair("cancelledtransactions", cancelArray));
2187  }
2188  else
2189  {
2190  // if cancelled, show cancellation txid
2191  if((statusText == "cancelled") || (statusText == "cancelled part filled")) { txobj.push_back(Pair("canceltxid", p_txlistdb->findMetaDExCancel(hash).GetHex())); }
2192  // add matches array to object
2193  txobj.push_back(Pair("matches", tradeArray)); // only action 1 offers can have matches
2194  }
2195 
2196  // return object
2197  return txobj;
2198 }
uint32_t GetLatestBlockTime(void)
Definition: mastercore.cpp:168
int getNumberOfPurchases(const uint256 txid)
void SetHex(const char *psz)
Definition: uint256.h:305
const string & getAddr() const
const string & getSender() const
Definition: mastercore_tx.h:84
bool close_early
Definition: mastercore_sp.h:28
#define OMNICORE_VERSION_TYPE
#define OMNICORE_VERSION_BASE
string global_alert_message
Definition: mastercore.cpp:95
unsigned char getAction() const
bool fixed
Definition: mastercore_sp.h:38
bool isTestEcosystemProperty(unsigned int property)
Definition: mastercore.cpp:495
Value getallbalancesforid_MP(const Array &params, bool fHelp)
CMPSPInfo * _my_sps
Definition: mastercore.cpp:362
uint256 txid
Definition: mastercore_sp.h:35
int64_t getAmountDesired() const
Definition: init.h:13
int ClassB_send(const string &senderAddress, const string &receiverAddress, const string &redemptionAddress, const vector< unsigned char > &data, uint256 &txid, int64_t additional=0)
Value sendtoowners_MP(const Array &params, bool fHelp)
Definition: core.h:396
uint64_t getFeePaid() const
Definition: mastercore_tx.h:82
CCriticalSection cs_wallet
Main wallet lock.
Definition: wallet.h:132
bool exists(const uint256 &txid)
string data
Definition: mastercore_sp.h:18
json_spirit::Value gettradehistory_MP(const json_spirit::Array &params, bool fHelp)
int parseTransaction(bool bRPConly, const CTransaction &wtx, int nBlock, unsigned int idx, CMPTransaction *mp_tx, unsigned int nTime)
std::string HelpExampleRpc(string methodname, string args)
Definition: rpcserver.cpp:923
uint256 hashBlock
Definition: main.h:432
static const int CLIENT_VERSION
Definition: version.h:15
AcceptMap my_accepts
Definition: mastercore.cpp:360
uint64_t getBlockTime() const
const string & getReceiver() const
Definition: mastercore_tx.h:85
bool getMatchingTrades(const uint256 txid, unsigned int propertyId, Array *tradeArray, uint64_t *totalSold, uint64_t *totalBought)
string issuer
Definition: mastercore_sp.h:11
uint256 GetHash() const
Definition: core.cpp:75
CMPTally * getTally(const string &address)
Definition: mastercore.cpp:425
uint64_t getOfferAmountOriginal() const
#define strprintf
Definition: util.h:116
Value getactivedexsells_MP(const Array &params, bool fHelp)
STL namespace.
void getRecipients(const uint256 txid, string filterAddress, Array *recipientArray, uint64_t *total, uint64_t *stoFee)
#define TEST_ECO_PROPERTY_1
Definition: mastercore.h:22
json_spirit::Value gettradessince_MP(const json_spirit::Array &params, bool fHelp)
unsigned char early_bird
Definition: mastercore_sp.h:24
Object JSONRPCError(int code, const string &message)
uint64_t num_tokens
Definition: mastercore_sp.h:19
Value sendrawtx_MP(const Array &params, bool fHelp)
std::map< std::string, std::vector< uint64_t > > getDatabase() const
string getSPName() const
Definition: mastercore_tx.h:90
json_spirit::Value getorderbook_MP(const json_spirit::Array &params, bool fHelp)
int populateRPCTransactionObject(uint256 txid, Object *txobj, string filterAddress="")
unsigned int init()
Definition: mastercore.h:212
CChain chainActive
The currently-connected chain of blocks.
Definition: main.cpp:48
uint256 txid_close
Definition: mastercore_sp.h:32
bool isPropertyDivisible(unsigned int propertyId)
unsigned int property_desired
Definition: mastercore_sp.h:22
Value gettransaction_MP(const Array &params, bool fHelp)
std::map< std::string, std::vector< uint64_t > > historicalData
Definition: mastercore_sp.h:43
json_spirit::Value getopenorders_MP(const json_spirit::Array &params, bool fHelp)
unsigned char getSubaction() const
Value send_MP(const Array &params, bool fHelp)
unsigned int getProperty() const
Definition: mastercore_tx.h:79
uint64_t timeclosed
Definition: mastercore_sp.h:31
Value getsto_MP(const Array &params, bool fHelp)
int getNumberOfMetaDExCancels(const uint256 txid)
uint64_t getAmountForSale() const
Definition: mastercore_sp.h:9
std::multimap< int64_t, TxPair > TxItems
Definition: wallet.h:237
bool IsMyAddress(const std::string &address)
const char * step2_SmartProperty(int &error_code)
uint64_t getAcceptAmountRemaining() const
bool isCrowdsalePurchase(uint256 txid, string address, int64_t *propertyId=NULL, int64_t *userTokens=NULL, int64_t *issuerTokens=NULL)
bool getValidMPTX(const uint256 &txid, int *block=NULL, unsigned int *type=NULL, uint64_t *nAmended=NULL)
int Height() const
Return the maximal height in the chain.
Definition: main.h:1043
void MetaDexObjectToJSON(const CMPMetaDEx &obj, Object &metadex_obj)
unsigned int next()
Definition: mastercore.h:222
string category
Definition: mastercore_sp.h:14
Value ValueFromAmount(int64_t amount)
Definition: rpcserver.cpp:94
Value listblocktransactions_MP(const Array &params, bool fHelp)
int getMPTradeCountTotal()
bool isCrowdsaleActive(unsigned int propertyId)
OfferMap my_offers
Definition: mastercore.cpp:359
CMPTradeList * t_tradelistdb
Definition: mastercore.cpp:156
#define LOCK(cs)
Definition: sync.h:156
uint256 getHash() const
void BalanceToJSON(const std::string &address, uint32_t property, Object &balance_obj)
std::vector< CTxOut > vout
Definition: core.h:191
uint64_t rounduint64(long double ld)
Converts numbers to 64 bit wide unsigned integer whereby any signedness is ignored.
const std::string COMMIT_INFO
uint64_t getBTCDesiredOriginal() const
uint64_t getMinFee() const
Value mscrpc(const Array &params, bool fHelp)
void printAll()
int check_prop_valid(int64_t tmpPropId, string error, string exist_error)
static bool error(const char *format)
Definition: util.h:148
bool max_tokens
Definition: mastercore_sp.h:29
bool isMPinBlockRange(int starting_block, int ending_block, bool bDeleteFound)
void printAll()
unsigned short getVersion() const
Definition: mastercore_tx.h:80
uint64_t missedTokens
Definition: mastercore_sp.h:30
void printStats()
std::map< string, CMPTally > mp_tally_map
Definition: mastercore.cpp:423
md_PropertiesMap metadex
bool isMetaDExOfferActive(const uint256 txid, unsigned int propertyId)
Definition: mastercore.cpp:509
int GetHeight(void)
Definition: mastercore.cpp:160
std::string error_str(int ec)
bool getSP(unsigned int spid, Entry &info)
bool isDivisible() const
Value listtransactions_MP(const Array &params, bool fHelp)
uint256 send_INTERNAL_1packet(const string &FromAddress, const string &ToAddress, const string &RedeemAddress, unsigned int PropertyID, uint64_t Amount, unsigned int PropertyID_2, uint64_t Amount_2, unsigned int TransactionType, int64_t additional, int *error_code=NULL)
string name
Definition: mastercore_sp.h:16
void MetaDEx_debug_print(bool bShowPriceLevel=false, bool bDisplay=false)
std::map< XDOUBLE, md_Set > md_PricesMap
unsigned int getProperty() const
int getBlock() const
unsigned int getProperty() const
std::string GetHex() const
Definition: uint256.h:297
uint256 findMetaDExCancel(const uint256 txid)
Value getallbalancesforaddress_MP(const Array &params, bool fHelp)
int64_t getTotalTokens(unsigned int propertyId, int64_t *n_owners_total=NULL)
Definition: mastercore.cpp:646
A transaction with a bunch of additional info that only the owner cares about.
Definition: wallet.h:451
CrowdMap my_crowds
Definition: mastercore.cpp:363
string subcategory
Definition: mastercore_sp.h:15
CMPTxList * p_txlistdb
Definition: mastercore.cpp:155
string getKeyValue(string key)
json_spirit::Value trade_MP(const json_spirit::Array &params, bool fHelp)
256-bit unsigned integer
Definition: uint256.h:531
static const int64_t COIN
Definition: util.h:38
std::set< CMPMetaDEx, MetaDEx_compare > md_Set
bool getPurchaseDetails(const uint256 txid, int purchaseNumber, string *buyer, string *seller, uint64_t *vout, uint64_t *propertyId, uint64_t *nValue)
bool ReadBlockFromDisk(CBlock &block, const CDiskBlockPos &pos)
Definition: main.cpp:1145
void printStats()
uint64_t getAmount() const
Definition: mastercore_tx.h:87
int step2_Value(void)
int getAcceptBlock()
unsigned char percentage
Definition: mastercore_sp.h:25
void PropertyToJSON(const CMPSPInfo::Entry &sProperty, Object &property_obj)
The block chain is a tree shaped structure starting with the genesis block at the root...
Definition: main.h:688
uint256 getHash() const
unsigned char getBlockTimeLimit() const
std::string ToString() const
Definition: uint256.h:340
void printStats()
int getMPTransactionCountBlock(int block)
unsigned int findSPByTX(uint256 const &txid)
CMPSTOList * s_stolistdb
Definition: mastercore.cpp:157
std::string FormatIndivisibleMP(int64_t n)
Definition: mastercore.cpp:343
string url
Definition: mastercore_sp.h:17
unsigned int peekNextSPID(unsigned char ecosystem)
A CWallet is an extension of a keystore, which also maintains a set of transactions and balances...
Definition: wallet.h:100
Value getbalance_MP(const Array &params, bool fHelp)
bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow)
Retrieve a transaction (from memory pool, or from disk, if possible)
Definition: main.cpp:1049
Value getgrants_MP(const Array &params, bool fHelp)
std::string FormatMP(unsigned int property, int64_t n, bool fSign)
Definition: mastercore.cpp:349
string FormatDivisibleMP(int64_t n, bool fSign)
Definition: mastercore.cpp:325
Value getcrowdsale_MP(const Array &params, bool fHelp)
std::string HelpExampleCli(string methodname, string args)
Definition: rpcserver.cpp:919
std::vector< CTransaction > vtx
Definition: core.h:400
The basic transaction that is broadcasted on the network and contained in blocks. ...
Definition: core.h:183
int nHeight
Definition: main.h:698
std::string getMasterCoreAlertString()
Definition: mastercore.cpp:530
static const CCheckpointData data
Definition: checkpoints.cpp:56
int getMPTransactionCountTotal()
const string getTypeString() const
Definition: mastercore_tx.h:78
uint64_t deadline
Definition: mastercore_sp.h:23
void MetaDexObjectsToJSON(std::vector< CMPMetaDEx > &vMetaDexObjs, Array &response)
unsigned int getPropertyId() const
Value gettrade_MP(const Array &params, bool fHelp)
void printAll()
map< uint256, CBlockIndex * > mapBlockIndex
Definition: main.cpp:47
vector< unsigned char > ParseHex(const char *psz)
Definition: util.cpp:419
int64_t StrToInt64(const std::string &str, bool divisible)
int64_t getMPbalance(const string &Address, unsigned int property, TallyType ttype)
Definition: mastercore.cpp:437
Value getactivecrowdsales_MP(const Array &params, bool fHelp)
int interpretPacket(CMPOffer *obj_o=NULL, CMPMetaDEx *mdex_o=NULL)
Value getinfo_MP(const Array &params, bool fHelp)
Value listproperties_MP(const Array &params, bool fHelp)
int64_t getUserAvailableMPbalance(const string &Address, unsigned int property)
Definition: mastercore.cpp:455
std::string getMySTOReceipts(string filterAddress)
Value getproperty_MP(const Array &params, bool fHelp)
uint256 getHash() const
CWallet * pwalletMain
int atoi(const std::string &str)
Definition: util.h:242
bool manual
Definition: mastercore_sp.h:39
unsigned int getDesProperty() const
TxItems OrderedTxItems(std::list< CAccountingEntry > &acentries, std::string strAccount="")
Get the wallet's activity log.
Definition: wallet.cpp:449
unsigned int getType() const
Definition: mastercore_tx.h:77