Use fully static linkage #364

Closed
bvbfan wants to merge 78 commits from static_link into master
13 changed files with 104 additions and 50 deletions
Showing only changes of commit 8743a93c9f - Show all commits

View file

@ -56,8 +56,8 @@ public:
{ {
static_assert(BITS/32 > 0 && BITS%32 == 0, "Template parameter BITS must be a positive multiple of 32."); static_assert(BITS/32 > 0 && BITS%32 == 0, "Template parameter BITS must be a positive multiple of 32.");
pn[0] = (unsigned int)b; pn[0] = uint32_t(b);
pn[1] = (unsigned int)(b >> 32); pn[1] = uint32_t(b >> 32U);
for (int i = 2; i < WIDTH; i++) for (int i = 2; i < WIDTH; i++)
pn[i] = 0; pn[i] = 0;
} }

View file

@ -72,12 +72,18 @@ void CBaseBlob<BITS>::SetNull()
} }
template<uint32_t BITS> template<uint32_t BITS>
std::string CBaseBlob<BITS>::GetHex() const std::string CBaseBlob<BITS>::GetHex(bool reverse) const
{ {
std::stringstream ss; std::stringstream ss;
ss << std::hex; ss << std::hex;
if (reverse) {
for (auto it = data.rbegin(); it != data.rend(); ++it) for (auto it = data.rbegin(); it != data.rend(); ++it)
ss << std::setw(2) << std::setfill('0') << uint32_t(*it); ss << std::setw(2) << std::setfill('0') << uint32_t(*it);
}
else {
for (auto it = data.begin(); it != data.end(); ++it)
ss << std::setw(2) << std::setfill('0') << uint32_t(*it);
}
return ss.str(); return ss.str();
} }

View file

@ -38,7 +38,7 @@ public:
bool IsNull() const; bool IsNull() const;
void SetNull(); void SetNull();
std::string GetHex() const; std::string GetHex(bool reverse=true) const;
void SetHex(const char* psz); void SetHex(const char* psz);
void SetHex(const std::string& str); void SetHex(const std::string& str);

View file

@ -49,10 +49,22 @@ namespace sqlite
inline uint256 get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<uint256>) { inline uint256 get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<uint256>) {
uint256 ret; uint256 ret;
// I think we need this, but I lost my specific test case:
// auto type = sqlite3_column_type(stmt, inx);
// if (type == SQLITE_NULL)
// return ret;
//
// if (type == SQLITE_INTEGER) {
// auto integer = sqlite3_column_int64(stmt, inx);
// return uint256(integer);
// }
// assert(type == SQLITE_BLOB);
auto ptr = sqlite3_column_blob(stmt, inx); auto ptr = sqlite3_column_blob(stmt, inx);
if (!ptr) return ret; if (!ptr) return ret;
int bytes = sqlite3_column_bytes(stmt, inx); int bytes = sqlite3_column_bytes(stmt, inx);
assert(bytes > 0 && bytes <= int(ret.size())); assert(bytes <= ret.size());
std::memcpy(ret.begin(), ptr, bytes); std::memcpy(ret.begin(), ptr, bytes);
return ret; return ret;
} }

View file

