diff --git a/src/omnicore/createpayload.cpp b/src/omnicore/createpayload.cpp index 03d9394e87fb..ff52b4fe3813 100644 --- a/src/omnicore/createpayload.cpp +++ b/src/omnicore/createpayload.cpp @@ -43,6 +43,21 @@ std::vector CreatePayload_SimpleSend(uint32_t propertyId, uint64_ return payload; } +std::vector CreatePayload_SendAll(uint8_t ecosystem) +{ + std::vector payload; + uint16_t messageVer = 0; + uint16_t messageType = 4; + mastercore::swapByteOrder16(messageVer); + mastercore::swapByteOrder16(messageType); + + PUSH_BACK_BYTES(payload, messageVer); + PUSH_BACK_BYTES(payload, messageType); + PUSH_BACK_BYTES(payload, ecosystem); + + return payload; +} + std::vector CreatePayload_DExSell(uint32_t propertyId, uint64_t amountForSale, uint64_t amountDesired, uint8_t timeLimit, uint64_t minFee, uint8_t subAction) { std::vector payload; diff --git a/src/omnicore/createpayload.h b/src/omnicore/createpayload.h index a838f3f04a2a..01e0720a692f 100644 --- a/src/omnicore/createpayload.h +++ b/src/omnicore/createpayload.h @@ -6,6 +6,7 @@ #include std::vector CreatePayload_SimpleSend(uint32_t propertyId, uint64_t amount); +std::vector CreatePayload_SendAll(uint8_t ecosystem); std::vector CreatePayload_DExSell(uint32_t propertyId, uint64_t amountForSale, uint64_t amountDesired, uint8_t timeLimit, uint64_t minFee, uint8_t subAction); std::vector CreatePayload_DExAccept(uint32_t propertyId, uint64_t amount); std::vector CreatePayload_SendToOwners(uint32_t propertyId, uint64_t amount); diff --git a/src/omnicore/doc/rpc-api.md b/src/omnicore/doc/rpc-api.md index fecd4bac2580..2071cd6e7745 100644 --- a/src/omnicore/doc/rpc-api.md +++ b/src/omnicore/doc/rpc-api.md @@ -152,7 +152,7 @@ Create new tokens with manageable supply. 1. ***fromaddress (string, required):*** the address to send from 2. ***propertyid (number, required):*** the identifier of the tokens to distribute 3. ***amount (string, required):*** the amount to distribute -4. ***redeemaddress (string, optional):*** an address that can spent the transaction dust (sender by default) +4. ***redeemaddress (string, optional):*** an address that can spend the transaction dust (sender by default) **Example:** @@ -293,6 +293,24 @@ Change the issuer on record of the given tokens. $ omnicore-cli "omni_sendchangeissuer" "1ARjWDkZ7kT9fwjPrjcQyvbXDkEySzKHwu" "3HTHRxu3aSDV4deakjC7VmsiUp7c6dfbvs" 3 ``` +### omni_sendall + +Transfers all available tokens in the given ecosystem to the recipient. + +**Arguments:** + +1. ***fromaddress (string, required):*** the address to send from +2. ***toaddress (string, required):*** the address of the receiver +3. ***ecosystem (number, required):*** the ecosystem of the tokens to send: (1) main, (2) test +4. ***redeemaddress (string, optional):*** an address that can spend the transaction dust (sender by default) +5. ***referenceamount (string, optional):*** a bitcoin amount that is sent to the receiver (minimal by default) + +**Example:** + +```bash +$ omnicore-cli "omni_sendall" "3M9qvHKtgARhqcMtM5cRT9VaiDJ5PSfQGY" "37FaKponF7zqoMLUjEiko25pDiuVH5YLEa" 2 +``` + ### omni_sendrawtx Broadcasts a raw Omni Layer transaction. @@ -302,7 +320,7 @@ Broadcasts a raw Omni Layer transaction. 1. ***fromaddress (string, required):*** the address to send from 2. ***rawtransaction (string, required):*** the hex-encoded raw transaction 3. ***referenceaddress (string, optional):*** a reference address (empty by default) -4. ***redeemaddress (string, optional):*** an address that can spent the transaction dust (sender by default) +4. ***redeemaddress (string, optional):*** an address that can spend the transaction dust (sender by default) 5. ***referenceamount (string, optional):*** a bitcoin amount that is sent to the receiver (minimal by default) **Example:** diff --git a/src/omnicore/omnicore.cpp b/src/omnicore/omnicore.cpp index eaf2540ea98a..187ea5f147d0 100644 --- a/src/omnicore/omnicore.cpp +++ b/src/omnicore/omnicore.cpp @@ -3111,24 +3111,24 @@ int CMPTxList::getNumberOfMetaDExCancels(const uint256 txid) return numberOfCancels; } -int CMPTxList::getNumberOfPurchases(const uint256 txid) +/** + * Returns the number of sub records. + */ +int CMPTxList::getNumberOfSubRecords(const uint256& txid) { - if (!pdb) return 0; - int numberOfPurchases = 0; - std::vector vstr; - string strValue; + int numberOfSubRecords = 0; + + std::string strValue; Status status = pdb->Get(readoptions, txid.ToString(), &strValue); - if (status.ok()) - { - // parse the string returned - boost::split(vstr, strValue, boost::is_any_of(":"), token_compress_on); - // obtain the number of purchases - if (4 <= vstr.size()) - { - numberOfPurchases = atoi(vstr[3]); + if (status.ok()) { + std::vector vstr; + boost::split(vstr, strValue, boost::is_any_of(":"), boost::token_compress_on); + if (4 <= vstr.size()) { + numberOfSubRecords = boost::lexical_cast(vstr[3]); } } - return numberOfPurchases; + + return numberOfSubRecords; } int CMPTxList::getMPTransactionCountTotal() @@ -3177,6 +3177,26 @@ string CMPTxList::getKeyValue(string key) if (status.ok()) { return strValue; } else { return ""; } } +/** + * Retrieves details about a "send all" record. + */ +bool CMPTxList::getSendAllDetails(const uint256& txid, int subSend, uint32_t& propertyId, int64_t& amount) +{ + std::string strKey = strprintf("%s-%d", txid.ToString(), subSend); + std::string strValue; + leveldb::Status status = pdb->Get(readoptions, strKey, &strValue); + if (status.ok()) { + std::vector vstr; + boost::split(vstr, strValue, boost::is_any_of(":"), boost::token_compress_on); + if (2 == vstr.size()) { + propertyId = boost::lexical_cast(vstr[0]); + amount = boost::lexical_cast(vstr[1]); + return true; + } + } + return false; +} + bool CMPTxList::getPurchaseDetails(const uint256 txid, int purchaseNumber, string *buyer, string *seller, uint64_t *vout, uint64_t *propertyId, uint64_t *nValue) { if (!pdb) return 0; @@ -3253,6 +3273,19 @@ void CMPTxList::recordMetaDExCancelTX(const uint256 &txidMaster, const uint256 & } } +/** + * Records a "send all" sub record. + */ +void CMPTxList::recordSendAllSubRecord(const uint256& txid, int subRecordNumber, uint32_t propertyId, int64_t nValue) +{ + std::string strKey = strprintf("%s-%d", txid.ToString(), subRecordNumber); + std::string strValue = strprintf("%d:%d", propertyId, nValue); + + leveldb::Status status = pdb->Put(writeoptions, strKey, strValue); + ++nWritten; + if (msc_debug_txdb) PrintToLog("%s(): store: %s=%s, status: %s\n", __func__, strKey, strValue, status.ToString()); +} + void CMPTxList::recordPaymentTX(const uint256 &txid, bool fValid, int nBlock, unsigned int vout, unsigned int propertyId, uint64_t nValue, string buyer, string seller) { if (!pdb) return; diff --git a/src/omnicore/omnicore.h b/src/omnicore/omnicore.h index dc777b689113..834f2182946b 100644 --- a/src/omnicore/omnicore.h +++ b/src/omnicore/omnicore.h @@ -66,6 +66,7 @@ enum TransactionType { MSC_TYPE_SIMPLE_SEND = 0, MSC_TYPE_RESTRICTED_SEND = 2, MSC_TYPE_SEND_TO_OWNERS = 3, + MSC_TYPE_SEND_ALL = 4, MSC_TYPE_SAVINGS_MARK = 10, MSC_TYPE_SAVINGS_COMPROMISED = 11, MSC_TYPE_RATELIMITED_MARK = 12, @@ -123,6 +124,7 @@ enum FILETYPES { #define PKT_ERROR_METADEX (-80000) #define METADEX_ERROR (-81000) #define PKT_ERROR_TOKENS (-82000) +#define PKT_ERROR_SEND_ALL (-83000) #define OMNI_PROPERTY_BTC 0 #define OMNI_PROPERTY_MSC 1 @@ -222,12 +224,17 @@ class CMPTxList : public CDBBase void recordTX(const uint256 &txid, bool fValid, int nBlock, unsigned int type, uint64_t nValue); void recordPaymentTX(const uint256 &txid, bool fValid, int nBlock, unsigned int vout, unsigned int propertyId, uint64_t nValue, string buyer, string seller); void recordMetaDExCancelTX(const uint256 &txidMaster, const uint256 &txidSub, bool fValid, int nBlock, unsigned int propertyId, uint64_t nValue); + /** Records a "send all" sub record. */ + void recordSendAllSubRecord(const uint256& txid, int subRecordNumber, uint32_t propertyId, int64_t nvalue); string getKeyValue(string key); uint256 findMetaDExCancel(const uint256 txid); - int getNumberOfPurchases(const uint256 txid); + /** Returns the number of sub records. */ + int getNumberOfSubRecords(const uint256& txid); int getNumberOfMetaDExCancels(const uint256 txid); bool getPurchaseDetails(const uint256 txid, int purchaseNumber, string *buyer, string *seller, uint64_t *vout, uint64_t *propertyId, uint64_t *nValue); + /** Retrieves details about a "send all" record. */ + bool getSendAllDetails(const uint256& txid, int subSend, uint32_t& propertyId, int64_t& amount); int getMPTransactionCountTotal(); int getMPTransactionCountBlock(int block); diff --git a/src/omnicore/rpctx.cpp b/src/omnicore/rpctx.cpp index 78862e4b9983..3835ada8da75 100644 --- a/src/omnicore/rpctx.cpp +++ b/src/omnicore/rpctx.cpp @@ -48,7 +48,7 @@ Value omni_send(const Array& params, bool fHelp) "2. toaddress (string, required) the address of the receiver\n" "3. propertyid (number, required) the identifier of the tokens to send\n" "4. amount (string, required) the amount to send\n" - "5. redeemaddress (string, optional) an address that can spent the transaction dust (sender by default)\n" + "5. redeemaddress (string, optional) an address that can spend the transaction dust (sender by default)\n" "6. referenceamount (string, optional) a bitcoin amount that is sent to the receiver (minimal by default)\n" "\nResult:\n" @@ -93,6 +93,61 @@ Value omni_send(const Array& params, bool fHelp) } } +// omni_sendall - send all +Value omni_sendall(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 3 || params.size() > 5) + throw runtime_error( + "omni_sendall \"fromaddress\" \"toaddress\" ecosystem ( \"redeemaddress\" \"referenceamount\" )\n" + + "\nTransfers all available tokens in the given ecosystem to the recipient.\n" + + "\nArguments:\n" + "1. fromaddress (string, required) the address to send from\n" + "2. toaddress (string, required) the address of the receiver\n" + "3. ecosystem (number, required) the ecosystem of the tokens to send: (1) main, (2) test\n" + "4. redeemaddress (string, optional) an address that can spend the transaction dust (sender by default)\n" + "5. referenceamount (string, optional) a bitcoin amount that is sent to the receiver (minimal by default)\n" + + "\nResult:\n" + "\"hash\" (string) the hex-encoded transaction hash\n" + + "\nExamples:\n" + + HelpExampleCli("omni_sendall", "\"3M9qvHKtgARhqcMtM5cRT9VaiDJ5PSfQGY\" \"37FaKponF7zqoMLUjEiko25pDiuVH5YLEa\" 2") + + HelpExampleRpc("omni_sendall", "\"3M9qvHKtgARhqcMtM5cRT9VaiDJ5PSfQGY\", \"37FaKponF7zqoMLUjEiko25pDiuVH5YLEa\" 2") + ); + + // obtain parameters & info + std::string fromAddress = ParseAddress(params[0]); + std::string toAddress = ParseAddress(params[1]); + uint8_t ecosystem = ParseEcosystem(params[2]); + std::string redeemAddress = (params.size() > 3 && !ParseText(params[3]).empty()) ? ParseAddress(params[3]): ""; + int64_t referenceAmount = (params.size() > 4) ? ParseAmount(params[4], true): 0; + + // perform checks + RequireSaneReferenceAmount(referenceAmount); + + // create a payload for the transaction + std::vector payload = CreatePayload_SendAll(ecosystem); + + // request the wallet build the transaction (and if needed commit it) + uint256 txid; + std::string rawHex; + int result = ClassAgnosticWalletTXBuilder(fromAddress, toAddress, redeemAddress, referenceAmount, payload, txid, rawHex, autoCommit); + + // check error and return the txid (or raw hex depending on autocommit) + if (result != 0) { + throw JSONRPCError(result, error_str(result)); + } else { + if (!autoCommit) { + return rawHex; + } else { + // TODO: pending + return txid.GetHex(); + } + } +} + // omni_senddexsell - DEx sell offer Value omni_senddexsell(const Array& params, bool fHelp) { @@ -480,7 +535,7 @@ Value omni_sendsto(const Array& params, bool fHelp) "1. fromaddress (string, required) the address to send from\n" "2. propertyid (number, required) the identifier of the tokens to distribute\n" "3. amount (string, required) the amount to distribute\n" - "4. redeemaddress (string, optional) an address that can spent the transaction dust (sender by default)\n" + "4. redeemaddress (string, optional) an address that can spend the transaction dust (sender by default)\n" "\nResult:\n" "\"hash\" (string) the hex-encoded transaction hash\n" diff --git a/src/omnicore/rpctxobject.cpp b/src/omnicore/rpctxobject.cpp index 58ec75f84e91..3e3890db6229 100644 --- a/src/omnicore/rpctxobject.cpp +++ b/src/omnicore/rpctxobject.cpp @@ -139,6 +139,9 @@ void populateRPCTypeInfo(CMPTransaction& mp_obj, Object& txobj, uint32_t txType, case MSC_TYPE_SEND_TO_OWNERS: populateRPCTypeSendToOwners(mp_obj, txobj, extendedDetails, extendedDetailsFilter); break; + case MSC_TYPE_SEND_ALL: + populateRPCTypeSendAll(mp_obj, txobj); + break; case MSC_TYPE_TRADE_OFFER: populateRPCTypeTradeOffer(mp_obj, txobj); break; @@ -201,6 +204,7 @@ bool showRefForTx(uint32_t txType) case MSC_TYPE_GRANT_PROPERTY_TOKENS: return true; case MSC_TYPE_REVOKE_PROPERTY_TOKENS: return false; case MSC_TYPE_CHANGE_ISSUER_ADDRESS: return true; + case MSC_TYPE_SEND_ALL: return true; } return true; // default to true, shouldn't be needed but just in case } @@ -243,6 +247,14 @@ void populateRPCTypeSendToOwners(CMPTransaction& omniObj, Object& txobj, bool ex if (extendedDetails) populateRPCExtendedTypeSendToOwners(omniObj.getHash(), extendedDetailsFilter, txobj); } +void populateRPCTypeSendAll(CMPTransaction& omniObj, Object& txobj) +{ + Array subSends; + if (omniObj.getEcosystem() == 1) txobj.push_back(Pair("ecosystem", "main")); + if (omniObj.getEcosystem() == 2) txobj.push_back(Pair("ecosystem", "test")); + if (populateRPCSendAllSubSends(omniObj.getHash(), subSends) > 0) txobj.push_back(Pair("subsends", subSends)); +} + void populateRPCTypeTradeOffer(CMPTransaction& omniObj, Object& txobj) { CMPOffer temp_offer(omniObj); @@ -519,6 +531,36 @@ void populateRPCExtendedTypeMetaDExCancel(const uint256& txid, Object& txobj) txobj.push_back(Pair("cancelledtransactions", cancelArray)); } +/* Function to enumerate sub sends for a given txid and add to supplied JSON array + * Note: this function exists as send all has the potential to carry multiple sends in a single transaction. + */ +int populateRPCSendAllSubSends(const uint256& txid, Array& subSends) +{ + int numberOfSubSends = 0; + { + LOCK(cs_tally); + numberOfSubSends = p_txlistdb->getNumberOfSubRecords(txid); + } + if (numberOfSubSends <= 0) { + PrintToLog("TXLISTDB Error: Transaction %s parsed as a send all but could not locate sub sends in txlistdb.\n", txid.GetHex()); + return -1; + } + for (int subSend = 1; subSend <= numberOfSubSends; subSend++) { + Object subSendObj; + uint32_t propertyId; + int64_t amount; + { + LOCK(cs_tally); + p_txlistdb->getSendAllDetails(txid, subSend, propertyId, amount); + } + subSendObj.push_back(Pair("propertyid", (uint64_t)propertyId)); + subSendObj.push_back(Pair("divisible", isPropertyDivisible(propertyId))); + subSendObj.push_back(Pair("amount", FormatMP(propertyId, amount))); + subSends.push_back(subSendObj); + } + return subSends.size(); +} + /* Function to enumerate DEx purchases for a given txid and add to supplied JSON array * Note: this function exists as it is feasible for a single transaction to carry multiple outputs * and thus make multiple purchases from a single transaction @@ -528,7 +570,7 @@ int populateRPCDExPurchases(const CTransaction& wtx, Array& purchases, std::stri int numberOfPurchases = 0; { LOCK(cs_tally); - numberOfPurchases = p_txlistdb->getNumberOfPurchases(wtx.GetHash()); + numberOfPurchases = p_txlistdb->getNumberOfSubRecords(wtx.GetHash()); } if (numberOfPurchases <= 0) { PrintToLog("TXLISTDB Error: Transaction %s parsed as a DEx payment but could not locate purchases in txlistdb.\n", wtx.GetHash().GetHex()); diff --git a/src/omnicore/rpctxobject.h b/src/omnicore/rpctxobject.h index 700a2f83b490..4d3c2b17aae9 100644 --- a/src/omnicore/rpctxobject.h +++ b/src/omnicore/rpctxobject.h @@ -14,6 +14,7 @@ void populateRPCTypeInfo(CMPTransaction& mp_obj, json_spirit::Object& txobj, uin void populateRPCTypeSimpleSend(CMPTransaction& omniObj, json_spirit::Object& txobj); void populateRPCTypeSendToOwners(CMPTransaction& omniObj, json_spirit::Object& txobj, bool extendedDetails, std::string extendedDetailsFilter); +void populateRPCTypeSendAll(CMPTransaction& omniObj, json_spirit::Object& txobj); void populateRPCTypeTradeOffer(CMPTransaction& omniObj, json_spirit::Object& txobj); void populateRPCTypeMetaDExTrade(CMPTransaction& omniObj, json_spirit::Object& txobj, bool extendedDetails); void populateRPCTypeMetaDExCancelPrice(CMPTransaction& omniObj, json_spirit::Object& txobj, bool extendedDetails); @@ -33,6 +34,8 @@ void populateRPCExtendedTypeMetaDExTrade(const uint256& txid, uint32_t propertyI void populateRPCExtendedTypeMetaDExCancel(const uint256& txid, json_spirit::Object& txobj); int populateRPCDExPurchases(const CTransaction& wtx, json_spirit::Array& purchases, std::string filterAddress); +int populateRPCSendAllSubSends(const uint256& txid, json_spirit::Array& subSends); + bool showRefForTx(uint32_t txType); #endif // OMNICORE_RPCTXOBJECT_H diff --git a/src/omnicore/rules.cpp b/src/omnicore/rules.cpp index fee541b72fa6..805a51fc026a 100644 --- a/src/omnicore/rules.cpp +++ b/src/omnicore/rules.cpp @@ -57,6 +57,8 @@ std::vector CConsensusParams::GetRestrictions() const { MSC_TYPE_METADEX_CANCEL_PAIR, MP_TX_PKT_V0, false, MSC_METADEX_BLOCK }, { MSC_TYPE_METADEX_CANCEL_ECOSYSTEM, MP_TX_PKT_V0, true, MSC_METADEX_BLOCK }, + { MSC_TYPE_SEND_ALL, MP_TX_PKT_V0, false, MSC_SEND_ALL_BLOCK }, + { MSC_TYPE_OFFER_ACCEPT_A_BET, MP_TX_PKT_V0, false, MSC_BET_BLOCK }, }; @@ -144,6 +146,7 @@ CMainConsensusParams::CMainConsensusParams() MSC_MANUALSP_BLOCK = 323230; MSC_STO_BLOCK = 342650; MSC_METADEX_BLOCK = 999999; + MSC_SEND_ALL_BLOCK = 999999; MSC_BET_BLOCK = 999999; // Other feature activations: GRANTEFFECTS_FEATURE_BLOCK = 999999; @@ -177,6 +180,7 @@ CTestNetConsensusParams::CTestNetConsensusParams() MSC_MANUALSP_BLOCK = 0; MSC_STO_BLOCK = 0; MSC_METADEX_BLOCK = 0; + MSC_SEND_ALL_BLOCK = 0; MSC_BET_BLOCK = 999999; // Other feature activations: GRANTEFFECTS_FEATURE_BLOCK = 999999; @@ -210,6 +214,7 @@ CRegTestConsensusParams::CRegTestConsensusParams() MSC_MANUALSP_BLOCK = 0; MSC_STO_BLOCK = 0; MSC_METADEX_BLOCK = 0; + MSC_SEND_ALL_BLOCK = 0; MSC_BET_BLOCK = 999999; // Other feature activations: GRANTEFFECTS_FEATURE_BLOCK = 999999; diff --git a/src/omnicore/rules.h b/src/omnicore/rules.h index aaa854e973ae..8498382306a9 100644 --- a/src/omnicore/rules.h +++ b/src/omnicore/rules.h @@ -96,6 +96,8 @@ class CConsensusParams int MSC_STO_BLOCK; //! Block to enable MetaDEx transactions int MSC_METADEX_BLOCK; + //! Block to enable "send all" transactions + int MSC_SEND_ALL_BLOCK; //! Block to enable betting transactions int MSC_BET_BLOCK; diff --git a/src/omnicore/test/create_payload_tests.cpp b/src/omnicore/test/create_payload_tests.cpp index e926249c1b79..d8eded810baa 100644 --- a/src/omnicore/test/create_payload_tests.cpp +++ b/src/omnicore/test/create_payload_tests.cpp @@ -30,6 +30,15 @@ BOOST_AUTO_TEST_CASE(payload_send_to_owners) BOOST_CHECK_EQUAL(HexStr(vch), "00000003000000010000000005f5e100"); } +BOOST_AUTO_TEST_CASE(payload_send_all) +{ + // Send to owners [type 4, version 0] + std::vector vch = CreatePayload_SendAll( + static_cast(2)); // ecosystem: Test + + BOOST_CHECK_EQUAL(HexStr(vch), "0000000402"); +} + BOOST_AUTO_TEST_CASE(payload_dex_offer) { // Sell tokens for bitcoins [type 20, version 1] diff --git a/src/omnicore/tx.cpp b/src/omnicore/tx.cpp index 65fb128c9740..ec05a299a0e6 100644 --- a/src/omnicore/tx.cpp +++ b/src/omnicore/tx.cpp @@ -39,6 +39,7 @@ std::string mastercore::strTransactionType(uint16_t txType) case MSC_TYPE_SIMPLE_SEND: return "Simple Send"; case MSC_TYPE_RESTRICTED_SEND: return "Restricted Send"; case MSC_TYPE_SEND_TO_OWNERS: return "Send To Owners"; + case MSC_TYPE_SEND_ALL: return "Send All"; case MSC_TYPE_SAVINGS_MARK: return "Savings"; case MSC_TYPE_SAVINGS_COMPROMISED: return "Savings COMPROMISED"; case MSC_TYPE_RATELIMITED_MARK: return "Rate-Limiting"; @@ -104,6 +105,9 @@ bool CMPTransaction::interpret_Transaction() case MSC_TYPE_SEND_TO_OWNERS: return interpret_SendToOwners(); + case MSC_TYPE_SEND_ALL: + return interpret_SendAll(); + case MSC_TYPE_TRADE_OFFER: return interpret_TradeOffer(); @@ -217,6 +221,23 @@ bool CMPTransaction::interpret_SendToOwners() return true; } +/** Tx 4 */ +bool CMPTransaction::interpret_SendAll() +{ + if (pkt_size < 5) { + return false; + } + memcpy(&ecosystem, &pkt[4], 1); + + property = ecosystem; // provide a hint for the UI, TODO: better handling! + + if ((!rpcOnly && msc_debug_packets) || msc_debug_packets_readonly) { + PrintToLog("\t ecosystem: %d\n", (int)ecosystem); + } + + return true; +} + /** Tx 20 */ bool CMPTransaction::interpret_TradeOffer() { @@ -661,6 +682,9 @@ int CMPTransaction::interpretPacket() case MSC_TYPE_SEND_TO_OWNERS: return logicMath_SendToOwners(); + case MSC_TYPE_SEND_ALL: + return logicMath_SendAll(); + case MSC_TYPE_TRADE_OFFER: return logicMath_TradeOffer(); @@ -950,6 +974,63 @@ int CMPTransaction::logicMath_SendToOwners() return 0; } +/** Tx 4 */ +int CMPTransaction::logicMath_SendAll() +{ + if (!IsTransactionTypeAllowed(block, ecosystem, type, version)) { + PrintToLog("%s(): rejected: type %d or version %d not permitted for property %d at block %d\n", + __func__, + type, + version, + ecosystem, + block); + return (PKT_ERROR_SEND_ALL -22); + } + + // ------------------------------------------ + + // Special case: if can't find the receiver -- assume send to self! + if (receiver.empty()) { + receiver = sender; + } + + CMPTally* ptally = getTally(sender); + if (ptally == NULL) { + PrintToLog("%s(): rejected: sender %s has no tokens to send\n", __func__, sender); + return (PKT_ERROR_SEND_ALL -54); + } + + uint32_t propertyId = ptally->init(); + int numberOfPropertiesSent = 0; + + while (0 != (propertyId = ptally->next())) { + // only transfer tokens in the specified ecosystem + if (ecosystem == OMNI_PROPERTY_MSC && isTestEcosystemProperty(propertyId)) { + continue; + } + if (ecosystem == OMNI_PROPERTY_TMSC && isMainEcosystemProperty(propertyId)) { + continue; + } + + int64_t moneyAvailable = ptally->getMoney(propertyId, BALANCE); + if (moneyAvailable > 0) { + ++numberOfPropertiesSent; + assert(update_tally_map(sender, propertyId, -moneyAvailable, BALANCE)); + assert(update_tally_map(receiver, propertyId, moneyAvailable, BALANCE)); + p_txlistdb->recordSendAllSubRecord(txid, numberOfPropertiesSent, propertyId, moneyAvailable); + } + } + + if (!numberOfPropertiesSent) { + PrintToLog("%s(): rejected: sender %s has no tokens to send\n", __func__, sender); + return (PKT_ERROR_SEND_ALL -55); + } + + nNewValue = numberOfPropertiesSent; + + return 0; +} + /** Tx 20 */ int CMPTransaction::logicMath_TradeOffer() { diff --git a/src/omnicore/tx.h b/src/omnicore/tx.h index 4c4e9ccca993..2530d39976e0 100644 --- a/src/omnicore/tx.h +++ b/src/omnicore/tx.h @@ -54,7 +54,7 @@ class CMPTransaction // CreatePropertyMananged, GrantTokens, RevokeTokens, ChangeIssuer unsigned int property; - // CreatePropertyFixed, CreatePropertyVariable, CreatePropertyMananged, MetaDEx + // CreatePropertyFixed, CreatePropertyVariable, CreatePropertyMananged, MetaDEx, SendAll unsigned char ecosystem; // CreatePropertyFixed, CreatePropertyVariable, CreatePropertyMananged @@ -99,6 +99,7 @@ class CMPTransaction bool interpret_TransactionType(); bool interpret_SimpleSend(); bool interpret_SendToOwners(); + bool interpret_SendAll(); bool interpret_TradeOffer(); bool interpret_MetaDExTrade(); bool interpret_MetaDExCancelPrice(); @@ -120,6 +121,7 @@ class CMPTransaction */ int logicMath_SimpleSend(); int logicMath_SendToOwners(); + int logicMath_SendAll(); int logicMath_TradeOffer(); int logicMath_AcceptOffer_BTC(); int logicMath_MetaDExTrade(); diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index c9e0200b728c..a5c43af83367 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -127,7 +127,9 @@ class TxViewDelegate : public QAbstractItemDelegate omniAmountStr = QString::fromStdString(FormatIndivisibleMP(p_pending->amount) + getTokenLabel(p_pending->prop)); } // override amount for cancels - if (p_pending->type == MSC_TYPE_METADEX_CANCEL_PRICE || p_pending->type == MSC_TYPE_METADEX_CANCEL_PAIR || p_pending->type == MSC_TYPE_METADEX_CANCEL_ECOSYSTEM) { + if (p_pending->type == MSC_TYPE_METADEX_CANCEL_PRICE || p_pending->type == MSC_TYPE_METADEX_CANCEL_PAIR || + p_pending->type == MSC_TYPE_METADEX_CANCEL_ECOSYSTEM || p_pending->type == MSC_TYPE_SEND_ALL || + p_pending->type == 0 /* Unknown */) { omniAmountStr = QString::fromStdString("N/A"); } } @@ -166,7 +168,7 @@ class TxViewDelegate : public QAbstractItemDelegate } bool bIsBuy = IsMyAddress(tmpBuyer); LOCK(cs_tally); - int numberOfPurchases=p_txlistdb->getNumberOfPurchases(hash); + int numberOfPurchases=p_txlistdb->getNumberOfSubRecords(hash); if (0getPurchaseDetails(hash,purchaseNumber,&tmpBuyer,&tmpSeller,&tmpVout,&tmpPropertyId,&tmpNValue); @@ -203,7 +205,9 @@ class TxViewDelegate : public QAbstractItemDelegate } // override amount for cancels - if (mp_obj.getType() == MSC_TYPE_METADEX_CANCEL_PRICE || mp_obj.getType() == MSC_TYPE_METADEX_CANCEL_PAIR || mp_obj.getType() == MSC_TYPE_METADEX_CANCEL_ECOSYSTEM) { + if (mp_obj.getType() == MSC_TYPE_METADEX_CANCEL_PRICE || mp_obj.getType() == MSC_TYPE_METADEX_CANCEL_PAIR || + mp_obj.getType() == MSC_TYPE_METADEX_CANCEL_ECOSYSTEM || mp_obj.getType() == MSC_TYPE_SEND_ALL || + mp_obj.getType() == 0 /* Unknown */) { omniAmountStr = QString::fromStdString("N/A"); } diff --git a/src/qt/txhistorydialog.cpp b/src/qt/txhistorydialog.cpp index 4ccfbf35dbab..d98701aef8a5 100644 --- a/src/qt/txhistorydialog.cpp +++ b/src/qt/txhistorydialog.cpp @@ -239,7 +239,11 @@ int TXHistoryDialog::PopulateHistoryMap() htxo.amount = "-" + FormatShortMP(pending.prop, pending.amount) + getTokenLabel(pending.prop); bool fundsMoved = true; htxo.txType = shrinkTxType(pending.type, &fundsMoved); - if (pending.type == MSC_TYPE_METADEX_CANCEL_PRICE || pending.type == MSC_TYPE_METADEX_CANCEL_PAIR || pending.type == MSC_TYPE_METADEX_CANCEL_ECOSYSTEM) htxo.amount = "N/A"; + if (pending.type == MSC_TYPE_METADEX_CANCEL_PRICE || pending.type == MSC_TYPE_METADEX_CANCEL_PAIR || + pending.type == MSC_TYPE_METADEX_CANCEL_ECOSYSTEM || pending.type == MSC_TYPE_SEND_ALL || + pending.type == 0 /* Unknown */) { + htxo.amount = "N/A"; + } txHistoryMap.insert(std::make_pair(txHash, htxo)); nProcessed++; continue; @@ -272,7 +276,7 @@ int TXHistoryDialog::PopulateHistoryMap() p_txlistdb->getPurchaseDetails(txHash, 1, &tmpBuyer, &tmpSeller, &tmpVout, &tmpPropertyId, &tmpNValue); } bIsBuy = IsMyAddress(tmpBuyer); - numberOfPurchases = p_txlistdb->getNumberOfPurchases(txHash); + numberOfPurchases = p_txlistdb->getNumberOfSubRecords(txHash); if (0 >= numberOfPurchases) continue; for (int purchaseNumber = 1; purchaseNumber <= numberOfPurchases; purchaseNumber++) { LOCK(cs_tally); @@ -320,7 +324,8 @@ int TXHistoryDialog::PopulateHistoryMap() } } // override - hide display amount for cancels and unknown transactions as we can't display amount/property as no prop exists - if (type == MSC_TYPE_METADEX_CANCEL_PRICE || type == MSC_TYPE_METADEX_CANCEL_PAIR || type == MSC_TYPE_METADEX_CANCEL_ECOSYSTEM || htxo.txType == "Unknown") { + if (type == MSC_TYPE_METADEX_CANCEL_PRICE || type == MSC_TYPE_METADEX_CANCEL_PAIR || + type == MSC_TYPE_METADEX_CANCEL_ECOSYSTEM || type == MSC_TYPE_SEND_ALL || htxo.txType == "Unknown") { displayAmount = "N/A"; } // override - display amount received not STO amount in packet (the total amount) for STOs I didn't send @@ -503,6 +508,7 @@ std::string TXHistoryDialog::shrinkTxType(int txType, bool *fundsMoved) case MSC_TYPE_SIMPLE_SEND: displayType = "Send"; break; case MSC_TYPE_RESTRICTED_SEND: displayType = "Rest. Send"; break; case MSC_TYPE_SEND_TO_OWNERS: displayType = "Send To Owners"; break; + case MSC_TYPE_SEND_ALL: displayType = "Send All"; break; case MSC_TYPE_SAVINGS_MARK: displayType = "Mark Savings"; *fundsMoved = false; break; case MSC_TYPE_SAVINGS_COMPROMISED: ; displayType = "Lock Savings"; break; case MSC_TYPE_RATELIMITED_MARK: displayType = "Rate Limit"; break; diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index f763dfa0aa14..43fd18f15614 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -133,6 +133,7 @@ static const CRPCConvertParam vRPCConvertParams[] = /* Omni Core - transaction calls */ { "omni_send", 2 }, { "omni_sendsto", 1 }, + { "omni_sendall", 2 }, { "omni_sendtrade", 1 }, { "omni_sendtrade", 3 }, { "omni_sendcanceltradesbyprice", 1 }, diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 124a5c5a4462..8081b93e2b74 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -400,6 +400,7 @@ static const CRPCCommand vRPCCommands[] = { "omni layer (transaction creation)", "omni_sendrevoke", &omni_sendrevoke, false, true, true }, { "omni layer (transaction creation)", "omni_sendclosecrowdsale", &omni_sendclosecrowdsale, false, true, true }, { "omni layer (transaction creation)", "omni_sendchangeissuer", &omni_sendchangeissuer, false, true, true }, + { "omni layer (transaction creation)", "omni_sendall", &omni_sendall, false, true, true }, /* Omni Core hidden calls - development usage (not shown in help) */ /* CATEGORY NAME ACTOR (FUNCTION) OKSAFEMODE THREADSAFE REQWALLET */ diff --git a/src/rpcserver.h b/src/rpcserver.h index 66bfb4c24bb9..7d981f0ea439 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -267,6 +267,7 @@ extern json_spirit::Value omni_sendgrant(const json_spirit::Array& params, bool extern json_spirit::Value omni_sendrevoke(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value omni_sendclosecrowdsale(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value omni_sendchangeissuer(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value omni_sendall(const json_spirit::Array& params, bool fHelp); /* Omni Core hidden calls - development usage (not shown in help) */ extern json_spirit::Value mscrpc(const json_spirit::Array& params, bool fHelp);