diff --git a/.travis.yml b/.travis.yml index caa14339fcc82..b5afc6cd1ce93 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ addons: hostname: bitcoin-tester os: linux -language: generic +language: minimal cache: directories: - depends/built diff --git a/src/omnicore/errors.h b/src/omnicore/errors.h index 5f085bb6c92d8..b564a5d066251 100644 --- a/src/omnicore/errors.h +++ b/src/omnicore/errors.h @@ -85,6 +85,9 @@ inline std::string error_str(int ec) { case PKT_ERROR -2: ec_str = "Failed to interpret transaction"; break; + case PKT_ERROR -3: + ec_str = "Sender is frozen for the property"; + break; case PKT_ERROR -22: ec_str = "Transaction type or version not permitted"; break; @@ -321,6 +324,18 @@ inline std::string error_str(int ec) { case PKT_ERROR_TOKENS -46: ec_str = "Receiver has an active crowdsale"; break; + case PKT_ERROR_TOKENS -47: + ec_str = "Freezing is not enabled for the property"; + break; + case PKT_ERROR_TOKENS -48: + ec_str = "Address is not frozen"; + break; + case PKT_ERROR_TOKENS -49: + ec_str = "Freezing is already enabled for the property"; + break; + case PKT_ERROR_TOKENS -50: + ec_str = "Address is already frozen"; + break; default: ec_str = "Unknown error"; diff --git a/src/omnicore/omnicore.cpp b/src/omnicore/omnicore.cpp index 00fe460e3c49c..1c04a7e21fa1a 100644 --- a/src/omnicore/omnicore.cpp +++ b/src/omnicore/omnicore.cpp @@ -110,6 +110,11 @@ std::map global_balance_reserved; //! Vector containing a list of properties relative to the wallet std::set global_wallet_property_list; +//! Set containing properties that have freezing enabled +std::set > setFreezingEnabledProperties; +//! Set containing addresses that have been frozen +std::set > setFrozenAddresses; + /** * Used to indicate, whether to automatically commit created transactions. * @@ -299,6 +304,84 @@ bool mastercore::isMainEcosystemProperty(uint32_t propertyId) return false; } +void mastercore::ClearFreezeState() +{ + // Should only ever be called in the event of a reorg + setFreezingEnabledProperties.clear(); + setFrozenAddresses.clear(); +} + +void mastercore::enableFreezing(uint32_t propertyId, int block) +{ + if (!IsFeatureActivated(FEATURE_FREEZENOTICE, block)) { + setFreezingEnabledProperties.insert(std::make_pair(propertyId, block)); + PrintToLog("Freezing for property %d has been enabled.\n", propertyId); + } else { + const CConsensusParams& params = ConsensusParams(); + int liveBlock = params.OMNI_FREEZE_WAIT_PERIOD + block; + setFreezingEnabledProperties.insert(std::make_pair(propertyId, liveBlock)); + PrintToLog("Freezing for property %d will be enabled at block %d.\n", propertyId, liveBlock); + } +} + +void mastercore::disableFreezing(uint32_t propertyId) +{ + int liveBlock = 0; + for (std::set >::iterator it = setFreezingEnabledProperties.begin(); it != setFreezingEnabledProperties.end(); it++) { + if (propertyId == (*it).first) { + liveBlock = (*it).second; + } + } + if (liveBlock == 0) { + PrintToLog("ERROR: Failed to determine live block to disable freezing for property %d!\n", propertyId); + } else { + setFreezingEnabledProperties.erase(std::make_pair(propertyId, liveBlock)); + PrintToLog("Freezing for property %d has been disabled.\n", propertyId); + } + + // When disabling freezing for a property, all frozen addresses for that property will be unfrozen! + for (std::set >::iterator it = setFrozenAddresses.begin(); it != setFrozenAddresses.end(); ) { + if ((*it).second == propertyId) { + PrintToLog("Address %s has been unfrozen for property %d.\n", (*it).first, propertyId); + it = setFrozenAddresses.erase(it); + } else { + it++; + } + } +} + +bool mastercore::isFreezingEnabled(uint32_t propertyId, int block) +{ + for (std::set >::iterator it = setFreezingEnabledProperties.begin(); it != setFreezingEnabledProperties.end(); it++) { + uint32_t itemPropertyId = (*it).first; + int itemBlock = (*it).second; + if (propertyId == itemPropertyId && block >= itemBlock) { + return true; + } + } + return false; +} + +void mastercore::freezeAddress(const std::string& address, uint32_t propertyId) +{ + setFrozenAddresses.insert(std::make_pair(address, propertyId)); + PrintToLog("Address %s has been frozen for property %d.\n", address, propertyId); +} + +void mastercore::unfreezeAddress(const std::string& address, uint32_t propertyId) +{ + setFrozenAddresses.erase(std::make_pair(address, propertyId)); + PrintToLog("Address %s has been unfrozen for property %d.\n", address, propertyId); +} + +bool mastercore::isAddressFrozen(const std::string& address, uint32_t propertyId) +{ + if (setFrozenAddresses.find(std::make_pair(address, propertyId)) != setFrozenAddresses.end()) { + return true; + } + return false; +} + std::string mastercore::getTokenLabel(uint32_t propertyId) { std::string tokenStr; @@ -374,6 +457,10 @@ bool mastercore::update_tally_map(const std::string& who, uint32_t propertyId, i LOCK(cs_tally); + if (ttype == BALANCE && amount < 0) { + assert(!isAddressFrozen(who, propertyId)); // for safety, this should never fail if everything else is working properly. + } + before = getMPbalance(who, propertyId, ttype); std::unordered_map::iterator my_it = mp_tally_map.find(who); @@ -2086,6 +2173,7 @@ void clear_all_state() ResetConsensusParams(); ClearActivations(); ClearAlerts(); + ClearFreezeState(); // LevelDB based storage _my_sps->Clear(); @@ -2224,6 +2312,15 @@ int mastercore_init() // load all alerts from levelDB (and immediately expire old ones) p_txlistdb->LoadAlerts(nWaterlineBlock); + // load the state of any freeable properties and frozen addresses from levelDB + if (!p_txlistdb->LoadFreezeState(nWaterlineBlock)) { + std::string strShutdownReason = "Failed to load freeze state from levelDB. It is unsafe to continue.\n"; + PrintToLog(strShutdownReason); + if (!GetBoolArg("-overrideforcedshutdown", false)) { + AbortNode(strShutdownReason, strShutdownReason); + } + } + // initial scan msc_initial_scan(nWaterlineBlock); @@ -2538,6 +2635,102 @@ std::set CMPTxList::GetSeedBlocks(int startHeight, int endHeight) return setSeedBlocks; } +bool CMPTxList::CheckForFreezeTxs(int blockHeight) +{ + assert(pdb); + Iterator* it = NewIterator(); + + for (it->SeekToFirst(); it->Valid(); it->Next()) { + std::string itData = it->value().ToString(); + std::vector vstr; + boost::split(vstr, itData, boost::is_any_of(":"), token_compress_on); + if (4 != vstr.size()) continue; + int block = atoi(vstr[1]); + if (block < blockHeight) continue; + uint16_t txtype = atoi(vstr[2]); + if (txtype == MSC_TYPE_FREEZE_PROPERTY_TOKENS || txtype == MSC_TYPE_UNFREEZE_PROPERTY_TOKENS || + txtype == MSC_TYPE_ENABLE_FREEZING || txtype == MSC_TYPE_DISABLE_FREEZING) { + delete it; + return true; + } + } + + delete it; + return false; +} + +bool CMPTxList::LoadFreezeState(int blockHeight) +{ + assert(pdb); + std::vector > loadOrder; + Iterator* it = NewIterator(); + PrintToLog("Loading freeze state from levelDB\n"); + + for (it->SeekToFirst(); it->Valid(); it->Next()) { + std::string itData = it->value().ToString(); + std::vector vstr; + boost::split(vstr, itData, boost::is_any_of(":"), token_compress_on); + if (4 != vstr.size()) continue; + uint16_t txtype = atoi(vstr[2]); + if (txtype != MSC_TYPE_FREEZE_PROPERTY_TOKENS && txtype != MSC_TYPE_UNFREEZE_PROPERTY_TOKENS && + txtype != MSC_TYPE_ENABLE_FREEZING && txtype != MSC_TYPE_DISABLE_FREEZING) continue; + if (atoi(vstr[0]) != 1) continue; // invalid, ignore + uint256 txid = uint256S(it->key().ToString()); + int txPosition = p_OmniTXDB->FetchTransactionPosition(txid); + std::string sortKey = strprintf("%06d%010d", atoi(vstr[1]), txPosition); + loadOrder.push_back(std::make_pair(sortKey, txid)); + } + + delete it; + + std::sort (loadOrder.begin(), loadOrder.end()); + + for (std::vector >::iterator it = loadOrder.begin(); it != loadOrder.end(); ++it) { + uint256 hash = (*it).second; + uint256 blockHash; + CTransaction wtx; + CMPTransaction mp_obj; + if (!GetTransaction(hash, wtx, Params().GetConsensus(), blockHash, true)) { + PrintToLog("ERROR: While loading freeze transaction %s: tx in levelDB but does not exist.\n", hash.GetHex()); + return false; + } + if (blockHash.IsNull() || (NULL == GetBlockIndex(blockHash))) { + PrintToLog("ERROR: While loading freeze transaction %s: failed to retrieve block hash.\n", hash.GetHex()); + return false; + } + CBlockIndex* pBlockIndex = GetBlockIndex(blockHash); + if (NULL == pBlockIndex) { + PrintToLog("ERROR: While loading freeze transaction %s: failed to retrieve block index.\n", hash.GetHex()); + return false; + } + int txBlockHeight = pBlockIndex->nHeight; + if (txBlockHeight > blockHeight) { + PrintToLog("ERROR: While loading freeze transaction %s: transaction is in the future.\n", hash.GetHex()); + return false; + } + if (0 != ParseTransaction(wtx, txBlockHeight, 0, mp_obj)) { + PrintToLog("ERROR: While loading freeze transaction %s: failed ParseTransaction.\n", hash.GetHex()); + return false; + } + if (!mp_obj.interpret_Transaction()) { + PrintToLog("ERROR: While loading freeze transaction %s: failed interpret_Transaction.\n", hash.GetHex()); + return false; + } + if (MSC_TYPE_FREEZE_PROPERTY_TOKENS != mp_obj.getType() && MSC_TYPE_UNFREEZE_PROPERTY_TOKENS != mp_obj.getType() && + MSC_TYPE_ENABLE_FREEZING != mp_obj.getType() && MSC_TYPE_DISABLE_FREEZING != mp_obj.getType()) { + PrintToLog("ERROR: While loading freeze transaction %s: levelDB type mismatch, not a freeze transaction.\n", hash.GetHex()); + return false; + } + mp_obj.unlockLogic(); + if (0 != mp_obj.interpretPacket()) { + PrintToLog("ERROR: While loading freeze transaction %s: non-zero return from interpretPacket\n", hash.GetHex()); + return false; + } + } + + return true; +} + void CMPTxList::LoadActivations(int blockHeight) { if (!pdb) return; @@ -3716,6 +3909,9 @@ int mastercore_handler_block_begin(int nBlockPrev, CBlockIndex const * pBlockInd if (reorgRecoveryMode > 0) { reorgRecoveryMode = 0; // clear reorgRecovery here as this is likely re-entrant + // Check if any freeze related transactions would be rolled back - if so wipe the state and startclean + bool reorgContainsFreeze = p_txlistdb->CheckForFreezeTxs(pBlockIndex->nHeight); + // NOTE: The blockNum parameter is inclusive, so deleteAboveBlock(1000) will delete records in block 1000 and above. p_txlistdb->isMPinBlockRange(pBlockIndex->nHeight, reorgRecoveryMaxHeight, true); t_tradelistdb->deleteAboveBlock(pBlockIndex->nHeight); @@ -3726,12 +3922,17 @@ int mastercore_handler_block_begin(int nBlockPrev, CBlockIndex const * pBlockInd nWaterlineBlock = ConsensusParams().GENESIS_BLOCK - 1; - int best_state_block = load_most_relevant_state(); - if (best_state_block < 0) { - // unable to recover easily, remove stale stale state bits and reparse from the beginning. - clear_all_state(); + if (reorgContainsFreeze) { + PrintToLog("Reorganization containing freeze related transactions detected, forcing a reparse...\n"); + clear_all_state(); // unable to reorg freezes safely, clear state and reparse } else { - nWaterlineBlock = best_state_block; + int best_state_block = load_most_relevant_state(); + if (best_state_block < 0) { + // unable to recover easily, remove stale stale state bits and reparse from the beginning. + clear_all_state(); + } else { + nWaterlineBlock = best_state_block; + } } // clear the global wallet property list, perform a forced wallet update and tell the UI that state is no longer valid, and UI views need to be reinit diff --git a/src/omnicore/omnicore.h b/src/omnicore/omnicore.h index e4020d8e61b1d..98a26b930a0df 100644 --- a/src/omnicore/omnicore.h +++ b/src/omnicore/omnicore.h @@ -36,7 +36,7 @@ int const MAX_STATE_HISTORY = 50; #define TEST_ECO_PROPERTY_1 (0x80000003UL) // increment this value to force a refresh of the state (similar to --startclean) -#define DB_VERSION 5 +#define DB_VERSION 6 // could probably also use: int64_t maxInt64 = std::numeric_limits::max(); // maximum numeric values from the spec: @@ -86,6 +86,10 @@ enum TransactionType { MSC_TYPE_GRANT_PROPERTY_TOKENS = 55, MSC_TYPE_REVOKE_PROPERTY_TOKENS = 56, MSC_TYPE_CHANGE_ISSUER_ADDRESS = 70, + MSC_TYPE_ENABLE_FREEZING = 71, + MSC_TYPE_DISABLE_FREEZING = 72, + MSC_TYPE_FREEZE_PROPERTY_TOKENS = 185, + MSC_TYPE_UNFREEZE_PROPERTY_TOKENS = 186, OMNICORE_MESSAGE_TYPE_DEACTIVATION = 65533, OMNICORE_MESSAGE_TYPE_ACTIVATION = 65534, OMNICORE_MESSAGE_TYPE_ALERT = 65535 @@ -278,6 +282,8 @@ class CMPTxList : public CDBBase std::set GetSeedBlocks(int startHeight, int endHeight); void LoadAlerts(int blockHeight); void LoadActivations(int blockHeight); + bool LoadFreezeState(int blockHeight); + bool CheckForFreezeTxs(int blockHeight); void printStats(); void printAll(); @@ -358,7 +364,26 @@ bool getValidMPTX(const uint256 &txid, int *block = NULL, unsigned int *type = N bool update_tally_map(const std::string& who, uint32_t propertyId, int64_t amount, TallyType ttype); std::string getTokenLabel(uint32_t propertyId); -} +/** + NOTE: The following functions are only permitted for properties + managed by a central issuer that have enabled freezing. + **/ +/** Adds an address and property to the frozenMap **/ +void freezeAddress(const std::string& address, uint32_t propertyId); +/** Removes an address and property from the frozenMap **/ +void unfreezeAddress(const std::string& address, uint32_t propertyId); +/** Checks whether an address and property are frozen **/ +bool isAddressFrozen(const std::string& address, uint32_t propertyId); +/** Adds a property to the freezingEnabledMap **/ +void enableFreezing(uint32_t propertyId, int block); +/** Removes a property from the freezingEnabledMap **/ +void disableFreezing(uint32_t propertyId); +/** Checks whether a property has freezing enabled **/ +bool isFreezingEnabled(uint32_t propertyId, int block); +/** Clears the freeze state in the event of a reorg **/ +void ClearFreezeState(); + +} #endif // OMNICORE_OMNICORE_H diff --git a/src/omnicore/rules.cpp b/src/omnicore/rules.cpp index bc54a05e634b8..7f9bf2545d7a3 100644 --- a/src/omnicore/rules.cpp +++ b/src/omnicore/rules.cpp @@ -56,6 +56,10 @@ std::vector CConsensusParams::GetRestrictions() const { MSC_TYPE_GRANT_PROPERTY_TOKENS, MP_TX_PKT_V0, false, MSC_MANUALSP_BLOCK }, { MSC_TYPE_REVOKE_PROPERTY_TOKENS, MP_TX_PKT_V0, false, MSC_MANUALSP_BLOCK }, { MSC_TYPE_CHANGE_ISSUER_ADDRESS, MP_TX_PKT_V0, false, MSC_MANUALSP_BLOCK }, + { MSC_TYPE_ENABLE_FREEZING, MP_TX_PKT_V0, false, MSC_MANUALSP_BLOCK }, + { MSC_TYPE_DISABLE_FREEZING, MP_TX_PKT_V0, false, MSC_MANUALSP_BLOCK }, + { MSC_TYPE_FREEZE_PROPERTY_TOKENS, MP_TX_PKT_V0, false, MSC_MANUALSP_BLOCK }, + { MSC_TYPE_UNFREEZE_PROPERTY_TOKENS, MP_TX_PKT_V0, false, MSC_MANUALSP_BLOCK }, { MSC_TYPE_SEND_TO_OWNERS, MP_TX_PKT_V0, false, MSC_STO_BLOCK }, { MSC_TYPE_SEND_TO_OWNERS, MP_TX_PKT_V1, false, MSC_STOV1_BLOCK }, @@ -163,6 +167,8 @@ CMainConsensusParams::CMainConsensusParams() // Notice range for feature activations: MIN_ACTIVATION_BLOCKS = 2048; // ~2 weeks MAX_ACTIVATION_BLOCKS = 12288; // ~12 weeks + // Waiting period for enabling freezing + OMNI_FREEZE_WAIT_PERIOD = 4096; // ~4 weeks // Script related: PUBKEYHASH_BLOCK = 0; SCRIPTHASH_BLOCK = 322000; @@ -185,6 +191,7 @@ CMainConsensusParams::CMainConsensusParams() SPCROWDCROSSOVER_FEATURE_BLOCK = 395000; TRADEALLPAIRS_FEATURE_BLOCK = 438500; FEES_FEATURE_BLOCK = 999999; + FREEZENOTICE_FEATURE_BLOCK = 999999; } /** @@ -201,6 +208,8 @@ CTestNetConsensusParams::CTestNetConsensusParams() // Notice range for feature activations: MIN_ACTIVATION_BLOCKS = 0; MAX_ACTIVATION_BLOCKS = 999999; + // Waiting period for enabling freezing + OMNI_FREEZE_WAIT_PERIOD = 0; // Script related: PUBKEYHASH_BLOCK = 0; SCRIPTHASH_BLOCK = 0; @@ -223,6 +232,7 @@ CTestNetConsensusParams::CTestNetConsensusParams() SPCROWDCROSSOVER_FEATURE_BLOCK = 0; TRADEALLPAIRS_FEATURE_BLOCK = 0; FEES_FEATURE_BLOCK = 0; + FREEZENOTICE_FEATURE_BLOCK = 0; } /** @@ -239,6 +249,8 @@ CRegTestConsensusParams::CRegTestConsensusParams() // Notice range for feature activations: MIN_ACTIVATION_BLOCKS = 5; MAX_ACTIVATION_BLOCKS = 10; + // Waiting period for enabling freezing + OMNI_FREEZE_WAIT_PERIOD = 10; // Script related: PUBKEYHASH_BLOCK = 0; SCRIPTHASH_BLOCK = 0; @@ -261,6 +273,7 @@ CRegTestConsensusParams::CRegTestConsensusParams() SPCROWDCROSSOVER_FEATURE_BLOCK = 999999; TRADEALLPAIRS_FEATURE_BLOCK = 999999; FEES_FEATURE_BLOCK = 999999; + FREEZENOTICE_FEATURE_BLOCK = 999999; } //! Consensus parameters for mainnet @@ -423,6 +436,9 @@ bool ActivateFeature(uint16_t featureId, int activationBlock, uint32_t minClient case FEATURE_STOV1: MutableConsensusParams().MSC_STOV1_BLOCK = activationBlock; break; + case FEATURE_FREEZENOTICE: + MutableConsensusParams().FREEZENOTICE_FEATURE_BLOCK = activationBlock; + break; default: supported = false; break; @@ -491,6 +507,9 @@ bool DeactivateFeature(uint16_t featureId, int transactionBlock) case FEATURE_STOV1: MutableConsensusParams().MSC_STOV1_BLOCK = 999999; break; + case FEATURE_FREEZENOTICE: + MutableConsensusParams().FREEZENOTICE_FEATURE_BLOCK = 999999; + break; default: return false; break; @@ -521,6 +540,7 @@ std::string GetFeatureName(uint16_t featureId) case FEATURE_TRADEALLPAIRS: return "Allow trading all pairs on the Distributed Exchange"; 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"; default: return "Unknown feature"; } @@ -565,6 +585,9 @@ bool IsFeatureActivated(uint16_t featureId, int transactionBlock) case FEATURE_STOV1: activationBlock = params.MSC_STOV1_BLOCK; break; + case FEATURE_FREEZENOTICE: + activationBlock = params.FREEZENOTICE_FEATURE_BLOCK; + break; default: return false; } diff --git a/src/omnicore/rules.h b/src/omnicore/rules.h index a974dd40cab3b..b2e5d62c888b9 100644 --- a/src/omnicore/rules.h +++ b/src/omnicore/rules.h @@ -34,6 +34,8 @@ const uint16_t FEATURE_TRADEALLPAIRS = 8; const uint16_t FEATURE_FEES = 9; //! Feature identifier to enable cross property (v1) Send To Owners 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; //! When (propertyTotalTokens / OMNI_FEE_THRESHOLD) is reached fee distribution will occur const int64_t OMNI_FEE_THRESHOLD = 100000; // 0.001% @@ -86,6 +88,9 @@ class CConsensusParams //! Maximum number of blocks to use for notice rules on activation int MAX_ACTIVATION_BLOCKS; + //! Waiting period after enabling freezing before addresses may be frozen + int OMNI_FREEZE_WAIT_PERIOD; + //! Block to enable pay-to-pubkey-hash support int PUBKEYHASH_BLOCK; //! Block to enable pay-to-script-hash support @@ -126,6 +131,8 @@ class CConsensusParams int TRADEALLPAIRS_FEATURE_BLOCK; //! Block to enable the fee system & 0.05% fee for trading non-Omni pairs int FEES_FEATURE_BLOCK; + //! Block to activate the waiting period for enabling managed property address freezing + int FREEZENOTICE_FEATURE_BLOCK; /** Returns a mapping of transaction types, and the blocks at which they are enabled. */ virtual std::vector GetRestrictions() const; diff --git a/src/omnicore/tx.cpp b/src/omnicore/tx.cpp index a76d3b6cc3eb2..94fa7b6bf1bc8 100644 --- a/src/omnicore/tx.cpp +++ b/src/omnicore/tx.cpp @@ -13,6 +13,7 @@ #include "omnicore/rules.h" #include "omnicore/sp.h" #include "omnicore/sto.h" +#include "omnicore/utils.h" #include "amount.h" #include "main.h" @@ -59,6 +60,10 @@ std::string mastercore::strTransactionType(uint16_t txType) case MSC_TYPE_GRANT_PROPERTY_TOKENS: return "Grant Property Tokens"; case MSC_TYPE_REVOKE_PROPERTY_TOKENS: return "Revoke Property Tokens"; case MSC_TYPE_CHANGE_ISSUER_ADDRESS: return "Change Issuer Address"; + case MSC_TYPE_ENABLE_FREEZING: return "Enable Freezing"; + case MSC_TYPE_DISABLE_FREEZING: return "Disable Freezing"; + case MSC_TYPE_FREEZE_PROPERTY_TOKENS: return "Freeze Property Tokens"; + case MSC_TYPE_UNFREEZE_PROPERTY_TOKENS: return "Unfreeze Property Tokens"; case MSC_TYPE_NOTIFICATION: return "Notification"; case OMNICORE_MESSAGE_TYPE_ALERT: return "ALERT"; case OMNICORE_MESSAGE_TYPE_DEACTIVATION: return "Feature Deactivation"; @@ -149,6 +154,15 @@ bool CMPTransaction::interpret_Transaction() case MSC_TYPE_CHANGE_ISSUER_ADDRESS: return interpret_ChangeIssuer(); + case MSC_TYPE_ENABLE_FREEZING: + return interpret_EnableFreezing(); + + case MSC_TYPE_DISABLE_FREEZING: + return interpret_DisableFreezing(); + + case MSC_TYPE_FREEZE_PROPERTY_TOKENS: + return interpret_FreezeTokens(); + case OMNICORE_MESSAGE_TYPE_DEACTIVATION: return interpret_Deactivation(); @@ -631,6 +645,73 @@ bool CMPTransaction::interpret_ChangeIssuer() return true; } +/** Tx 71 */ +bool CMPTransaction::interpret_EnableFreezing() +{ + if (pkt_size < 8) { + return false; + } + memcpy(&property, &pkt[4], 4); + swapByteOrder32(property); + + if ((!rpcOnly && msc_debug_packets) || msc_debug_packets_readonly) { + PrintToLog("\t property: %d (%s)\n", property, strMPProperty(property)); + } + + return true; +} + +/** Tx 72 */ +bool CMPTransaction::interpret_DisableFreezing() +{ + if (pkt_size < 8) { + return false; + } + memcpy(&property, &pkt[4], 4); + swapByteOrder32(property); + + if ((!rpcOnly && msc_debug_packets) || msc_debug_packets_readonly) { + PrintToLog("\t property: %d (%s)\n", property, strMPProperty(property)); + } + + return true; +} + +/** Tx 185 */ +bool CMPTransaction::interpret_FreezeTokens() +{ + if (pkt_size < 37) { + return false; + } + memcpy(&property, &pkt[4], 4); + swapByteOrder32(property); + memcpy(&nValue, &pkt[8], 8); + swapByteOrder64(nValue); + nNewValue = nValue; + + /** + Note, TX185 is a virtual reference transaction type. + With virtual reference transactions a hash160 in the payload sets the receiver. + Reference outputs are ignored. + **/ + unsigned char address_version; + uint160 address_hash160; + memcpy(&address_version, &pkt[16], 1); + memcpy(&address_hash160, &pkt[17], 20); + receiver = HashToAddress(address_version, address_hash160); + if (receiver.empty()) { + return false; + } + + if ((!rpcOnly && msc_debug_packets) || msc_debug_packets_readonly) { + PrintToLog("\t property: %d (%s)\n", property, strMPProperty(property)); + PrintToLog("\t value (unused): %s\n", FormatMP(property, nValue)); + PrintToLog("\t address: %s\n", receiver); + } + + return true; +} + /** Tx 65533 */ bool CMPTransaction::interpret_Deactivation() { @@ -720,6 +801,11 @@ int CMPTransaction::interpretPacket() LOCK(cs_tally); + if (isAddressFrozen(sender, property)) { + PrintToLog("%s(): REJECTED: address %s is frozen for property %d\n", __func__, sender, property); + return (PKT_ERROR -3); + } + switch (type) { case MSC_TYPE_SIMPLE_SEND: return logicMath_SimpleSend(); @@ -769,6 +855,15 @@ int CMPTransaction::interpretPacket() case MSC_TYPE_CHANGE_ISSUER_ADDRESS: return logicMath_ChangeIssuer(); + case MSC_TYPE_ENABLE_FREEZING: + return logicMath_EnableFreezing(); + + case MSC_TYPE_DISABLE_FREEZING: + return logicMath_DisableFreezing(); + + case MSC_TYPE_FREEZE_PROPERTY_TOKENS: + return logicMath_FreezeTokens(); + case OMNICORE_MESSAGE_TYPE_DEACTIVATION: return logicMath_Deactivation(); @@ -1077,6 +1172,12 @@ int CMPTransaction::logicMath_SendAll() continue; } + // do not transfer tokens from a frozen property + if (isAddressFrozen(sender, propertyId)) { + PrintToLog("%s(): sender %s is frozen for property %d - the property will not be included in processing.\n", __func__, sender, propertyId); + continue; + } + int64_t moneyAvailable = ptally->getMoney(propertyId, BALANCE); if (moneyAvailable > 0) { ++numberOfPropertiesSent; @@ -1947,6 +2048,170 @@ int CMPTransaction::logicMath_ChangeIssuer() return 0; } +/** Tx 71 */ +int CMPTransaction::logicMath_EnableFreezing() +{ + uint256 blockHash; + { + LOCK(cs_main); + + CBlockIndex* pindex = chainActive[block]; + if (pindex == NULL) { + PrintToLog("%s(): ERROR: block %d not in the active chain\n", __func__, block); + return (PKT_ERROR_TOKENS -20); + } + blockHash = pindex->GetBlockHash(); + } + + if (!IsTransactionTypeAllowed(block, property, type, version)) { + PrintToLog("%s(): rejected: type %d or version %d not permitted for property %d at block %d\n", + __func__, + type, + version, + property, + block); + return (PKT_ERROR_TOKENS -22); + } + + if (!IsPropertyIdValid(property)) { + PrintToLog("%s(): rejected: property %d does not exist\n", __func__, property); + return (PKT_ERROR_TOKENS -24); + } + + CMPSPInfo::Entry sp; + assert(_my_sps->getSP(property, sp)); + + if (!sp.manual) { + PrintToLog("%s(): rejected: property %d is not managed\n", __func__, property); + return (PKT_ERROR_TOKENS -42); + } + + if (sender != sp.issuer) { + PrintToLog("%s(): rejected: sender %s is not issuer of property %d [issuer=%s]\n", __func__, sender, property, sp.issuer); + return (PKT_ERROR_TOKENS -43); + } + + if (isFreezingEnabled(property, block)) { + PrintToLog("%s(): rejected: freezing is already enabled for property %d\n", __func__, property); + return (PKT_ERROR_TOKENS -49); + } + + enableFreezing(property, block); + + return 0; +} + +/** Tx 72 */ +int CMPTransaction::logicMath_DisableFreezing() +{ + uint256 blockHash; + { + LOCK(cs_main); + + CBlockIndex* pindex = chainActive[block]; + if (pindex == NULL) { + PrintToLog("%s(): ERROR: block %d not in the active chain\n", __func__, block); + return (PKT_ERROR_TOKENS -20); + } + blockHash = pindex->GetBlockHash(); + } + + if (!IsTransactionTypeAllowed(block, property, type, version)) { + PrintToLog("%s(): rejected: type %d or version %d not permitted for property %d at block %d\n", + __func__, + type, + version, + property, + block); + return (PKT_ERROR_TOKENS -22); + } + + if (!IsPropertyIdValid(property)) { + PrintToLog("%s(): rejected: property %d does not exist\n", __func__, property); + return (PKT_ERROR_TOKENS -24); + } + + CMPSPInfo::Entry sp; + assert(_my_sps->getSP(property, sp)); + + if (!sp.manual) { + PrintToLog("%s(): rejected: property %d is not managed\n", __func__, property); + return (PKT_ERROR_TOKENS -42); + } + + if (sender != sp.issuer) { + PrintToLog("%s(): rejected: sender %s is not issuer of property %d [issuer=%s]\n", __func__, sender, property, sp.issuer); + return (PKT_ERROR_TOKENS -43); + } + + if (!isFreezingEnabled(property, block)) { + PrintToLog("%s(): rejected: freezing is not enabled for property %d\n", __func__, property); + return (PKT_ERROR_TOKENS -47); + } + + disableFreezing(property); + + return 0; +} + +/** Tx 185 */ +int CMPTransaction::logicMath_FreezeTokens() +{ + uint256 blockHash; + { + LOCK(cs_main); + + CBlockIndex* pindex = chainActive[block]; + if (pindex == NULL) { + PrintToLog("%s(): ERROR: block %d not in the active chain\n", __func__, block); + return (PKT_ERROR_TOKENS -20); + } + blockHash = pindex->GetBlockHash(); + } + + if (!IsTransactionTypeAllowed(block, property, type, version)) { + PrintToLog("%s(): rejected: type %d or version %d not permitted for property %d at block %d\n", + __func__, + type, + version, + property, + block); + return (PKT_ERROR_TOKENS -22); + } + + if (!IsPropertyIdValid(property)) { + PrintToLog("%s(): rejected: property %d does not exist\n", __func__, property); + return (PKT_ERROR_TOKENS -24); + } + + CMPSPInfo::Entry sp; + assert(_my_sps->getSP(property, sp)); + + if (!sp.manual) { + PrintToLog("%s(): rejected: property %d is not managed\n", __func__, property); + return (PKT_ERROR_TOKENS -42); + } + + if (sender != sp.issuer) { + PrintToLog("%s(): rejected: sender %s is not issuer of property %d [issuer=%s]\n", __func__, sender, property, sp.issuer); + return (PKT_ERROR_TOKENS -43); + } + + if (!isFreezingEnabled(property, block)) { + PrintToLog("%s(): rejected: freezing is not enabled for property %d\n", __func__, property); + return (PKT_ERROR_TOKENS -47); + } + + if (isAddressFrozen(receiver, property)) { + PrintToLog("%s(): rejected: address %s is already frozen for property %d\n", __func__, receiver, property); + return (PKT_ERROR_TOKENS -50); + } + + freezeAddress(receiver, property); + + return 0; +} + /** Tx 65533 */ int CMPTransaction::logicMath_Deactivation() { diff --git a/src/omnicore/tx.h b/src/omnicore/tx.h index 6a09806219c55..865819c936c02 100644 --- a/src/omnicore/tx.h +++ b/src/omnicore/tx.h @@ -119,6 +119,9 @@ class CMPTransaction bool interpret_GrantTokens(); bool interpret_RevokeTokens(); bool interpret_ChangeIssuer(); + bool interpret_EnableFreezing(); + bool interpret_DisableFreezing(); + bool interpret_FreezeTokens(); bool interpret_Activation(); bool interpret_Deactivation(); bool interpret_Alert(); @@ -142,6 +145,9 @@ class CMPTransaction int logicMath_GrantTokens(); int logicMath_RevokeTokens(); int logicMath_ChangeIssuer(); + int logicMath_EnableFreezing(); + int logicMath_DisableFreezing(); + int logicMath_FreezeTokens(); int logicMath_Activation(); int logicMath_Deactivation(); int logicMath_Alert(); diff --git a/src/omnicore/utils.cpp b/src/omnicore/utils.cpp index a1a8c87b779a0..dfe755f6979f8 100644 --- a/src/omnicore/utils.cpp +++ b/src/omnicore/utils.cpp @@ -7,11 +7,15 @@ #include "omnicore/utils.h" +#include "base58.h" #include "utilstrencodings.h" // TODO: use crypto/sha256 instead of openssl #include "openssl/sha.h" +#include "omnicore/log.h" +#include "omnicore/script.h" + #include #include @@ -55,3 +59,35 @@ void PrepareObfuscatedHashes(const std::string& strSeed, int hashCount, std::str strcpy((char *)sha_input, vstrHashes[j].c_str()); } } + +std::string HashToAddress(unsigned char version, const uint160& hash) +{ + CBitcoinAddress address; + if (version == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)[0]) { + CKeyID keyId = hash; + address.Set(keyId); + return address.ToString(); + } else if (version == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS)[0]) { + CScriptID scriptId = hash; + address.Set(scriptId); + return address.ToString(); + } + + return ""; +} + +std::vector AddressToBytes(const std::string& address) +{ + std::vector addressBytes; + bool success = DecodeBase58(address, addressBytes); + if (!success) { + PrintToLog("ERROR: failed to decode address %s.\n", address); + } + if (addressBytes.size() == 25) { + addressBytes.resize(21); // truncate checksum + } else { + PrintToLog("ERROR: unexpected size from DecodeBase58 when decoding address %s.\n", address); + } + + return addressBytes; +} diff --git a/src/omnicore/utils.h b/src/omnicore/utils.h index e84e17988c93d..9204fd9400032 100644 --- a/src/omnicore/utils.h +++ b/src/omnicore/utils.h @@ -3,10 +3,17 @@ #include +#include "uint256.h" + #define MAX_SHA256_OBFUSCATION_TIMES 255 /** Generates hashes used for obfuscation via ToUpper(HexStr(SHA256(x))). */ void PrepareObfuscatedHashes(const std::string& strSeed, int hashCount, std::string(&vstrHashes)[1+MAX_SHA256_OBFUSCATION_TIMES]); +/** Determines the Bitcoin address associated with a given hash and version. */ +std::string HashToAddress(unsigned char version, const uint160& hash); + +/** Returns a vector of bytes containing the version and hash160 for an address.*/ +std::vector AddressToBytes(const std::string& address); #endif // OMNICORE_UTILS_H