@ -77,34 +77,19 @@ CClaimTrie::CClaimTrie(std::size_t cacheBytes, bool fWipe, int height,
"parent BLOB REFERENCES node(name) DEFERRABLE INITIALLY DEFERRED, " "parent BLOB REFERENCES node(name) DEFERRABLE INITIALLY DEFERRED, "
"hash BLOB)"; "hash BLOB)";
db << "CREATE INDEX IF NOT EXISTS node_hash_len_name ON node (hash, LENGTH(name) DESC)";
// db << "CREATE UNIQUE INDEX IF NOT EXISTS node_parent_name ON node (parent, name)"; // no apparent gain
db << "CREATE INDEX IF NOT EXISTS node_parent ON node (parent)";
db << "CREATE TABLE IF NOT EXISTS takeover (name BLOB NOT NULL, height INTEGER NOT NULL, "
"claimID BLOB, PRIMARY KEY(name, height DESC));";
db << "CREATE INDEX IF NOT EXISTS takeover_height ON takeover (height)";
db << "CREATE TABLE IF NOT EXISTS claim (claimID BLOB NOT NULL PRIMARY KEY, name BLOB NOT NULL, " db << "CREATE TABLE IF NOT EXISTS claim (claimID BLOB NOT NULL PRIMARY KEY, name BLOB NOT NULL, "
"nodeName BLOB NOT NULL REFERENCES node(name) DEFERRABLE INITIALLY DEFERRED, " "nodeName BLOB NOT NULL REFERENCES node(name) DEFERRABLE INITIALLY DEFERRED, "
"txID BLOB NOT NULL, txN INTEGER NOT NULL, originalHeight INTEGER NOT NULL, updateHeight INTEGER NOT NULL, " "txID BLOB NOT NULL, txN INTEGER NOT NULL, originalHeight INTEGER NOT NULL, updateHeight INTEGER NOT NULL, "
"validHeight INTEGER NOT NULL, activationHeight INTEGER NOT NULL, " "validHeight INTEGER NOT NULL, activationHeight INTEGER NOT NULL, "
"expirationHeight INTEGER NOT NULL, amount INTEGER NOT NULL);"; "expirationHeight INTEGER NOT NULL, amount INTEGER NOT NULL);";
db << "CREATE INDEX IF NOT EXISTS claim_activationHeight ON claim (activationHeight)";
db << "CREATE INDEX IF NOT EXISTS claim_expirationHeight ON claim (expirationHeight)";
db << "CREATE INDEX IF NOT EXISTS claim_nodeName ON claim (nodeName)";
db << "CREATE TABLE IF NOT EXISTS support (txID BLOB NOT NULL, txN INTEGER NOT NULL, " db << "CREATE TABLE IF NOT EXISTS support (txID BLOB NOT NULL, txN INTEGER NOT NULL, "
"supportedClaimID BLOB NOT NULL, name BLOB NOT NULL, nodeName BLOB NOT NULL, " "supportedClaimID BLOB NOT NULL, name BLOB NOT NULL, nodeName BLOB NOT NULL, "
"blockHeight INTEGER NOT NULL, validHeight INTEGER NOT NULL, activationHeight INTEGER NOT NULL, " "blockHeight INTEGER NOT NULL, validHeight INTEGER NOT NULL, activationHeight INTEGER NOT NULL, "
"expirationHeight INTEGER NOT NULL, amount INTEGER NOT NULL, PRIMARY KEY(txID, txN));"; "expirationHeight INTEGER NOT NULL, amount INTEGER NOT NULL, PRIMARY KEY(txID, txN));";
db << "CREATE INDEX IF NOT EXISTS support_supportedClaimID ON support (supportedClaimID)"; db << "CREATE TABLE IF NOT EXISTS takeover (name BLOB NOT NULL, height INTEGER NOT NULL, "
db << "CREATE INDEX IF NOT EXISTS support_activationHeight ON support (activationHeight)"; "claimID BLOB, PRIMARY KEY(name, height DESC));";
db << "CREATE INDEX IF NOT EXISTS support_expirationHeight ON support (expirationHeight)";
db << "CREATE INDEX IF NOT EXISTS support_nodeName ON support (nodeName)";
if (fWipe) { if (fWipe) {
db << "DELETE FROM node"; db << "DELETE FROM node";
@ -113,6 +98,21 @@ CClaimTrie::CClaimTrie(std::size_t cacheBytes, bool fWipe, int height,
db << "DELETE FROM takeover"; db << "DELETE FROM takeover";
} }
db << "CREATE INDEX IF NOT EXISTS node_hash_len_name ON node (hash, LENGTH(name) DESC)";
// db << "CREATE UNIQUE INDEX IF NOT EXISTS node_parent_name ON node (parent, name)"; // no apparent gain
db << "CREATE INDEX IF NOT EXISTS node_parent ON node (parent)";
db << "CREATE INDEX IF NOT EXISTS takeover_height ON takeover (height)";
db << "CREATE INDEX IF NOT EXISTS claim_activationHeight ON claim (activationHeight)";
db << "CREATE INDEX IF NOT EXISTS claim_expirationHeight ON claim (expirationHeight)";
db << "CREATE INDEX IF NOT EXISTS claim_nodeName ON claim (nodeName)";
db << "CREATE INDEX IF NOT EXISTS support_supportedClaimID ON support (supportedClaimID)";
db << "CREATE INDEX IF NOT EXISTS support_activationHeight ON support (activationHeight)";
db << "CREATE INDEX IF NOT EXISTS support_expirationHeight ON support (expirationHeight)";
db << "CREATE INDEX IF NOT EXISTS support_nodeName ON support (nodeName)";
db << "INSERT OR IGNORE INTO node(name, hash) VALUES(x'', ?)" << one; // ensure that we always have our root node db << "INSERT OR IGNORE INTO node(name, hash) VALUES(x'', ?)" << one; // ensure that we always have our root node
} }
@ -457,9 +457,7 @@ bool CClaimTrieCacheBase::validateDb(int height, const uint256& rootHash)
{ {
base->nNextHeight = nNextHeight = height + 1; base->nNextHeight = nNextHeight = height + 1;
logPrint << "Checking claim trie consistency... " << Clog::flush;
if (checkConsistency()) { if (checkConsistency()) {
logPrint << "consistent" << Clog::endl;
if (rootHash != getMerkleHash()) { if (rootHash != getMerkleHash()) {
logPrint << "CClaimTrieCacheBase::" << __func__ << "(): the block's root claim hash doesn't match the persisted claim root hash." << Clog::endl; logPrint << "CClaimTrieCacheBase::" << __func__ << "(): the block's root claim hash doesn't match the persisted claim root hash." << Clog::endl;
return false; return false;
@ -470,7 +468,6 @@ bool CClaimTrieCacheBase::validateDb(int height, const uint256& rootHash)
return true; return true;
} }
logPrint << "inconsistent!" << Clog::endl;
return false; return false;
} }
@ -650,7 +647,6 @@ bool CClaimTrieCacheBase::removeClaim(const uint160& claimId, const COutPoint& o
*it >> nodeName >> originalHeight >> validHeight; *it >> nodeName >> originalHeight >> validHeight;
} }
ensureTransacting(); ensureTransacting();
db << "DELETE FROM claim WHERE claimID = ? AND txID = ? AND txN = ?" db << "DELETE FROM claim WHERE claimID = ? AND txID = ? AND txN = ?"
@ -683,13 +679,18 @@ bool CClaimTrieCacheBase::removeClaim(const uint160& claimId, const COutPoint& o
bool CClaimTrieCacheBase::removeSupport(const COutPoint& outPoint, std::string& nodeName, int& validHeight) bool CClaimTrieCacheBase::removeSupport(const COutPoint& outPoint, std::string& nodeName, int& validHeight)
{ {
ensureTransacting(); {
auto query = db << "SELECT nodeName, activationHeight FROM support "
auto query = db << "SELECT nodeName, activationHeight FROM support WHERE txID = ? AND txN = ? AND expirationHeight >= ?" "WHERE txID = ? AND txN = ? AND expirationHeight >= ?"
<< outPoint.hash << outPoint.n << nNextHeight; << outPoint.hash << outPoint.n << nNextHeight;
auto it = query.begin(); auto it = query.begin();
if (it == query.end()) return false; if (it == query.end())
return false;
*it >> nodeName >> validHeight; *it >> nodeName >> validHeight;
}
ensureTransacting();
db << "DELETE FROM support WHERE txID = ? AND txN = ?" << outPoint.hash << outPoint.n; db << "DELETE FROM support WHERE txID = ? AND txN = ?" << outPoint.hash << outPoint.n;
if (!db.rows_modified()) if (!db.rows_modified())
return false; return false;
@ -925,7 +926,7 @@ std::vector<uint160> CClaimTrieCacheBase::getActivatedClaims(int height) {
} }
std::vector<uint160> CClaimTrieCacheBase::getClaimsWithActivatedSupports(int height) { std::vector<uint160> CClaimTrieCacheBase::getClaimsWithActivatedSupports(int height) {
std::vector<uint160> ret; std::vector<uint160> ret;
auto query = db << "SELECT DISTINCT supportedClaimID FROM support WHERE activationHeight = ?1 AND updateHeight < ?1" << height; auto query = db << "SELECT DISTINCT supportedClaimID FROM support WHERE activationHeight = ?1 AND blockHeight < ?1" << height;
for (auto&& row: query) { for (auto&& row: query) {
ret.emplace_back(); ret.emplace_back();
row >> ret.back(); row >> ret.back();
@ -943,7 +944,7 @@ std::vector<uint160> CClaimTrieCacheBase::getExpiredClaims(int height) {
} }
std::vector<uint160> CClaimTrieCacheBase::getClaimsWithExpiredSupports(int height) { std::vector<uint160> CClaimTrieCacheBase::getClaimsWithExpiredSupports(int height) {
std::vector<uint160> ret; std::vector<uint160> ret;
auto query = db << "SELECT DISTINCT supportedClaimID FROM support WHERE expirationHeight = ?1 AND updateHeight < ?1" << height; auto query = db << "SELECT DISTINCT supportedClaimID FROM support WHERE expirationHeight = ?1 AND blockHeight < ?1" << height;
for (auto&& row: query) { for (auto&& row: query) {
ret.emplace_back(); ret.emplace_back();
row >> ret.back(); row >> ret.back();

View file

@ -1,5 +1,6 @@
#include <uints.h> #include <uints.h>
#include <cstring>
uint160::uint160(const std::vector<uint8_t>& vec) : CBaseBlob<160>(vec) uint160::uint160(const std::vector<uint8_t>& vec) : CBaseBlob<160>(vec)
{ {
@ -9,6 +10,10 @@ uint256::uint256(const std::vector<uint8_t>& vec) : CBaseBlob<256>(vec)
{ {
} }
uint256::uint256(int64_t value) : CBaseBlob<256>() {
std::memcpy(this->begin(), &value, sizeof(value)); // TODO: fix the endianness here
}
uint160 uint160S(const char* str) uint160 uint160S(const char* str)
{ {
uint160 s; uint160 s;

View file

@ -25,6 +25,7 @@ public:
uint256() = default; uint256() = default;
explicit uint256(const std::vector<uint8_t>& vec); explicit uint256(const std::vector<uint8_t>& vec);
explicit uint256(int64_t value);
uint256(uint256&&) = default; uint256(uint256&&) = default;
uint256& operator=(uint256&&) = default; uint256& operator=(uint256&&) = default;

View file

@ -1543,8 +1543,9 @@ bool AppInitMain()
} }
auto tip = chainActive.Tip(); auto tip = chainActive.Tip();
LogPrintf("Checking existing claim trie consistency...\n");
if (tip && !CClaimTrieCache(pclaimTrie).validateDb(tip->nHeight, tip->hashClaimTrie)) { if (tip && !CClaimTrieCache(pclaimTrie).validateDb(tip->nHeight, tip->hashClaimTrie)) {
strLoadError = _("Error validating the claim trie from disk"); strLoadError = _("Error validating the stored claim trie");
break; break;
} }

View file

@ -434,7 +434,7 @@ boost::test_tools::predicate_result ClaimTrieChainFixture::best_claim_effective_
bool ClaimTrieChainFixture::getClaimById(const uint160 &claimId, std::string &name, CClaimValue &value) bool ClaimTrieChainFixture::getClaimById(const uint160 &claimId, std::string &name, CClaimValue &value)
{ {
auto query = db << "SELECT nodeName, claimID, txID, txN, amount, validHeight, blockHeight " auto query = db << "SELECT nodeName, claimID, txID, txN, amount, validHeight, updateHeight "
"FROM claim WHERE claimID = ?" << claimId; "FROM claim WHERE claimID = ?" << claimId;
auto hit = false; auto hit = false;
for (auto&& row: query) { for (auto&& row: query) {

View file

@ -280,4 +280,20 @@ BOOST_AUTO_TEST_CASE( operator_with_self )
BOOST_CHECK(v == UintToArith256(uint256S("0"))); BOOST_CHECK(v == UintToArith256(uint256S("0")));
} }
BOOST_AUTO_TEST_CASE( bit64_all_ways )
{
std::vector<int64_t> tests = { 0, 1, -1, 42, 1LL << 33U, std::numeric_limits<int64_t>::max(),
std::numeric_limits<int64_t>::lowest() };
for (auto test: tests) {
auto a = uint256(test);
auto b = arith_uint256(uint64_t(test));
auto c = ArithToUint256(b);
auto d = UintToArith256(a);
BOOST_CHECK(a == c);
BOOST_CHECK(b == d);
BOOST_CHECK_EQUAL(a.GetHex(false), c.GetHex(false));
}
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

View file

@ -53,7 +53,7 @@ CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe)
bool CCoinsViewDB::GetCoin(const COutPoint &outpoint, Coin &coin) const { bool CCoinsViewDB::GetCoin(const COutPoint &outpoint, Coin &coin) const {
auto query = db << "SELECT isCoinbase, blockHeight, amount, script FROM unspent " auto query = db << "SELECT isCoinbase, blockHeight, amount, script FROM unspent "
"WHERE txID = ? and txN = ?" << outpoint.hash << outpoint.n; "WHERE txID = ? AND txN = ?" << outpoint.hash << outpoint.n;
for (auto&& row: query) { for (auto&& row: query) {
uint32_t coinbase = 0, height = 0; uint32_t coinbase = 0, height = 0;
row >> coinbase >> height >> coin.out.nValue >> coin.out.scriptPubKey; row >> coinbase >> height >> coin.out.nValue >> coin.out.scriptPubKey;
@ -66,10 +66,8 @@ bool CCoinsViewDB::GetCoin(const COutPoint &outpoint, Coin &coin) const {
bool CCoinsViewDB::HaveCoin(const COutPoint &outpoint) const { bool CCoinsViewDB::HaveCoin(const COutPoint &outpoint) const {
auto query = db << "SELECT 1 FROM unspent " auto query = db << "SELECT 1 FROM unspent "
"WHERE txID = ? and txN = ?" << outpoint.hash << outpoint.n; "WHERE txID = ? AND txN = ?" << outpoint.hash << outpoint.n;
for (auto&& row: query) return query.begin() != query.end();
return true;
return false;
} }
uint256 CCoinsViewDB::GetBestBlock() const { uint256 CCoinsViewDB::GetBestBlock() const {
@ -113,20 +111,26 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, boo
db << "BEGIN"; db << "BEGIN";
db << "INSERT OR REPLACE INTO marker VALUES('head_block', ?)" << hashBlock; db << "INSERT OR REPLACE INTO marker VALUES('head_block', ?)" << hashBlock;
auto dbd = db << "DELETE FROM unspent WHERE txID = ? AND txN = ?";
auto dbi = db << "INSERT OR REPLACE INTO unspent VALUES(?,?,?,?,?,?,?)";
for (auto it = mapCoins.begin(); it != mapCoins.end();) { for (auto it = mapCoins.begin(); it != mapCoins.end();) {
if (it->second.flags & CCoinsCacheEntry::DIRTY) { if (it->second.flags & CCoinsCacheEntry::DIRTY) {
if (it->second.coin.IsSpent()) { if (it->second.coin.IsSpent()) {
// at present the "IsSpent" flag is used for both "spent" and "block going backwards" // at present the "IsSpent" flag is used for both "spent" and "block going backwards"
db << "DELETE FROM unspent WHERE txID = ? AND txN = ?" << it->first.hash << it->first.n; dbd << it->first.hash << it->first.n;
dbd++;
} }
else { else {
CTxDestination address; CTxDestination address;
std::string destination; std::string destination;
if (ExtractDestination(it->second.coin.out.scriptPubKey, address)) if (ExtractDestination(it->second.coin.out.scriptPubKey, address))
destination = EncodeDestination(address); destination = EncodeDestination(address);
db << "INSERT OR REPLACE INTO unspent VALUES(?,?,?,?,?,?,?)" << it->first.hash << it->first.n uint32_t isCoinBase = it->second.coin.fCoinBase; // bit-field
<< it->second.coin.fCoinBase << it->second.coin.nHeight uint32_t coinHeight = it->second.coin.nHeight; // bit-field
dbi << it->first.hash << it->first.n << isCoinBase << coinHeight
<< it->second.coin.out.nValue << it->second.coin.out.scriptPubKey << destination; << it->second.coin.out.nValue << it->second.coin.out.scriptPubKey << destination;
dbi++;
} }
changed++; changed++;
} }
@ -141,6 +145,8 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, boo
} }
} }
} }
dbd.used(true);
dbi.used(true);
db << "INSERT OR REPLACE INTO marker VALUES('best_block', ?)" << hashBlock; db << "INSERT OR REPLACE INTO marker VALUES('best_block', ?)" << hashBlock;
db << "DELETE FROM marker WHERE name = 'head_block'"; db << "DELETE FROM marker WHERE name = 'head_block'";
@ -201,9 +207,6 @@ CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe)
"nonce INTEGER NOT NULL " "nonce INTEGER NOT NULL "
")"; ")";
db << "CREATE UNIQUE INDEX IF NOT EXISTS block_info_height ON block_info (height)";
db << "CREATE UNIQUE INDEX IF NOT EXISTS block_file_data_pos ON block_info (file, dataPos)";
db << "CREATE TABLE IF NOT EXISTS tx_to_block (" db << "CREATE TABLE IF NOT EXISTS tx_to_block ("
"txID BLOB NOT NULL PRIMARY KEY, " "txID BLOB NOT NULL PRIMARY KEY, "
"file INTEGER NOT NULL, " "file INTEGER NOT NULL, "
@ -220,6 +223,9 @@ CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe)
db << "DELETE FROM tx_to_block"; db << "DELETE FROM tx_to_block";
db << "DELETE FROM flag"; db << "DELETE FROM flag";
} }
// not unique because we actually want to store forks:
db << "CREATE INDEX IF NOT EXISTS block_info_height ON block_info (height)";
} }
bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) { bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) {

View file

@ -2493,6 +2493,12 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp
assert(flushed); assert(flushed);
flushed = trieCache.flush(); flushed = trieCache.flush();
assert(flushed); assert(flushed);
// for verifying that rollback code works:
// auto result = DisconnectBlock(blockConnecting, pindexNew, view, trieCache);
// assert(result == DisconnectResult::DISCONNECT_OK);
// assert(trieCache.getMerkleHash() == pindexNew->pprev->hashClaimTrie);
// LogPrintf("Verified %d!\n", pindexNew->nHeight);
} }
int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3; int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3;
LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime4 - nTime3) * MILLI, nTimeFlush * MICRO, nTimeFlush * MILLI / nBlocksTotal); LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime4 - nTime3) * MILLI, nTimeFlush * MICRO, nTimeFlush * MILLI / nBlocksTotal);

View file

@ -102,7 +102,7 @@ static const int MAX_BLOCKTXN_DEPTH = 10;
* want to make this a per-peer adaptive value at some point. */ * want to make this a per-peer adaptive value at some point. */
static const unsigned int BLOCK_DOWNLOAD_WINDOW = 1024; static const unsigned int BLOCK_DOWNLOAD_WINDOW = 1024;
/** Time to wait (in seconds) between writing blocks/block index to disk. */ /** Time to wait (in seconds) between writing blocks/block index to disk. */
static const unsigned int DATABASE_WRITE_INTERVAL = 60 * 60; static const unsigned int DATABASE_WRITE_INTERVAL = 15 * 60;
/** Time to wait (in seconds) between flushing chainstate to disk. */ /** Time to wait (in seconds) between flushing chainstate to disk. */
static const unsigned int DATABASE_FLUSH_INTERVAL = 24 * 60 * 60; static const unsigned int DATABASE_FLUSH_INTERVAL = 24 * 60 * 60;
/** Maximum length of reject messages. */ /** Maximum length of reject messages. */