Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test Freeze PR with minimal consensus changes only #84

Open
wants to merge 34 commits into
base: 0.2.99-Z-DevelopBaseline
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
033b11a
travis: move back to the minimal image
theuni Oct 18, 2017
74b9f9e
Merge #511: Travis CI: move back to the minimal image
dexX7 Dec 9, 2017
454dfca
Add HashToAddress() helper to determine Bitcoin address from hash160 …
zathras-crypto Nov 28, 2017
19f4684
Add AddressToHash() helper to determine hash160 & version from Bitcoi…
zathras-crypto Nov 28, 2017
0aa1741
Add type ints for new 'freeze', 'unfreeze' & 'change freeze setting' …
zathras-crypto Nov 28, 2017
58fb4b5
Add sets to hold which properties allow freezing and which addresses …
zathras-crypto Nov 28, 2017
af928d5
Add freezeAddress() & unfreezeAddress() helpers
zathras-crypto Nov 28, 2017
20bf1fc
Add isAddressFrozen() check
zathras-crypto Nov 28, 2017
9390a02
Add descriptions for new transactions
zathras-crypto Nov 28, 2017
37a2a6f
Update consensus rules to allow freezing for managed properties
zathras-crypto Nov 28, 2017
02d8274
Add enableFreezing() function
zathras-crypto Nov 28, 2017
d4f50f3
Add disableFreezing() function
zathras-crypto Nov 28, 2017
de938f1
Add isFreezingEnabled() helper
zathras-crypto Nov 28, 2017
d80d0f7
Add processing for 'change freeze setting' transaction
zathras-crypto Nov 28, 2017
7fff94a
Add processing for 'freeze' transaction
zathras-crypto Nov 28, 2017
6d7f86b
Reject transactions from frozen addresses prior to logic processing
zathras-crypto Nov 28, 2017
1f7d3bd
Add freeze error descriptions to errors.h
zathras-crypto Nov 28, 2017
2f02db0
Load the freeze state at startup
zathras-crypto Nov 28, 2017
9aefde0
Increment DB_VERSION to force a reparse (consensus changes)
zathras-crypto Nov 29, 2017
fbb296b
Use DecodeBase58() in AddressToBytes() function - thanks @dexx7
zathras-crypto Nov 30, 2017
567cc81
Add const to HashToAddress() to avoid copying object - thanks @dexx7
zathras-crypto Nov 30, 2017
9c759e7
Optimize isAddressFrozen() - thanks @dexx7
zathras-crypto Dec 1, 2017
2a44fe1
Move isAddressFrozen() check inside cs_tally LOCK - thanks @dexx7
zathras-crypto Dec 1, 2017
3599c5a
Move deletion of iterator in LoadFreezeState() - thanks @dexx7
zathras-crypto Dec 1, 2017
5b6c113
Add safety net to ensure update_tally_map() cannot take tokens from a…
zathras-crypto Dec 1, 2017
16ca3ef
Restrict Send All from trying to transfer frozen properties
zathras-crypto Dec 1, 2017
962dbf9
Swap 'Change Freeze Setting' for 'Enable Freezing' & 'Disable Freezin…
zathras-crypto Dec 5, 2017
6a4d70a
Make sure LoadFreeze() uses actual position in blockchain
zathras-crypto Dec 6, 2017
2ffdc56
Bugfix: remove checksum from being included in the payload
zathras-crypto Dec 8, 2017
24b8eaf
Clear the freeze state when `clear_all_state()` is called
zathras-crypto Dec 8, 2017
31d0737
Fix LoadFreezeState() failure due to not loading unfreeze txs
zathras-crypto Dec 9, 2017
33e19d0
Fix: only trigger assert on bad attempts to change BALANCE tally
zathras-crypto Dec 10, 2017
01653f6
Reparse if reorging the freeze state
zathras-crypto Dec 10, 2017
8d42b2a
Update consensus rules to enforce a waiting period for enabling freezing
zathras-crypto Nov 28, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ addons:
hostname: bitcoin-tester

os: linux
language: generic
language: minimal
cache:
directories:
- depends/built
Expand Down
15 changes: 15 additions & 0 deletions src/omnicore/errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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";
Expand Down
211 changes: 206 additions & 5 deletions src/omnicore/omnicore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ std::map<uint32_t, int64_t> global_balance_reserved;
//! Vector containing a list of properties relative to the wallet
std::set<uint32_t> global_wallet_property_list;

//! Set containing properties that have freezing enabled
std::set<std::pair<uint32_t,int> > setFreezingEnabledProperties;
//! Set containing addresses that have been frozen
std::set<std::pair<std::string,uint32_t> > setFrozenAddresses;

/**
* Used to indicate, whether to automatically commit created transactions.
*
Expand Down Expand Up @@ -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<std::pair<uint32_t,int> >::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<std::pair<std::string,uint32_t> >::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<std::pair<uint32_t,int> >::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;
Expand Down Expand Up @@ -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<std::string, CMPTally>::iterator my_it = mp_tally_map.find(who);
Expand Down Expand Up @@ -2086,6 +2173,7 @@ void clear_all_state()
ResetConsensusParams();
ClearActivations();
ClearAlerts();
ClearFreezeState();

// LevelDB based storage
_my_sps->Clear();
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -2538,6 +2635,102 @@ std::set<int> 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<std::string> 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<std::pair<std::string, uint256> > 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<std::string> 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<std::pair<std::string, uint256> >::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;
Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand Down
29 changes: 27 additions & 2 deletions src/omnicore/omnicore.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<int64_t>::max();
// maximum numeric values from the spec:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -278,6 +282,8 @@ class CMPTxList : public CDBBase
std::set<int> 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();
Expand Down Expand Up @@ -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
Loading