From 158250d480ccd97d3981e7124956d0b5dd8dc7a2 Mon Sep 17 00:00:00 2001 From: dexX7 Date: Tue, 29 Oct 2019 08:11:33 +0100 Subject: [PATCH 1/7] Add feature activation for freeing the DEx --- src/omnicore/rules.cpp | 13 +++++++++++++ src/omnicore/rules.h | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/src/omnicore/rules.cpp b/src/omnicore/rules.cpp index 947ab07ba59e0..fa251eeb6419d 100644 --- a/src/omnicore/rules.cpp +++ b/src/omnicore/rules.cpp @@ -253,6 +253,7 @@ CMainConsensusParams::CMainConsensusParams() TRADEALLPAIRS_FEATURE_BLOCK = 438500; FEES_FEATURE_BLOCK = 999999; FREEZENOTICE_FEATURE_BLOCK = 999999; + FREEDEX_FEATURE_BLOCK = 999999; } /** @@ -294,6 +295,7 @@ CTestNetConsensusParams::CTestNetConsensusParams() TRADEALLPAIRS_FEATURE_BLOCK = 0; FEES_FEATURE_BLOCK = 0; FREEZENOTICE_FEATURE_BLOCK = 0; + FREEDEX_FEATURE_BLOCK = 0; } /** @@ -335,6 +337,7 @@ CRegTestConsensusParams::CRegTestConsensusParams() TRADEALLPAIRS_FEATURE_BLOCK = 999999; FEES_FEATURE_BLOCK = 999999; FREEZENOTICE_FEATURE_BLOCK = 999999; + FREEDEX_FEATURE_BLOCK = 999999; } //! Consensus parameters for mainnet @@ -500,6 +503,9 @@ bool ActivateFeature(uint16_t featureId, int activationBlock, uint32_t minClient case FEATURE_FREEZENOTICE: MutableConsensusParams().FREEZENOTICE_FEATURE_BLOCK = activationBlock; break; + case FEATURE_FREEDEX: + MutableConsensusParams().FREEDEX_FEATURE_BLOCK = activationBlock; + break; default: supported = false; break; @@ -571,6 +577,9 @@ bool DeactivateFeature(uint16_t featureId, int transactionBlock) case FEATURE_FREEZENOTICE: MutableConsensusParams().FREEZENOTICE_FEATURE_BLOCK = 999999; break; + case FEATURE_FREEDEX: + MutableConsensusParams().FREEDEX_FEATURE_BLOCK = 999999; + break; default: return false; break; @@ -602,6 +611,7 @@ std::string GetFeatureName(uint16_t featureId) case FEATURE_FEES: return "Fee system (inc 0.05% fee from trades of non-Omni pairs)"; case FEATURE_STOV1: return "Cross-property Send To Owners"; case FEATURE_FREEZENOTICE: return "Activate the waiting period for enabling freezing"; + case FEATURE_FREEDEX: return "Activate trading of any token on the distributed exchange"; default: return "Unknown feature"; } @@ -649,6 +659,9 @@ bool IsFeatureActivated(uint16_t featureId, int transactionBlock) case FEATURE_FREEZENOTICE: activationBlock = params.FREEZENOTICE_FEATURE_BLOCK; break; + case FEATURE_FREEDEX: + activationBlock = params.FREEDEX_FEATURE_BLOCK; + break; default: return false; } diff --git a/src/omnicore/rules.h b/src/omnicore/rules.h index 38674ad8afefc..7c0f9e925960e 100644 --- a/src/omnicore/rules.h +++ b/src/omnicore/rules.h @@ -36,6 +36,8 @@ const uint16_t FEATURE_FEES = 9; const uint16_t FEATURE_STOV1 = 10; //! Feature identifier to activate the waiting period for enabling managed property address freezing const uint16_t FEATURE_FREEZENOTICE = 14; +//! Feature identifier to activate trading of any token on the distributed exchange +const uint16_t FEATURE_FREEDEX = 15; //! When (propertyTotalTokens / OMNI_FEE_THRESHOLD) is reached fee distribution will occur const int64_t OMNI_FEE_THRESHOLD = 100000; // 0.001% @@ -141,6 +143,8 @@ class CConsensusParams int FEES_FEATURE_BLOCK; //! Block to activate the waiting period for enabling managed property address freezing int FREEZENOTICE_FEATURE_BLOCK; + //! Block to activate the waiting period to activate trading of any token on the distributed exchange + int FREEDEX_FEATURE_BLOCK; /** Returns a mapping of transaction types, and the blocks at which they are enabled. */ virtual std::vector GetRestrictions() const; From f57de0ee41a0e27190b6b83a5176e27ddf9670c0 Mon Sep 17 00:00:00 2001 From: dexX7 Date: Tue, 29 Oct 2019 08:15:44 +0100 Subject: [PATCH 2/7] When creating DEx offer, check there is no other --- src/omnicore/dex.cpp | 14 ++++++++++++++ src/omnicore/dex.h | 1 + src/omnicore/rpcrequirements.cpp | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/omnicore/dex.cpp b/src/omnicore/dex.cpp index a5b07f482c224..cd87bf3930ed6 100644 --- a/src/omnicore/dex.cpp +++ b/src/omnicore/dex.cpp @@ -42,6 +42,20 @@ bool DEx_offerExists(const std::string& addressSeller, uint32_t propertyId) return !(my_offers.find(key) == my_offers.end()); } +/** + * Checks, if the seller has any open offer. + */ +bool DEx_hasOffer(const std::string& addressSeller) +{ + for (auto const& offer : my_offers) { + if (offer.first.find(addressSeller) == 0) { + return true; + } + } + + return false; +} + /** * Retrieves a sell offer. * diff --git a/src/omnicore/dex.h b/src/omnicore/dex.h index 2ce14e9a4ee0b..46d1bdc18b765 100644 --- a/src/omnicore/dex.h +++ b/src/omnicore/dex.h @@ -231,6 +231,7 @@ extern AcceptMap my_accepts; int64_t calculateDesiredBTC(const int64_t amountOffered, const int64_t amountDesired, const int64_t amountAvailable); bool DEx_offerExists(const std::string& addressSeller, uint32_t propertyId); +bool DEx_hasOffer(const std::string& addressSeller); CMPOffer* DEx_getOffer(const std::string& addressSeller, uint32_t propertyId); bool DEx_acceptExists(const std::string& addressSeller, uint32_t propertyId, const std::string& addressBuyer); CMPAccept* DEx_getAccept(const std::string& addressSeller, uint32_t propertyId, const std::string& addressBuyer); diff --git a/src/omnicore/rpcrequirements.cpp b/src/omnicore/rpcrequirements.cpp index c150b81a50305..27b6bf781432b 100644 --- a/src/omnicore/rpcrequirements.cpp +++ b/src/omnicore/rpcrequirements.cpp @@ -118,7 +118,7 @@ void RequireMatchingDExOffer(const std::string& address, uint32_t propertyId) void RequireNoOtherDExOffer(const std::string& address, uint32_t propertyId) { LOCK(cs_tally); - if (mastercore::DEx_offerExists(address, propertyId)) { + if (mastercore::DEx_hasOffer(address)) { throw JSONRPCError(RPC_TYPE_ERROR, "Another active sell offer from the given address already exists on the distributed exchange"); } } From eeaa8830291984bf8b659a62dc111e64d6677050 Mon Sep 17 00:00:00 2001 From: dexX7 Date: Tue, 29 Oct 2019 08:20:32 +0100 Subject: [PATCH 3/7] When creating DEx orders, parse amount based on token divisibility --- src/omnicore/rpcpayload.cpp | 2 +- src/omnicore/rpcrequirements.cpp | 2 +- src/omnicore/rpcrequirements.h | 2 +- src/omnicore/rpctx.cpp | 8 +++++--- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/omnicore/rpcpayload.cpp b/src/omnicore/rpcpayload.cpp index 38187c9caba9e..990d26899d08b 100644 --- a/src/omnicore/rpcpayload.cpp +++ b/src/omnicore/rpcpayload.cpp @@ -98,7 +98,7 @@ static UniValue omni_createpayload_dexsell(const JSONRPCRequest& request) int64_t minAcceptFee = 0; // depending on action if (action <= CMPTransaction::UPDATE) { // actions 3 permit zero values, skip check - amountForSale = ParseAmount(request.params[1], true); // TMSC/MSC is divisible + amountForSale = ParseAmount(request.params[1], isPropertyDivisible(propertyIdForSale)); amountDesired = ParseAmount(request.params[2], true); // BTC is divisible paymentWindow = ParseDExPaymentWindow(request.params[3]); minAcceptFee = ParseDExFee(request.params[4]); diff --git a/src/omnicore/rpcrequirements.cpp b/src/omnicore/rpcrequirements.cpp index 27b6bf781432b..6f1411d5b910c 100644 --- a/src/omnicore/rpcrequirements.cpp +++ b/src/omnicore/rpcrequirements.cpp @@ -115,7 +115,7 @@ void RequireMatchingDExOffer(const std::string& address, uint32_t propertyId) } } -void RequireNoOtherDExOffer(const std::string& address, uint32_t propertyId) +void RequireNoOtherDExOffer(const std::string& address) { LOCK(cs_tally); if (mastercore::DEx_hasOffer(address)) { diff --git a/src/omnicore/rpcrequirements.h b/src/omnicore/rpcrequirements.h index 87a5a0adf8abf..7046b57b5768d 100644 --- a/src/omnicore/rpcrequirements.h +++ b/src/omnicore/rpcrequirements.h @@ -15,7 +15,7 @@ void RequireActiveCrowdsale(uint32_t propertyId); void RequireManagedProperty(uint32_t propertyId); void RequireTokenIssuer(const std::string& address, uint32_t propertyId); void RequireMatchingDExOffer(const std::string& address, uint32_t propertyId); -void RequireNoOtherDExOffer(const std::string& address, uint32_t propertyId); +void RequireNoOtherDExOffer(const std::string& address); void RequireSaneReferenceAmount(int64_t amount); void RequireSaneDExPaymentWindow(const std::string& address, uint32_t propertyId); void RequireSaneDExFee(const std::string& address, uint32_t propertyId); diff --git a/src/omnicore/rpctx.cpp b/src/omnicore/rpctx.cpp index d7924b23b64a1..57198bf930819 100644 --- a/src/omnicore/rpctx.cpp +++ b/src/omnicore/rpctx.cpp @@ -11,8 +11,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -356,7 +358,7 @@ static UniValue omni_senddexsell(const JSONRPCRequest& request) // perform conversions if (action <= CMPTransaction::UPDATE) { // actions 3 permit zero values, skip check - amountForSale = ParseAmount(request.params[2], true); // TMSC/MSC is divisible + amountForSale = ParseAmount(request.params[2], isPropertyDivisible(propertyIdForSale)); amountDesired = ParseAmount(request.params[3], true); // BTC is divisible paymentWindow = ParseDExPaymentWindow(request.params[4]); minAcceptFee = ParseDExFee(request.params[5]); @@ -368,7 +370,7 @@ static UniValue omni_senddexsell(const JSONRPCRequest& request) { RequirePrimaryToken(propertyIdForSale); RequireBalance(fromAddress, propertyIdForSale, amountForSale); - RequireNoOtherDExOffer(fromAddress, propertyIdForSale); + RequireNoOtherDExOffer(fromAddress); break; } case CMPTransaction::UPDATE: @@ -441,7 +443,7 @@ static UniValue omni_senddexaccept(const JSONRPCRequest& request) std::string fromAddress = ParseAddress(request.params[0]); std::string toAddress = ParseAddress(request.params[1]); uint32_t propertyId = ParsePropertyId(request.params[2]); - int64_t amount = ParseAmount(request.params[3], true); // MSC/TMSC is divisible + int64_t amount = ParseAmount(request.params[3], isPropertyDivisible(propertyId)); bool override = (request.params.size() > 4) ? request.params[4].get_bool(): false; // perform checks From dceb321c2a4abde88e5e1e0442142dee1d1da766 Mon Sep 17 00:00:00 2001 From: dexX7 Date: Tue, 29 Oct 2019 08:30:17 +0100 Subject: [PATCH 4/7] Only check for primary tokens, when DEx is not free yet --- src/omnicore/rpctx.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/omnicore/rpctx.cpp b/src/omnicore/rpctx.cpp index 57198bf930819..d5db0ff1f9305 100644 --- a/src/omnicore/rpctx.cpp +++ b/src/omnicore/rpctx.cpp @@ -365,24 +365,25 @@ static UniValue omni_senddexsell(const JSONRPCRequest& request) } // perform checks + if (!IsFeatureActivated(FEATURE_FREEDEX, GetHeight())) { + RequirePrimaryToken(propertyIdForSale); + } + switch (action) { case CMPTransaction::NEW: { - RequirePrimaryToken(propertyIdForSale); RequireBalance(fromAddress, propertyIdForSale, amountForSale); RequireNoOtherDExOffer(fromAddress); break; } case CMPTransaction::UPDATE: { - RequirePrimaryToken(propertyIdForSale); RequireBalance(fromAddress, propertyIdForSale, amountForSale); RequireMatchingDExOffer(fromAddress, propertyIdForSale); break; } case CMPTransaction::CANCEL: { - RequirePrimaryToken(propertyIdForSale); RequireMatchingDExOffer(fromAddress, propertyIdForSale); break; } From 7e34e107c223962673782e110d449d9d76382cfd Mon Sep 17 00:00:00 2001 From: dexX7 Date: Tue, 29 Oct 2019 08:51:11 +0100 Subject: [PATCH 5/7] Handle DEx payment based on offer for sale --- src/omnicore/dex.cpp | 64 +++++++++++++++++++++++++++++++++++++++++--- src/omnicore/dex.h | 1 + 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/src/omnicore/dex.cpp b/src/omnicore/dex.cpp index cd87bf3930ed6..c4be723b64b3a 100644 --- a/src/omnicore/dex.cpp +++ b/src/omnicore/dex.cpp @@ -18,6 +18,7 @@ #include #include +#include #include @@ -56,6 +57,43 @@ bool DEx_hasOffer(const std::string& addressSeller) return false; } +/** + * Retrieves the identifier of the token for sale. + * + * NOTE: special care, if there are multiple open offers! + * NOTE: the assumption is there can only be one active offer per seller! + * + * @param addressSeller The address of the seller with an open offer + * @param retTokenId The token identifier for sale + * @return True, if there is an open offer + */ +bool DEx_getTokenForSale(const std::string& addressSeller, uint32_t& retTokenId) +{ + for (auto const& offer : my_offers) { + if (offer.first.find(addressSeller) == 0) { + + // Format is: "address-tokenid" + std::vector vstr; + boost::split(vstr, offer.first, boost::is_any_of("-"), boost::token_compress_on); + + if (vstr.size() != 2) { + PrintToLog("ERROR: failed to parse token for sale: %s\n", __func__, offer.first); + return false; + } + + try { + retTokenId = boost::lexical_cast(vstr[1]); + return true; + } + catch (boost::bad_lexical_cast const& e) { + PrintToLog("ERROR: failed to parse token for sale: %s (%s)\n", __func__, offer.first, e.what()); + } + } + } + + return false; +} + /** * Retrieves a sell offer. * @@ -457,12 +495,30 @@ int DEx_payment(const uint256& txid, unsigned int vout, const std::string& addre int rc = DEX_ERROR_PAYMENT; - uint32_t propertyId = OMNI_PROPERTY_MSC; // test for MSC accept first - CMPAccept* p_accept = DEx_getAccept(addressSeller, propertyId, addressBuyer); + uint32_t propertyId = OMNI_PROPERTY_MSC; + CMPAccept* p_accept = NULL; - if (!p_accept) { - propertyId = OMNI_PROPERTY_TMSC; // test for TMSC accept second + /** + * When the feature is not activated, first check, if there is an open offer + * for OMNI, and if not, check if there is an open offer for TOMNI. + * + * If the feature is activated, simply retrieve the token identifier of the + * token for sale. + */ + if (!IsFeatureActivated(FEATURE_FREEDEX, block)) { + propertyId = OMNI_PROPERTY_MSC; // test for OMNI accept first p_accept = DEx_getAccept(addressSeller, propertyId, addressBuyer); + + if (!p_accept) { + propertyId = OMNI_PROPERTY_TMSC; // test for TOMNI accept second + p_accept = DEx_getAccept(addressSeller, propertyId, addressBuyer); + } + } else { + // Retrieve and get the token for sale for that seller + + if (DEx_getTokenForSale(addressSeller, propertyId)) { + p_accept = DEx_getAccept(addressSeller, propertyId, addressBuyer); + } } if (!p_accept) { diff --git a/src/omnicore/dex.h b/src/omnicore/dex.h index 46d1bdc18b765..607fe4bfed2bb 100644 --- a/src/omnicore/dex.h +++ b/src/omnicore/dex.h @@ -232,6 +232,7 @@ int64_t calculateDesiredBTC(const int64_t amountOffered, const int64_t amountDes bool DEx_offerExists(const std::string& addressSeller, uint32_t propertyId); bool DEx_hasOffer(const std::string& addressSeller); +bool DEx_getTokenForSale(const std::string& addressSeller, uint32_t& retTokenId); CMPOffer* DEx_getOffer(const std::string& addressSeller, uint32_t propertyId); bool DEx_acceptExists(const std::string& addressSeller, uint32_t propertyId, const std::string& addressBuyer); CMPAccept* DEx_getAccept(const std::string& addressSeller, uint32_t propertyId, const std::string& addressBuyer); From 1e7bd04340f73c5d8bae35ab931adeb56dd0117d Mon Sep 17 00:00:00 2001 From: dexX7 Date: Tue, 29 Oct 2019 09:00:57 +0100 Subject: [PATCH 6/7] Ensure there can only be one active offer, when creating a new DEx offer --- src/omnicore/dex.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/omnicore/dex.cpp b/src/omnicore/dex.cpp index c4be723b64b3a..8d183188d5043 100644 --- a/src/omnicore/dex.cpp +++ b/src/omnicore/dex.cpp @@ -205,6 +205,13 @@ int DEx_offerCreate(const std::string& addressSeller, uint32_t propertyId, int64 return (DEX_ERROR_SELLOFFER -10); // offer already exists } + // Ensure further there can only be one active offer + if (IsFeatureActivated(FEATURE_FREEDEX, block)) { + if (DEx_hasOffer(addressSeller)) { + return (DEX_ERROR_SELLOFFER -10); // offer already exists + } + } + const std::string key = STR_SELLOFFER_ADDR_PROP_COMBO(addressSeller, propertyId); if (msc_debug_dex) PrintToLog("%s(%s|%s), nValue=%d)\n", __func__, addressSeller, key, amountOffered); From f7d958d51bf4734d6152872af24cd029b63b5765 Mon Sep 17 00:00:00 2001 From: dexX7 Date: Tue, 29 Oct 2019 09:14:17 +0100 Subject: [PATCH 7/7] Free the DEx and allow other tokens to be traded --- src/omnicore/tx.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/omnicore/tx.cpp b/src/omnicore/tx.cpp index cb79befb1c42f..190c5b4581fa3 100644 --- a/src/omnicore/tx.cpp +++ b/src/omnicore/tx.cpp @@ -1275,9 +1275,12 @@ int CMPTransaction::logicMath_TradeOffer() return (PKT_ERROR_TRADEOFFER -23); } - if (OMNI_PROPERTY_TMSC != property && OMNI_PROPERTY_MSC != property) { - PrintToLog("%s(): rejected: property for sale %d must be OMN or TOMN\n", __func__, property); - return (PKT_ERROR_TRADEOFFER -47); + // Ensure only OMNI and TOMNI are allowed, when the DEx is not yet free + if (!IsFeatureActivated(FEATURE_FREEDEX, block)) { + if (OMNI_PROPERTY_TMSC != property && OMNI_PROPERTY_MSC != property) { + PrintToLog("%s(): rejected: property for sale %d must be OMN or TOMN\n", __func__, property); + return (PKT_ERROR_TRADEOFFER -47); + } } // ------------------------------------------