it compiles

This commit is contained in:
Brannon King 2019-10-23 14:15:37 -06:00 committed by Anthony Fieroni
parent fd92912b6b
commit 319a1d465f
38 changed files with 258293 additions and 3336 deletions

View file

@ -61,8 +61,8 @@ case $host in
lt_cv_deplibs_check_method="pass_all"
;;
esac
dnl Require C++11 compiler (no GNU extensions)
AX_CXX_COMPILE_STDCXX([11], [noext], [mandatory], [nodefault])
dnl Require C++14 compiler (no GNU extensions)
AX_CXX_COMPILE_STDCXX([14], [noext], [mandatory], [nodefault])
dnl Check if -latomic is required for <std::atomic>
CHECK_ATOMIC

View file

@ -172,7 +172,6 @@ BITCOIN_CORE_H = \
policy/rbf.h \
policy/settings.h \
pow.h \
prefixtrie.h \
protocol.h \
psbt.h \
random.h \
@ -196,6 +195,8 @@ BITCOIN_CORE_H = \
script/standard.h \
shutdown.h \
streams.h \
sqlite/sqlite3.h \
sqlite/hdr/sqlite_modern_cpp.h \
support/allocators/secure.h \
support/allocators/zeroafterfree.h \
support/cleanse.h \
@ -299,7 +300,6 @@ libbitcoin_server_a_SOURCES = \
policy/rbf.cpp \
policy/settings.cpp \
pow.cpp \
prefixtrie.cpp \
rest.cpp \
rpc/blockchain.cpp \
rpc/claimtrie.cpp \
@ -309,6 +309,7 @@ libbitcoin_server_a_SOURCES = \
rpc/rawtransaction.cpp \
rpc/server.cpp \
script/sigcache.cpp \
sqlite/sqlite3.c \
shutdown.cpp \
timedata.cpp \
torcontrol.cpp \

View file

@ -131,7 +131,6 @@ BITCOIN_TESTS =\
test/pmt_tests.cpp \
test/policyestimator_tests.cpp \
test/pow_tests.cpp \
test/prefixtrie_tests.cpp \
test/prevector_tests.cpp \
test/raii_event_tests.cpp \
test/random_tests.cpp \

View file

@ -11,38 +11,44 @@ CClaimScriptAddOp::CClaimScriptAddOp(const COutPoint& point, CAmount nValue, int
{
}
bool CClaimScriptAddOp::claimName(CClaimTrieCache& trieCache, const std::string& name)
bool CClaimScriptAddOp::claimName(CClaimTrieCache& trieCache, const std::string& name,
const std::vector<unsigned char>& metadata)
{
return addClaim(trieCache, name, ClaimIdHash(point.hash, point.n));
return addClaim(trieCache, name, ClaimIdHash(point.hash, point.n), metadata);
}
bool CClaimScriptAddOp::updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId)
bool CClaimScriptAddOp::updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId,
const std::vector<unsigned char>& metadata)
{
return addClaim(trieCache, name, claimId);
return addClaim(trieCache, name, claimId, metadata);
}
bool CClaimScriptAddOp::addClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId)
bool CClaimScriptAddOp::addClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId,
const std::vector<unsigned char>& metadata)
{
return trieCache.addClaim(name, point, claimId, nValue, nHeight);
return trieCache.addClaim(name, point, claimId, nValue, nHeight, metadata);
}
bool CClaimScriptAddOp::supportClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId)
bool CClaimScriptAddOp::supportClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId,
const std::vector<unsigned char>& metadata)
{
return trieCache.addSupport(name, point, nValue, claimId, nHeight);
return trieCache.addSupport(name, point, nValue, claimId, nHeight, metadata);
}
CClaimScriptUndoAddOp::CClaimScriptUndoAddOp(const COutPoint& point, int nHeight) : point(point), nHeight(nHeight)
{
}
bool CClaimScriptUndoAddOp::claimName(CClaimTrieCache& trieCache, const std::string& name)
bool CClaimScriptUndoAddOp::claimName(CClaimTrieCache& trieCache, const std::string& name,
const std::vector<unsigned char>& metadata)
{
auto claimId = ClaimIdHash(point.hash, point.n);
LogPrint(BCLog::CLAIMS, "--- [%lu]: OP_CLAIM_NAME \"%s\" with claimId %s and tx prevout %s at index %d\n", nHeight, name, claimId.GetHex(), point.hash.ToString(), point.n);
return undoAddClaim(trieCache, name, claimId);
}
bool CClaimScriptUndoAddOp::updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId)
bool CClaimScriptUndoAddOp::updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId,
const std::vector<unsigned char>& metadata)
{
LogPrint(BCLog::CLAIMS, "--- [%lu]: OP_UPDATE_CLAIM \"%s\" with claimId %s and tx prevout %s at index %d\n", nHeight, name, claimId.GetHex(), point.hash.ToString(), point.n);
return undoAddClaim(trieCache, name, claimId);
@ -51,13 +57,16 @@ bool CClaimScriptUndoAddOp::updateClaim(CClaimTrieCache& trieCache, const std::s
bool CClaimScriptUndoAddOp::undoAddClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId)
{
LogPrint(BCLog::CLAIMS, "%s: (txid: %s, nOut: %d) Removing %s, claimId: %s, from the claim trie due to block disconnect\n", __func__, point.hash.ToString(), point.n, name, claimId.ToString());
bool res = trieCache.undoAddClaim(name, point, nHeight);
std::string nodeName;
int validHeight;
bool res = trieCache.removeClaim(claimId, nodeName, validHeight);
if (!res)
LogPrint(BCLog::CLAIMS, "%s: Removing claim fails\n", __func__);
return res;
}
bool CClaimScriptUndoAddOp::supportClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId)
bool CClaimScriptUndoAddOp::supportClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId,
const std::vector<unsigned char>& metadata)
{
if (LogAcceptCategory(BCLog::CLAIMS)) {
LogPrintf("--- [%lu]: OP_SUPPORT_CLAIM \"%s\" with claimId %s and tx prevout %s at index %d\n", nHeight, name,
@ -66,7 +75,9 @@ bool CClaimScriptUndoAddOp::supportClaim(CClaimTrieCache& trieCache, const std::
"%s: (txid: %s, nOut: %d) Removing support for %s, claimId: %s, from the claim trie due to block disconnect\n",
__func__, point.hash.ToString(), point.n, name, claimId.ToString());
}
bool res = trieCache.undoAddSupport(name, point, nHeight);
std::string nodeName;
int validHeight;
bool res = trieCache.removeSupport(point, nodeName, validHeight);
if (!res)
LogPrint(BCLog::CLAIMS, "%s: Removing support fails\n", __func__);
return res;
@ -77,14 +88,16 @@ CClaimScriptSpendOp::CClaimScriptSpendOp(const COutPoint& point, int nHeight, in
{
}
bool CClaimScriptSpendOp::claimName(CClaimTrieCache& trieCache, const std::string& name)
bool CClaimScriptSpendOp::claimName(CClaimTrieCache& trieCache, const std::string& name,
const std::vector<unsigned char>& metadata)
{
auto claimId = ClaimIdHash(point.hash, point.n);
LogPrint(BCLog::CLAIMS, "+++ [%lu]: OP_CLAIM_NAME \"%s\" with claimId %s and tx prevout %s at index %d\n", nHeight, name, claimId.GetHex(), point.hash.ToString(), point.n);
return spendClaim(trieCache, name, claimId);
}
bool CClaimScriptSpendOp::updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId)
bool CClaimScriptSpendOp::updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId,
const std::vector<unsigned char>& metadata)
{
LogPrint(BCLog::CLAIMS, "+++ [%lu]: OP_UPDATE_CLAIM \"%s\" with claimId %s and tx prevout %s at index %d\n", nHeight, name, claimId.GetHex(), point.hash.ToString(), point.n);
return spendClaim(trieCache, name, claimId);
@ -92,14 +105,16 @@ bool CClaimScriptSpendOp::updateClaim(CClaimTrieCache& trieCache, const std::str
bool CClaimScriptSpendOp::spendClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId)
{
LogPrint(BCLog::CLAIMS, "%s: (txid: %s, nOut: %d) Removing %s, claimId: %s, from the claim trie\n", __func__, point.hash.ToString(), point.n, name, claimId.ToString());
bool res = trieCache.spendClaim(name, point, nHeight, nValidHeight);
LogPrint(BCLog::CLAIMS, "%s: (txid: %s, nOut: %d) Spending %s, claimId: %s, from the claim trie\n", __func__, point.hash.ToString(), point.n, name, claimId.ToString());
std::string nodeName;
bool res = trieCache.removeClaim(claimId, nodeName, nValidHeight);
if (!res)
LogPrint(BCLog::CLAIMS, "%s: Removing fails\n", __func__);
return res;
}
bool CClaimScriptSpendOp::supportClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId)
bool CClaimScriptSpendOp::supportClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId,
const std::vector<unsigned char>& metadata)
{
if (LogAcceptCategory(BCLog::CLAIMS)) {
LogPrintf("+++ [%lu]: OP_SUPPORT_CLAIM \"%s\" with claimId %s and tx prevout %s at index %d\n", nHeight, name,
@ -107,7 +122,8 @@ bool CClaimScriptSpendOp::supportClaim(CClaimTrieCache& trieCache, const std::st
LogPrintf("%s: (txid: %s, nOut: %d) Restoring support for %s, claimId: %s, to the claim trie\n", __func__,
point.hash.ToString(), point.n, name, claimId.ToString());
}
bool res = trieCache.spendSupport(name, point, nHeight, nValidHeight);
std::string nodeName;
bool res = trieCache.removeSupport(point, nodeName, nValidHeight);
if (!res)
LogPrint(BCLog::CLAIMS, "%s: Removing support fails\n", __func__);
return res;
@ -118,26 +134,30 @@ CClaimScriptUndoSpendOp::CClaimScriptUndoSpendOp(const COutPoint& point, CAmount
{
}
bool CClaimScriptUndoSpendOp::claimName(CClaimTrieCache& trieCache, const std::string& name)
bool CClaimScriptUndoSpendOp::claimName(CClaimTrieCache& trieCache, const std::string& name,
const std::vector<unsigned char>& metadata)
{
return undoSpendClaim(trieCache, name, ClaimIdHash(point.hash, point.n));
return undoSpendClaim(trieCache, name, ClaimIdHash(point.hash, point.n), metadata);
}
bool CClaimScriptUndoSpendOp::updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId)
bool CClaimScriptUndoSpendOp::updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId,
const std::vector<unsigned char>& metadata)
{
return undoSpendClaim(trieCache, name, claimId);
return undoSpendClaim(trieCache, name, claimId, metadata);
}
bool CClaimScriptUndoSpendOp::undoSpendClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId)
bool CClaimScriptUndoSpendOp::undoSpendClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId,
const std::vector<unsigned char>& metadata)
{
LogPrint(BCLog::CLAIMS, "%s: (txid: %s, nOut: %d) Restoring %s, claimId: %s, to the claim trie due to block disconnect\n", __func__, point.hash.ToString(), point.n, name, claimId.ToString());
return trieCache.undoSpendClaim(name, point, claimId, nValue, nHeight, nValidHeight);
return trieCache.addClaim(name, point, claimId, nValue, nHeight, metadata);
}
bool CClaimScriptUndoSpendOp::supportClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId)
bool CClaimScriptUndoSpendOp::supportClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId,
const std::vector<unsigned char>& metadata)
{
LogPrint(BCLog::CLAIMS, "%s: (txid: %s, nOut: %d) Restoring support for %s, claimId: %s, to the claim trie due to block disconnect\n", __func__, point.hash.ToString(), point.n, name, claimId.ToString());
return trieCache.undoSpendSupport(name, point, claimId, nValue, nHeight, nValidHeight);
return trieCache.addSupport(name, point, nValue, claimId, nHeight, metadata);
}
static std::string vchToString(const std::vector<unsigned char>& name)
@ -154,11 +174,13 @@ bool ProcessClaim(CClaimScriptOp& claimOp, CClaimTrieCache& trieCache, const CSc
switch (op) {
case OP_CLAIM_NAME:
return claimOp.claimName(trieCache, vchToString(vvchParams[0]));
return claimOp.claimName(trieCache, vchToString(vvchParams[0]), vvchParams[1]);
case OP_SUPPORT_CLAIM:
return claimOp.supportClaim(trieCache, vchToString(vvchParams[0]), uint160(vvchParams[1]));
return claimOp.supportClaim(trieCache, vchToString(vvchParams[0]), uint160(vvchParams[1]),
vvchParams.size() > 2 ? vvchParams[2] : std::vector<unsigned char>());
case OP_UPDATE_CLAIM:
return claimOp.updateClaim(trieCache, vchToString(vvchParams[0]), uint160(vvchParams[1]));
return claimOp.updateClaim(trieCache, vchToString(vvchParams[0]), uint160(vvchParams[1]),
vvchParams[2]);
}
throw std::runtime_error("Unimplemented OP handler.");
}
@ -213,10 +235,11 @@ void UpdateCache(const CTransaction& tx, CClaimTrieCache& trieCache, const CCoin
public:
using CClaimScriptAddOp::CClaimScriptAddOp;
bool updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId) override
bool updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId,
const std::vector<unsigned char>& metadata) override
{
if (callback(name, claimId))
return CClaimScriptAddOp::updateClaim(trieCache, name, claimId);
return CClaimScriptAddOp::updateClaim(trieCache, name, claimId, metadata);
return false;
}
std::function<bool(const std::string& name, const uint160& claimId)> callback;

View file

@ -22,27 +22,30 @@
class CClaimScriptOp
{
public:
virtual ~CClaimScriptOp() {}
virtual ~CClaimScriptOp() = default;
/**
* Pure virtual, OP_CLAIM_NAME handler
* @param[in] trieCache trie to operate on
* @param[in] name name of the claim
*/
virtual bool claimName(CClaimTrieCache& trieCache, const std::string& name) = 0;
virtual bool claimName(CClaimTrieCache& trieCache, const std::string& name,
const std::vector<unsigned char>& metadata) = 0;
/**
* Pure virtual, OP_UPDATE_CLAIM handler
* @param[in] trieCache trie to operate on
* @param[in] name name of the claim
* @param[in] claimId id of the claim
*/
virtual bool updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId) = 0;
virtual bool updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId,
const std::vector<unsigned char>& metadata) = 0;
/**
* Pure virtual, OP_SUPPORT_CLAIM handler
* @param[in] trieCache trie to operate on
* @param[in] name name of the claim
* @param[in] claimId id of the claim
*/
virtual bool supportClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId) = 0;
virtual bool supportClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId,
const std::vector<unsigned char>& metadata) = 0;
};
/**
@ -62,17 +65,20 @@ public:
* Implementation of OP_CLAIM_NAME handler
* @see CClaimScriptOp::claimName
*/
bool claimName(CClaimTrieCache& trieCache, const std::string& name) override;
bool claimName(CClaimTrieCache& trieCache, const std::string& name,
const std::vector<unsigned char>& metadata) override;
/**
* Implementation of OP_UPDATE_CLAIM handler
* @see CClaimScriptOp::updateClaim
*/
bool updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId) override;
bool updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId,
const std::vector<unsigned char>& metadata) override;
/**
* Implementation of OP_SUPPORT_CLAIM handler
* @see CClaimScriptOp::supportClaim
*/
bool supportClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId) override;
bool supportClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId,
const std::vector<unsigned char>& metadata) override;
protected:
/**
@ -81,7 +87,8 @@ protected:
* @param[in] name name of the claim
* @param[in] claimId id of the claim
*/
virtual bool addClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId);
virtual bool addClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId,
const std::vector<unsigned char>& metadata);
const COutPoint point;
const CAmount nValue;
const int nHeight;
@ -103,17 +110,20 @@ public:
* Implementation of OP_CLAIM_NAME handler
* @see CClaimScriptOp::claimName
*/
bool claimName(CClaimTrieCache& trieCache, const std::string& name) override;
bool claimName(CClaimTrieCache& trieCache, const std::string& name,
const std::vector<unsigned char>& metadata) override;
/**
* Implementation of OP_UPDATE_CLAIM handler
* @see CClaimScriptOp::updateClaim
*/
bool updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId) override;
bool updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId,
const std::vector<unsigned char>& metadata) override;
/**
* Implementation of OP_SUPPORT_CLAIM handler
* @see CClaimScriptOp::supportClaim
*/
bool supportClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId) override;
bool supportClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId,
const std::vector<unsigned char>& metadata) override;
protected:
/**
@ -144,17 +154,20 @@ public:
* Implementation of OP_CLAIM_NAME handler
* @see CClaimScriptOp::claimName
*/
bool claimName(CClaimTrieCache& trieCache, const std::string& name) override;
bool claimName(CClaimTrieCache& trieCache, const std::string& name,
const std::vector<unsigned char>& metadata) override;
/**
* Implementation of OP_UPDATE_CLAIM handler
* @see CClaimScriptOp::updateClaim
*/
bool updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId) override;
bool updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId,
const std::vector<unsigned char>& metadata) override;
/**
* Implementation of OP_SUPPORT_CLAIM handler
* @see CClaimScriptOp::supportClaim
*/
bool supportClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId) override;
bool supportClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId,
const std::vector<unsigned char>& metadata) override;
protected:
/**
@ -187,17 +200,20 @@ public:
* Implementation of OP_CLAIM_NAME handler
* @see CClaimScriptOp::claimName
*/
bool claimName(CClaimTrieCache& trieCache, const std::string& name) override;
bool claimName(CClaimTrieCache& trieCache, const std::string& name,
const std::vector<unsigned char>& metadata) override;
/**
* Implementation of OP_UPDATE_CLAIM handler
* @see CClaimScriptOp::updateClaim
*/
bool updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId) override;
bool updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId,
const std::vector<unsigned char>& metadata) override;
/**
* Implementation of OP_SUPPORT_CLAIM handler
* @see CClaimScriptOp::supportClaim
*/
bool supportClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId) override;
bool supportClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId,
const std::vector<unsigned char>& metadata) override;
protected:
/**
@ -206,7 +222,8 @@ protected:
* @param[in] name name of the claim
* @param[in] claimId id of the claim
*/
virtual bool undoSpendClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId);
virtual bool undoSpendClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId,
const std::vector<unsigned char>& metadata);
const COutPoint point;
const CAmount nValue;
const int nHeight;

File diff suppressed because it is too large Load diff

View file

@ -4,8 +4,7 @@
#include <amount.h>
#include <chain.h>
#include <chainparams.h>
#include <dbwrapper.h>
#include <prefixtrie.h>
#include <primitives/transaction.h>
#include <serialize.h>
#include <uint256.h>
@ -17,17 +16,57 @@
#include <unordered_map>
#include <unordered_set>
// leveldb keys
#define TRIE_NODE 'n'
#define TRIE_NODE_CHILDREN 'b'
#define CLAIM_BY_ID 'i'
#define CLAIM_QUEUE_ROW 'r'
#define CLAIM_QUEUE_NAME_ROW 'm'
#define CLAIM_EXP_QUEUE_ROW 'e'
#define SUPPORT 's'
#define SUPPORT_QUEUE_ROW 'u'
#define SUPPORT_QUEUE_NAME_ROW 'p'
#define SUPPORT_EXP_QUEUE_ROW 'x'
#include <sqlite/sqlite3.h>
namespace sqlite
{
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const uint160& vec) {
void const* buf = reinterpret_cast<void const *>(vec.begin());
return sqlite3_bind_blob(stmt, inx, buf, int(vec.size()), SQLITE_STATIC);
}
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const uint256& vec) {
void const* buf = reinterpret_cast<void const *>(vec.begin());
return sqlite3_bind_blob(stmt, inx, buf, int(vec.size()), SQLITE_STATIC);
}
inline void store_result_in_db(sqlite3_context* db, const uint160& val) {
sqlite3_result_blob(db, val.begin(), int(val.size()), SQLITE_TRANSIENT);
}
inline void store_result_in_db(sqlite3_context* db, const uint256& val) {
sqlite3_result_blob(db, val.begin(), int(val.size()), SQLITE_TRANSIENT);
}
}
#include <sqlite/hdr/sqlite_modern_cpp.h>
namespace sqlite {
template<>
struct has_sqlite_type<uint256, SQLITE_BLOB, void> : std::true_type {};
template<>
struct has_sqlite_type<uint160, SQLITE_BLOB, void> : std::true_type {};
inline uint160 get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<uint160>) {
int bytes = sqlite3_column_bytes(stmt, inx);
uint160 ret;
assert(bytes == ret.size());
auto ptr = sqlite3_column_blob(stmt, inx);
std::memcpy(ret.begin(), ptr, bytes);
return ret;
}
inline uint256 get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<uint256>) {
int bytes = sqlite3_column_bytes(stmt, inx);
uint256 ret;
assert(bytes == ret.size());
auto ptr = sqlite3_column_blob(stmt, inx);
std::memcpy(ret.begin(), ptr, bytes);
return ret;
}
}
uint256 getValueHash(const COutPoint& outPoint, int nHeightOfLastTakeover);
@ -134,83 +173,6 @@ struct CSupportValue
typedef std::vector<CClaimValue> claimEntryType;
typedef std::vector<CSupportValue> supportEntryType;
struct CClaimTrieData
{
uint256 hash;
claimEntryType claims;
int nHeightOfLastTakeover = 0;
CClaimTrieData() = default;
CClaimTrieData(CClaimTrieData&&) = default;
CClaimTrieData(const CClaimTrieData&) = default;
CClaimTrieData& operator=(CClaimTrieData&&) = default;
CClaimTrieData& operator=(const CClaimTrieData& d) = default;
bool insertClaim(const CClaimValue& claim);
bool removeClaim(const COutPoint& outPoint, CClaimValue& claim);
bool getBestClaim(CClaimValue& claim) const;
bool haveClaim(const COutPoint& outPoint) const;
void reorderClaims(const supportEntryType& support);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action)
{
READWRITE(hash);
if (ser_action.ForRead()) {
if (s.eof()) {
claims.clear();
nHeightOfLastTakeover = 0;
return;
}
}
else if (claims.empty())
return;
READWRITE(claims);
READWRITE(nHeightOfLastTakeover);
}
bool operator==(const CClaimTrieData& other) const
{
return hash == other.hash && nHeightOfLastTakeover == other.nHeightOfLastTakeover && claims == other.claims;
}
bool operator!=(const CClaimTrieData& other) const
{
return !(*this == other);
}
bool empty() const
{
return claims.empty();
}
};
struct COutPointHeightType
{
COutPoint outPoint;
int nHeight = 0;
COutPointHeightType() = default;
COutPointHeightType(const COutPoint& outPoint, int nHeight)
: outPoint(outPoint), nHeight(nHeight)
{
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action)
{
READWRITE(outPoint);
READWRITE(nHeight);
}
};
struct CNameOutPointHeightType
{
std::string name;
@ -220,7 +182,7 @@ struct CNameOutPointHeightType
CNameOutPointHeightType() = default;
CNameOutPointHeightType(std::string name, const COutPoint& outPoint, int nHeight)
: name(std::move(name)), outPoint(outPoint), nHeight(nHeight)
: name(std::move(name)), outPoint(outPoint), nHeight(nHeight)
{
}
@ -243,7 +205,7 @@ struct CNameOutPointType
CNameOutPointType() = default;
CNameOutPointType(std::string name, const COutPoint& outPoint)
: name(std::move(name)), outPoint(outPoint)
: name(std::move(name)), outPoint(outPoint)
{
}
@ -262,28 +224,6 @@ struct CNameOutPointType
}
};
struct CClaimIndexElement
{
CClaimIndexElement() = default;
CClaimIndexElement(std::string name, CClaimValue claim)
: name(std::move(name)), claim(std::move(claim))
{
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action)
{
READWRITE(name);
READWRITE(claim);
}
std::string name;
CClaimValue claim;
};
struct CClaimNsupports
{
CClaimNsupports() = default;
@ -303,6 +243,10 @@ struct CClaimNsupports
return claim.claimId.IsNull();
}
bool operator<(const CClaimNsupports& other) const {
return claim < other.claim;
}
CClaimValue claim;
CAmount effectiveAmount = 0;
std::vector<CSupportValue> supports;
@ -343,35 +287,22 @@ struct CClaimSupportToName
const std::vector<CSupportValue> unmatchedSupports;
};
class CClaimTrie : public CPrefixTrie<std::string, CClaimTrieData>
class CClaimTrie
{
public:
CClaimTrie() = default;
virtual ~CClaimTrie() = default;
CClaimTrie() = delete;
CClaimTrie(CClaimTrie&&) = delete;
CClaimTrie(const CClaimTrie&) = delete;
CClaimTrie(bool fMemory, bool fWipe, int proportionalDelayFactor = 32, std::size_t cacheMB=200);
CClaimTrie(bool fMemory, bool fWipe, int height, int proportionalDelayFactor = 32, std::size_t cacheMB=50);
CClaimTrie& operator=(CClaimTrie&&) = delete;
CClaimTrie& operator=(const CClaimTrie&) = delete;
bool SyncToDisk();
friend class CClaimTrieCacheBase;
friend struct ClaimTrieChainFixture;
friend class CClaimTrieCacheExpirationFork;
friend class CClaimTrieCacheNormalizationFork;
friend bool getClaimById(const uint160&, std::string&, CClaimValue*);
friend bool getClaimById(const std::string&, std::string&, CClaimValue*);
std::size_t getTotalNamesInTrie() const;
std::size_t getTotalClaimsInTrie() const;
CAmount getTotalValueOfClaimsInTrie(bool fControllingOnly) const;
protected:
bool empty(); // for tests
sqlite::database _db;
int nNextHeight = 0;
int nProportionalDelayFactor = 0;
std::unique_ptr<CDBWrapper> db;
};
struct CClaimTrieProofNode
@ -406,88 +337,24 @@ struct CClaimTrieProof
COutPoint outPoint;
};
template <typename T>
class COptional
{
bool own;
T* value;
public:
COptional(T* value = nullptr) : own(false), value(value) {}
COptional(COptional&& o)
{
own = o.own;
value = o.value;
o.own = false;
o.value = nullptr;
}
COptional(T&& o) : own(true)
{
value = new T(std::move(o));
}
~COptional()
{
if (own)
delete value;
}
COptional& operator=(COptional&&) = delete;
bool unique() const
{
return own;
}
operator bool() const
{
return value;
}
operator T*() const
{
return value;
}
T* operator->() const
{
return value;
}
operator T&() const
{
return *value;
}
T& operator*() const
{
return *value;
}
};
template <typename T>
using queueEntryType = std::pair<std::string, T>;
typedef std::vector<queueEntryType<CClaimValue>> claimQueueRowType;
typedef std::map<int, claimQueueRowType> claimQueueType;
typedef std::vector<queueEntryType<CSupportValue>> supportQueueRowType;
typedef std::map<int, supportQueueRowType> supportQueueType;
typedef std::vector<COutPointHeightType> queueNameRowType;
typedef std::map<std::string, queueNameRowType> queueNameType;
typedef std::vector<CNameOutPointHeightType> insertUndoType;
typedef std::vector<CNameOutPointType> expirationQueueRowType;
typedef std::map<int, expirationQueueRowType> expirationQueueType;
typedef std::set<CClaimValue> claimIndexClaimListType;
typedef std::vector<CClaimIndexElement> claimIndexElementListType;
class CClaimTrieCacheBase
{
public:
explicit CClaimTrieCacheBase(CClaimTrie* base);
virtual ~CClaimTrieCacheBase() = default;
virtual ~CClaimTrieCacheBase();
uint256 getMerkleHash();
bool flush();
bool empty() const;
bool checkConsistency() const;
bool ReadFromDisk(const CBlockIndex* tip);
bool checkConsistency();
bool ValidateTipMatches(const CBlockIndex* tip);
bool haveClaim(const std::string& name, const COutPoint& outPoint) const;
bool haveClaimInQueue(const std::string& name, const COutPoint& outPoint, int& nValidAtHeight) const;
@ -495,17 +362,13 @@ public:
bool haveSupport(const std::string& name, const COutPoint& outPoint) const;
bool haveSupportInQueue(const std::string& name, const COutPoint& outPoint, int& nValidAtHeight) const;
bool addClaim(const std::string& name, const COutPoint& outPoint, const uint160& claimId, CAmount nAmount, int nHeight);
bool undoAddClaim(const std::string& name, const COutPoint& outPoint, int nHeight);
bool addClaim(const std::string& name, const COutPoint& outPoint, const uint160& claimId, CAmount nAmount,
int nHeight, const std::vector<unsigned char>& metadata);
bool addSupport(const std::string& name, const COutPoint& outPoint, CAmount nAmount,
const uint160& supportedClaimId, int nHeight, const std::vector<unsigned char>& metadata);
bool spendClaim(const std::string& name, const COutPoint& outPoint, int nHeight, int& nValidAtHeight);
bool undoSpendClaim(const std::string& name, const COutPoint& outPoint, const uint160& claimId, CAmount nAmount, int nHeight, int nValidAtHeight);
bool addSupport(const std::string& name, const COutPoint& outPoint, CAmount nAmount, const uint160& supportedClaimId, int nHeight);
bool undoAddSupport(const std::string& name, const COutPoint& outPoint, int nHeight);
bool spendSupport(const std::string& name, const COutPoint& outPoint, int nHeight, int& nValidAtHeight);
bool undoSpendSupport(const std::string& name, const COutPoint& outPoint, const uint160& supportedClaimId, CAmount nAmount, int nHeight, int nValidAtHeight);
bool removeSupport(const COutPoint& outPoint, std::string& nodeName, int& validHeight);
bool removeClaim(const uint160& claimId, std::string& nodeName, int& validHeight);
virtual bool incrementBlock(insertUndoType& insertUndo,
claimQueueRowType& expireUndo,
@ -518,140 +381,49 @@ public:
insertUndoType& insertSupportUndo,
supportQueueRowType& expireSupportUndo);
virtual bool getProofForName(const std::string& name, CClaimTrieProof& proof);
virtual bool getInfoForName(const std::string& name, CClaimValue& claim) const;
virtual bool getProofForName(const std::string& name, const uint160& finalClaim, CClaimTrieProof& proof);
virtual int expirationTime() const;
virtual bool finalizeDecrement(std::vector<std::pair<std::string, int>>& takeoverHeightUndo);
virtual CClaimSupportToName getClaimsForName(const std::string& name) const;
CClaimTrie::const_iterator find(const std::string& name) const;
void iterate(std::function<void(const std::string&, const CClaimTrieData&)> callback) const;
void dumpToLog(CClaimTrie::const_iterator it, bool diffFromBase = true) const;
virtual std::string adjustNameForValidHeight(const std::string& name, int validHeight) const;
std::size_t getTotalNamesInTrie() const;
std::size_t getTotalClaimsInTrie() const;
CAmount getTotalValueOfClaimsInTrie(bool fControllingOnly) const;
bool findNameForClaim(const std::vector<unsigned char>& claim, CClaimValue& value, std::string& name);
void getNamesInTrie(std::function<void(const std::string&)> callback);
bool getLastTakeoverForName(const std::string& name, uint160& claimId, int& takeoverHeight) const;
protected:
CClaimTrie* base;
CClaimTrie nodesToAddOrUpdate; // nodes pulled in from base (and possibly modified thereafter), written to base on flush
std::unordered_set<std::string> nodesAlreadyCached; // set of nodes already pulled into cache from base
std::unordered_set<std::string> namesToCheckForTakeover; // takeover numbers are updated on increment
bool dirtyNodes;
virtual uint256 recursiveComputeMerkleHash(CClaimTrie::iterator& it);
virtual bool recursiveCheckConsistency(CClaimTrie::const_iterator& it, std::string& failed) const;
virtual bool insertClaimIntoTrie(const std::string& name, const CClaimValue& claim, bool fCheckTakeover);
virtual bool removeClaimFromTrie(const std::string& name, const COutPoint& outPoint, CClaimValue& claim, bool fCheckTakeover);
virtual bool insertSupportIntoMap(const std::string& name, const CSupportValue& support, bool fCheckTakeover);
virtual bool removeSupportFromMap(const std::string& name, const COutPoint& outPoint, CSupportValue& support, bool fCheckTakeover);
virtual uint256 recursiveComputeMerkleHash(const std::string& name, int lastTakeoverHeight, bool checkOnly);
supportEntryType getSupportsForName(const std::string& name) const;
int getDelayForName(const std::string& name) const;
virtual int getDelayForName(const std::string& name, const uint160& claimId) const;
CClaimTrie::iterator cacheData(const std::string& name, bool create = true);
bool getLastTakeoverForName(const std::string& name, uint160& claimId, int& takeoverHeight) const;
int getNumBlocksOfContinuousOwnership(const std::string& name) const;
void reactivateClaim(const expirationQueueRowType& row, int height, bool increment);
void reactivateSupport(const expirationQueueRowType& row, int height, bool increment);
expirationQueueType expirationQueueCache;
expirationQueueType supportExpirationQueueCache;
bool deleteNodeIfPossible(const std::string& name, std::string& parent, std::vector<std::string>& claims);
void ensureTreeStructureIsUpToDate();
int nNextHeight; // Height of the block that is being worked on, which is
// one greater than the height of the chain's tip
private:
uint256 hashBlock;
std::unordered_map<std::string, std::pair<uint160, int>> takeoverCache;
claimQueueType claimQueueCache; // claims not active yet: to be written to disk on flush
queueNameType claimQueueNameCache;
supportQueueType supportQueueCache; // supports not active yet: to be written to disk on flush
queueNameType supportQueueNameCache;
claimIndexElementListType claimsToAddToByIdIndex; // written to index on flush
claimIndexClaimListType claimsToDeleteFromByIdIndex;
std::unordered_map<std::string, supportEntryType> supportCache; // to be added/updated to base (and disk) on flush
std::unordered_set<std::string> nodesToDelete; // to be removed from base (and disk) on flush
std::unordered_map<std::string, bool> takeoverWorkaround;
std::unordered_set<std::string> removalWorkaround;
bool shouldUseTakeoverWorkaround(const std::string& key) const;
void addTakeoverWorkaroundPotential(const std::string& key);
void confirmTakeoverWorkaroundNeeded(const std::string& key);
bool clear();
void markAsDirty(const std::string& name, bool fCheckTakeover);
bool removeSupport(const std::string& name, const COutPoint& outPoint, int nHeight, int& nValidAtHeight, bool fCheckTakeover);
bool removeClaim(const std::string& name, const COutPoint& outPoint, int nHeight, int& nValidAtHeight, bool fCheckTakeover);
template <typename T>
void insertRowsFromQueue(std::vector<T>& result, const std::string& name) const;
template <typename T>
std::vector<queueEntryType<T>>* getQueueCacheRow(int nHeight, bool createIfNotExists);
template <typename T>
COptional<const std::vector<queueEntryType<T>>> getQueueCacheRow(int nHeight) const;
template <typename T>
queueNameRowType* getQueueCacheNameRow(const std::string& name, bool createIfNotExists);
template <typename T>
COptional<const queueNameRowType> getQueueCacheNameRow(const std::string& name) const;
template <typename T>
expirationQueueRowType* getExpirationQueueCacheRow(int nHeight, bool createIfNotExists);
template <typename T>
bool haveInQueue(const std::string& name, const COutPoint& outPoint, int& nValidAtHeight) const;
template <typename T>
T add(const std::string& name, const COutPoint& outPoint, const uint160& claimId, CAmount nAmount, int nHeight);
template <typename T>
bool remove(T& value, const std::string& name, const COutPoint& outPoint, int nHeight, int& nValidAtHeight, bool fCheckTakeover = false);
template <typename T>
bool addToQueue(const std::string& name, const T& value);
template <typename T>
bool removeFromQueue(const std::string& name, const COutPoint& outPoint, T& value);
template <typename T>
bool addToCache(const std::string& name, const T& value, bool fCheckTakeover = false);
template <typename T>
bool removeFromCache(const std::string& name, const COutPoint& outPoint, T& value, bool fCheckTakeover = false);
template <typename T>
bool undoSpend(const std::string& name, const T& value, int nValidAtHeight);
template <typename T>
void undoIncrement(insertUndoType& insertUndo, std::vector<queueEntryType<T>>& expireUndo, std::set<T>* deleted = nullptr);
template <typename T>
void undoDecrement(insertUndoType& insertUndo, std::vector<queueEntryType<T>>& expireUndo, std::vector<CClaimIndexElement>* added = nullptr, std::set<T>* deleted = nullptr);
template <typename T>
void undoIncrement(const std::string& name, insertUndoType& insertUndo, std::vector<queueEntryType<T>>& expireUndo);
template <typename T>
void reactivate(const expirationQueueRowType& row, int height, bool increment);
// for unit test
friend struct ClaimTrieChainFixture;
friend class CClaimTrieCacheTest;
void activateAllFor(insertUndoType& insertUndo, insertUndoType& insertSupportUndo,
const std::string& takeover);
};
class CClaimTrieCacheExpirationFork : public CClaimTrieCacheBase
@ -685,7 +457,7 @@ class CClaimTrieCacheNormalizationFork : public CClaimTrieCacheExpirationFork
{
public:
explicit CClaimTrieCacheNormalizationFork(CClaimTrie* base)
: CClaimTrieCacheExpirationFork(base), overrideInsertNormalization(false), overrideRemoveNormalization(false)
: CClaimTrieCacheExpirationFork(base)
{
}
@ -706,29 +478,16 @@ public:
insertUndoType& insertSupportUndo,
supportQueueRowType& expireSupportUndo) override;
bool getProofForName(const std::string& name, CClaimTrieProof& proof) override;
bool getProofForName(const std::string& name, const uint160& finalClaim, CClaimTrieProof& proof) override;
bool getInfoForName(const std::string& name, CClaimValue& claim) const override;
CClaimSupportToName getClaimsForName(const std::string& name) const override;
std::string adjustNameForValidHeight(const std::string& name, int validHeight) const override;
protected:
bool insertClaimIntoTrie(const std::string& name, const CClaimValue& claim, bool fCheckTakeover) override;
bool removeClaimFromTrie(const std::string& name, const COutPoint& outPoint, CClaimValue& claim, bool fCheckTakeover) override;
bool insertSupportIntoMap(const std::string& name, const CSupportValue& support, bool fCheckTakeover) override;
bool removeSupportFromMap(const std::string& name, const COutPoint& outPoint, CSupportValue& support, bool fCheckTakeover) override;
int getDelayForName(const std::string& name, const uint160& claimId) const override;
private:
bool overrideInsertNormalization;
bool overrideRemoveNormalization;
bool normalizeAllNamesInTrieIfNecessary(insertUndoType& insertUndo,
claimQueueRowType& removeUndo,
insertUndoType& insertSupportUndo,
supportQueueRowType& expireSupportUndo,
std::vector<std::pair<std::string, int>>& takeoverHeightUndo);
bool normalizeAllNamesInTrieIfNecessary(bool forward);
};
class CClaimTrieCacheHashFork : public CClaimTrieCacheNormalizationFork
@ -736,19 +495,14 @@ class CClaimTrieCacheHashFork : public CClaimTrieCacheNormalizationFork
public:
explicit CClaimTrieCacheHashFork(CClaimTrie* base);
bool getProofForName(const std::string& name, CClaimTrieProof& proof) override;
bool getProofForName(const std::string& name, CClaimTrieProof& proof, const std::function<bool(const CClaimValue&)>& comp);
bool getProofForName(const std::string& name, const uint160& finalClaim, CClaimTrieProof& proof) override;
void initializeIncrement() override;
bool finalizeDecrement(std::vector<std::pair<std::string, int>>& takeoverHeightUndo) override;
bool allowSupportMetadata() const;
protected:
uint256 recursiveComputeMerkleHash(CClaimTrie::iterator& it) override;
bool recursiveCheckConsistency(CClaimTrie::const_iterator& it, std::string& failed) const override;
private:
void copyAllBaseToCache();
uint256 recursiveComputeMerkleHash(const std::string& name, int lastTakeoverHeight, bool checkOnly) override;
};
typedef CClaimTrieCacheHashFork CClaimTrieCache;

View file

@ -7,7 +7,6 @@
#include <boost/locale.hpp>
#include <boost/locale/conversion.hpp>
#include <boost/locale/localization_backend.hpp>
#include <boost/scope_exit.hpp>
#include <boost/scoped_ptr.hpp>
CClaimTrieCacheExpirationFork::CClaimTrieCacheExpirationFork(CClaimTrie* base)
@ -71,30 +70,12 @@ bool CClaimTrieCacheExpirationFork::forkForExpirationChange(bool increment)
will have their expiration extension removed.
*/
//look through db for expiration queues, if we haven't already found it in dirty expiration queue
boost::scoped_ptr<CDBIterator> pcursor(base->db->NewIterator());
for (pcursor->SeekToFirst(); pcursor->Valid(); pcursor->Next()) {
std::pair<uint8_t, int> key;
if (!pcursor->GetKey(key))
continue;
int height = key.second;
if (key.first == CLAIM_EXP_QUEUE_ROW) {
expirationQueueRowType row;
if (pcursor->GetValue(row)) {
reactivateClaim(row, height, increment);
} else {
return error("%s(): error reading expiration queue rows from disk", __func__);
}
} else if (key.first == SUPPORT_EXP_QUEUE_ROW) {
expirationQueueRowType row;
if (pcursor->GetValue(row)) {
reactivateSupport(row, height, increment);
} else {
return error("%s(): error reading support expiration queue rows from disk", __func__);
}
}
}
auto extension = Params().GetConsensus().nExtendedClaimExpirationTime - Params().GetConsensus().nOriginalClaimExpirationTime;
if (!increment) extension = -extension;
base->_db << "UPDATE claims SET expirationHeight = expirationHeight + ? WHERE expirationHeight >= ?" << extension << nNextHeight;
base->_db << "UPDATE supports SET expirationHeight = expirationHeight + ? WHERE expirationHeight >= ?" << extension << nNextHeight;
base->_db << "UPDATE nodes SET hash = NULL, claimHash = NULL"; // recompute all hashes (as there aren't that many at this point)
dirtyNodes = true;
return true;
}
@ -144,27 +125,7 @@ std::string CClaimTrieCacheNormalizationFork::normalizeClaimName(const std::stri
return normalized;
}
bool CClaimTrieCacheNormalizationFork::insertClaimIntoTrie(const std::string& name, const CClaimValue& claim, bool fCheckTakeover)
{
return CClaimTrieCacheExpirationFork::insertClaimIntoTrie(normalizeClaimName(name, overrideInsertNormalization), claim, fCheckTakeover);
}
bool CClaimTrieCacheNormalizationFork::removeClaimFromTrie(const std::string& name, const COutPoint& outPoint, CClaimValue& claim, bool fCheckTakeover)
{
return CClaimTrieCacheExpirationFork::removeClaimFromTrie(normalizeClaimName(name, overrideRemoveNormalization), outPoint, claim, fCheckTakeover);
}
bool CClaimTrieCacheNormalizationFork::insertSupportIntoMap(const std::string& name, const CSupportValue& support, bool fCheckTakeover)
{
return CClaimTrieCacheExpirationFork::insertSupportIntoMap(normalizeClaimName(name, overrideInsertNormalization), support, fCheckTakeover);
}
bool CClaimTrieCacheNormalizationFork::removeSupportFromMap(const std::string& name, const COutPoint& outPoint, CSupportValue& support, bool fCheckTakeover)
{
return CClaimTrieCacheExpirationFork::removeSupportFromMap(normalizeClaimName(name, overrideRemoveNormalization), outPoint, support, fCheckTakeover);
}
bool CClaimTrieCacheNormalizationFork::normalizeAllNamesInTrieIfNecessary(insertUndoType& insertUndo, claimQueueRowType& removeUndo, insertUndoType& insertSupportUndo, supportQueueRowType& expireSupportUndo, std::vector<std::pair<std::string, int>>& takeoverHeightUndo)
bool CClaimTrieCacheNormalizationFork::normalizeAllNamesInTrieIfNecessary(bool forward)
{
if (nNextHeight != Params().GetConsensus().nNormalizedNameForkHeight)
return false;
@ -172,66 +133,40 @@ bool CClaimTrieCacheNormalizationFork::normalizeAllNamesInTrieIfNecessary(insert
// run the one-time upgrade of all names that need to change
// it modifies the (cache) trie as it goes, so we need to grab everything to be modified first
for (auto it = base->cbegin(); it != base->cend(); ++it) {
const std::string normalized = normalizeClaimName(it.key(), true);
if (normalized == it.key())
continue;
base->_db.define("NORMALIZED", [this](const std::string& str) { return normalizeClaimName(str, true); });
auto& name = it.key();
auto supports = getSupportsForName(name);
for (auto support : supports) {
// if it's already going to expire just skip it
if (support.nHeight + expirationTime() <= nNextHeight)
continue;
assert(removeSupportFromMap(name, support.outPoint, support, false));
expireSupportUndo.emplace_back(name, support);
assert(insertSupportIntoMap(normalized, support, false));
insertSupportUndo.emplace_back(name, support.outPoint, -1);
}
namesToCheckForTakeover.insert(normalized);
auto cached = cacheData(name, false);
if (!cached || cached->empty())
continue;
auto claimsCopy = cached->claims;
auto takeoverHeightCopy = cached->nHeightOfLastTakeover;
for (auto claim : claimsCopy) {
if (claim.nHeight + expirationTime() <= nNextHeight)
continue;
assert(removeClaimFromTrie(name, claim.outPoint, claim, false));
removeUndo.emplace_back(name, claim);
assert(insertClaimIntoTrie(normalized, claim, true));
insertUndo.emplace_back(name, claim.outPoint, -1);
}
takeoverHeightUndo.emplace_back(name, takeoverHeightCopy);
auto query = base->_db << "SELECT NORMALIZED(name), name, claimID as nn FROM claims HAVING nodeName != nn";
for(auto&& row: query) {
std::string newName, oldName;
uint160 claimID;
row >> newName >> oldName >> claimID;
if (!forward) std::swap(newName, oldName);
base->_db << "UPDATE claims SET nodeName = ? WHERE claimID = ?" << newName << claimID;
base->_db << "DELETE FROM nodes WHERE name = ?" << oldName;
base->_db << "INSERT INTO nodes(name) VALUES(?) ON CONFLICT DO UPDATE hash = NULL, claimHash = NULL" << newName;
}
dirtyNodes = true;
return true;
}
bool CClaimTrieCacheNormalizationFork::incrementBlock(insertUndoType& insertUndo, claimQueueRowType& expireUndo, insertUndoType& insertSupportUndo, supportQueueRowType& expireSupportUndo, std::vector<std::pair<std::string, int>>& takeoverHeightUndo)
{
overrideInsertNormalization = normalizeAllNamesInTrieIfNecessary(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo, takeoverHeightUndo);
BOOST_SCOPE_EXIT(&overrideInsertNormalization) { overrideInsertNormalization = false; }
BOOST_SCOPE_EXIT_END
normalizeAllNamesInTrieIfNecessary(true);
return CClaimTrieCacheExpirationFork::incrementBlock(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo, takeoverHeightUndo);
}
bool CClaimTrieCacheNormalizationFork::decrementBlock(insertUndoType& insertUndo, claimQueueRowType& expireUndo, insertUndoType& insertSupportUndo, supportQueueRowType& expireSupportUndo)
{
overrideRemoveNormalization = shouldNormalize();
BOOST_SCOPE_EXIT(&overrideRemoveNormalization) { overrideRemoveNormalization = false; }
BOOST_SCOPE_EXIT_END
return CClaimTrieCacheExpirationFork::decrementBlock(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo);
auto ret = CClaimTrieCacheExpirationFork::decrementBlock(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo);
if (ret)
normalizeAllNamesInTrieIfNecessary(false);
return ret;
}
bool CClaimTrieCacheNormalizationFork::getProofForName(const std::string& name, CClaimTrieProof& proof)
bool CClaimTrieCacheNormalizationFork::getProofForName(const std::string& name, const uint160& finalClaim, CClaimTrieProof& proof)
{
return CClaimTrieCacheExpirationFork::getProofForName(normalizeClaimName(name), proof);
return CClaimTrieCacheExpirationFork::getProofForName(normalizeClaimName(name), finalClaim, proof);
}
bool CClaimTrieCacheNormalizationFork::getInfoForName(const std::string& name, CClaimValue& claim) const
@ -261,72 +196,46 @@ CClaimTrieCacheHashFork::CClaimTrieCacheHashFork(CClaimTrie* base) : CClaimTrieC
static const uint256 leafHash = uint256S("0000000000000000000000000000000000000000000000000000000000000002");
static const uint256 emptyHash = uint256S("0000000000000000000000000000000000000000000000000000000000000003");
std::vector<uint256> getClaimHashes(const CClaimTrieData& data)
uint256 CClaimTrieCacheHashFork::recursiveComputeMerkleHash(const std::string& name, int lastTakeoverHeight, bool checkOnly)
{
std::vector<uint256> hashes;
for (auto& claim : data.claims)
hashes.push_back(getValueHash(claim.outPoint, data.nHeightOfLastTakeover));
return hashes;
}
if (nNextHeight < Params().GetConsensus().nAllClaimsInMerkleForkHeight)
return CClaimTrieCacheNormalizationFork::recursiveComputeMerkleHash(name, lastTakeoverHeight, checkOnly);
template <typename T>
using iCbType = std::function<uint256(T&)>;
auto childQuery = base->_db << "SELECT name, hash, lastTakeoverHeight FROM nodes WHERE parent = ? ORDER BY name" << name;
template <typename TIterator>
uint256 recursiveBinaryTreeHash(TIterator& it, const iCbType<TIterator>& process)
{
std::vector<uint256> childHashes;
for (auto& child : it.children())
childHashes.emplace_back(process(child));
for (auto&& row: childQuery) {
std::string key;
int keyLastTakeoverHeight;
std::unique_ptr<uint256> hash;
row >> key >> hash >> keyLastTakeoverHeight;
if (hash == nullptr) hash = std::make_unique<uint256>();
if (hash->IsNull()) {
*hash = recursiveComputeMerkleHash(key, keyLastTakeoverHeight, checkOnly);
}
childHashes.push_back(*hash);
}
auto claimQuery = base->_db << "SELECT c.txID, c.txN, c.validHeight, c.amount + "
"SUM(SELECT s.amount FROM supports s WHERE s.supportedClaimID = c.claimID AND s.validHeight < ?) as effectiveAmount"
"FROM claims c WHERE c.nodeName = ? AND c.validHeight < ? "
"ORDER BY effectiveAmount DESC, c.blockHeight, c.txID, c.txN" << nNextHeight << name << nNextHeight;
std::vector<uint256> claimHashes;
if (!it->empty())
claimHashes = getClaimHashes(it.data());
else if (!it.hasChildren())
return {};
for (auto&& row: claimQuery) {
COutPoint p;
row >> p.hash >> p.n;
auto claimHash = getValueHash(p, lastTakeoverHeight);
claimHashes.push_back(claimHash);
}
auto left = childHashes.empty() ? leafHash : ComputeMerkleRoot(childHashes);
auto right = claimHashes.empty() ? emptyHash : ComputeMerkleRoot(claimHashes);
return Hash(left.begin(), left.end(), right.begin(), right.end());
}
uint256 CClaimTrieCacheHashFork::recursiveComputeMerkleHash(CClaimTrie::iterator& it)
{
if (nNextHeight < Params().GetConsensus().nAllClaimsInMerkleForkHeight)
return CClaimTrieCacheNormalizationFork::recursiveComputeMerkleHash(it);
using iterator = CClaimTrie::iterator;
iCbType<iterator> process = [&process](iterator& it) -> uint256 {
if (it->hash.IsNull())
it->hash = recursiveBinaryTreeHash(it, process);
assert(!it->hash.IsNull());
return it->hash;
};
return process(it);
}
bool CClaimTrieCacheHashFork::recursiveCheckConsistency(CClaimTrie::const_iterator& it, std::string& failed) const
{
if (nNextHeight < Params().GetConsensus().nAllClaimsInMerkleForkHeight)
return CClaimTrieCacheNormalizationFork::recursiveCheckConsistency(it, failed);
struct CRecursiveBreak {};
using iterator = CClaimTrie::const_iterator;
iCbType<iterator> process = [&failed, &process](iterator& it) -> uint256 {
if (it->hash.IsNull() || it->hash != recursiveBinaryTreeHash(it, process)) {
failed = it.key();
throw CRecursiveBreak();
}
return it->hash;
};
try {
process(it);
} catch (const CRecursiveBreak&) {
return false;
}
return true;
auto computedHash = Hash(left.begin(), left.end(), right.begin(), right.end());
if (!checkOnly)
base->_db << "UPDATE nodes SET hash = ? WHERE name = ?" << computedHash << name;
return computedHash;
}
std::vector<uint256> ComputeMerklePath(const std::vector<uint256>& hashes, uint32_t idx)
@ -384,15 +293,10 @@ std::vector<uint256> ComputeMerklePath(const std::vector<uint256>& hashes, uint3
return res;
}
bool CClaimTrieCacheHashFork::getProofForName(const std::string& name, CClaimTrieProof& proof)
{
return getProofForName(name, proof, nullptr);
}
bool CClaimTrieCacheHashFork::getProofForName(const std::string& name, CClaimTrieProof& proof, const std::function<bool(const CClaimValue&)>& comp)
bool CClaimTrieCacheHashFork::getProofForName(const std::string& name, const uint160& finalClaim, CClaimTrieProof& proof)
{
if (nNextHeight < Params().GetConsensus().nAllClaimsInMerkleForkHeight)
return CClaimTrieCacheNormalizationFork::getProofForName(name, proof);
return CClaimTrieCacheNormalizationFork::getProofForName(name, finalClaim, proof);
auto fillPairs = [&proof](const std::vector<uint256>& hashes, uint32_t idx) {
auto partials = ComputeMerklePath(hashes, idx);
@ -401,39 +305,51 @@ bool CClaimTrieCacheHashFork::getProofForName(const std::string& name, CClaimTri
};
// cache the parent nodes
cacheData(name, false);
getMerkleHash();
proof = CClaimTrieProof();
for (auto& it : static_cast<const CClaimTrie&>(nodesToAddOrUpdate).nodes(name)) {
auto nodeQuery = base->_db << "SELECT name, lastTakeoverHeight FROM nodes WHERE "
"name IN (WITH RECURSIVE prefix(p) AS (VALUES(?) UNION ALL "
"SELECT SUBSTR(p, 0, LENGTH(p)) FROM prefix WHERE p != '') SELECT p FROM prefix) "
"ORDER BY LENGTH(name)" << name;
for (auto&& row: nodeQuery) {
std::string key; int lastTakeover;
row >> key >> lastTakeover;
std::vector<uint256> childHashes;
uint32_t nextCurrentIdx = 0;
for (auto& child : it.children()) {
if (name.find(child.key()) == 0)
auto childQuery = base->_db << "SELECT name, hash FROM nodes WHERE parent = ?" << key;
for (auto&& child : childQuery) {
std::string childKey;
uint256 childHash;
child >> childKey >> childHash;
if (name.find(childKey) == 0)
nextCurrentIdx = uint32_t(childHashes.size());
childHashes.push_back(child->hash);
childHashes.push_back(childHash);
}
auto cns = getClaimsForName(key);
std::vector<uint256> claimHashes;
if (!it->empty())
claimHashes = getClaimHashes(it.data());
uint32_t finalClaimIdx = 0;
COutPoint finalOutPoint;
for (uint32_t i = 0; i < cns.claimsNsupports.size(); ++i) {
auto& child = cns.claimsNsupports[i].claim;
claimHashes.push_back(getValueHash(child.outPoint, lastTakeover));
if (child.claimId == finalClaim) {
finalClaimIdx = i;
finalOutPoint = child.outPoint;
}
}
// I am on a node; I need a hash(children, claims)
// if I am the last node on the list, it will be hash(children, x)
// else it will be hash(x, claims)
if (it.key() == name) {
uint32_t nClaimIndex = 0;
auto& claims = it->claims;
auto itClaim = !comp ? claims.begin() : std::find_if(claims.begin(), claims.end(), comp);
if (itClaim != claims.end()) {
proof.hasValue = true;
proof.outPoint = itClaim->outPoint;
proof.nHeightOfLastTakeover = it->nHeightOfLastTakeover;
nClaimIndex = std::distance(claims.begin(), itClaim);
}
if (key == name) {
proof.outPoint = finalOutPoint;
proof.nHeightOfLastTakeover = lastTakeover;
proof.hasValue = true;
auto hash = childHashes.empty() ? leafHash : ComputeMerkleRoot(childHashes);
proof.pairs.emplace_back(true, hash);
if (!claimHashes.empty())
fillPairs(claimHashes, nClaimIndex);
fillPairs(claimHashes, finalClaimIdx);
} else {
auto hash = claimHashes.empty() ? emptyHash : ComputeMerkleRoot(claimHashes);
proof.pairs.emplace_back(false, hash);
@ -445,33 +361,19 @@ bool CClaimTrieCacheHashFork::getProofForName(const std::string& name, CClaimTri
return true;
}
void CClaimTrieCacheHashFork::copyAllBaseToCache()
{
for (auto it = base->cbegin(); it != base->cend(); ++it)
if (nodesAlreadyCached.insert(it.key()).second)
nodesToAddOrUpdate.insert(it.key(), it.data());
for (auto it = nodesToAddOrUpdate.begin(); it != nodesToAddOrUpdate.end(); ++it)
it->hash.SetNull();
}
void CClaimTrieCacheHashFork::initializeIncrement()
{
CClaimTrieCacheNormalizationFork::initializeIncrement();
// we could do this in the constructor, but that would not allow for multiple increments in a row (as done in unit tests)
if (nNextHeight != Params().GetConsensus().nAllClaimsInMerkleForkHeight - 1)
return;
// if we are forking, we load the entire base trie into the cache trie
// we reset its hash computation so it can be recomputed completely
copyAllBaseToCache();
if (nNextHeight == Params().GetConsensus().nAllClaimsInMerkleForkHeight - 1)
base->_db << "UPDATE nodes SET hash = NULL";
}
bool CClaimTrieCacheHashFork::finalizeDecrement(std::vector<std::pair<std::string, int>>& takeoverHeightUndo)
{
auto ret = CClaimTrieCacheNormalizationFork::finalizeDecrement(takeoverHeightUndo);
if (ret && nNextHeight == Params().GetConsensus().nAllClaimsInMerkleForkHeight - 1)
copyAllBaseToCache();
base->_db << "UPDATE nodes SET hash = NULL";
return ret;
}

View file

@ -1590,7 +1590,7 @@ bool AppInitMain(InitInterfaces& interfaces)
}
CClaimTrieCache trieCache(pclaimTrie);
if (!trieCache.ReadFromDisk(chainActive.Tip()))
if (!trieCache.ValidateTipMatches(chainActive.Tip()))
{
strLoadError = _("Error loading the claim trie from disk");
break;

View file

@ -199,8 +199,6 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
CValidationState state;
if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) {
if (!trieCache.empty())
trieCache.dumpToLog(trieCache.find({}));
throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state)));
}
int64_t nTime2 = GetTimeMicros();

View file

@ -1,577 +0,0 @@
#include <claimtrie.h>
#include <fs.h>
#include <lbry.h>
#include <limits>
#include <memory>
#include <prefixtrie.h>
#include <boost/interprocess/allocators/private_node_allocator.hpp>
#include <boost/interprocess/indexes/null_index.hpp>
#include <boost/interprocess/managed_mapped_file.hpp>
namespace bip = boost::interprocess;
typedef bip::basic_managed_mapped_file <
char,
bip::rbtree_best_fit<bip::null_mutex_family, bip::offset_ptr<void>>,
bip::null_index
> managed_mapped_file;
template <typename T>
using node_allocator = bip::private_node_allocator<T, managed_mapped_file::segment_manager>;
static managed_mapped_file::segment_manager* segmentManager()
{
struct CSharedMemoryFile
{
CSharedMemoryFile() : file(GetDataDir() / "shared.mem")
{
fs::remove(file);
auto size = (uint64_t)g_memfileSize * 1024ULL * 1024ULL * 1024ULL;
// using string() to remove w_char filename encoding on Windows
menaged_file.reset(new managed_mapped_file(bip::create_only, file.string().c_str(), size));
}
~CSharedMemoryFile()
{
menaged_file.reset();
fs::remove(file);
}
managed_mapped_file::segment_manager* segmentManager()
{
return menaged_file->get_segment_manager();
}
const fs::path file;
std::unique_ptr<managed_mapped_file> menaged_file;
};
static CSharedMemoryFile shem;
return shem.segmentManager();
}
template <typename T>
static node_allocator<T>& nodeAllocator()
{
static node_allocator<T> allocator(segmentManager());
return allocator;
}
template <typename T, class... Args>
static std::shared_ptr<T> nodeAllocate(Args&&... args)
{
return std::allocate_shared<T>(nodeAllocator<T>(), std::forward<Args>(args)...);
}
template <typename T, class... Args>
static std::shared_ptr<T> allocateShared(Args&&... args)
{
static auto allocate = g_memfileSize ? nodeAllocate<T, Args...> : std::make_shared<T, Args...>;
try {
return allocate(std::forward<Args>(args)...);
}
catch (const bip::bad_alloc&) {
allocate = std::make_shared<T, Args...>; // in case we fill up the memfile
LogPrint(BCLog::BENCH, "WARNING: The memfile is full; reverting to the RAM allocator for %s.\n", typeid(T).name());
return allocate(std::forward<Args>(args)...);
}
}
template <typename TKey, typename TData>
template <bool IsConst>
CPrefixTrie<TKey, TData>::Iterator<IsConst>::Iterator(const TKey& name, const std::shared_ptr<Node>& node) noexcept : name(name), node(node)
{
}
template <typename TKey, typename TData>
template <bool IsConst>
template <bool C>
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>& CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator=(const CPrefixTrie<TKey, TData>::Iterator<C>& o) noexcept
{
name = o.name;
node = o.node;
stack.clear();
stack.reserve(o.stack.size());
for (auto& i : o.stack)
stack.push_back(Bookmark{i.name, i.it, i.end});
return *this;
}
template <typename TKey, typename TData>
template <bool IsConst>
bool CPrefixTrie<TKey, TData>::Iterator<IsConst>::hasNext() const
{
auto shared = node.lock();
if (!shared) return false;
if (!shared->children.empty()) return true;
for (auto it = stack.rbegin(); it != stack.rend(); ++it) {
auto mark = *it; // copy
if (++mark.it != mark.end)
return true;
}
return false;
}
template <typename TKey, typename TData>
template <bool IsConst>
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>& CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator++()
{
auto shared = node.lock();
assert(shared);
// going in pre-order (NLR). See https://en.wikipedia.org/wiki/Tree_traversal
// if there are any children we have to go there first
if (!shared->children.empty()) {
auto& children = shared->children;
auto it = children.begin();
stack.emplace_back(Bookmark{name, it, children.end()});
auto& postfix = it->first;
name.insert(name.end(), postfix.begin(), postfix.end());
node = it->second;
return *this;
}
// move to next sibling:
while (!stack.empty()) {
auto& back = stack.back();
if (++back.it != back.end) {
name = back.name;
auto& postfix = back.it->first;
name.insert(name.end(), postfix.begin(), postfix.end());
node = back.it->second;
return *this;
}
stack.pop_back();
}
// must be at the end:
node.reset();
name = TKey();
return *this;
}
template <typename TKey, typename TData>
template <bool IsConst>
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst> CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator++(int x)
{
auto ret = *this;
++(*this);
return ret;
}
template <typename TKey, typename TData>
template <bool IsConst>
CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator bool() const
{
return !node.expired();
}
template <typename TKey, typename TData>
template <bool IsConst>
bool CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator==(const Iterator& o) const
{
return node.lock() == o.node.lock();
}
template <typename TKey, typename TData>
template <bool IsConst>
bool CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator!=(const Iterator& o) const
{
return !(*this == o);
}
template <typename TKey, typename TData>
template <bool IsConst>
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>::reference CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator*()
{
return reference{name, data()};
}
template <typename TKey, typename TData>
template <bool IsConst>
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>::const_reference CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator*() const
{
return const_reference{name, data()};
}
template <typename TKey, typename TData>
template <bool IsConst>
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>::pointer CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator->()
{
return &(data());
}
template <typename TKey, typename TData>
template <bool IsConst>
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>::const_pointer CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator->() const
{
return &(data());
}
template <typename TKey, typename TData>
template <bool IsConst>
const TKey& CPrefixTrie<TKey, TData>::Iterator<IsConst>::key() const
{
return name;
}
template <typename TKey, typename TData>
template <bool IsConst>
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>::data_reference CPrefixTrie<TKey, TData>::Iterator<IsConst>::data()
{
auto shared = node.lock();
assert(shared);
return *(shared->data);
}
template <typename TKey, typename TData>
template <bool IsConst>
const TData& CPrefixTrie<TKey, TData>::Iterator<IsConst>::data() const
{
auto shared = node.lock();
assert(shared);
return *(shared->data);
}
template <typename TKey, typename TData>
template <bool IsConst>
std::size_t CPrefixTrie<TKey, TData>::Iterator<IsConst>::depth() const
{
return stack.size();
}
template <typename TKey, typename TData>
template <bool IsConst>
bool CPrefixTrie<TKey, TData>::Iterator<IsConst>::hasChildren() const
{
auto shared = node.lock();
return shared && !shared->children.empty();
}
template <typename TKey, typename TData>
template <bool IsConst>
std::vector<typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>> CPrefixTrie<TKey, TData>::Iterator<IsConst>::children() const
{
auto shared = node.lock();
if (!shared) return {};
std::vector<Iterator<IsConst>> ret;
ret.reserve(shared->children.size());
for (auto& child : shared->children) {
auto key = name;
key.insert(key.end(), child.first.begin(), child.first.end());
ret.emplace_back(key, child.second);
}
return ret;
}
template <typename TKey, typename TData>
template <typename TIterator, typename TNode>
TIterator CPrefixTrie<TKey, TData>::find(const TKey& key, TNode node, TIterator end)
{
TIterator it(key, TNode());
using CBType = callback<TNode>;
CBType cb = [&it](const TKey&, TNode node) {
it.node = node;
};
return find(key, node, cb) ? it : end;
}
template <typename TKey, typename TData>
template <typename TNode>
bool CPrefixTrie<TKey, TData>::find(const TKey& key, TNode node, const callback<TNode>& cb)
{
auto& children = node->children;
if (children.empty()) return false;
auto it = children.lower_bound(key);
if (it != children.end() && it->first == key) {
cb(key, it->second);
return true;
}
if (it != children.begin()) --it;
const auto count = match(key, it->first);
if (count != it->first.size()) return false;
if (count == key.size()) return false;
cb(it->first, it->second);
return find(TKey(key.begin() + count, key.end()), it->second, cb);
}
template <typename TKey, typename TData>
template <typename TIterator, typename TNode>
std::vector<TIterator> CPrefixTrie<TKey, TData>::nodes(const TKey& key, TNode root)
{
std::vector<TIterator> ret;
ret.reserve(1 + key.size());
ret.emplace_back(TKey{}, root);
if (key.empty()) return ret;
TKey name;
using CBType = callback<TNode>;
CBType cb = [&name, &ret](const TKey& key, TNode node) {
name.insert(name.end(), key.begin(), key.end());
ret.emplace_back(name, node);
};
find(key, root, cb);
return ret;
}
template <typename TKey, typename TData>
std::shared_ptr<typename CPrefixTrie<TKey, TData>::Node>& CPrefixTrie<TKey, TData>::insert(const TKey& key, std::shared_ptr<typename CPrefixTrie<TKey, TData>::Node>& node)
{
std::size_t count = 0;
auto& children = node->children;
auto it = children.lower_bound(key);
if (it != children.end()) {
if (it->first == key)
return it->second;
count = match(key, it->first);
}
if (count == 0 && it != children.begin()) {
--it;
count = match(key, it->first);
}
if (count == 0) {
++size;
it = children.emplace(key, allocateShared<Node>()).first;
return it->second;
}
if (count < it->first.size()) {
TKey prefix(key.begin(), key.begin() + count);
TKey postfix(it->first.begin() + count, it->first.end());
auto nodes = std::move(it->second);
children.erase(it);
++size;
it = children.emplace(std::move(prefix), allocateShared<Node>()).first;
it->second->children.emplace(std::move(postfix), std::move(nodes));
if (key.size() == count)
return it->second;
it->second->data = allocateShared<TData>();
}
return insert(TKey(key.begin() + count, key.end()), it->second);
}
template <typename TKey, typename TData>
void CPrefixTrie<TKey, TData>::erase(const TKey& key, std::shared_ptr<Node>& node)
{
std::vector<typename TChildren::value_type> nodes;
nodes.emplace_back(TKey(), node);
using CBType = callback<std::shared_ptr<Node>>;
CBType cb = [&nodes](const TKey& k, std::shared_ptr<Node> n) {
nodes.emplace_back(k, n);
};
if (!find(key, node, cb))
return;
nodes.back().second->data = allocateShared<TData>();
for (; nodes.size() > 1; nodes.pop_back()) {
// if we have only one child and no data ourselves, bring them up to our level
auto& cNode = nodes.back().second;
auto onlyOneChild = cNode->children.size() == 1;
auto noData = cNode->data->empty();
if (onlyOneChild && noData) {
auto child = cNode->children.begin();
auto& prefix = nodes.back().first;
auto newKey = prefix;
auto& postfix = child->first;
newKey.insert(newKey.end(), postfix.begin(), postfix.end());
auto& pNode = nodes[nodes.size() - 2].second;
pNode->children.emplace(std::move(newKey), std::move(child->second));
pNode->children.erase(prefix);
--size;
continue;
}
auto noChildren = cNode->children.empty();
if (noChildren && noData) {
auto& pNode = nodes[nodes.size() - 2].second;
pNode->children.erase(nodes.back().first);
--size;
continue;
}
break;
}
}
template <typename TKey, typename TData>
CPrefixTrie<TKey, TData>::CPrefixTrie() : size(0), root(allocateShared<Node>())
{
root->data = allocateShared<TData>();
}
template <typename TKey, typename TData>
template <typename TDataUni>
typename CPrefixTrie<TKey, TData>::iterator CPrefixTrie<TKey, TData>::insert(const TKey& key, TDataUni&& data)
{
auto& node = key.empty() ? root : insert(key, root);
node->data = allocateShared<TData>(std::forward<TDataUni>(data));
return key.empty() ? begin() : iterator{key, node};
}
template <typename TKey, typename TData>
typename CPrefixTrie<TKey, TData>::iterator CPrefixTrie<TKey, TData>::copy(CPrefixTrie<TKey, TData>::const_iterator it)
{
auto& key = it.key();
auto& node = key.empty() ? root : insert(key, root);
node->data = it.node.lock()->data;
return key.empty() ? begin() : iterator{key, node};
}
template <typename TKey, typename TData>
template <typename TDataUni>
typename CPrefixTrie<TKey, TData>::iterator CPrefixTrie<TKey, TData>::insert(CPrefixTrie<TKey, TData>::iterator& it, const TKey& key, TDataUni&& data)
{
auto shared = it.node.lock();
assert(shared);
auto copy = it;
if (!key.empty()) {
auto name = it.key();
name.insert(name.end(), key.begin(), key.end());
auto& node = insert(key, shared);
copy = iterator{std::move(name), node};
}
copy.node.lock()->data = allocateShared<TData>(std::forward<TDataUni>(data));
return copy;
}
template <typename TKey, typename TData>
typename CPrefixTrie<TKey, TData>::iterator CPrefixTrie<TKey, TData>::find(const TKey& key)
{
if (empty()) return end();
if (key.empty()) return {key, root};
return find(key, root, end());
}
template <typename TKey, typename TData>
typename CPrefixTrie<TKey, TData>::const_iterator CPrefixTrie<TKey, TData>::find(const TKey& key) const
{
if (empty()) return end();
if (key.empty()) return {key, root};
return find(key, root, end());
}
template <typename TKey, typename TData>
typename CPrefixTrie<TKey, TData>::iterator CPrefixTrie<TKey, TData>::find(CPrefixTrie<TKey, TData>::iterator& it, const TKey& key)
{
if (key.empty()) return it;
auto shared = it.node.lock();
assert(shared);
return find(key, shared, end());
}
template <typename TKey, typename TData>
typename CPrefixTrie<TKey, TData>::const_iterator CPrefixTrie<TKey, TData>::find(CPrefixTrie<TKey, TData>::const_iterator& it, const TKey& key) const
{
if (key.empty()) return it;
auto shared = it.node.lock();
assert(shared);
return find(key, shared, end());
}
template <typename TKey, typename TData>
bool CPrefixTrie<TKey, TData>::contains(const TKey& key) const
{
return find(key) != end();
}
template <typename TKey, typename TData>
TData& CPrefixTrie<TKey, TData>::at(const TKey& key)
{
return find(key).data();
}
template <typename TKey, typename TData>
std::vector<typename CPrefixTrie<TKey, TData>::iterator> CPrefixTrie<TKey, TData>::nodes(const TKey& key)
{
if (empty()) return {};
return nodes<iterator>(key, root);
}
template <typename TKey, typename TData>
std::vector<typename CPrefixTrie<TKey, TData>::const_iterator> CPrefixTrie<TKey, TData>::nodes(const TKey& key) const
{
if (empty()) return {};
return nodes<const_iterator>(key, root);
}
template <typename TKey, typename TData>
bool CPrefixTrie<TKey, TData>::erase(const TKey& key)
{
auto size_was = height();
if (key.empty()) {
root->data = allocateShared<TData>();
} else {
erase(key, root);
}
return size_was != height();
}
template <typename TKey, typename TData>
void CPrefixTrie<TKey, TData>::clear()
{
size = 0;
root->data = allocateShared<TData>();
root->children.clear();
}
template <typename TKey, typename TData>
bool CPrefixTrie<TKey, TData>::empty() const
{
return height() == 0;
}
template <typename TKey, typename TData>
std::size_t CPrefixTrie<TKey, TData>::height() const
{
return size + (root->data->empty() ? 0 : 1);
}
template <typename TKey, typename TData>
typename CPrefixTrie<TKey, TData>::iterator CPrefixTrie<TKey, TData>::begin()
{
return find(TKey());
}
template <typename TKey, typename TData>
typename CPrefixTrie<TKey, TData>::iterator CPrefixTrie<TKey, TData>::end()
{
return {};
}
template <typename TKey, typename TData>
typename CPrefixTrie<TKey, TData>::const_iterator CPrefixTrie<TKey, TData>::cbegin()
{
return find(TKey());
}
template <typename TKey, typename TData>
typename CPrefixTrie<TKey, TData>::const_iterator CPrefixTrie<TKey, TData>::cend()
{
return {};
}
template <typename TKey, typename TData>
typename CPrefixTrie<TKey, TData>::const_iterator CPrefixTrie<TKey, TData>::begin() const
{
return find(TKey());
}
template <typename TKey, typename TData>
typename CPrefixTrie<TKey, TData>::const_iterator CPrefixTrie<TKey, TData>::end() const
{
return {};
}
using Key = std::string;
using Data = CClaimTrieData;
using Trie = CPrefixTrie<Key, Data>;
using iterator = Trie::iterator;
using const_iterator = Trie::const_iterator;
template class CPrefixTrie<Key, Data>;
template class Trie::Iterator<true>;
template class Trie::Iterator<false>;
template const_iterator& const_iterator::operator=<>(const iterator&) noexcept;
template iterator Trie::insert<>(const Key&, Data&);
template iterator Trie::insert<>(const Key&, Data&&);
template iterator Trie::insert<>(const Key&, const Data&);
template iterator Trie::insert<>(iterator&, const Key&, Data&);
template iterator Trie::insert<>(iterator&, const Key&, Data&&);
template iterator Trie::insert<>(iterator&, const Key&, const Data&);

View file

@ -1,225 +0,0 @@
#ifndef BITCOIN_PREFIXTRIE_H
#define BITCOIN_PREFIXTRIE_H
#include <algorithm>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <type_traits>
#include <vector>
#include <boost/container/flat_map.hpp>
namespace bc = boost::container;
template <typename TKey, typename TData>
class CPrefixTrie
{
class Node
{
template <bool>
friend class Iterator;
friend class CPrefixTrie<TKey, TData>;
bc::flat_map<TKey, std::shared_ptr<Node>> children;
public:
Node() = default;
Node(const Node&) = delete;
Node(Node&& o) noexcept = default;
Node& operator=(Node&&) noexcept = default;
Node& operator=(const Node&) = delete;
std::shared_ptr<TData> data;
};
using TChildren = decltype(Node::children);
template <bool IsConst>
class Iterator
{
template <bool>
friend class Iterator;
friend class CPrefixTrie<TKey, TData>;
using TKeyRef = std::reference_wrapper<const TKey>;
using TDataRef = std::reference_wrapper<typename std::conditional<IsConst, const TData, TData>::type>;
using TPair = std::pair<TKeyRef, TDataRef>;
using ConstTPair = std::pair<TKeyRef, const TData>;
TKey name;
std::weak_ptr<Node> node;
struct Bookmark {
TKey name;
typename TChildren::iterator it;
typename TChildren::iterator end;
};
std::vector<Bookmark> stack;
public:
// Iterator traits
using value_type = TPair;
using const_pointer = const TData* const;
using const_reference = ConstTPair;
using data_reference = typename std::conditional<IsConst, const TData&, TData&>::type;
using pointer = typename std::conditional<IsConst, const TData* const, TData* const>::type;
using reference = typename std::conditional<IsConst, ConstTPair, TPair>::type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
Iterator() = default;
Iterator(const Iterator&) = default;
Iterator(Iterator&& o) noexcept = default;
Iterator(const TKey& name, const std::shared_ptr<Node>& node) noexcept;
template <bool C>
inline Iterator(const Iterator<C>& o) noexcept
{
*this = o;
}
Iterator& operator=(const Iterator&) = default;
Iterator& operator=(Iterator&& o) = default;
template <bool C>
Iterator& operator=(const Iterator<C>& o) noexcept;
bool hasNext() const;
Iterator& operator++();
Iterator operator++(int);
operator bool() const;
bool operator==(const Iterator& o) const;
bool operator!=(const Iterator& o) const;
reference operator*();
const_reference operator*() const;
pointer operator->();
const_pointer operator->() const;
const TKey& key() const;
data_reference data();
const TData& data() const;
std::size_t depth() const;
bool hasChildren() const;
std::vector<Iterator> children() const;
};
size_t size;
std::shared_ptr<Node> root;
template <typename TNode>
using callback = std::function<void(const TKey&, TNode)>;
template <typename TIterator, typename TNode>
static TIterator find(const TKey& key, TNode node, TIterator end);
template <typename TNode>
static bool find(const TKey& key, TNode node, const callback<TNode>& cb);
template <typename TIterator, typename TNode>
static std::vector<TIterator> nodes(const TKey& key, TNode root);
std::shared_ptr<Node>& insert(const TKey& key, std::shared_ptr<Node>& node);
void erase(const TKey& key, std::shared_ptr<Node>& node);
public:
using iterator = Iterator<false>;
using const_iterator = Iterator<true>;
CPrefixTrie();
template <typename TDataUni>
iterator insert(const TKey& key, TDataUni&& data);
template <typename TDataUni>
iterator insert(iterator& it, const TKey& key, TDataUni&& data);
iterator copy(const_iterator it);
iterator find(const TKey& key);
const_iterator find(const TKey& key) const;
iterator find(iterator& it, const TKey& key);
const_iterator find(const_iterator& it, const TKey& key) const;
bool contains(const TKey& key) const;
TData& at(const TKey& key);
std::vector<iterator> nodes(const TKey& key);
std::vector<const_iterator> nodes(const TKey& key) const;
bool erase(const TKey& key);
void clear();
bool empty() const;
size_t height() const;
iterator begin();
iterator end();
const_iterator cbegin();
const_iterator cend();
const_iterator begin() const;
const_iterator end() const;
};
template <typename T, typename O>
inline bool operator==(const std::reference_wrapper<T>& ref, const O& obj)
{
return ref.get() == obj;
}
template <typename T, typename O>
inline bool operator!=(const std::reference_wrapper<T>& ref, const O& obj)
{
return !(ref == obj);
}
template <typename T>
inline bool operator==(const std::reference_wrapper<std::shared_ptr<T>>& ref, const T& obj)
{
auto ptr = ref.get();
return ptr && *ptr == obj;
}
template <typename T>
inline bool operator!=(const std::reference_wrapper<std::shared_ptr<T>>& ref, const T& obj)
{
return !(ref == obj);
}
template <typename T>
inline bool operator==(const std::reference_wrapper<std::unique_ptr<T>>& ref, const T& obj)
{
auto ptr = ref.get();
return ptr && *ptr == obj;
}
template <typename T>
inline bool operator!=(const std::reference_wrapper<std::unique_ptr<T>>& ref, const T& obj)
{
return !(ref == obj);
}
template <typename TKey>
static std::size_t match(const TKey& a, const TKey& b)
{
std::size_t count = 0;
auto ait = a.cbegin(), aend = a.cend();
auto bit = b.cbegin(), bend = b.cend();
while (ait != aend && bit != bend) {
if (*ait != *bit) break;
++count;
++ait;
++bit;
}
return count;
}
#endif // BITCOIN_PREFIXTRIE_H

View file

@ -48,8 +48,7 @@
#define T_PENDINGAMOUNT "pendingAmount"
enum {
GETCLAIMSINTRIE = 0,
GETNAMESINTRIE,
GETNAMESINTRIE = 0,
GETVALUEFORNAME,
GETCLAIMSFORNAME,
GETCLAIMBYID,
@ -127,26 +126,6 @@ S3(" ", T_LASTTAKEOVERHEIGHT, " (numeric) the last height at which owners
static const char* const rpc_help[] = {
// GETCLAIMSINTRIE
S1("getclaimsintrie ( \"" T_BLOCKHASH R"(" )
Return all claims in the name trie. Deprecated
Arguments:)")
S3("1. ", T_BLOCKHASH, BLOCKHASH_TEXT)
S1("Result: [")
S3(" ", T_NORMALIZEDNAME, " (string) the name of the claim(s) (after normalization)")
S3(" ", T_CLAIMS, ": [ (array of object) the claims for this name")
S3(" ", T_NAME, " (string) the original name of this claim (before normalization)")
S3(" ", T_VALUE, " (string) the value of this claim")
S3(" ", T_ADDRESS, " (string) the destination address of this claim")
S3(" ", T_CLAIMID, " (string) the claimId of the claim")
S3(" ", T_TXID, " (string) the txid of the claim")
S3(" ", T_N, " (numeric) the index of the claim in the transaction's list of outputs")
S3(" ", T_HEIGHT, " (numeric) the height of the block in which this transaction is located")
S3(" ", T_VALIDATHEIGHT, " (numeric) the height at which the claim became/becomes valid")
S3(" ", T_AMOUNT, " (numeric) the amount of the claim")
S1(" ]")
"]",
// GETNAMESINTRIE
S1("getnamesintrie ( \"" T_BLOCKHASH R"(" )
Return all claim names in the trie.

View file

@ -15,7 +15,6 @@
#include <validation.h>
#include <boost/locale.hpp>
#include <boost/locale/conversion.hpp>
#include <boost/thread.hpp>
#include <cmath>
@ -108,13 +107,14 @@ std::string escapeNonUtf8(const std::string& name)
}
}
static bool extractValue(const CScript& scriptPubKey, std::string& sValue)
static bool extractValue(const CScript& scriptPubKey, std::string& name, std::string& sValue)
{
int op;
std::vector<std::vector<unsigned char>> vvchParams;
if (!DecodeClaimScript(scriptPubKey, op, vvchParams))
return false;
name = std::string(vvchParams[0].begin(), vvchParams[0].end());
if (op == OP_CLAIM_NAME)
sValue = HexStr(vvchParams[1].begin(), vvchParams[1].end());
else if (vvchParams.size() > 2) // both UPDATE and SUPPORT
@ -124,52 +124,6 @@ static bool extractValue(const CScript& scriptPubKey, std::string& sValue)
return true;
}
bool getClaimById(const uint160& claimId, std::string& name, CClaimValue* claim = nullptr)
{
if (claimId.IsNull())
return false;
CClaimIndexElement element;
if (!pclaimTrie->db->Read(std::make_pair(CLAIM_BY_ID, claimId), element))
return false;
if (element.claim.claimId == claimId) {
name = element.name;
if (claim)
*claim = element.claim;
return true;
}
return false;
}
// name can be setted explicitly
bool getClaimById(const std::string& partialId, std::string& name, CClaimValue* claim = nullptr)
{
if (partialId.empty())
return false;
std::unique_ptr<CDBIterator> pcursor(pclaimTrie->db->NewIterator());
for (pcursor->SeekToFirst(); pcursor->Valid(); pcursor->Next()) {
std::pair<uint8_t, uint160> key;
if (!pcursor->GetKey(key) || key.first != CLAIM_BY_ID)
continue;
if (key.second.GetHex().find(partialId) != 0)
continue;
CClaimIndexElement element;
if (pcursor->GetValue(element)) {
if (!name.empty() && name != element.name)
continue;
name = element.name;
if (claim)
*claim = element.claim;
return true;
}
}
return false;
}
std::vector<CClaimNsupports> seqSort(const std::vector<CClaimNsupports>& source)
{
auto claimsNsupports = source;
@ -196,15 +150,13 @@ UniValue claimToJSON(const CCoinsViewCache& coinsCache, const CClaimValue& claim
{
UniValue result(UniValue::VOBJ);
std::string targetName;
if (getClaimById(claim.claimId, targetName))
result.pushKV(T_NAME, escapeNonUtf8(targetName));
auto& coin = coinsCache.AccessCoin(claim.outPoint);
if (!coin.IsSpent()) {
std::string value;
if (extractValue(coin.out.scriptPubKey, value))
std::string name, value;
if (extractValue(coin.out.scriptPubKey, name, value)) {
result.pushKV(T_NAME, escapeNonUtf8(name));
result.pushKV(T_VALUE, value);
}
CTxDestination address;
if (ExtractDestination(coin.out.scriptPubKey, address))
@ -227,9 +179,11 @@ UniValue supportToJSON(const CCoinsViewCache& coinsCache, const CSupportValue& s
auto& coin = coinsCache.AccessCoin(support.outPoint);
if (!coin.IsSpent()) {
std::string value;
if (extractValue(coin.out.scriptPubKey, value))
std::string name, value;
if (extractValue(coin.out.scriptPubKey, name, value)) {
ret.pushKV(T_NAME, name);
ret.pushKV(T_VALUE, value);
}
CTxDestination address;
if (ExtractDestination(coin.out.scriptPubKey, address))
@ -286,48 +240,6 @@ void validateRequest(const JSONRPCRequest& request, int findex, uint8_t required
throw std::runtime_error(rpc_help[findex]);
}
static UniValue getclaimsintrie(const JSONRPCRequest& request)
{
validateRequest(request, GETCLAIMSINTRIE, 0, 1);
if (!IsDeprecatedRPCEnabled("getclaimsintrie")) {
const auto msg = "getclaimsintrie is deprecated and will be removed in v0.18. To use this command, start with -deprecatedrpc=getclaimsintrie";
if (request.fHelp)
throw std::runtime_error(msg);
throw JSONRPCError(RPC_METHOD_DEPRECATED, msg);
}
LOCK(cs_main);
CCoinsViewCache coinsCache(pcoinsTip.get());
CClaimTrieCache trieCache(pclaimTrie);
if (!request.params.empty()) {
CBlockIndex* blockIndex = BlockHashIndex(ParseHashV(request.params[0], T_BLOCKHASH " (optional parameter 1)"));
RollBackTo(blockIndex, coinsCache, trieCache);
}
UniValue ret(UniValue::VARR);
trieCache.iterate([&ret, &trieCache, &coinsCache] (const std::string& name, const CClaimTrieData& data) {
if (ShutdownRequested())
throw JSONRPCError(RPC_INTERNAL_ERROR, "Shutdown requested");
boost::this_thread::interruption_point();
if (data.empty())
return;
UniValue claims(UniValue::VARR);
for (auto& claim : data.claims)
claims.push_back(claimToJSON(coinsCache, claim));
UniValue nodeObj(UniValue::VOBJ);
nodeObj.pushKV(T_NORMALIZEDNAME, escapeNonUtf8(name));
nodeObj.pushKV(T_CLAIMS, claims);
ret.push_back(nodeObj);
});
return ret;
}
static UniValue getclaimtrie(const JSONRPCRequest& request)
{
throw JSONRPCError(RPC_METHOD_DEPRECATED, "getclaimtrie was removed in v0.17.\n"
@ -348,12 +260,11 @@ static UniValue getnamesintrie(const JSONRPCRequest& request)
}
UniValue ret(UniValue::VARR);
trieCache.iterate([&ret](const std::string &name, const CClaimTrieData &data) {
if (!data.empty())
ret.push_back(escapeNonUtf8(name));
trieCache.getNamesInTrie([&ret](const std::string& name) {
ret.push_back(escapeNonUtf8(name));
if (ShutdownRequested())
throw JSONRPCError(RPC_INTERNAL_ERROR, "Shutdown requested");
boost::this_thread::interruption_point();
});
@ -393,10 +304,10 @@ static UniValue getvalueforname(const JSONRPCRequest& request)
std::size_t seq = 0, bid = 0;
if (csToName.claimsNsupports.size() > 1) {
auto& claimId = claimNsupports.claim.claimId;
auto& claimIdIn = claimNsupports.claim.claimId;
auto seqOrder = seqSort(csToName.claimsNsupports);
seq = indexOf(seqOrder, claimId);
bid = indexOf(csToName.claimsNsupports, claimId);
seq = indexOf(seqOrder, claimIdIn);
bid = indexOf(csToName.claimsNsupports, claimIdIn);
}
ret.pushKV(T_NORMALIZEDNAME, escapeNonUtf8(csToName.name));
@ -550,19 +461,19 @@ UniValue getclaimbyid(const JSONRPCRequest& request)
if (claimId.length() < 3)
throw JSONRPCError(RPC_INVALID_PARAMETER, T_CLAIMID " (parameter 1) should be at least 3 chars");
std::string name;
CClaimValue claim;
std::string foundName;
CClaimValue foundClaim;
UniValue ret(UniValue::VOBJ);
bool found = claimId.length() == claimIdHexLength && getClaimById(uint160S(claimId), name, &claim);
if (found || getClaimById(claimId, name, &claim)) {
auto csToName = trieCache.getClaimsForName(name);
auto& claimNsupports = csToName.find(claim.claimId);
auto parsed = ParseHex(claimId);
if (trieCache.findNameForClaim(parsed, foundClaim, foundName)) {
auto csToName = trieCache.getClaimsForName(foundName);
auto& claimNsupports = csToName.find(foundClaim.claimId);
if (!claimNsupports.IsNull()) {
std::size_t seq = 0, bid = 0;
if (csToName.claimsNsupports.size() > 1) {
auto seqOrder = seqSort(csToName.claimsNsupports);
seq = indexOf(seqOrder, claim.claimId);
bid = indexOf(csToName.claimsNsupports, claim.claimId);
seq = indexOf(seqOrder, foundClaim.claimId);
bid = indexOf(csToName.claimsNsupports, foundClaim.claimId);
}
ret.pushKV(T_NORMALIZEDNAME, escapeNonUtf8(csToName.name));
ret.pushKVs(claimAndSupportsToJSON(coinsCache, claimNsupports));
@ -579,7 +490,8 @@ UniValue gettotalclaimednames(const JSONRPCRequest& request)
validateRequest(request, GETTOTALCLAIMEDNAMES, 0, 0);
LOCK(cs_main);
auto num_names = pclaimTrie->getTotalNamesInTrie();
CClaimTrieCache trieCache(pclaimTrie); // TODO: add rollback support here
auto num_names = trieCache.getTotalNamesInTrie();
return int(num_names);
}
@ -588,7 +500,8 @@ UniValue gettotalclaims(const JSONRPCRequest& request)
validateRequest(request, GETTOTALCLAIMS, 0, 0);
LOCK(cs_main);
auto num_claims = pclaimTrie->getTotalClaimsInTrie();
CClaimTrieCache trieCache(pclaimTrie); // TODO: add rollback support here
auto num_claims = trieCache.getTotalClaimsInTrie();
return int(num_claims);
}
@ -600,7 +513,8 @@ UniValue gettotalvalueofclaims(const JSONRPCRequest& request)
bool controlling_only = false;
if (request.params.size() == 1)
controlling_only = request.params[0].get_bool();
auto total_amount = pclaimTrie->getTotalValueOfClaimsInTrie(controlling_only);
CClaimTrieCache trieCache(pclaimTrie); // TODO: add rollback support here
auto total_amount = trieCache.getTotalValueOfClaimsInTrie(controlling_only);
return ValueFromAmount(total_amount);
}
@ -739,30 +653,28 @@ UniValue getnameproof(const JSONRPCRequest& request)
CCoinsViewCache coinsCache(pcoinsTip.get());
CClaimTrieCache trieCache(pclaimTrie);
int validHeight = pclaimTrie->nNextHeight - 1;
if (request.params.size() > 1) {
CBlockIndex* pblockIndex = BlockHashIndex(ParseHashV(request.params[1], T_BLOCKHASH " (optional parameter 2)"));
RollBackTo(pblockIndex, coinsCache, trieCache);
validHeight = pblockIndex->nHeight;
}
std::string claimId;
if (request.params.size() > 2)
std::string name = request.params[0].get_str();
CClaimValue claim;
if (request.params.size() > 2) {
std::string claimId;
ParseClaimtrieId(request.params[2], claimId, T_CLAIMID " (optional parameter 3)");
std::function<bool(const CClaimValue&)> comp;
if (claimId.length() == claimIdHexLength) {
auto claimIdx = uint160S(claimId);
comp = [claimIdx](const CClaimValue& claim) {
return claim.claimId == claimIdx;
};
} else if (!claimId.empty()) {
comp = [&claimId](const CClaimValue& claim) {
return claim.claimId.GetHex().find(claimId) == 0;
};
std::string foundClaimName;
if (!trieCache.findNameForClaim(ParseHex(claimId), claim, foundClaimName)
|| foundClaimName != trieCache.adjustNameForValidHeight(name, validHeight))
throw JSONRPCError(RPC_INVALID_PARAMETER, "Failed to locate a claim with ID " + claimId);
}
else trieCache.getInfoForName(name, claim);
CClaimTrieProof proof;
std::string name = request.params[0].get_str();
if (!trieCache.getProofForName(name, proof, comp))
if (!trieCache.getProofForName(name, claim.claimId, proof))
throw JSONRPCError(RPC_INTERNAL_ERROR, "Failed to generate proof");
return proofToJSON(proof);
@ -790,19 +702,13 @@ UniValue getclaimproofbybid(const JSONRPCRequest& request)
std::string name = request.params[0].get_str();
std::function<bool(const CClaimValue&)> comp;
if (bid) {
auto csToName = trieCache.getClaimsForName(name);
if (uint32_t(bid) >= csToName.claimsNsupports.size())
return {UniValue::VARR};
auto claimId = csToName.claimsNsupports[bid].claim.claimId;
comp = [claimId](const CClaimValue& claim) {
return claim.claimId == claimId;
};
}
auto csToName = trieCache.getClaimsForName(name);
if (std::size_t(bid) >= csToName.claimsNsupports.size())
return {UniValue::VARR};
auto match = csToName.claimsNsupports[bid].claim.claimId;
CClaimTrieProof proof;
if (!trieCache.getProofForName(name, proof, comp))
if (!trieCache.getProofForName(name, match, proof))
throw JSONRPCError(RPC_INTERNAL_ERROR, "Failed to generate proof");
return proofToJSON(proof);
@ -830,18 +736,14 @@ UniValue getclaimproofbyseq(const JSONRPCRequest& request)
std::string name = request.params[0].get_str();
auto csToName = trieCache.getClaimsForName(name);
if (uint32_t(seq) >= csToName.claimsNsupports.size())
if (std::size_t(seq) >= csToName.claimsNsupports.size())
return {UniValue::VARR};
std::function<bool(const CClaimValue&)> comp;
auto claimId = (csToName.claimsNsupports.size() == 1 ?
csToName.claimsNsupports[0] : seqSort(csToName.claimsNsupports)[seq]).claim.claimId;
comp = [&claimId](const CClaimValue& claim) {
return claim.claimId == claimId;
};
auto sorted = seqSort(csToName.claimsNsupports);
auto match = sorted[seq].claim.claimId;
CClaimTrieProof proof;
if (!trieCache.getProofForName(name, proof, comp))
if (!trieCache.getProofForName(name, match, proof))
throw JSONRPCError(RPC_INTERNAL_ERROR, "Failed to generate proof");
return proofToJSON(proof);
@ -905,7 +807,6 @@ UniValue checknormalization(const JSONRPCRequest& request)
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
{ "Claimtrie", "getclaimsintrie", &getclaimsintrie, { T_BLOCKHASH } },
{ "Claimtrie", "getnamesintrie", &getnamesintrie, { T_BLOCKHASH } },
{ "hidden", "getclaimtrie", &getclaimtrie, { } },
{ "Claimtrie", "getvalueforname", &getvalueforname, { T_NAME,T_BLOCKHASH,T_CLAIMID } },

View file

@ -0,0 +1,682 @@
#pragma once
#include <algorithm>
#include <cctype>
#include <string>
#include <functional>
#include <tuple>
#include <memory>
#define MODERN_SQLITE_VERSION 3002008
#include <sqlite/sqlite3.h>
#include "sqlite_modern_cpp/type_wrapper.h"
#include "sqlite_modern_cpp/errors.h"
#include "sqlite_modern_cpp/utility/function_traits.h"
#include "sqlite_modern_cpp/utility/uncaught_exceptions.h"
#include "sqlite_modern_cpp/utility/utf16_utf8.h"
namespace sqlite {
class database;
class database_binder;
template<std::size_t> class binder;
typedef std::shared_ptr<sqlite3> connection_type;
template<class T, bool Name = false>
struct index_binding_helper {
index_binding_helper(const index_binding_helper &) = delete;
#if __cplusplus < 201703
index_binding_helper(index_binding_helper &&) = default;
#endif
typename std::conditional<Name, const char *, int>::type index;
T value;
};
template<class T>
auto named_parameter(const char *name, T &&arg) {
return index_binding_helper<decltype(arg), true>{name, std::forward<decltype(arg)>(arg)};
}
template<class T>
auto indexed_parameter(int index, T &&arg) {
return index_binding_helper<decltype(arg)>{index, std::forward<decltype(arg)>(arg)};
}
class row_iterator;
class database_binder {
public:
// database_binder is not copyable
database_binder() = delete;
database_binder(const database_binder& other) = delete;
database_binder& operator=(const database_binder&) = delete;
database_binder(database_binder&& other) :
_db(std::move(other._db)),
_stmt(std::move(other._stmt)),
_inx(other._inx), execution_started(other.execution_started) { }
void execute();
std::string sql() {
#if SQLITE_VERSION_NUMBER >= 3014000
auto sqlite_deleter = [](void *ptr) {sqlite3_free(ptr);};
std::unique_ptr<char, decltype(sqlite_deleter)> str(sqlite3_expanded_sql(_stmt.get()), sqlite_deleter);
return str ? str.get() : original_sql();
#else
return original_sql();
#endif
}
std::string original_sql() {
return sqlite3_sql(_stmt.get());
}
void used(bool state) {
if(!state) {
// We may have to reset first if we haven't done so already:
_next_index();
--_inx;
}
execution_started = state;
}
bool used() const { return execution_started; }
row_iterator begin();
row_iterator end();
private:
std::shared_ptr<sqlite3> _db;
std::unique_ptr<sqlite3_stmt, decltype(&sqlite3_finalize)> _stmt;
utility::UncaughtExceptionDetector _has_uncaught_exception;
int _inx;
bool execution_started = false;
int _next_index() {
if(execution_started && !_inx) {
sqlite3_reset(_stmt.get());
sqlite3_clear_bindings(_stmt.get());
}
return ++_inx;
}
sqlite3_stmt* _prepare(u16str_ref sql) {
return _prepare(utility::utf16_to_utf8(sql));
}
sqlite3_stmt* _prepare(str_ref sql) {
int hresult;
sqlite3_stmt* tmp = nullptr;
const char *remaining;
hresult = sqlite3_prepare_v2(_db.get(), sql.data(), sql.length(), &tmp, &remaining);
if(hresult != SQLITE_OK) errors::throw_sqlite_error(hresult, sql);
if(!std::all_of(remaining, sql.data() + sql.size(), [](char ch) {return std::isspace(ch);}))
throw errors::more_statements("Multiple semicolon separated statements are unsupported", sql);
return tmp;
}
template<typename T> friend database_binder& operator<<(database_binder& db, T&&);
template<typename T> friend database_binder& operator<<(database_binder& db, index_binding_helper<T>);
template<typename T> friend database_binder& operator<<(database_binder& db, index_binding_helper<T, true>);
friend void operator++(database_binder& db, int);
public:
database_binder(std::shared_ptr<sqlite3> db, u16str_ref sql):
_db(db),
_stmt(_prepare(sql), sqlite3_finalize),
_inx(0) {
}
database_binder(std::shared_ptr<sqlite3> db, str_ref sql):
_db(db),
_stmt(_prepare(sql), sqlite3_finalize),
_inx(0) {
}
~database_binder() noexcept(false) {
/* Will be executed if no >>op is found, but not if an exception
is in mid flight */
if(!used() && !_has_uncaught_exception && _stmt) {
execute();
}
}
friend class row_iterator;
};
class row_iterator {
public:
class value_type {
public:
value_type(database_binder *_binder): _binder(_binder) {};
template<class T>
typename std::enable_if<is_sqlite_value<T>::value, value_type &>::type operator >>(T &result) {
result = get_col_from_db(_binder->_stmt.get(), next_index++, result_type<T>());
return *this;
}
template<class ...Types>
value_type &operator >>(std::tuple<Types...>& values) {
values = handle_tuple<std::tuple<typename std::decay<Types>::type...>>(std::index_sequence_for<Types...>());
next_index += sizeof...(Types);
return *this;
}
template<class ...Types>
value_type &operator >>(std::tuple<Types...>&& values) {
return *this >> values;
}
template<class ...Types>
operator std::tuple<Types...>() {
std::tuple<Types...> value;
*this >> value;
return value;
}
explicit operator bool() {
return sqlite3_column_count(_binder->_stmt.get()) >= next_index;
}
private:
template<class Tuple, std::size_t ...Index>
Tuple handle_tuple(std::index_sequence<Index...>) {
return Tuple(
get_col_from_db(
_binder->_stmt.get(),
next_index + Index,
result_type<typename std::tuple_element<Index, Tuple>::type>())...);
}
database_binder *_binder;
int next_index = 0;
};
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
using iterator_category = std::input_iterator_tag;
row_iterator() = default;
explicit row_iterator(database_binder &binder): _binder(&binder) {
_binder->_next_index();
_binder->_inx = 0;
_binder->used(true);
++*this;
}
reference operator*() const { return value;}
pointer operator->() const { return std::addressof(**this); }
row_iterator &operator++() {
switch(int result = sqlite3_step(_binder->_stmt.get())) {
case SQLITE_ROW:
value = {_binder};
break;
case SQLITE_DONE:
_binder = nullptr;
break;
default:
exceptions::throw_sqlite_error(result, _binder->sql());
}
return *this;
}
friend inline bool operator ==(const row_iterator &a, const row_iterator &b) {
return a._binder == b._binder;
}
friend inline bool operator !=(const row_iterator &a, const row_iterator &b) {
return !(a==b);
}
private:
database_binder *_binder = nullptr;
mutable value_type value{_binder}; // mutable, because `changing` the value is just reading it
};
inline row_iterator database_binder::begin() {
return row_iterator(*this);
}
inline row_iterator database_binder::end() {
return row_iterator();
}
namespace detail {
template<class Callback>
void _extract_single_value(database_binder &binder, Callback call_back) {
auto iter = binder.begin();
if(iter == binder.end())
throw errors::no_rows("no rows to extract: exactly 1 row expected", binder.sql(), SQLITE_DONE);
call_back(*iter);
if(++iter != binder.end())
throw errors::more_rows("not all rows extracted", binder.sql(), SQLITE_ROW);
}
}
inline void database_binder::execute() {
for(auto &&row : *this)
(void)row;
}
namespace detail {
template<class T> using void_t = void;
template<class T, class = void>
struct sqlite_direct_result : std::false_type {};
template<class T>
struct sqlite_direct_result<
T,
void_t<decltype(std::declval<row_iterator::value_type&>() >> std::declval<T&&>())>
> : std::true_type {};
}
template <typename Result>
inline typename std::enable_if<detail::sqlite_direct_result<Result>::value>::type operator>>(database_binder &binder, Result&& value) {
detail::_extract_single_value(binder, [&value] (row_iterator::value_type &row) {
row >> std::forward<Result>(value);
});
}
template <typename Function>
inline typename std::enable_if<!detail::sqlite_direct_result<Function>::value>::type operator>>(database_binder &db_binder, Function&& func) {
using traits = utility::function_traits<Function>;
for(auto &&row : db_binder) {
binder<traits::arity>::run(row, func);
}
}
template <typename Result>
inline decltype(auto) operator>>(database_binder &&binder, Result&& value) {
return binder >> std::forward<Result>(value);
}
namespace sql_function_binder {
template<
typename ContextType,
std::size_t Count,
typename Functions
>
inline void step(
sqlite3_context* db,
int count,
sqlite3_value** vals
);
template<
std::size_t Count,
typename Functions,
typename... Values
>
inline typename std::enable_if<(sizeof...(Values) && sizeof...(Values) < Count), void>::type step(
sqlite3_context* db,
int count,
sqlite3_value** vals,
Values&&... values
);
template<
std::size_t Count,
typename Functions,
typename... Values
>
inline typename std::enable_if<(sizeof...(Values) == Count), void>::type step(
sqlite3_context* db,
int,
sqlite3_value**,
Values&&... values
);
template<
typename ContextType,
typename Functions
>
inline void final(sqlite3_context* db);
template<
std::size_t Count,
typename Function,
typename... Values
>
inline typename std::enable_if<(sizeof...(Values) < Count), void>::type scalar(
sqlite3_context* db,
int count,
sqlite3_value** vals,
Values&&... values
);
template<
std::size_t Count,
typename Function,
typename... Values
>
inline typename std::enable_if<(sizeof...(Values) == Count), void>::type scalar(
sqlite3_context* db,
int,
sqlite3_value**,
Values&&... values
);
}
enum class OpenFlags {
READONLY = SQLITE_OPEN_READONLY,
READWRITE = SQLITE_OPEN_READWRITE,
CREATE = SQLITE_OPEN_CREATE,
NOMUTEX = SQLITE_OPEN_NOMUTEX,
FULLMUTEX = SQLITE_OPEN_FULLMUTEX,
SHAREDCACHE = SQLITE_OPEN_SHAREDCACHE,
PRIVATECACH = SQLITE_OPEN_PRIVATECACHE,
URI = SQLITE_OPEN_URI
};
inline OpenFlags operator|(const OpenFlags& a, const OpenFlags& b) {
return static_cast<OpenFlags>(static_cast<int>(a) | static_cast<int>(b));
}
enum class Encoding {
ANY = SQLITE_ANY,
UTF8 = SQLITE_UTF8,
UTF16 = SQLITE_UTF16
};
struct sqlite_config {
OpenFlags flags = OpenFlags::READWRITE | OpenFlags::CREATE;
const char *zVfs = nullptr;
Encoding encoding = Encoding::ANY;
};
class database {
protected:
std::shared_ptr<sqlite3> _db;
public:
database(const std::string &db_name, const sqlite_config &config = {}): _db(nullptr) {
sqlite3* tmp = nullptr;
auto ret = sqlite3_open_v2(db_name.data(), &tmp, static_cast<int>(config.flags), config.zVfs);
_db = std::shared_ptr<sqlite3>(tmp, [=](sqlite3* ptr) { sqlite3_close_v2(ptr); }); // this will close the connection eventually when no longer needed.
if(ret != SQLITE_OK) errors::throw_sqlite_error(_db ? sqlite3_extended_errcode(_db.get()) : ret);
sqlite3_extended_result_codes(_db.get(), true);
if(config.encoding == Encoding::UTF16)
*this << R"(PRAGMA encoding = "UTF-16";)";
}
database(const std::u16string &db_name, const sqlite_config &config = {}): database(utility::utf16_to_utf8(db_name), config) {
if (config.encoding == Encoding::ANY)
*this << R"(PRAGMA encoding = "UTF-16";)";
}
database(std::shared_ptr<sqlite3> db):
_db(db) {}
database_binder operator<<(str_ref sql) {
return database_binder(_db, sql);
}
database_binder operator<<(u16str_ref sql) {
return database_binder(_db, sql);
}
connection_type connection() const { return _db; }
sqlite3_int64 last_insert_rowid() const {
return sqlite3_last_insert_rowid(_db.get());
}
int rows_modified() const {
return sqlite3_changes(_db.get());
}
template <typename Function>
void define(const std::string &name, Function&& func) {
typedef utility::function_traits<Function> traits;
auto funcPtr = new auto(std::forward<Function>(func));
if(int result = sqlite3_create_function_v2(
_db.get(), name.data(), traits::arity, SQLITE_UTF8 | SQLITE_DETERMINISTIC, funcPtr,
sql_function_binder::scalar<traits::arity, typename std::remove_reference<Function>::type>,
nullptr, nullptr, [](void* ptr){
delete static_cast<decltype(funcPtr)>(ptr);
}))
errors::throw_sqlite_error(result);
}
template <typename StepFunction, typename FinalFunction>
void define(const std::string &name, StepFunction&& step, FinalFunction&& final) {
typedef utility::function_traits<StepFunction> traits;
using ContextType = typename std::remove_reference<typename traits::template argument<0>>::type;
auto funcPtr = new auto(std::make_pair(std::forward<StepFunction>(step), std::forward<FinalFunction>(final)));
if(int result = sqlite3_create_function_v2(
_db.get(), name.c_str(), traits::arity - 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, funcPtr, nullptr,
sql_function_binder::step<ContextType, traits::arity, typename std::remove_reference<decltype(*funcPtr)>::type>,
sql_function_binder::final<ContextType, typename std::remove_reference<decltype(*funcPtr)>::type>,
[](void* ptr){
delete static_cast<decltype(funcPtr)>(ptr);
}))
errors::throw_sqlite_error(result);
}
};
template<std::size_t Count>
class binder {
private:
template <
typename Function,
std::size_t Index
>
using nth_argument_type = typename utility::function_traits<
Function
>::template argument<Index>;
public:
// `Boundary` needs to be defaulted to `Count` so that the `run` function
// template is not implicitly instantiated on class template instantiation.
// Look up section 14.7.1 _Implicit instantiation_ of the ISO C++14 Standard
// and the [dicussion](https://github.com/aminroosta/sqlite_modern_cpp/issues/8)
// on Github.
template<
typename Function,
typename... Values,
std::size_t Boundary = Count
>
static typename std::enable_if<(sizeof...(Values) < Boundary), void>::type run(
row_iterator::value_type& row,
Function&& function,
Values&&... values
) {
typename std::decay<nth_argument_type<Function, sizeof...(Values)>>::type value;
row >> value;
run<Function>(row, function, std::forward<Values>(values)..., std::move(value));
}
template<
typename Function,
typename... Values,
std::size_t Boundary = Count
>
static typename std::enable_if<(sizeof...(Values) == Boundary), void>::type run(
row_iterator::value_type&,
Function&& function,
Values&&... values
) {
function(std::move(values)...);
}
};
// Some ppl are lazy so we have a operator for proper prep. statemant handling.
void inline operator++(database_binder& db, int) { db.execute(); }
template<typename T> database_binder &operator<<(database_binder& db, index_binding_helper<T> val) {
db._next_index(); --db._inx;
int result = bind_col_in_db(db._stmt.get(), val.index, std::forward<T>(val.value));
if(result != SQLITE_OK)
exceptions::throw_sqlite_error(result, db.sql());
return db;
}
template<typename T> database_binder &operator<<(database_binder& db, index_binding_helper<T, true> val) {
db._next_index(); --db._inx;
int index = sqlite3_bind_parameter_index(db._stmt.get(), val.index);
if(!index)
throw errors::unknown_binding("The given binding name is not valid for this statement", db.sql());
int result = bind_col_in_db(db._stmt.get(), index, std::forward<T>(val.value));
if(result != SQLITE_OK)
exceptions::throw_sqlite_error(result, db.sql());
return db;
}
template<typename T> database_binder &operator<<(database_binder& db, T&& val) {
int result = bind_col_in_db(db._stmt.get(), db._next_index(), std::forward<T>(val));
if(result != SQLITE_OK)
exceptions::throw_sqlite_error(result, db.sql());
return db;
}
// Convert the rValue binder to a reference and call first op<<, its needed for the call that creates the binder (be carefull of recursion here!)
template<typename T> database_binder operator << (database_binder&& db, const T& val) { db << val; return std::move(db); }
template<typename T, bool Name> database_binder operator << (database_binder&& db, index_binding_helper<T, Name> val) { db << index_binding_helper<T, Name>{val.index, std::forward<T>(val.value)}; return std::move(db); }
namespace sql_function_binder {
template<class T>
struct AggregateCtxt {
T obj;
bool constructed = true;
};
template<
typename ContextType,
std::size_t Count,
typename Functions
>
inline void step(
sqlite3_context* db,
int count,
sqlite3_value** vals
) {
auto ctxt = static_cast<AggregateCtxt<ContextType>*>(sqlite3_aggregate_context(db, sizeof(AggregateCtxt<ContextType>)));
if(!ctxt) return;
try {
if(!ctxt->constructed) new(ctxt) AggregateCtxt<ContextType>();
step<Count, Functions>(db, count, vals, ctxt->obj);
return;
} catch(const sqlite_exception &e) {
sqlite3_result_error_code(db, e.get_code());
sqlite3_result_error(db, e.what(), -1);
} catch(const std::exception &e) {
sqlite3_result_error(db, e.what(), -1);
} catch(...) {
sqlite3_result_error(db, "Unknown error", -1);
}
if(ctxt && ctxt->constructed)
ctxt->~AggregateCtxt();
}
template<
std::size_t Count,
typename Functions,
typename... Values
>
inline typename std::enable_if<(sizeof...(Values) && sizeof...(Values) < Count), void>::type step(
sqlite3_context* db,
int count,
sqlite3_value** vals,
Values&&... values
) {
using arg_type = typename std::remove_cv<
typename std::remove_reference<
typename utility::function_traits<
typename Functions::first_type
>::template argument<sizeof...(Values)>
>::type
>::type;
step<Count, Functions>(
db,
count,
vals,
std::forward<Values>(values)...,
get_val_from_db(vals[sizeof...(Values) - 1], result_type<arg_type>()));
}
template<
std::size_t Count,
typename Functions,
typename... Values
>
inline typename std::enable_if<(sizeof...(Values) == Count), void>::type step(
sqlite3_context* db,
int,
sqlite3_value**,
Values&&... values
) {
static_cast<Functions*>(sqlite3_user_data(db))->first(std::forward<Values>(values)...);
}
template<
typename ContextType,
typename Functions
>
inline void final(sqlite3_context* db) {
auto ctxt = static_cast<AggregateCtxt<ContextType>*>(sqlite3_aggregate_context(db, sizeof(AggregateCtxt<ContextType>)));
try {
if(!ctxt) return;
if(!ctxt->constructed) new(ctxt) AggregateCtxt<ContextType>();
store_result_in_db(db,
static_cast<Functions*>(sqlite3_user_data(db))->second(ctxt->obj));
} catch(const sqlite_exception &e) {
sqlite3_result_error_code(db, e.get_code());
sqlite3_result_error(db, e.what(), -1);
} catch(const std::exception &e) {
sqlite3_result_error(db, e.what(), -1);
} catch(...) {
sqlite3_result_error(db, "Unknown error", -1);
}
if(ctxt && ctxt->constructed)
ctxt->~AggregateCtxt();
}
template<
std::size_t Count,
typename Function,
typename... Values
>
inline typename std::enable_if<(sizeof...(Values) < Count), void>::type scalar(
sqlite3_context* db,
int count,
sqlite3_value** vals,
Values&&... values
) {
using arg_type = typename std::remove_cv<
typename std::remove_reference<
typename utility::function_traits<Function>::template argument<sizeof...(Values)>
>::type
>::type;
scalar<Count, Function>(
db,
count,
vals,
std::forward<Values>(values)...,
get_val_from_db(vals[sizeof...(Values)], result_type<arg_type>()));
}
template<
std::size_t Count,
typename Function,
typename... Values
>
inline typename std::enable_if<(sizeof...(Values) == Count), void>::type scalar(
sqlite3_context* db,
int,
sqlite3_value**,
Values&&... values
) {
try {
store_result_in_db(db,
(*static_cast<Function*>(sqlite3_user_data(db)))(std::forward<Values>(values)...));
} catch(const sqlite_exception &e) {
sqlite3_result_error_code(db, e.get_code());
sqlite3_result_error(db, e.what(), -1);
} catch(const std::exception &e) {
sqlite3_result_error(db, e.what(), -1);
} catch(...) {
sqlite3_result_error(db, "Unknown error", -1);
}
}
}
}

View file

@ -0,0 +1,69 @@
#pragma once
#include <string>
#include <stdexcept>
#include <sqlite/sqlite3.h>
namespace sqlite {
class sqlite_exception: public std::runtime_error {
public:
sqlite_exception(const char* msg, str_ref sql, int code = -1): runtime_error(msg), code(code), sql(sql) {}
sqlite_exception(int code, str_ref sql): runtime_error(sqlite3_errstr(code)), code(code), sql(sql) {}
int get_code() const {return code & 0xFF;}
int get_extended_code() const {return code;}
std::string get_sql() const {return sql;}
private:
int code;
std::string sql;
};
namespace errors {
//One more or less trivial derived error class for each SQLITE error.
//Note the following are not errors so have no classes:
//SQLITE_OK, SQLITE_NOTICE, SQLITE_WARNING, SQLITE_ROW, SQLITE_DONE
//
//Note these names are exact matches to the names of the SQLITE error codes.
#define SQLITE_MODERN_CPP_ERROR_CODE(NAME,name,derived) \
class name: public sqlite_exception { using sqlite_exception::sqlite_exception; };\
derived
#define SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BASE,SUB,base,sub) \
class base ## _ ## sub: public base { using base::base; };
#include "lists/error_codes.h"
#undef SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED
#undef SQLITE_MODERN_CPP_ERROR_CODE
//Some additional errors are here for the C++ interface
class more_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; };
class no_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; };
class more_statements: public sqlite_exception { using sqlite_exception::sqlite_exception; }; // Prepared statements can only contain one statement
class invalid_utf16: public sqlite_exception { using sqlite_exception::sqlite_exception; };
class unknown_binding: public sqlite_exception { using sqlite_exception::sqlite_exception; };
static void throw_sqlite_error(const int& error_code, str_ref sql = "") {
switch(error_code & 0xFF) {
#define SQLITE_MODERN_CPP_ERROR_CODE(NAME,name,derived) \
case SQLITE_ ## NAME: switch(error_code) { \
derived \
case SQLITE_ ## NAME: \
default: throw name(error_code, sql); \
}
#if SQLITE_VERSION_NUMBER < 3010000
#define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8))
#define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
#define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8))
#endif
#define SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BASE,SUB,base,sub) \
case SQLITE_ ## BASE ## _ ## SUB: throw base ## _ ## sub(error_code, sql);
#include "lists/error_codes.h"
#undef SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED
#undef SQLITE_MODERN_CPP_ERROR_CODE
default: throw sqlite_exception(error_code, sql);
}
}
}
namespace exceptions = errors;
}

View file

@ -0,0 +1,88 @@
SQLITE_MODERN_CPP_ERROR_CODE(ERROR,error,)
SQLITE_MODERN_CPP_ERROR_CODE(INTERNAL,internal,)
SQLITE_MODERN_CPP_ERROR_CODE(PERM,perm,)
SQLITE_MODERN_CPP_ERROR_CODE(ABORT,abort,
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(ABORT,ROLLBACK,abort,rollback)
)
SQLITE_MODERN_CPP_ERROR_CODE(BUSY,busy,
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BUSY,RECOVERY,busy,recovery)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BUSY,SNAPSHOT,busy,snapshot)
)
SQLITE_MODERN_CPP_ERROR_CODE(LOCKED,locked,
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(LOCKED,SHAREDCACHE,locked,sharedcache)
)
SQLITE_MODERN_CPP_ERROR_CODE(NOMEM,nomem,)
SQLITE_MODERN_CPP_ERROR_CODE(READONLY,readonly,)
SQLITE_MODERN_CPP_ERROR_CODE(INTERRUPT,interrupt,)
SQLITE_MODERN_CPP_ERROR_CODE(IOERR,ioerr,
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,READ,ioerr,read)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,SHORT_READ,ioerr,short_read)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,WRITE,ioerr,write)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,FSYNC,ioerr,fsync)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,DIR_FSYNC,ioerr,dir_fsync)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,TRUNCATE,ioerr,truncate)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,FSTAT,ioerr,fstat)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,UNLOCK,ioerr,unlock)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,RDLOCK,ioerr,rdlock)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,DELETE,ioerr,delete)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,BLOCKED,ioerr,blocked)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,NOMEM,ioerr,nomem)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,ACCESS,ioerr,access)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,CHECKRESERVEDLOCK,ioerr,checkreservedlock)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,LOCK,ioerr,lock)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,CLOSE,ioerr,close)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,DIR_CLOSE,ioerr,dir_close)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,SHMOPEN,ioerr,shmopen)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,SHMSIZE,ioerr,shmsize)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,SHMLOCK,ioerr,shmlock)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,SHMMAP,ioerr,shmmap)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,SEEK,ioerr,seek)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,DELETE_NOENT,ioerr,delete_noent)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,MMAP,ioerr,mmap)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,GETTEMPPATH,ioerr,gettemppath)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,CONVPATH,ioerr,convpath)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,VNODE,ioerr,vnode)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,AUTH,ioerr,auth)
)
SQLITE_MODERN_CPP_ERROR_CODE(CORRUPT,corrupt,
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CORRUPT,VTAB,corrupt,vtab)
)
SQLITE_MODERN_CPP_ERROR_CODE(NOTFOUND,notfound,)
SQLITE_MODERN_CPP_ERROR_CODE(FULL,full,)
SQLITE_MODERN_CPP_ERROR_CODE(CANTOPEN,cantopen,
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CANTOPEN,NOTEMPDIR,cantopen,notempdir)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CANTOPEN,ISDIR,cantopen,isdir)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CANTOPEN,FULLPATH,cantopen,fullpath)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CANTOPEN,CONVPATH,cantopen,convpath)
)
SQLITE_MODERN_CPP_ERROR_CODE(PROTOCOL,protocol,)
SQLITE_MODERN_CPP_ERROR_CODE(EMPTY,empty,)
SQLITE_MODERN_CPP_ERROR_CODE(SCHEMA,schema,)
SQLITE_MODERN_CPP_ERROR_CODE(TOOBIG,toobig,)
SQLITE_MODERN_CPP_ERROR_CODE(CONSTRAINT,constraint,
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,CHECK,constraint,check)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,COMMITHOOK,constraint,commithook)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,FOREIGNKEY,constraint,foreignkey)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,FUNCTION,constraint,function)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,NOTNULL,constraint,notnull)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,PRIMARYKEY,constraint,primarykey)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,TRIGGER,constraint,trigger)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,UNIQUE,constraint,unique)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,VTAB,constraint,vtab)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,ROWID,constraint,rowid)
)
SQLITE_MODERN_CPP_ERROR_CODE(MISMATCH,mismatch,)
SQLITE_MODERN_CPP_ERROR_CODE(MISUSE,misuse,)
SQLITE_MODERN_CPP_ERROR_CODE(NOLFS,nolfs,)
SQLITE_MODERN_CPP_ERROR_CODE(AUTH,auth,
)
SQLITE_MODERN_CPP_ERROR_CODE(FORMAT,format,)
SQLITE_MODERN_CPP_ERROR_CODE(RANGE,range,)
SQLITE_MODERN_CPP_ERROR_CODE(NOTADB,notadb,)
SQLITE_MODERN_CPP_ERROR_CODE(NOTICE,notice,
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(NOTICE,RECOVER_WAL,notice,recover_wal)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(NOTICE,RECOVER_ROLLBACK,notice,recover_rollback)
)
SQLITE_MODERN_CPP_ERROR_CODE(WARNING,warning,
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(WARNING,AUTOINDEX,warning,autoindex)
)

View file

@ -0,0 +1,101 @@
#include "errors.h"
#include <sqlite/sqlite3.h>
#include <utility>
#include <tuple>
#include <type_traits>
namespace sqlite {
namespace detail {
template<class>
using void_t = void;
template<class T, class = void>
struct is_callable : std::false_type {};
template<class Functor, class ...Arguments>
struct is_callable<Functor(Arguments...), void_t<decltype(std::declval<Functor>()(std::declval<Arguments>()...))>> : std::true_type {};
template<class Functor, class ...Functors>
class FunctorOverload: public Functor, public FunctorOverload<Functors...> {
public:
template<class Functor1, class ...Remaining>
FunctorOverload(Functor1 &&functor, Remaining &&... remaining):
Functor(std::forward<Functor1>(functor)),
FunctorOverload<Functors...>(std::forward<Remaining>(remaining)...) {}
using Functor::operator();
using FunctorOverload<Functors...>::operator();
};
template<class Functor>
class FunctorOverload<Functor>: public Functor {
public:
template<class Functor1>
FunctorOverload(Functor1 &&functor):
Functor(std::forward<Functor1>(functor)) {}
using Functor::operator();
};
template<class Functor>
class WrapIntoFunctor: public Functor {
public:
template<class Functor1>
WrapIntoFunctor(Functor1 &&functor):
Functor(std::forward<Functor1>(functor)) {}
using Functor::operator();
};
template<class ReturnType, class ...Arguments>
class WrapIntoFunctor<ReturnType(*)(Arguments...)> {
ReturnType(*ptr)(Arguments...);
public:
WrapIntoFunctor(ReturnType(*ptr)(Arguments...)): ptr(ptr) {}
ReturnType operator()(Arguments... arguments) { return (*ptr)(std::forward<Arguments>(arguments)...); }
};
inline void store_error_log_data_pointer(std::shared_ptr<void> ptr) {
static std::shared_ptr<void> stored;
stored = std::move(ptr);
}
template<class T>
std::shared_ptr<typename std::decay<T>::type> make_shared_inferred(T &&t) {
return std::make_shared<typename std::decay<T>::type>(std::forward<T>(t));
}
}
template<class Handler>
typename std::enable_if<!detail::is_callable<Handler(const sqlite_exception&)>::value>::type
error_log(Handler &&handler);
template<class Handler>
typename std::enable_if<detail::is_callable<Handler(const sqlite_exception&)>::value>::type
error_log(Handler &&handler);
template<class ...Handler>
typename std::enable_if<sizeof...(Handler)>=2>::type
error_log(Handler &&...handler) {
return error_log(detail::FunctorOverload<detail::WrapIntoFunctor<typename std::decay<Handler>::type>...>(std::forward<Handler>(handler)...));
}
template<class Handler>
typename std::enable_if<!detail::is_callable<Handler(const sqlite_exception&)>::value>::type
error_log(Handler &&handler) {
return error_log(std::forward<Handler>(handler), [](const sqlite_exception&) {});
}
template<class Handler>
typename std::enable_if<detail::is_callable<Handler(const sqlite_exception&)>::value>::type
error_log(Handler &&handler) {
auto ptr = detail::make_shared_inferred([handler = std::forward<Handler>(handler)](int error_code, const char *errstr) mutable {
switch(error_code & 0xFF) {
#define SQLITE_MODERN_CPP_ERROR_CODE(NAME,name,derived) \
case SQLITE_ ## NAME: switch(error_code) { \
derived \
default: handler(errors::name(errstr, "", error_code)); \
};break;
#define SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BASE,SUB,base,sub) \
case SQLITE_ ## BASE ## _ ## SUB: \
handler(errors::base ## _ ## sub(errstr, "", error_code)); \
break;
#include "lists/error_codes.h"
#undef SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED
#undef SQLITE_MODERN_CPP_ERROR_CODE
default: handler(sqlite_exception(errstr, "", error_code)); \
}
});
sqlite3_config(SQLITE_CONFIG_LOG, static_cast<void(*)(void*,int,const char*)>([](void *functor, int error_code, const char *errstr) {
(*static_cast<decltype(ptr.get())>(functor))(error_code, errstr);
}), ptr.get());
detail::store_error_log_data_pointer(std::move(ptr));
}
}

View file

@ -0,0 +1,44 @@
#pragma once
#ifndef SQLITE_HAS_CODEC
#define SQLITE_HAS_CODEC
#endif
#include "../sqlite_modern_cpp.h"
namespace sqlite {
struct sqlcipher_config : public sqlite_config {
std::string key;
};
class sqlcipher_database : public database {
public:
sqlcipher_database(std::string db, const sqlcipher_config &config): database(db, config) {
set_key(config.key);
}
sqlcipher_database(std::u16string db, const sqlcipher_config &config): database(db, config) {
set_key(config.key);
}
void set_key(const std::string &key) {
if(auto ret = sqlite3_key(_db.get(), key.data(), key.size()))
errors::throw_sqlite_error(ret);
}
void set_key(const std::string &key, const std::string &db_name) {
if(auto ret = sqlite3_key_v2(_db.get(), db_name.c_str(), key.data(), key.size()))
errors::throw_sqlite_error(ret);
}
void rekey(const std::string &new_key) {
if(auto ret = sqlite3_rekey(_db.get(), new_key.data(), new_key.size()))
errors::throw_sqlite_error(ret);
}
void rekey(const std::string &new_key, const std::string &db_name) {
if(auto ret = sqlite3_rekey_v2(_db.get(), db_name.c_str(), new_key.data(), new_key.size()))
errors::throw_sqlite_error(ret);
}
};
}

View file

@ -0,0 +1,418 @@
#pragma once
#include <type_traits>
#include <string>
#include <memory>
#include <vector>
#ifdef __has_include
#if __cplusplus >= 201703 && __has_include(<string_view>)
#define MODERN_SQLITE_STRINGVIEW_SUPPORT
#endif
#endif
#ifdef __has_include
#if __cplusplus > 201402 && __has_include(<optional>)
#define MODERN_SQLITE_STD_OPTIONAL_SUPPORT
#elif __has_include(<experimental/optional>)
#define MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT
#endif
#endif
#ifdef __has_include
#if __cplusplus > 201402 && __has_include(<variant>)
#define MODERN_SQLITE_STD_VARIANT_SUPPORT
#endif
#endif
#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT
#include <optional>
#endif
#ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT
#include <experimental/optional>
#define MODERN_SQLITE_STD_OPTIONAL_SUPPORT
#endif
#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT
#include <variant>
#endif
#ifdef MODERN_SQLITE_STRINGVIEW_SUPPORT
#include <string_view>
namespace sqlite
{
typedef const std::string_view str_ref;
typedef const std::u16string_view u16str_ref;
}
#else
namespace sqlite
{
typedef const std::string& str_ref;
typedef const std::u16string& u16str_ref;
}
#endif
#include <sqlite/sqlite3.h>
#include "errors.h"
namespace sqlite {
template<class T, int Type, class = void>
struct has_sqlite_type : std::false_type {};
template<class T>
using is_sqlite_value = std::integral_constant<bool, false
|| has_sqlite_type<T, SQLITE_NULL>::value
|| has_sqlite_type<T, SQLITE_INTEGER>::value
|| has_sqlite_type<T, SQLITE_FLOAT>::value
|| has_sqlite_type<T, SQLITE_TEXT>::value
|| has_sqlite_type<T, SQLITE_BLOB>::value
>;
template<class T, int Type>
struct has_sqlite_type<T&, Type> : has_sqlite_type<T, Type> {};
template<class T, int Type>
struct has_sqlite_type<const T, Type> : has_sqlite_type<T, Type> {};
template<class T, int Type>
struct has_sqlite_type<volatile T, Type> : has_sqlite_type<T, Type> {};
template<class T>
struct result_type {
using type = T;
constexpr result_type() = default;
template<class U, class = typename std::enable_if<std::is_assignable<U, T>::value>>
constexpr result_type(result_type<U>) { }
};
// int
template<>
struct has_sqlite_type<int, SQLITE_INTEGER> : std::true_type {};
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const int& val) {
return sqlite3_bind_int(stmt, inx, val);
}
inline void store_result_in_db(sqlite3_context* db, const int& val) {
sqlite3_result_int(db, val);
}
inline int get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<int>) {
return sqlite3_column_type(stmt, inx) == SQLITE_NULL ? 0 :
sqlite3_column_int(stmt, inx);
}
inline int get_val_from_db(sqlite3_value *value, result_type<int>) {
return sqlite3_value_type(value) == SQLITE_NULL ? 0 :
sqlite3_value_int(value);
}
// sqlite_int64
template<>
struct has_sqlite_type<sqlite_int64, SQLITE_INTEGER, void> : std::true_type {};
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const sqlite_int64& val) {
return sqlite3_bind_int64(stmt, inx, val);
}
inline void store_result_in_db(sqlite3_context* db, const sqlite_int64& val) {
sqlite3_result_int64(db, val);
}
inline sqlite_int64 get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<sqlite_int64 >) {
return sqlite3_column_type(stmt, inx) == SQLITE_NULL ? 0 :
sqlite3_column_int64(stmt, inx);
}
inline sqlite3_int64 get_val_from_db(sqlite3_value *value, result_type<sqlite3_int64>) {
return sqlite3_value_type(value) == SQLITE_NULL ? 0 :
sqlite3_value_int64(value);
}
// float
template<>
struct has_sqlite_type<float, SQLITE_FLOAT, void> : std::true_type {};
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const float& val) {
return sqlite3_bind_double(stmt, inx, double(val));
}
inline void store_result_in_db(sqlite3_context* db, const float& val) {
sqlite3_result_double(db, val);
}
inline float get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<float>) {
return sqlite3_column_type(stmt, inx) == SQLITE_NULL ? 0 :
sqlite3_column_double(stmt, inx);
}
inline float get_val_from_db(sqlite3_value *value, result_type<float>) {
return sqlite3_value_type(value) == SQLITE_NULL ? 0 :
sqlite3_value_double(value);
}
// double
template<>
struct has_sqlite_type<double, SQLITE_FLOAT, void> : std::true_type {};
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const double& val) {
return sqlite3_bind_double(stmt, inx, val);
}
inline void store_result_in_db(sqlite3_context* db, const double& val) {
sqlite3_result_double(db, val);
}
inline double get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<double>) {
return sqlite3_column_type(stmt, inx) == SQLITE_NULL ? 0 :
sqlite3_column_double(stmt, inx);
}
inline double get_val_from_db(sqlite3_value *value, result_type<double>) {
return sqlite3_value_type(value) == SQLITE_NULL ? 0 :
sqlite3_value_double(value);
}
/* for nullptr support */
template<>
struct has_sqlite_type<std::nullptr_t, SQLITE_NULL, void> : std::true_type {};
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, std::nullptr_t) {
return sqlite3_bind_null(stmt, inx);
}
inline void store_result_in_db(sqlite3_context* db, std::nullptr_t) {
sqlite3_result_null(db);
}
#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT
template<>
struct has_sqlite_type<std::monostate, SQLITE_NULL, void> : std::true_type {};
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, std::monostate) {
return sqlite3_bind_null(stmt, inx);
}
inline void store_result_in_db(sqlite3_context* db, std::monostate) {
sqlite3_result_null(db);
}
inline std::monostate get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<std::monostate>) {
return std::monostate();
}
inline std::monostate get_val_from_db(sqlite3_value *value, result_type<std::monostate>) {
return std::monostate();
}
#endif
// str_ref
template<>
struct has_sqlite_type<std::string, SQLITE3_TEXT, void> : std::true_type {};
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, str_ref val) {
return sqlite3_bind_text(stmt, inx, val.data(), val.length(), SQLITE_STATIC);
}
// Convert char* to string_view to trigger op<<(..., const str_ref )
template<std::size_t N> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const char(&STR)[N]) {
return sqlite3_bind_text(stmt, inx, &STR[0], N-1, SQLITE_STATIC);
}
inline std::string get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<std::string>) {
return sqlite3_column_type(stmt, inx) == SQLITE_NULL ? std::string() :
std::string(reinterpret_cast<char const *>(sqlite3_column_text(stmt, inx)), sqlite3_column_bytes(stmt, inx));
}
inline std::string get_val_from_db(sqlite3_value *value, result_type<std::string >) {
return sqlite3_value_type(value) == SQLITE_NULL ? std::string() :
std::string(reinterpret_cast<char const *>(sqlite3_value_text(value)), sqlite3_value_bytes(value));
}
inline void store_result_in_db(sqlite3_context* db, str_ref val) {
sqlite3_result_text(db, val.data(), val.length(), SQLITE_TRANSIENT);
}
// u16str_ref
template<>
struct has_sqlite_type<std::u16string, SQLITE3_TEXT, void> : std::true_type {};
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, u16str_ref val) {
return sqlite3_bind_text16(stmt, inx, val.data(), sizeof(char16_t) * val.length(), SQLITE_STATIC);
}
// Convert char* to string_view to trigger op<<(..., const str_ref )
template<std::size_t N> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const char16_t(&STR)[N]) {
return sqlite3_bind_text16(stmt, inx, &STR[0], sizeof(char16_t) * (N-1), SQLITE_STATIC);
}
inline std::u16string get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<std::u16string>) {
return sqlite3_column_type(stmt, inx) == SQLITE_NULL ? std::u16string() :
std::u16string(reinterpret_cast<char16_t const *>(sqlite3_column_text16(stmt, inx)), sqlite3_column_bytes16(stmt, inx));
}
inline std::u16string get_val_from_db(sqlite3_value *value, result_type<std::u16string>) {
return sqlite3_value_type(value) == SQLITE_NULL ? std::u16string() :
std::u16string(reinterpret_cast<char16_t const *>(sqlite3_value_text16(value)), sqlite3_value_bytes16(value));
}
inline void store_result_in_db(sqlite3_context* db, u16str_ref val) {
sqlite3_result_text16(db, val.data(), sizeof(char16_t) * val.length(), SQLITE_TRANSIENT);
}
// Other integer types
template<class Integral>
struct has_sqlite_type<Integral, SQLITE_INTEGER, typename std::enable_if<std::is_integral<Integral>::value>::type> : std::true_type {};
template<class Integral, class = typename std::enable_if<std::is_integral<Integral>::value>::type>
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const Integral& val) {
return bind_col_in_db(stmt, inx, static_cast<sqlite3_int64>(val));
}
template<class Integral, class = std::enable_if<std::is_integral<Integral>::type>>
inline void store_result_in_db(sqlite3_context* db, const Integral& val) {
store_result_in_db(db, static_cast<sqlite3_int64>(val));
}
template<class Integral, class = typename std::enable_if<std::is_integral<Integral>::value>::type>
inline Integral get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<Integral>) {
return get_col_from_db(stmt, inx, result_type<sqlite3_int64>());
}
template<class Integral, class = typename std::enable_if<std::is_integral<Integral>::value>::type>
inline Integral get_val_from_db(sqlite3_value *value, result_type<Integral>) {
return get_val_from_db(value, result_type<sqlite3_int64>());
}
// vector<T, A>
template<typename T, typename A>
struct has_sqlite_type<std::vector<T, A>, SQLITE_BLOB, void> : std::true_type {};
template<typename T, typename A> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const std::vector<T, A>& vec) {
void const* buf = reinterpret_cast<void const *>(vec.data());
int bytes = vec.size() * sizeof(T);
return sqlite3_bind_blob(stmt, inx, buf, bytes, SQLITE_STATIC);
}
template<typename T, typename A> inline void store_result_in_db(sqlite3_context* db, const std::vector<T, A>& vec) {
void const* buf = reinterpret_cast<void const *>(vec.data());
int bytes = vec.size() * sizeof(T);
sqlite3_result_blob(db, buf, bytes, SQLITE_TRANSIENT);
}
template<typename T, typename A> inline std::vector<T, A> get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<std::vector<T, A>>) {
if(sqlite3_column_type(stmt, inx) == SQLITE_NULL) {
return {};
}
int bytes = sqlite3_column_bytes(stmt, inx);
T const* buf = reinterpret_cast<T const *>(sqlite3_column_blob(stmt, inx));
return std::vector<T, A>(buf, buf + bytes/sizeof(T));
}
template<typename T, typename A> inline std::vector<T, A> get_val_from_db(sqlite3_value *value, result_type<std::vector<T, A>>) {
if(sqlite3_value_type(value) == SQLITE_NULL) {
return {};
}
int bytes = sqlite3_value_bytes(value);
T const* buf = reinterpret_cast<T const *>(sqlite3_value_blob(value));
return std::vector<T, A>(buf, buf + bytes/sizeof(T));
}
/* for unique_ptr<T> support */
template<typename T, int Type>
struct has_sqlite_type<std::unique_ptr<T>, Type, void> : has_sqlite_type<T, Type> {};
template<typename T>
struct has_sqlite_type<std::unique_ptr<T>, SQLITE_NULL, void> : std::true_type {};
template<typename T> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const std::unique_ptr<T>& val) {
return val ? bind_col_in_db(stmt, inx, *val) : bind_col_in_db(stmt, inx, nullptr);
}
template<typename T> inline std::unique_ptr<T> get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<std::unique_ptr<T>>) {
if(sqlite3_column_type(stmt, inx) == SQLITE_NULL) {
return nullptr;
}
return std::make_unique<T>(get_col_from_db(stmt, inx, result_type<T>()));
}
template<typename T> inline std::unique_ptr<T> get_val_from_db(sqlite3_value *value, result_type<std::unique_ptr<T>>) {
if(sqlite3_value_type(value) == SQLITE_NULL) {
return nullptr;
}
return std::make_unique<T>(get_val_from_db(value, result_type<T>()));
}
// std::optional support for NULL values
#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT
#ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT
template<class T>
using optional = std::experimental::optional<T>;
#else
template<class T>
using optional = std::optional<T>;
#endif
template<typename T, int Type>
struct has_sqlite_type<optional<T>, Type, void> : has_sqlite_type<T, Type> {};
template<typename T>
struct has_sqlite_type<optional<T>, SQLITE_NULL, void> : std::true_type {};
template <typename OptionalT> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const optional<OptionalT>& val) {
return val ? bind_col_in_db(stmt, inx, *val) : bind_col_in_db(stmt, inx, nullptr);
}
template <typename OptionalT> inline void store_result_in_db(sqlite3_context* db, const optional<OptionalT>& val) {
if(val)
store_result_in_db(db, *val);
else
sqlite3_result_null(db);
}
template <typename OptionalT> inline optional<OptionalT> get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<optional<OptionalT>>) {
#ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT
if(sqlite3_column_type(stmt, inx) == SQLITE_NULL) {
return std::experimental::nullopt;
}
return std::experimental::make_optional(get_col_from_db(stmt, inx, result_type<OptionalT>()));
#else
if(sqlite3_column_type(stmt, inx) == SQLITE_NULL) {
return std::nullopt;
}
return std::make_optional(get_col_from_db(stmt, inx, result_type<OptionalT>()));
#endif
}
template <typename OptionalT> inline optional<OptionalT> get_val_from_db(sqlite3_value *value, result_type<optional<OptionalT>>) {
#ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT
if(sqlite3_value_type(value) == SQLITE_NULL) {
return std::experimental::nullopt;
}
return std::experimental::make_optional(get_val_from_db(value, result_type<OptionalT>()));
#else
if(sqlite3_value_type(value) == SQLITE_NULL) {
return std::nullopt;
}
return std::make_optional(get_val_from_db(value, result_type<OptionalT>()));
#endif
}
#endif
#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT
namespace detail {
template<class T, class U>
struct tag_trait : U { using tag = T; };
}
template<int Type, class ...Options>
struct has_sqlite_type<std::variant<Options...>, Type, void> : std::disjunction<detail::tag_trait<Options, has_sqlite_type<Options, Type>>...> {};
namespace detail {
template<int Type, typename ...Options, typename Callback, typename first_compatible = has_sqlite_type<std::variant<Options...>, Type>>
inline std::variant<Options...> variant_select_type(Callback &&callback) {
if constexpr(first_compatible::value)
return callback(result_type<typename first_compatible::tag>());
else
throw errors::mismatch("The value is unsupported by this variant.", "", SQLITE_MISMATCH);
}
template<typename ...Options, typename Callback> inline decltype(auto) variant_select(int type, Callback &&callback) {
switch(type) {
case SQLITE_NULL:
return variant_select_type<SQLITE_NULL, Options...>(std::forward<Callback>(callback));
case SQLITE_INTEGER:
return variant_select_type<SQLITE_INTEGER, Options...>(std::forward<Callback>(callback));
case SQLITE_FLOAT:
return variant_select_type<SQLITE_FLOAT, Options...>(std::forward<Callback>(callback));
case SQLITE_TEXT:
return variant_select_type<SQLITE_TEXT, Options...>(std::forward<Callback>(callback));
case SQLITE_BLOB:
return variant_select_type<SQLITE_BLOB, Options...>(std::forward<Callback>(callback));
}
#ifdef _MSC_VER
__assume(false);
#else
__builtin_unreachable();
#endif
}
}
template <typename ...Args> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const std::variant<Args...>& val) {
return std::visit([&](auto &&opt) {return bind_col_in_db(stmt, inx, std::forward<decltype(opt)>(opt));}, val);
}
template <typename ...Args> inline void store_result_in_db(sqlite3_context* db, const std::variant<Args...>& val) {
std::visit([&](auto &&opt) {store_result_in_db(db, std::forward<decltype(opt)>(opt));}, val);
}
template <typename ...Args> inline std::variant<Args...> get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<std::variant<Args...>>) {
return detail::variant_select<Args...>(sqlite3_column_type(stmt, inx), [&](auto v) {
return std::variant<Args...>(std::in_place_type<typename decltype(v)::type>, get_col_from_db(stmt, inx, v));
});
}
template <typename ...Args> inline std::variant<Args...> get_val_from_db(sqlite3_value *value, result_type<std::variant<Args...>>) {
return detail::variant_select<Args...>(sqlite3_value_type(value), [&](auto v) {
return std::variant<Args...>(std::in_place_type<typename decltype(v)::type>, get_val_from_db(value, v));
});
}
#endif
}

View file

@ -0,0 +1,56 @@
#pragma once
#include <tuple>
#include<type_traits>
namespace sqlite {
namespace utility {
template<typename> struct function_traits;
template <typename Function>
struct function_traits : public function_traits<
decltype(&std::remove_reference<Function>::type::operator())
> { };
template <
typename ClassType,
typename ReturnType,
typename... Arguments
>
struct function_traits<
ReturnType(ClassType::*)(Arguments...) const
> : function_traits<ReturnType(*)(Arguments...)> { };
/* support the non-const operator ()
* this will work with user defined functors */
template <
typename ClassType,
typename ReturnType,
typename... Arguments
>
struct function_traits<
ReturnType(ClassType::*)(Arguments...)
> : function_traits<ReturnType(*)(Arguments...)> { };
template <
typename ReturnType,
typename... Arguments
>
struct function_traits<
ReturnType(*)(Arguments...)
> {
typedef ReturnType result_type;
using argument_tuple = std::tuple<Arguments...>;
template <std::size_t Index>
using argument = typename std::tuple_element<
Index,
argument_tuple
>::type;
static const std::size_t arity = sizeof...(Arguments);
};
}
}

View file

@ -0,0 +1,41 @@
#pragma once
#include <cassert>
#include <exception>
#include <iostream>
// Consider that std::uncaught_exceptions is available if explicitly indicated
// by the standard library, if compiler advertises full C++17 support or, as a
// special case, for MSVS 2015+ (which doesn't define __cplusplus correctly by
// default as of 2017.7 version and couldn't do it at all until it).
#ifndef MODERN_SQLITE_UNCAUGHT_EXCEPTIONS_SUPPORT
#ifdef __cpp_lib_uncaught_exceptions
#define MODERN_SQLITE_UNCAUGHT_EXCEPTIONS_SUPPORT
#elif __cplusplus >= 201703L
#define MODERN_SQLITE_UNCAUGHT_EXCEPTIONS_SUPPORT
#elif defined(_MSC_VER) && _MSC_VER >= 1900
#define MODERN_SQLITE_UNCAUGHT_EXCEPTIONS_SUPPORT
#endif
#endif
namespace sqlite {
namespace utility {
#ifdef MODERN_SQLITE_UNCAUGHT_EXCEPTIONS_SUPPORT
class UncaughtExceptionDetector {
public:
operator bool() {
return count != std::uncaught_exceptions();
}
private:
int count = std::uncaught_exceptions();
};
#else
class UncaughtExceptionDetector {
public:
operator bool() {
return std::uncaught_exception();
}
};
#endif
}
}

View file

@ -0,0 +1,42 @@
#pragma once
#include <locale>
#include <string>
#include <algorithm>
#include "../errors.h"
namespace sqlite {
namespace utility {
inline std::string utf16_to_utf8(u16str_ref input) {
struct : std::codecvt<char16_t, char, std::mbstate_t> {
} codecvt;
std::mbstate_t state{};
std::string result((std::max)(input.size() * 3 / 2, std::size_t(4)), '\0');
const char16_t *remaining_input = input.data();
std::size_t produced_output = 0;
while(true) {
char *used_output;
switch(codecvt.out(state, remaining_input, &input[input.size()],
remaining_input, &result[produced_output],
&result[result.size() - 1] + 1, used_output)) {
case std::codecvt_base::ok:
result.resize(used_output - result.data());
return result;
case std::codecvt_base::noconv:
// This should be unreachable
case std::codecvt_base::error:
throw errors::invalid_utf16("Invalid UTF-16 input", "");
case std::codecvt_base::partial:
if(used_output == result.data() + produced_output)
throw errors::invalid_utf16("Unexpected end of input", "");
produced_output = used_output - result.data();
result.resize(
result.size()
+ (std::max)((&input[input.size()] - remaining_input) * 3 / 2,
std::ptrdiff_t(4)));
}
}
}
} // namespace utility
} // namespace sqlite

18992
src/sqlite/shell.c Normal file

File diff suppressed because it is too large Load diff

223788
src/sqlite/sqlite3.c Normal file

File diff suppressed because it is too large Load diff

11792
src/sqlite/sqlite3.h Normal file

File diff suppressed because it is too large Load diff

634
src/sqlite/sqlite3ext.h Normal file
View file

@ -0,0 +1,634 @@
/*
** 2006 June 7
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the SQLite interface for use by
** shared libraries that want to be imported as extensions into
** an SQLite instance. Shared libraries that intend to be loaded
** as extensions by SQLite should #include this file instead of
** sqlite3.h.
*/
#ifndef SQLITE3EXT_H
#define SQLITE3EXT_H
#include "sqlite3.h"
/*
** The following structure holds pointers to all of the SQLite API
** routines.
**
** WARNING: In order to maintain backwards compatibility, add new
** interfaces to the end of this structure only. If you insert new
** interfaces in the middle of this structure, then older different
** versions of SQLite will not be able to load each other's shared
** libraries!
*/
struct sqlite3_api_routines {
void * (*aggregate_context)(sqlite3_context*,int nBytes);
int (*aggregate_count)(sqlite3_context*);
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
int (*bind_double)(sqlite3_stmt*,int,double);
int (*bind_int)(sqlite3_stmt*,int,int);
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
int (*bind_null)(sqlite3_stmt*,int);
int (*bind_parameter_count)(sqlite3_stmt*);
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
int (*busy_timeout)(sqlite3*,int ms);
int (*changes)(sqlite3*);
int (*close)(sqlite3*);
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
int eTextRep,const char*));
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
int eTextRep,const void*));
const void * (*column_blob)(sqlite3_stmt*,int iCol);
int (*column_bytes)(sqlite3_stmt*,int iCol);
int (*column_bytes16)(sqlite3_stmt*,int iCol);
int (*column_count)(sqlite3_stmt*pStmt);
const char * (*column_database_name)(sqlite3_stmt*,int);
const void * (*column_database_name16)(sqlite3_stmt*,int);
const char * (*column_decltype)(sqlite3_stmt*,int i);
const void * (*column_decltype16)(sqlite3_stmt*,int);
double (*column_double)(sqlite3_stmt*,int iCol);
int (*column_int)(sqlite3_stmt*,int iCol);
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
const char * (*column_name)(sqlite3_stmt*,int);
const void * (*column_name16)(sqlite3_stmt*,int);
const char * (*column_origin_name)(sqlite3_stmt*,int);
const void * (*column_origin_name16)(sqlite3_stmt*,int);
const char * (*column_table_name)(sqlite3_stmt*,int);
const void * (*column_table_name16)(sqlite3_stmt*,int);
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
const void * (*column_text16)(sqlite3_stmt*,int iCol);
int (*column_type)(sqlite3_stmt*,int iCol);
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
int (*complete)(const char*sql);
int (*complete16)(const void*sql);
int (*create_collation)(sqlite3*,const char*,int,void*,
int(*)(void*,int,const void*,int,const void*));
int (*create_collation16)(sqlite3*,const void*,int,void*,
int(*)(void*,int,const void*,int,const void*));
int (*create_function)(sqlite3*,const char*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*));
int (*create_function16)(sqlite3*,const void*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*));
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
int (*data_count)(sqlite3_stmt*pStmt);
sqlite3 * (*db_handle)(sqlite3_stmt*);
int (*declare_vtab)(sqlite3*,const char*);
int (*enable_shared_cache)(int);
int (*errcode)(sqlite3*db);
const char * (*errmsg)(sqlite3*);
const void * (*errmsg16)(sqlite3*);
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
int (*expired)(sqlite3_stmt*);
int (*finalize)(sqlite3_stmt*pStmt);
void (*free)(void*);
void (*free_table)(char**result);
int (*get_autocommit)(sqlite3*);
void * (*get_auxdata)(sqlite3_context*,int);
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
int (*global_recover)(void);
void (*interruptx)(sqlite3*);
sqlite_int64 (*last_insert_rowid)(sqlite3*);
const char * (*libversion)(void);
int (*libversion_number)(void);
void *(*malloc)(int);
char * (*mprintf)(const char*,...);
int (*open)(const char*,sqlite3**);
int (*open16)(const void*,sqlite3**);
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
void *(*realloc)(void*,int);
int (*reset)(sqlite3_stmt*pStmt);
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_double)(sqlite3_context*,double);
void (*result_error)(sqlite3_context*,const char*,int);
void (*result_error16)(sqlite3_context*,const void*,int);
void (*result_int)(sqlite3_context*,int);
void (*result_int64)(sqlite3_context*,sqlite_int64);
void (*result_null)(sqlite3_context*);
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_value)(sqlite3_context*,sqlite3_value*);
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
const char*,const char*),void*);
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
char * (*xsnprintf)(int,char*,const char*,...);
int (*step)(sqlite3_stmt*);
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
char const**,char const**,int*,int*,int*);
void (*thread_cleanup)(void);
int (*total_changes)(sqlite3*);
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
sqlite_int64),void*);
void * (*user_data)(sqlite3_context*);
const void * (*value_blob)(sqlite3_value*);
int (*value_bytes)(sqlite3_value*);
int (*value_bytes16)(sqlite3_value*);
double (*value_double)(sqlite3_value*);
int (*value_int)(sqlite3_value*);
sqlite_int64 (*value_int64)(sqlite3_value*);
int (*value_numeric_type)(sqlite3_value*);
const unsigned char * (*value_text)(sqlite3_value*);
const void * (*value_text16)(sqlite3_value*);
const void * (*value_text16be)(sqlite3_value*);
const void * (*value_text16le)(sqlite3_value*);
int (*value_type)(sqlite3_value*);
char *(*vmprintf)(const char*,va_list);
/* Added ??? */
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
/* Added by 3.3.13 */
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
int (*clear_bindings)(sqlite3_stmt*);
/* Added by 3.4.1 */
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
void (*xDestroy)(void *));
/* Added by 3.5.0 */
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
int (*blob_bytes)(sqlite3_blob*);
int (*blob_close)(sqlite3_blob*);
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
int,sqlite3_blob**);
int (*blob_read)(sqlite3_blob*,void*,int,int);
int (*blob_write)(sqlite3_blob*,const void*,int,int);
int (*create_collation_v2)(sqlite3*,const char*,int,void*,
int(*)(void*,int,const void*,int,const void*),
void(*)(void*));
int (*file_control)(sqlite3*,const char*,int,void*);
sqlite3_int64 (*memory_highwater)(int);
sqlite3_int64 (*memory_used)(void);
sqlite3_mutex *(*mutex_alloc)(int);
void (*mutex_enter)(sqlite3_mutex*);
void (*mutex_free)(sqlite3_mutex*);
void (*mutex_leave)(sqlite3_mutex*);
int (*mutex_try)(sqlite3_mutex*);
int (*open_v2)(const char*,sqlite3**,int,const char*);
int (*release_memory)(int);
void (*result_error_nomem)(sqlite3_context*);
void (*result_error_toobig)(sqlite3_context*);
int (*sleep)(int);
void (*soft_heap_limit)(int);
sqlite3_vfs *(*vfs_find)(const char*);
int (*vfs_register)(sqlite3_vfs*,int);
int (*vfs_unregister)(sqlite3_vfs*);
int (*xthreadsafe)(void);
void (*result_zeroblob)(sqlite3_context*,int);
void (*result_error_code)(sqlite3_context*,int);
int (*test_control)(int, ...);
void (*randomness)(int,void*);
sqlite3 *(*context_db_handle)(sqlite3_context*);
int (*extended_result_codes)(sqlite3*,int);
int (*limit)(sqlite3*,int,int);
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
const char *(*sql)(sqlite3_stmt*);
int (*status)(int,int*,int*,int);
int (*backup_finish)(sqlite3_backup*);
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
int (*backup_pagecount)(sqlite3_backup*);
int (*backup_remaining)(sqlite3_backup*);
int (*backup_step)(sqlite3_backup*,int);
const char *(*compileoption_get)(int);
int (*compileoption_used)(const char*);
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*),
void(*xDestroy)(void*));
int (*db_config)(sqlite3*,int,...);
sqlite3_mutex *(*db_mutex)(sqlite3*);
int (*db_status)(sqlite3*,int,int*,int*,int);
int (*extended_errcode)(sqlite3*);
void (*log)(int,const char*,...);
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
const char *(*sourceid)(void);
int (*stmt_status)(sqlite3_stmt*,int,int);
int (*strnicmp)(const char*,const char*,int);
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
int (*wal_autocheckpoint)(sqlite3*,int);
int (*wal_checkpoint)(sqlite3*,const char*);
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
int (*vtab_config)(sqlite3*,int op,...);
int (*vtab_on_conflict)(sqlite3*);
/* Version 3.7.16 and later */
int (*close_v2)(sqlite3*);
const char *(*db_filename)(sqlite3*,const char*);
int (*db_readonly)(sqlite3*,const char*);
int (*db_release_memory)(sqlite3*);
const char *(*errstr)(int);
int (*stmt_busy)(sqlite3_stmt*);
int (*stmt_readonly)(sqlite3_stmt*);
int (*stricmp)(const char*,const char*);
int (*uri_boolean)(const char*,const char*,int);
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
const char *(*uri_parameter)(const char*,const char*);
char *(*xvsnprintf)(int,char*,const char*,va_list);
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
/* Version 3.8.7 and later */
int (*auto_extension)(void(*)(void));
int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64,
void(*)(void*));
int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64,
void(*)(void*),unsigned char);
int (*cancel_auto_extension)(void(*)(void));
int (*load_extension)(sqlite3*,const char*,const char*,char**);
void *(*malloc64)(sqlite3_uint64);
sqlite3_uint64 (*msize)(void*);
void *(*realloc64)(void*,sqlite3_uint64);
void (*reset_auto_extension)(void);
void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64,
void(*)(void*));
void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64,
void(*)(void*), unsigned char);
int (*strglob)(const char*,const char*);
/* Version 3.8.11 and later */
sqlite3_value *(*value_dup)(const sqlite3_value*);
void (*value_free)(sqlite3_value*);
int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64);
int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64);
/* Version 3.9.0 and later */
unsigned int (*value_subtype)(sqlite3_value*);
void (*result_subtype)(sqlite3_context*,unsigned int);
/* Version 3.10.0 and later */
int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int);
int (*strlike)(const char*,const char*,unsigned int);
int (*db_cacheflush)(sqlite3*);
/* Version 3.12.0 and later */
int (*system_errno)(sqlite3*);
/* Version 3.14.0 and later */
int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*);
char *(*expanded_sql)(sqlite3_stmt*);
/* Version 3.18.0 and later */
void (*set_last_insert_rowid)(sqlite3*,sqlite3_int64);
/* Version 3.20.0 and later */
int (*prepare_v3)(sqlite3*,const char*,int,unsigned int,
sqlite3_stmt**,const char**);
int (*prepare16_v3)(sqlite3*,const void*,int,unsigned int,
sqlite3_stmt**,const void**);
int (*bind_pointer)(sqlite3_stmt*,int,void*,const char*,void(*)(void*));
void (*result_pointer)(sqlite3_context*,void*,const char*,void(*)(void*));
void *(*value_pointer)(sqlite3_value*,const char*);
int (*vtab_nochange)(sqlite3_context*);
int (*value_nochange)(sqlite3_value*);
const char *(*vtab_collation)(sqlite3_index_info*,int);
/* Version 3.24.0 and later */
int (*keyword_count)(void);
int (*keyword_name)(int,const char**,int*);
int (*keyword_check)(const char*,int);
sqlite3_str *(*str_new)(sqlite3*);
char *(*str_finish)(sqlite3_str*);
void (*str_appendf)(sqlite3_str*, const char *zFormat, ...);
void (*str_vappendf)(sqlite3_str*, const char *zFormat, va_list);
void (*str_append)(sqlite3_str*, const char *zIn, int N);
void (*str_appendall)(sqlite3_str*, const char *zIn);
void (*str_appendchar)(sqlite3_str*, int N, char C);
void (*str_reset)(sqlite3_str*);
int (*str_errcode)(sqlite3_str*);
int (*str_length)(sqlite3_str*);
char *(*str_value)(sqlite3_str*);
/* Version 3.25.0 and later */
int (*create_window_function)(sqlite3*,const char*,int,int,void*,
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*),
void (*xValue)(sqlite3_context*),
void (*xInv)(sqlite3_context*,int,sqlite3_value**),
void(*xDestroy)(void*));
/* Version 3.26.0 and later */
const char *(*normalized_sql)(sqlite3_stmt*);
/* Version 3.28.0 and later */
int (*stmt_isexplain)(sqlite3_stmt*);
int (*value_frombind)(sqlite3_value*);
};
/*
** This is the function signature used for all extension entry points. It
** is also defined in the file "loadext.c".
*/
typedef int (*sqlite3_loadext_entry)(
sqlite3 *db, /* Handle to the database. */
char **pzErrMsg, /* Used to set error string on failure. */
const sqlite3_api_routines *pThunk /* Extension API function pointers. */
);
/*
** The following macros redefine the API routines so that they are
** redirected through the global sqlite3_api structure.
**
** This header file is also used by the loadext.c source file
** (part of the main SQLite library - not an extension) so that
** it can get access to the sqlite3_api_routines structure
** definition. But the main library does not want to redefine
** the API. So the redefinition macros are only valid if the
** SQLITE_CORE macros is undefined.
*/
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
#endif
#define sqlite3_bind_blob sqlite3_api->bind_blob
#define sqlite3_bind_double sqlite3_api->bind_double
#define sqlite3_bind_int sqlite3_api->bind_int
#define sqlite3_bind_int64 sqlite3_api->bind_int64
#define sqlite3_bind_null sqlite3_api->bind_null
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
#define sqlite3_bind_text sqlite3_api->bind_text
#define sqlite3_bind_text16 sqlite3_api->bind_text16
#define sqlite3_bind_value sqlite3_api->bind_value
#define sqlite3_busy_handler sqlite3_api->busy_handler
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
#define sqlite3_changes sqlite3_api->changes
#define sqlite3_close sqlite3_api->close
#define sqlite3_collation_needed sqlite3_api->collation_needed
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
#define sqlite3_column_blob sqlite3_api->column_blob
#define sqlite3_column_bytes sqlite3_api->column_bytes
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
#define sqlite3_column_count sqlite3_api->column_count
#define sqlite3_column_database_name sqlite3_api->column_database_name
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
#define sqlite3_column_decltype sqlite3_api->column_decltype
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
#define sqlite3_column_double sqlite3_api->column_double
#define sqlite3_column_int sqlite3_api->column_int
#define sqlite3_column_int64 sqlite3_api->column_int64
#define sqlite3_column_name sqlite3_api->column_name
#define sqlite3_column_name16 sqlite3_api->column_name16
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
#define sqlite3_column_table_name sqlite3_api->column_table_name
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
#define sqlite3_column_text sqlite3_api->column_text
#define sqlite3_column_text16 sqlite3_api->column_text16
#define sqlite3_column_type sqlite3_api->column_type
#define sqlite3_column_value sqlite3_api->column_value
#define sqlite3_commit_hook sqlite3_api->commit_hook
#define sqlite3_complete sqlite3_api->complete
#define sqlite3_complete16 sqlite3_api->complete16
#define sqlite3_create_collation sqlite3_api->create_collation
#define sqlite3_create_collation16 sqlite3_api->create_collation16
#define sqlite3_create_function sqlite3_api->create_function
#define sqlite3_create_function16 sqlite3_api->create_function16
#define sqlite3_create_module sqlite3_api->create_module
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
#define sqlite3_data_count sqlite3_api->data_count
#define sqlite3_db_handle sqlite3_api->db_handle
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
#define sqlite3_errcode sqlite3_api->errcode
#define sqlite3_errmsg sqlite3_api->errmsg
#define sqlite3_errmsg16 sqlite3_api->errmsg16
#define sqlite3_exec sqlite3_api->exec
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_expired sqlite3_api->expired
#endif
#define sqlite3_finalize sqlite3_api->finalize
#define sqlite3_free sqlite3_api->free
#define sqlite3_free_table sqlite3_api->free_table
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
#define sqlite3_get_table sqlite3_api->get_table
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_global_recover sqlite3_api->global_recover
#endif
#define sqlite3_interrupt sqlite3_api->interruptx
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
#define sqlite3_libversion sqlite3_api->libversion
#define sqlite3_libversion_number sqlite3_api->libversion_number
#define sqlite3_malloc sqlite3_api->malloc
#define sqlite3_mprintf sqlite3_api->mprintf
#define sqlite3_open sqlite3_api->open
#define sqlite3_open16 sqlite3_api->open16
#define sqlite3_prepare sqlite3_api->prepare
#define sqlite3_prepare16 sqlite3_api->prepare16
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_profile sqlite3_api->profile
#define sqlite3_progress_handler sqlite3_api->progress_handler
#define sqlite3_realloc sqlite3_api->realloc
#define sqlite3_reset sqlite3_api->reset
#define sqlite3_result_blob sqlite3_api->result_blob
#define sqlite3_result_double sqlite3_api->result_double
#define sqlite3_result_error sqlite3_api->result_error
#define sqlite3_result_error16 sqlite3_api->result_error16
#define sqlite3_result_int sqlite3_api->result_int
#define sqlite3_result_int64 sqlite3_api->result_int64
#define sqlite3_result_null sqlite3_api->result_null
#define sqlite3_result_text sqlite3_api->result_text
#define sqlite3_result_text16 sqlite3_api->result_text16
#define sqlite3_result_text16be sqlite3_api->result_text16be
#define sqlite3_result_text16le sqlite3_api->result_text16le
#define sqlite3_result_value sqlite3_api->result_value
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
#define sqlite3_snprintf sqlite3_api->xsnprintf
#define sqlite3_step sqlite3_api->step
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
#define sqlite3_total_changes sqlite3_api->total_changes
#define sqlite3_trace sqlite3_api->trace
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
#endif
#define sqlite3_update_hook sqlite3_api->update_hook
#define sqlite3_user_data sqlite3_api->user_data
#define sqlite3_value_blob sqlite3_api->value_blob
#define sqlite3_value_bytes sqlite3_api->value_bytes
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
#define sqlite3_value_double sqlite3_api->value_double
#define sqlite3_value_int sqlite3_api->value_int
#define sqlite3_value_int64 sqlite3_api->value_int64
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
#define sqlite3_value_text sqlite3_api->value_text
#define sqlite3_value_text16 sqlite3_api->value_text16
#define sqlite3_value_text16be sqlite3_api->value_text16be
#define sqlite3_value_text16le sqlite3_api->value_text16le
#define sqlite3_value_type sqlite3_api->value_type
#define sqlite3_vmprintf sqlite3_api->vmprintf
#define sqlite3_vsnprintf sqlite3_api->xvsnprintf
#define sqlite3_overload_function sqlite3_api->overload_function
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
#define sqlite3_blob_close sqlite3_api->blob_close
#define sqlite3_blob_open sqlite3_api->blob_open
#define sqlite3_blob_read sqlite3_api->blob_read
#define sqlite3_blob_write sqlite3_api->blob_write
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
#define sqlite3_file_control sqlite3_api->file_control
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
#define sqlite3_memory_used sqlite3_api->memory_used
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
#define sqlite3_mutex_free sqlite3_api->mutex_free
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
#define sqlite3_mutex_try sqlite3_api->mutex_try
#define sqlite3_open_v2 sqlite3_api->open_v2
#define sqlite3_release_memory sqlite3_api->release_memory
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
#define sqlite3_sleep sqlite3_api->sleep
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
#define sqlite3_vfs_find sqlite3_api->vfs_find
#define sqlite3_vfs_register sqlite3_api->vfs_register
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
#define sqlite3_result_error_code sqlite3_api->result_error_code
#define sqlite3_test_control sqlite3_api->test_control
#define sqlite3_randomness sqlite3_api->randomness
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
#define sqlite3_limit sqlite3_api->limit
#define sqlite3_next_stmt sqlite3_api->next_stmt
#define sqlite3_sql sqlite3_api->sql
#define sqlite3_status sqlite3_api->status
#define sqlite3_backup_finish sqlite3_api->backup_finish
#define sqlite3_backup_init sqlite3_api->backup_init
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
#define sqlite3_backup_step sqlite3_api->backup_step
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
#define sqlite3_db_config sqlite3_api->db_config
#define sqlite3_db_mutex sqlite3_api->db_mutex
#define sqlite3_db_status sqlite3_api->db_status
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
#define sqlite3_log sqlite3_api->log
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
#define sqlite3_sourceid sqlite3_api->sourceid
#define sqlite3_stmt_status sqlite3_api->stmt_status
#define sqlite3_strnicmp sqlite3_api->strnicmp
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
#define sqlite3_wal_hook sqlite3_api->wal_hook
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
#define sqlite3_vtab_config sqlite3_api->vtab_config
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
/* Version 3.7.16 and later */
#define sqlite3_close_v2 sqlite3_api->close_v2
#define sqlite3_db_filename sqlite3_api->db_filename
#define sqlite3_db_readonly sqlite3_api->db_readonly
#define sqlite3_db_release_memory sqlite3_api->db_release_memory
#define sqlite3_errstr sqlite3_api->errstr
#define sqlite3_stmt_busy sqlite3_api->stmt_busy
#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly
#define sqlite3_stricmp sqlite3_api->stricmp
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
#define sqlite3_uri_int64 sqlite3_api->uri_int64
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
#define sqlite3_uri_vsnprintf sqlite3_api->xvsnprintf
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
/* Version 3.8.7 and later */
#define sqlite3_auto_extension sqlite3_api->auto_extension
#define sqlite3_bind_blob64 sqlite3_api->bind_blob64
#define sqlite3_bind_text64 sqlite3_api->bind_text64
#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension
#define sqlite3_load_extension sqlite3_api->load_extension
#define sqlite3_malloc64 sqlite3_api->malloc64
#define sqlite3_msize sqlite3_api->msize
#define sqlite3_realloc64 sqlite3_api->realloc64
#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension
#define sqlite3_result_blob64 sqlite3_api->result_blob64
#define sqlite3_result_text64 sqlite3_api->result_text64
#define sqlite3_strglob sqlite3_api->strglob
/* Version 3.8.11 and later */
#define sqlite3_value_dup sqlite3_api->value_dup
#define sqlite3_value_free sqlite3_api->value_free
#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64
#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64
/* Version 3.9.0 and later */
#define sqlite3_value_subtype sqlite3_api->value_subtype
#define sqlite3_result_subtype sqlite3_api->result_subtype
/* Version 3.10.0 and later */
#define sqlite3_status64 sqlite3_api->status64
#define sqlite3_strlike sqlite3_api->strlike
#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
/* Version 3.12.0 and later */
#define sqlite3_system_errno sqlite3_api->system_errno
/* Version 3.14.0 and later */
#define sqlite3_trace_v2 sqlite3_api->trace_v2
#define sqlite3_expanded_sql sqlite3_api->expanded_sql
/* Version 3.18.0 and later */
#define sqlite3_set_last_insert_rowid sqlite3_api->set_last_insert_rowid
/* Version 3.20.0 and later */
#define sqlite3_prepare_v3 sqlite3_api->prepare_v3
#define sqlite3_prepare16_v3 sqlite3_api->prepare16_v3
#define sqlite3_bind_pointer sqlite3_api->bind_pointer
#define sqlite3_result_pointer sqlite3_api->result_pointer
#define sqlite3_value_pointer sqlite3_api->value_pointer
/* Version 3.22.0 and later */
#define sqlite3_vtab_nochange sqlite3_api->vtab_nochange
#define sqlite3_value_nochange sqlite3_api->value_nochange
#define sqlite3_vtab_collation sqlite3_api->vtab_collation
/* Version 3.24.0 and later */
#define sqlite3_keyword_count sqlite3_api->keyword_count
#define sqlite3_keyword_name sqlite3_api->keyword_name
#define sqlite3_keyword_check sqlite3_api->keyword_check
#define sqlite3_str_new sqlite3_api->str_new
#define sqlite3_str_finish sqlite3_api->str_finish
#define sqlite3_str_appendf sqlite3_api->str_appendf
#define sqlite3_str_vappendf sqlite3_api->str_vappendf
#define sqlite3_str_append sqlite3_api->str_append
#define sqlite3_str_appendall sqlite3_api->str_appendall
#define sqlite3_str_appendchar sqlite3_api->str_appendchar
#define sqlite3_str_reset sqlite3_api->str_reset
#define sqlite3_str_errcode sqlite3_api->str_errcode
#define sqlite3_str_length sqlite3_api->str_length
#define sqlite3_str_value sqlite3_api->str_value
/* Version 3.25.0 and later */
#define sqlite3_create_window_function sqlite3_api->create_window_function
/* Version 3.26.0 and later */
#define sqlite3_normalized_sql sqlite3_api->normalized_sql
/* Version 3.28.0 and later */
#define sqlite3_stmt_isexplain sqlite3_api->isexplain
#define sqlite3_value_frombind sqlite3_api->frombind
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
/* This case when the file really is being compiled as a loadable
** extension */
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
# define SQLITE_EXTENSION_INIT3 \
extern const sqlite3_api_routines *sqlite3_api;
#else
/* This case when the file is being statically linked into the
** application */
# define SQLITE_EXTENSION_INIT1 /*no-op*/
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
# define SQLITE_EXTENSION_INIT3 /*no-op*/
#endif
#endif /* SQLITE3EXT_H */

View file

@ -363,7 +363,7 @@ BOOST_AUTO_TEST_CASE(support_spend_test)
uint32_t prevout = 0;
CMutableTransaction tx;
tx.nVersion = CTransaction::CURRENT_VERSION;
tx.nLockTime = 1 << 31; // Disable BIP68
tx.nLockTime = 1U << 31; // Disable BIP68
tx.vin.resize(2);
tx.vout.resize(1);
tx.vin[0].prevout.hash = tx3.GetHash();
@ -447,7 +447,7 @@ BOOST_AUTO_TEST_CASE(claimtrie_update_test)
CMutableTransaction tx;
tx.nVersion = CTransaction::CURRENT_VERSION;
tx.nLockTime = 1 << 31; // Disable BIP68
tx.nLockTime = 1U << 31; // Disable BIP68
tx.vin.resize(2);
tx.vout.resize(1);
tx.vin[0].prevout.hash = tx8.GetHash();
@ -530,7 +530,7 @@ BOOST_AUTO_TEST_CASE(get_claim_by_id_test)
CClaimValue claimValue;
std::string claimName;
BOOST_CHECK(getClaimById(claimId, claimName, &claimValue));
BOOST_CHECK(fixture.getClaimById(claimId, claimName, claimValue));
BOOST_CHECK_EQUAL(claimName, name);
BOOST_CHECK_EQUAL(claimValue.claimId, claimId);
@ -541,14 +541,14 @@ BOOST_AUTO_TEST_CASE(get_claim_by_id_test)
uint160 claimId2 = ClaimIdHash(tx2.GetHash(), 0);
fixture.IncrementBlocks(1);
BOOST_CHECK(getClaimById(claimId2, claimName, &claimValue));
BOOST_CHECK(fixture.getClaimById(claimId2, claimName, claimValue));
BOOST_CHECK_EQUAL(claimName, name);
BOOST_CHECK_EQUAL(claimValue.claimId, claimId2);
CMutableTransaction u1 = fixture.MakeUpdate(tx1, name, "updated one", claimId, 1);
fixture.IncrementBlocks(1);
BOOST_CHECK(getClaimById(claimId, claimName, &claimValue));
BOOST_CHECK(fixture.getClaimById(claimId, claimName, claimValue));
BOOST_CHECK_EQUAL(claimName, name);
BOOST_CHECK_EQUAL(claimValue.claimId, claimId);
BOOST_CHECK_EQUAL(claimValue.nAmount, 1);
@ -556,24 +556,24 @@ BOOST_AUTO_TEST_CASE(get_claim_by_id_test)
fixture.Spend(u1);
fixture.IncrementBlocks(1);
BOOST_CHECK(!getClaimById(claimId, claimName, &claimValue));
BOOST_CHECK(!fixture.getClaimById(claimId, claimName, claimValue));
fixture.DecrementBlocks(3);
CClaimValue claimValue2;
claimName = "";
BOOST_CHECK(!getClaimById(claimId2, claimName, &claimValue2));
BOOST_CHECK(!fixture.getClaimById(claimId2, claimName, claimValue2));
BOOST_CHECK(claimName != name);
BOOST_CHECK(claimValue2.claimId != claimId2);
BOOST_CHECK(getClaimById(claimId, claimName, &claimValue));
BOOST_CHECK(fixture.getClaimById(claimId, claimName, claimValue));
BOOST_CHECK_EQUAL(claimName, name);
BOOST_CHECK_EQUAL(claimValue.claimId, claimId);
fixture.DecrementBlocks(2);
claimName = "";
BOOST_CHECK(!getClaimById(claimId, claimName, &claimValue2));
BOOST_CHECK(!fixture.getClaimById(claimId, claimName, claimValue2));
BOOST_CHECK(claimName != name);
BOOST_CHECK(claimValue2.claimId != claimId);
}
@ -1816,7 +1816,7 @@ BOOST_AUTO_TEST_CASE(get_claim_by_id_test_2)
CClaimValue claimValue;
std::string claimName;
BOOST_CHECK(getClaimById(claimId, claimName, &claimValue));
BOOST_CHECK(fixture.getClaimById(claimId, claimName, claimValue));
BOOST_CHECK_EQUAL(claimName, name);
BOOST_CHECK_EQUAL(claimValue.claimId, claimId);
@ -1824,16 +1824,16 @@ BOOST_AUTO_TEST_CASE(get_claim_by_id_test_2)
CMutableTransaction txb = fixture.Spend(txx);
fixture.IncrementBlocks(1);
BOOST_CHECK(!getClaimById(claimId, claimName, &claimValue));
BOOST_CHECK(!getClaimById(claimIdx, claimName, &claimValue));
BOOST_CHECK(!fixture.getClaimById(claimId, claimName, claimValue));
BOOST_CHECK(!fixture.getClaimById(claimIdx, claimName, claimValue));
fixture.DecrementBlocks(1);
BOOST_CHECK(getClaimById(claimId, claimName, &claimValue));
BOOST_CHECK(fixture.getClaimById(claimId, claimName, claimValue));
BOOST_CHECK_EQUAL(claimName, name);
BOOST_CHECK_EQUAL(claimValue.claimId, claimId);
// this test fails
BOOST_CHECK(getClaimById(claimIdx, claimName, &claimValue));
BOOST_CHECK(fixture.getClaimById(claimIdx, claimName, claimValue));
BOOST_CHECK_EQUAL(claimName, name);
BOOST_CHECK_EQUAL(claimValue.claimId, claimIdx);
}
@ -1852,18 +1852,19 @@ BOOST_AUTO_TEST_CASE(update_on_support2_test)
CMutableTransaction s2 = fixture.MakeSupport(fixture.GetCoinbase(), tx1, name, 1);
fixture.IncrementBlocks(1);
auto it = pclaimTrie->find(name);
BOOST_CHECK(it);
BOOST_CHECK_EQUAL(it->nHeightOfLastTakeover, height + 1);
uint160 claimId;
int lastTakeover;
BOOST_CHECK(fixture.getLastTakeoverForName(name, claimId, lastTakeover));
BOOST_CHECK_EQUAL(lastTakeover, height + 1);
BOOST_CHECK_EQUAL(ClaimIdHash(tx1.GetHash(), 0), claimId);
fixture.Spend(s1);
fixture.Spend(s2);
CMutableTransaction u1 = fixture.MakeUpdate(tx1, name, value, ClaimIdHash(tx1.GetHash(), 0), 3);
fixture.IncrementBlocks(1);
it = pclaimTrie->find(name);
BOOST_CHECK(it);
BOOST_CHECK_EQUAL(it->nHeightOfLastTakeover, height + 1);
BOOST_CHECK(fixture.getLastTakeoverForName(name, claimId, lastTakeover));
BOOST_CHECK_EQUAL(lastTakeover, height + 1);
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -17,29 +17,29 @@ public:
{
}
using CClaimTrieCacheBase::insertSupportIntoMap;
using CClaimTrieCacheBase::removeSupportFromMap;
using CClaimTrieCacheBase::insertClaimIntoTrie;
using CClaimTrieCacheBase::removeClaimFromTrie;
void insert(const std::string& key, CClaimTrieData&& data)
bool insertClaimIntoTrie(const std::string& key, const CClaimValue& data)
{
nodesToAddOrUpdate.insert(key, std::move(data));
return addClaim(key, data.outPoint, data.claimId, data.nAmount, data.nHeight, {});
}
bool erase(const std::string& key)
{
return nodesToAddOrUpdate.erase(key);
bool removeClaimFromTrie(const std::string& key, const COutPoint& p) {
int validHeight;
std::string nodeName;
auto ret = removeClaim(ClaimIdHash(p.hash, p.n), nodeName, validHeight);
assert(!ret || nodeName == key);
return ret;
}
int cacheSize()
{
return nodesToAddOrUpdate.height();
bool insertSupportIntoMap(const std::string& key, const CSupportValue& value) {
return addSupport(key, value.outPoint, value.nAmount, value.supportedClaimId, value.nHeight, {});
}
CClaimTrie::iterator getCache(const std::string& key)
{
return nodesToAddOrUpdate.find(key);
bool removeSupportFromMap(const std::string& key, const COutPoint& p) {
int validHeight;
std::string nodeName;
auto ret = removeSupport(p, nodeName, validHeight);
assert(!ret || nodeName == key);
return ret;
}
};
@ -53,13 +53,12 @@ BOOST_AUTO_TEST_CASE(merkle_hash_single_test)
BOOST_CHECK_EQUAL(one, cc.getMerkleHash());
// we cannot have leaf root node
auto it = cc.getCache("");
BOOST_CHECK(!it);
auto it = cc.getClaimsForName("");
BOOST_CHECK_EQUAL(it.claimsNsupports.size(), 0U);
}
BOOST_AUTO_TEST_CASE(merkle_hash_multiple_test)
{
CClaimValue unused;
uint256 hash0(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
uint160 hash160;
CMutableTransaction tx1 = BuildTransaction(hash0);
@ -90,19 +89,19 @@ BOOST_AUTO_TEST_CASE(merkle_hash_multiple_test)
BOOST_CHECK(pclaimTrie->empty());
CClaimTrieCacheTest ntState(pclaimTrie);
ntState.insertClaimIntoTrie(std::string("test"), CClaimValue(tx1OutPoint, hash160, 50, 100, 200), true);
ntState.insertClaimIntoTrie(std::string("test2"), CClaimValue(tx2OutPoint, hash160, 50, 100, 200), true);
ntState.insertClaimIntoTrie(std::string("test"), CClaimValue(tx1OutPoint, hash160, 50, 100, 200));
ntState.insertClaimIntoTrie(std::string("test2"), CClaimValue(tx2OutPoint, hash160, 50, 100, 200));
BOOST_CHECK(pclaimTrie->empty());
BOOST_CHECK(!ntState.empty());
BOOST_CHECK(ntState.getTotalClaimsInTrie() > 0);
BOOST_CHECK_EQUAL(ntState.getMerkleHash(), hash1);
ntState.insertClaimIntoTrie(std::string("test"), CClaimValue(tx3OutPoint, hash160, 50, 101, 201), true);
ntState.insertClaimIntoTrie(std::string("test"), CClaimValue(tx3OutPoint, hash160, 50, 101, 201));
BOOST_CHECK_EQUAL(ntState.getMerkleHash(), hash1);
ntState.insertClaimIntoTrie(std::string("tes"), CClaimValue(tx4OutPoint, hash160, 50, 100, 200), true);
ntState.insertClaimIntoTrie(std::string("tes"), CClaimValue(tx4OutPoint, hash160, 50, 100, 200));
BOOST_CHECK_EQUAL(ntState.getMerkleHash(), hash2);
ntState.insertClaimIntoTrie(std::string("testtesttesttest"), CClaimValue(tx5OutPoint, hash160, 50, 100, 200), true);
ntState.removeClaimFromTrie(std::string("testtesttesttest"), tx5OutPoint, unused, true);
ntState.insertClaimIntoTrie(std::string("testtesttesttest"), CClaimValue(tx5OutPoint, hash160, 50, 100, 200));
ntState.removeClaimFromTrie(std::string("testtesttesttest"), tx5OutPoint);
BOOST_CHECK_EQUAL(ntState.getMerkleHash(), hash2);
ntState.flush();
@ -111,16 +110,16 @@ BOOST_AUTO_TEST_CASE(merkle_hash_multiple_test)
BOOST_CHECK(ntState.checkConsistency());
CClaimTrieCacheTest ntState1(pclaimTrie);
ntState1.removeClaimFromTrie(std::string("test"), tx1OutPoint, unused, true);
ntState1.removeClaimFromTrie(std::string("test2"), tx2OutPoint, unused, true);
ntState1.removeClaimFromTrie(std::string("test"), tx3OutPoint, unused, true);
ntState1.removeClaimFromTrie(std::string("tes"), tx4OutPoint, unused, true);
ntState1.removeClaimFromTrie(std::string("test"), tx1OutPoint);
ntState1.removeClaimFromTrie(std::string("test2"), tx2OutPoint);
ntState1.removeClaimFromTrie(std::string("test"), tx3OutPoint);
ntState1.removeClaimFromTrie(std::string("tes"), tx4OutPoint);
BOOST_CHECK_EQUAL(ntState1.getMerkleHash(), hash0);
CClaimTrieCacheTest ntState2(pclaimTrie);
ntState2.insertClaimIntoTrie(std::string("abab"), CClaimValue(tx6OutPoint, hash160, 50, 100, 200), true);
ntState2.removeClaimFromTrie(std::string("test"), tx1OutPoint, unused, true);
ntState2.insertClaimIntoTrie(std::string("abab"), CClaimValue(tx6OutPoint, hash160, 50, 100, 200));
ntState2.removeClaimFromTrie(std::string("test"), tx1OutPoint);
BOOST_CHECK_EQUAL(ntState2.getMerkleHash(), hash3);
@ -131,7 +130,7 @@ BOOST_AUTO_TEST_CASE(merkle_hash_multiple_test)
BOOST_CHECK(ntState2.checkConsistency());
CClaimTrieCacheTest ntState3(pclaimTrie);
ntState3.insertClaimIntoTrie(std::string("test"), CClaimValue(tx1OutPoint, hash160, 50, 100, 200), true);
ntState3.insertClaimIntoTrie(std::string("test"), CClaimValue(tx1OutPoint, hash160, 50, 100, 200));
BOOST_CHECK_EQUAL(ntState3.getMerkleHash(), hash4);
ntState3.flush();
BOOST_CHECK(!pclaimTrie->empty());
@ -139,7 +138,7 @@ BOOST_AUTO_TEST_CASE(merkle_hash_multiple_test)
BOOST_CHECK(ntState3.checkConsistency());
CClaimTrieCacheTest ntState4(pclaimTrie);
ntState4.removeClaimFromTrie(std::string("abab"), tx6OutPoint, unused, true);
ntState4.removeClaimFromTrie(std::string("abab"), tx6OutPoint);
BOOST_CHECK_EQUAL(ntState4.getMerkleHash(), hash2);
ntState4.flush();
BOOST_CHECK(!pclaimTrie->empty());
@ -147,7 +146,7 @@ BOOST_AUTO_TEST_CASE(merkle_hash_multiple_test)
BOOST_CHECK(ntState4.checkConsistency());
CClaimTrieCacheTest ntState5(pclaimTrie);
ntState5.removeClaimFromTrie(std::string("test"), tx3OutPoint, unused, true);
ntState5.removeClaimFromTrie(std::string("test"), tx3OutPoint);
BOOST_CHECK_EQUAL(ntState5.getMerkleHash(), hash2);
ntState5.flush();
@ -156,7 +155,7 @@ BOOST_AUTO_TEST_CASE(merkle_hash_multiple_test)
BOOST_CHECK(ntState5.checkConsistency());
CClaimTrieCacheTest ntState6(pclaimTrie);
ntState6.insertClaimIntoTrie(std::string("test"), CClaimValue(tx3OutPoint, hash160, 50, 101, 201), true);
ntState6.insertClaimIntoTrie(std::string("test"), CClaimValue(tx3OutPoint, hash160, 50, 101, 201));
BOOST_CHECK_EQUAL(ntState6.getMerkleHash(), hash2);
ntState6.flush();
@ -165,10 +164,10 @@ BOOST_AUTO_TEST_CASE(merkle_hash_multiple_test)
BOOST_CHECK(ntState6.checkConsistency());
CClaimTrieCacheTest ntState7(pclaimTrie);
ntState7.removeClaimFromTrie(std::string("test"), tx3OutPoint, unused, true);
ntState7.removeClaimFromTrie(std::string("test"), tx1OutPoint, unused, true);
ntState7.removeClaimFromTrie(std::string("tes"), tx4OutPoint, unused, true);
ntState7.removeClaimFromTrie(std::string("test2"), tx2OutPoint, unused, true);
ntState7.removeClaimFromTrie(std::string("test"), tx3OutPoint);
ntState7.removeClaimFromTrie(std::string("test"), tx1OutPoint);
ntState7.removeClaimFromTrie(std::string("tes"), tx4OutPoint);
ntState7.removeClaimFromTrie(std::string("test2"), tx2OutPoint);
BOOST_CHECK_EQUAL(ntState7.getMerkleHash(), hash0);
ntState7.flush();
@ -192,7 +191,7 @@ BOOST_AUTO_TEST_CASE(basic_insertion_info_test)
int height = 0;
int validHeight = 0;
CClaimValue claimVal(claimOutPoint, claimId, amount, height, validHeight);
ctc.insertClaimIntoTrie("test", claimVal, true);
ctc.insertClaimIntoTrie("test", claimVal);
// try getClaimsForName, effectiveAmount, getInfoForName
auto res = ctc.getClaimsForName("test");
@ -213,7 +212,7 @@ BOOST_AUTO_TEST_CASE(basic_insertion_info_test)
COutPoint supportOutPoint(tx2.GetHash(), 0);
CSupportValue support(supportOutPoint, claimId, supportAmount, height, validHeight);
ctc.insertSupportIntoMap("test", support, false);
ctc.insertSupportIntoMap("test", support);
auto res1 = ctc.getClaimsForName("test");
BOOST_CHECK_EQUAL(res1.claimsNsupports.size(), 1);
@ -223,71 +222,50 @@ BOOST_AUTO_TEST_CASE(basic_insertion_info_test)
BOOST_CHECK_EQUAL(20, res1.claimsNsupports[0].effectiveAmount);
}
BOOST_AUTO_TEST_CASE(recursive_prune_test)
{
CClaimTrieCacheTest cc(pclaimTrie);
BOOST_CHECK_EQUAL(0, cc.cacheSize());
COutPoint outpoint;
uint160 claimId;
CAmount amount(20);
int height = 0;
int validAtHeight = 0;
CClaimValue test_claim(outpoint, claimId, amount, height, validAtHeight);
CClaimTrieData data;
// base node has a claim, so it should not be pruned
data.insertClaim(test_claim);
cc.insert("", std::move(data));
// node 1 has a claim so it should not be pruned
data.insertClaim(test_claim);
// set this just to make sure we get the right CClaimTrieNode back
data.nHeightOfLastTakeover = 10;
cc.insert("t", std::move(data));
//node 2 does not have a claim so it should be pruned
// thus we should find pruned node 1 in cache
cc.insert("te", CClaimTrieData{});
BOOST_CHECK(cc.erase("te"));
BOOST_CHECK_EQUAL(2, cc.cacheSize());
auto it = cc.getCache("t");
BOOST_CHECK_EQUAL(10, it->nHeightOfLastTakeover);
BOOST_CHECK_EQUAL(1, it->claims.size());
BOOST_CHECK_EQUAL(2, cc.cacheSize());
cc.insert("te", CClaimTrieData{});
// erasing "t" will make it weak
BOOST_CHECK(cc.erase("t"));
// so now we erase "e" as well as "t"
BOOST_CHECK(cc.erase("te"));
// we have claim in root
BOOST_CHECK_EQUAL(1, cc.cacheSize());
BOOST_CHECK(cc.erase(""));
BOOST_CHECK_EQUAL(0, cc.cacheSize());
}
BOOST_AUTO_TEST_CASE(iteratetrie_test)
{
BOOST_CHECK(pclaimTrie->empty());
CClaimTrieCacheTest ctc(pclaimTrie);
uint256 hash0(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
CMutableTransaction tx1 = BuildTransaction(hash0);
const uint256 txhash = tx1.GetHash();
CClaimValue claimVal(COutPoint(txhash, 0), ClaimIdHash(txhash, 0), CAmount(10), 0, 0);
ctc.insertClaimIntoTrie("test", claimVal, true);
BOOST_CHECK(ctc.flush());
auto hit = pclaimTrie->find("");
BOOST_CHECK(hit);
BOOST_CHECK_EQUAL(hit.children().size(), 1U);
BOOST_CHECK(hit = pclaimTrie->find("test"));
BOOST_CHECK_EQUAL(hit.children().size(), 0U);
BOOST_CHECK_EQUAL(hit.data().claims.size(), 1);
}
//BOOST_AUTO_TEST_CASE(recursive_prune_test)
//{
// CClaimTrieCacheTest cc(pclaimTrie);
// BOOST_CHECK_EQUAL(0, cc.getTotalClaimsInTrie());
//
// COutPoint outpoint;
// uint160 claimId;
// CAmount amount(20);
// int height = 0;
// int validAtHeight = 0;
// CClaimValue test_claim(outpoint, claimId, amount, height, validAtHeight);
//
// CClaimTrieData data;
// // base node has a claim, so it should not be pruned
// data.insertClaim(test_claim);
// cc.insert("", std::move(data));
//
// // node 1 has a claim so it should not be pruned
// data.insertClaim(test_claim);
// // set this just to make sure we get the right CClaimTrieNode back
// data.nHeightOfLastTakeover = 10;
// cc.insert("t", std::move(data));
//
// //node 2 does not have a claim so it should be pruned
// // thus we should find pruned node 1 in cache
// cc.insert("te", CClaimTrieData{});
//
// BOOST_CHECK(cc.erase("te"));
// BOOST_CHECK_EQUAL(2, cc.cacheSize());
// auto it = cc.getCache("t");
// BOOST_CHECK_EQUAL(10, it->nHeightOfLastTakeover);
// BOOST_CHECK_EQUAL(1, it->claims.size());
// BOOST_CHECK_EQUAL(2, cc.cacheSize());
//
// cc.insert("te", CClaimTrieData{});
// // erasing "t" will make it weak
// BOOST_CHECK(cc.erase("t"));
// // so now we erase "e" as well as "t"
// BOOST_CHECK(cc.erase("te"));
// // we have claim in root
// BOOST_CHECK_EQUAL(1, cc.cacheSize());
// BOOST_CHECK(cc.erase(""));
// BOOST_CHECK_EQUAL(0, cc.cacheSize());
//}
BOOST_AUTO_TEST_CASE(trie_stays_consistent_test)
{
@ -300,14 +278,13 @@ BOOST_AUTO_TEST_CASE(trie_stays_consistent_test)
CClaimValue value;
for (auto& name: names)
BOOST_CHECK(cache.insertClaimIntoTrie(name, value, false));
BOOST_CHECK(cache.insertClaimIntoTrie(name, value));
cache.flush();
BOOST_CHECK(cache.checkConsistency());
for (auto& name: names) {
CClaimValue temp;
BOOST_CHECK(cache.removeClaimFromTrie(name, COutPoint(), temp, false));
BOOST_CHECK(cache.removeClaimFromTrie(name, COutPoint()));
cache.flush();
BOOST_CHECK(cache.checkConsistency());
}
@ -332,37 +309,41 @@ BOOST_AUTO_TEST_CASE(takeover_workaround_triggers)
CClaimValue value;
value.nHeight = 1;
BOOST_CHECK(cache.insertClaimIntoTrie("a", value, true));
BOOST_CHECK(cache.insertClaimIntoTrie("b", value, true));
BOOST_CHECK(cache.insertClaimIntoTrie("c", value, true));
BOOST_CHECK(cache.insertClaimIntoTrie("aa", value, true));
BOOST_CHECK(cache.insertClaimIntoTrie("bb", value, true));
BOOST_CHECK(cache.insertClaimIntoTrie("cc", value, true));
BOOST_CHECK(cache.insertSupportIntoMap("aa", CSupportValue(), false));
BOOST_CHECK(cache.insertClaimIntoTrie("a", value));
BOOST_CHECK(cache.insertClaimIntoTrie("b", value));
BOOST_CHECK(cache.insertClaimIntoTrie("c", value));
BOOST_CHECK(cache.insertClaimIntoTrie("aa", value));
BOOST_CHECK(cache.insertClaimIntoTrie("bb", value));
BOOST_CHECK(cache.insertClaimIntoTrie("cc", value));
BOOST_CHECK(cache.insertSupportIntoMap("aa", CSupportValue()));
BOOST_CHECK(cache.incrementBlock(icu, ecu, isu, esu, thu));
BOOST_CHECK(cache.flush());
BOOST_CHECK(cache.incrementBlock(icu, ecu, isu, esu, thu));
BOOST_CHECK_EQUAL(0, cache.cacheSize());
BOOST_CHECK_EQUAL(0, cache.getTotalNamesInTrie());
CSupportValue temp;
BOOST_CHECK(cache.insertSupportIntoMap("bb", temp, false));
BOOST_CHECK(!cache.getCache("aa"));
BOOST_CHECK(cache.removeSupportFromMap("aa", COutPoint(), temp, false));
CClaimValue cv;
BOOST_CHECK(cache.insertSupportIntoMap("bb", temp));
BOOST_CHECK(!cache.getInfoForName("aa", cv));
BOOST_CHECK(cache.removeSupportFromMap("aa", COutPoint()));
BOOST_CHECK(cache.removeClaimFromTrie("aa", COutPoint(), value, false));
BOOST_CHECK(cache.removeClaimFromTrie("bb", COutPoint(), value, false));
BOOST_CHECK(cache.removeClaimFromTrie("cc", COutPoint(), value, false));
BOOST_CHECK(cache.removeClaimFromTrie("aa", COutPoint()));
BOOST_CHECK(cache.removeClaimFromTrie("bb", COutPoint()));
BOOST_CHECK(cache.removeClaimFromTrie("cc", COutPoint()));
BOOST_CHECK(cache.insertClaimIntoTrie("aa", value, true));
BOOST_CHECK(cache.insertClaimIntoTrie("bb", value, true));
BOOST_CHECK(cache.insertClaimIntoTrie("cc", value, true));
BOOST_CHECK(cache.insertClaimIntoTrie("aa", value));
BOOST_CHECK(cache.insertClaimIntoTrie("bb", value));
BOOST_CHECK(cache.insertClaimIntoTrie("cc", value));
BOOST_CHECK(cache.incrementBlock(icu, ecu, isu, esu, thu));
BOOST_CHECK_EQUAL(3, cache.getCache("aa")->nHeightOfLastTakeover);
BOOST_CHECK_EQUAL(3, cache.getCache("bb")->nHeightOfLastTakeover);
BOOST_CHECK_EQUAL(1, cache.getCache("cc")->nHeightOfLastTakeover);
BOOST_CHECK(cache.getInfoForName("aa", cv));
BOOST_CHECK_EQUAL(3, cv.nValidAtHeight);
BOOST_CHECK(cache.getInfoForName("bb", cv));
BOOST_CHECK_EQUAL(3, cv.nValidAtHeight);
BOOST_CHECK(cache.getInfoForName("cc", cv));
BOOST_CHECK_EQUAL(1, cv.nValidAtHeight);
}
BOOST_AUTO_TEST_CASE(verify_basic_serialization)
@ -384,67 +365,4 @@ BOOST_AUTO_TEST_CASE(verify_basic_serialization)
BOOST_CHECK_EQUAL(cv, cv2);
}
BOOST_AUTO_TEST_CASE(claimtrienode_serialize_unserialize)
{
CDataStream ss(SER_DISK, 0);
uint160 hash160;
CClaimTrieData n1;
CClaimTrieData n2;
CClaimValue throwaway;
ss << n1;
ss >> n2;
BOOST_CHECK_EQUAL(n1, n2);
CClaimValue v1(COutPoint(uint256S("0000000000000000000000000000000000000000000000000000000000000001"), 0), hash160, 50, 0, 100);
CClaimValue v2(COutPoint(uint256S("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"), 1), hash160, 100, 1, 101);
n1.insertClaim(v1);
BOOST_CHECK(n1 != n2);
ss << n1;
ss >> n2;
BOOST_CHECK_EQUAL(n1, n2);
n1.insertClaim(v2);
BOOST_CHECK(n1 != n2);
ss << n1;
ss >> n2;
BOOST_CHECK_EQUAL(n1, n2);
n1.removeClaim(v1.outPoint, throwaway);
BOOST_CHECK(n1 != n2);
ss << n1;
ss >> n2;
BOOST_CHECK_EQUAL(n1, n2);
n1.removeClaim(v2.outPoint, throwaway);
BOOST_CHECK(n1 != n2);
ss << n1;
ss >> n2;
BOOST_CHECK_EQUAL(n1, n2);
}
BOOST_AUTO_TEST_CASE(claimtrienode_remove_invalid_claim)
{
uint160 hash160;
CClaimTrieData n1;
CClaimTrieData n2;
CClaimValue throwaway;
CClaimValue v1(COutPoint(uint256S("0000000000000000000000000000000000000000000000000000000000000001"), 0), hash160, 50, 0, 100);
CClaimValue v2(COutPoint(uint256S("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"), 1), hash160, 100, 1, 101);
n1.insertClaim(v1);
n2.insertClaim(v2);
bool invalidClaim = n2.removeClaim(v1.outPoint, throwaway);
BOOST_CHECK_EQUAL(invalidClaim, false);
invalidClaim = n1.removeClaim(v2.outPoint, throwaway);
BOOST_CHECK_EQUAL(invalidClaim, false);
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -276,7 +276,7 @@ BOOST_AUTO_TEST_CASE(hardfork_disk_test)
BOOST_CHECK_EQUAL(fixture.expirationTime(), 3);
fixture.IncrementBlocks(7, true);
BOOST_CHECK_EQUAL(fixture.expirationTime(), 6);
fixture.ReadFromDisk(chainActive.Tip());
fixture.ValidateTipMatches(chainActive.Tip());
BOOST_CHECK_EQUAL(fixture.expirationTime(), 6);
// Create a claim and support 1 block before the fork height that will expire after the fork height.
@ -287,7 +287,7 @@ BOOST_AUTO_TEST_CASE(hardfork_disk_test)
CMutableTransaction s1 = fixture.MakeSupport(fixture.GetCoinbase(), tx1, "test", 1);
fixture.IncrementBlocks(1);
fixture.ReadFromDisk(chainActive.Tip());
fixture.ValidateTipMatches(chainActive.Tip());
BOOST_CHECK_EQUAL(fixture.expirationTime(), 3);
fixture.IncrementBlocks(1);
BOOST_CHECK_EQUAL(fixture.expirationTime(), 6);
@ -308,7 +308,7 @@ BOOST_AUTO_TEST_CASE(hardfork_disk_test)
CMutableTransaction tx2 = fixture.MakeClaim(fixture.GetCoinbase(),"test2","one",1);
CMutableTransaction s2 = fixture.MakeSupport(fixture.GetCoinbase(),tx2,"test2",1);
fixture.IncrementBlocks(1);
fixture.ReadFromDisk(chainActive.Tip());
fixture.ValidateTipMatches(chainActive.Tip());
CMutableTransaction u2 = fixture.MakeUpdate(tx2, "test2", "two", ClaimIdHash(tx2.GetHash(), 0), 1);
// increment to fork
fixture.IncrementBlocks(2);
@ -762,7 +762,7 @@ BOOST_AUTO_TEST_CASE(get_claim_by_id_test_3)
CClaimValue claimValue;
std::string claimName;
BOOST_CHECK(getClaimById(claimId, claimName, &claimValue));
BOOST_CHECK(fixture.getClaimById(claimId, claimName, claimValue));
BOOST_CHECK_EQUAL(claimName, name);
BOOST_CHECK_EQUAL(claimValue.claimId, claimId);
// make second claim with activation delay 1
@ -772,14 +772,14 @@ BOOST_AUTO_TEST_CASE(get_claim_by_id_test_3)
fixture.IncrementBlocks(1);
// second claim is not activated yet, but can still access by claim id
BOOST_CHECK(fixture.is_best_claim(name, tx1));
BOOST_CHECK(getClaimById(claimId2, claimName, &claimValue));
BOOST_CHECK(fixture.getClaimById(claimId2, claimName, claimValue));
BOOST_CHECK_EQUAL(claimName, name);
BOOST_CHECK_EQUAL(claimValue.claimId, claimId2);
fixture.IncrementBlocks(1);
// second claim has activated
BOOST_CHECK(fixture.is_best_claim(name, tx2));
BOOST_CHECK(getClaimById(claimId2, claimName, &claimValue));
BOOST_CHECK(fixture.getClaimById(claimId2, claimName, claimValue));
BOOST_CHECK_EQUAL(claimName, name);
BOOST_CHECK_EQUAL(claimValue.claimId, claimId2);
@ -788,14 +788,14 @@ BOOST_AUTO_TEST_CASE(get_claim_by_id_test_3)
// second claim has been deactivated via decrement
// should still be accesible via claim id
BOOST_CHECK(fixture.is_best_claim(name, tx1));
BOOST_CHECK(getClaimById(claimId2, claimName, &claimValue));
BOOST_CHECK(fixture.getClaimById(claimId2, claimName, claimValue));
BOOST_CHECK_EQUAL(claimName, name);
BOOST_CHECK_EQUAL(claimValue.claimId, claimId2);
fixture.IncrementBlocks(1);
// second claim should have been re activated via increment
BOOST_CHECK(fixture.is_best_claim(name, tx2));
BOOST_CHECK(getClaimById(claimId2, claimName, &claimValue));
BOOST_CHECK(fixture.getClaimById(claimId2, claimName, claimValue));
BOOST_CHECK_EQUAL(claimName, name);
BOOST_CHECK_EQUAL(claimValue.claimId, claimId2);
}

View file

@ -268,7 +268,6 @@ void ClaimTrieChainFixture::IncrementBlocks(int num_blocks, bool mark)
if (mark)
marks.push_back(chainActive.Height());
clear(); // clears the internal cache
for (int i = 0; i < num_blocks; ++i) {
CScript coinbase_scriptpubkey;
coinbase_scriptpubkey << CScriptNum(chainActive.Height());
@ -286,7 +285,6 @@ void ClaimTrieChainFixture::IncrementBlocks(int num_blocks, bool mark)
// disconnect i blocks from tip
void ClaimTrieChainFixture::DecrementBlocks(int num_blocks)
{
clear(); // clears the internal cache
CValidationState state;
{
LOCK(cs_main);
@ -309,52 +307,32 @@ void ClaimTrieChainFixture::DecrementBlocks()
DecrementBlocks(chainActive.Height() - mark);
}
template <typename K>
bool ClaimTrieChainFixture::keyTypeEmpty(uint8_t keyType)
{
boost::scoped_ptr<CDBIterator> pcursor(base->db->NewIterator());
pcursor->SeekToFirst();
while (pcursor->Valid()) {
std::pair<uint8_t, K> key;
if (pcursor->GetKey(key))
if (key.first == keyType)
return false;
pcursor->Next();
}
return true;
}
bool ClaimTrieChainFixture::queueEmpty()
{
for (const auto& claimQueue: claimQueueCache)
if (!claimQueue.second.empty())
return false;
return keyTypeEmpty<int>(CLAIM_QUEUE_ROW);
int64_t count;
base->_db << "SELECT COUNT(*) FROM claims WHERE validHeight >= ?" << nNextHeight >> count;
return count == 0;
}
bool ClaimTrieChainFixture::expirationQueueEmpty()
{
for (const auto& expirationQueue: expirationQueueCache)
if (!expirationQueue.second.empty())
return false;
return keyTypeEmpty<int>(CLAIM_EXP_QUEUE_ROW);
int64_t count;
base->_db << "SELECT COUNT(*) FROM claims WHERE expirationHeight >= ?" << nNextHeight >> count;
return count == 0;
}
bool ClaimTrieChainFixture::supportEmpty()
{
for (const auto& entry: supportCache)
if (!entry.second.empty())
return false;
return supportCache.empty() && keyTypeEmpty<std::string>(SUPPORT);
int64_t count;
base->_db << "SELECT COUNT(*) FROM supports" >> count;
return count == 0;
}
bool ClaimTrieChainFixture::supportQueueEmpty()
{
for (const auto& support: supportQueueCache)
if (!support.second.empty())
return false;
return keyTypeEmpty<int>(SUPPORT_QUEUE_ROW);
int64_t count;
base->_db << "SELECT COUNT(*) FROM supports WHERE validHeight >= ?" << nNextHeight >> count;
return count == 0;
}
int ClaimTrieChainFixture::proportionalDelayFactor()
@ -413,7 +391,7 @@ boost::test_tools::predicate_result ClaimTrieChainFixture::best_claim_effective_
return true;
}
std::size_t ClaimTrieChainFixture::getTotalNamesInTrie() const
{
return base->getTotalNamesInTrie();
bool ClaimTrieChainFixture::getClaimById(const uint160 &claimId, std::string &name, CClaimValue &value) {
std::vector<unsigned char> claim(claimId.begin(), claimId.end());
return findNameForClaim(claim, value, name);
}

View file

@ -28,8 +28,6 @@
extern ::CChainState g_chainstate;
extern ::ArgsManager gArgs;
extern std::vector<std::string> random_strings(std::size_t count);
extern bool getClaimById(const uint160&, std::string&, CClaimValue*);
CMutableTransaction BuildTransaction(const uint256& prevhash);
CMutableTransaction BuildTransaction(const CTransaction& prev, uint32_t prevout=0, unsigned int numOutputs=1, int locktime=0);
@ -56,7 +54,7 @@ struct ClaimTrieChainFixture: public CClaimTrieCache
ClaimTrieChainFixture();
~ClaimTrieChainFixture();
~ClaimTrieChainFixture() override;
void setExpirationForkHeight(int targetMinusCurrent, int64_t preForkExpirationTime, int64_t postForkExpirationTime);
@ -105,6 +103,8 @@ struct ClaimTrieChainFixture: public CClaimTrieCache
int proportionalDelayFactor();
bool getClaimById(const uint160& claimId, std::string& name, CClaimValue& value);
// is a claim in queue
boost::test_tools::predicate_result is_claim_in_queue(const std::string& name, const CTransaction &tx);
@ -113,12 +113,6 @@ struct ClaimTrieChainFixture: public CClaimTrieCache
// check effective quantity of best claim
boost::test_tools::predicate_result best_claim_effective_amount_equals(const std::string& name, CAmount amount);
std::size_t getTotalNamesInTrie() const;
private:
template <typename K>
bool keyTypeEmpty(uint8_t keyType);
};
#endif // _CLAIMTRIEFIXTURE_H_

View file

@ -49,9 +49,7 @@ BOOST_AUTO_TEST_CASE(hash_includes_all_claims_single_test)
uint160 claimId = ClaimIdHash(tx1.GetHash(), 0);
CClaimTrieProof proof;
BOOST_CHECK(fixture.getProofForName("test", proof, [&claimId](const CClaimValue& claim) {
return claim.claimId == claimId;
}));
BOOST_CHECK(fixture.getProofForName("test", claimId, proof));
BOOST_CHECK(proof.hasValue);
BOOST_CHECK_EQUAL(proof.outPoint, outPoint);
auto claimHash = getValueHash(outPoint, proof.nHeightOfLastTakeover);
@ -79,9 +77,7 @@ BOOST_AUTO_TEST_CASE(hash_includes_all_claims_triple_test)
for (auto& claimSupports : fixture.getClaimsForName(name).claimsNsupports) {
CClaimTrieProof proof;
auto& claim = claimSupports.claim;
BOOST_CHECK(fixture.getProofForName(name, proof, [&claim](const CClaimValue& value) {
return claim.claimId == value.claimId;
}));
BOOST_CHECK(fixture.getProofForName(name, claim.claimId, proof));
BOOST_CHECK(proof.hasValue);
BOOST_CHECK_EQUAL(proof.outPoint, claim.outPoint);
uint256 claimHash = getValueHash(claim.outPoint, proof.nHeightOfLastTakeover);
@ -110,9 +106,7 @@ BOOST_AUTO_TEST_CASE(hash_includes_all_claims_branched_test)
for (auto& claimSupports : fixture.getClaimsForName(name).claimsNsupports) {
CClaimTrieProof proof;
auto& claim = claimSupports.claim;
BOOST_CHECK(fixture.getProofForName(name, proof, [&claim](const CClaimValue& value) {
return claim.claimId == value.claimId;
}));
BOOST_CHECK(fixture.getProofForName(name, claim.claimId, proof));
BOOST_CHECK(proof.hasValue);
BOOST_CHECK_EQUAL(proof.outPoint, claim.outPoint);
uint256 claimHash = getValueHash(claim.outPoint, proof.nHeightOfLastTakeover);
@ -121,6 +115,34 @@ BOOST_AUTO_TEST_CASE(hash_includes_all_claims_branched_test)
}
}
std::vector<std::string> random_strings(std::size_t count)
{
static auto& chrs = "0123456789"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
FastRandomContext frc(true);
std::unordered_set<std::string> strings;
strings.reserve(count);
while(strings.size() < count) {
auto length = frc.randrange(sizeof(chrs) - 2) + 1;
std::string s;
s.reserve(length);
while (length--)
s += chrs[frc.randrange(sizeof(chrs) - 1)];
strings.emplace(s);
}
std::vector<std::string> ret(strings.begin(), strings.end());
std::random_shuffle(ret.begin(), ret.end(), [&frc](std::size_t n) { return frc.randrange(n); });
return ret;
}
BOOST_AUTO_TEST_CASE(hash_claims_children_fuzzer_test)
{
ClaimTrieChainFixture fixture;
@ -145,9 +167,7 @@ BOOST_AUTO_TEST_CASE(hash_claims_children_fuzzer_test)
for (auto& claimSupports : fixture.getClaimsForName(name).claimsNsupports) {
CClaimTrieProof proof;
auto& claim = claimSupports.claim;
BOOST_CHECK(fixture.getProofForName(name, proof, [&claim](const CClaimValue& value) {
return claim.claimId == value.claimId;
}));
BOOST_CHECK(fixture.getProofForName(name, claim.claimId, proof));
BOOST_CHECK(proof.hasValue);
BOOST_CHECK_EQUAL(proof.outPoint, claim.outPoint);
uint256 claimHash = getValueHash(claim.outPoint, proof.nHeightOfLastTakeover);
@ -278,31 +298,31 @@ BOOST_AUTO_TEST_CASE(value_proof_test)
CClaimTrieProof proof;
BOOST_CHECK(fixture.getProofForName(sName1, proof));
BOOST_CHECK(fixture.getProofForName(sName1, ClaimIdHash(tx1.GetHash(), 0), proof));
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName1));
BOOST_CHECK_EQUAL(proof.outPoint, tx1OutPoint);
BOOST_CHECK(fixture.getProofForName(sName2, proof));
BOOST_CHECK(fixture.getProofForName(sName2, ClaimIdHash(tx2.GetHash(), 0), proof));
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName2));
BOOST_CHECK_EQUAL(proof.outPoint, tx2OutPoint);
BOOST_CHECK(fixture.getProofForName(sName3, proof));
BOOST_CHECK(fixture.getProofForName(sName3, ClaimIdHash(tx3.GetHash(), 0), proof));
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName3));
BOOST_CHECK_EQUAL(proof.outPoint, tx3OutPoint);
BOOST_CHECK(fixture.getProofForName(sName4, proof));
BOOST_CHECK(fixture.getProofForName(sName4, ClaimIdHash(tx4.GetHash(), 0), proof));
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName4));
BOOST_CHECK_EQUAL(proof.outPoint, tx4OutPoint);
BOOST_CHECK(fixture.getProofForName(sName5, proof));
BOOST_CHECK(fixture.getProofForName(sName5, ClaimIdHash(tx1.GetHash(), 0), proof));
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName5));
BOOST_CHECK_EQUAL(proof.hasValue, false);
BOOST_CHECK(fixture.getProofForName(sName6, proof));
BOOST_CHECK(fixture.getProofForName(sName6, ClaimIdHash({}, 0), proof));
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName6));
BOOST_CHECK_EQUAL(proof.hasValue, false);
BOOST_CHECK(fixture.getProofForName(sName7, proof));
BOOST_CHECK(fixture.getProofForName(sName7, ClaimIdHash(fixture.getMerkleHash(), 0), proof));
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName7));
BOOST_CHECK_EQUAL(proof.hasValue, false);
@ -314,34 +334,26 @@ BOOST_AUTO_TEST_CASE(value_proof_test)
BOOST_CHECK(fixture.getInfoForName(sName7, val));
BOOST_CHECK_EQUAL(val.outPoint, tx5OutPoint);
BOOST_CHECK(fixture.getProofForName(sName1, proof));
BOOST_CHECK(fixture.getProofForName(sName1, ClaimIdHash(tx1.GetHash(), 0), proof));
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName1));
BOOST_CHECK_EQUAL(proof.outPoint, tx1OutPoint);
BOOST_CHECK(fixture.getProofForName(sName2, proof));
BOOST_CHECK(fixture.getProofForName(sName2, ClaimIdHash(tx2.GetHash(), 0), proof));
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName2));
BOOST_CHECK_EQUAL(proof.outPoint, tx2OutPoint);
BOOST_CHECK(fixture.getProofForName(sName3, proof));
BOOST_CHECK(fixture.getProofForName(sName3, ClaimIdHash(tx3.GetHash(), 0), proof));
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName3));
BOOST_CHECK_EQUAL(proof.outPoint, tx3OutPoint);
BOOST_CHECK(fixture.getProofForName(sName4, proof));
BOOST_CHECK(fixture.getProofForName(sName4, ClaimIdHash(tx4.GetHash(), 0), proof));
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName4));
BOOST_CHECK_EQUAL(proof.outPoint, tx4OutPoint);
BOOST_CHECK(fixture.getProofForName(sName5, proof));
BOOST_CHECK(fixture.getProofForName(sName5, ClaimIdHash(tx5.GetHash(), 0), proof));
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName5));
BOOST_CHECK_EQUAL(proof.hasValue, false);
BOOST_CHECK(fixture.getProofForName(sName6, proof));
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName6));
BOOST_CHECK_EQUAL(proof.hasValue, false);
BOOST_CHECK(fixture.getProofForName(sName7, proof));
BOOST_CHECK(verify_proof(proof, chainActive.Tip()->hashClaimTrie, sName7));
BOOST_CHECK_EQUAL(proof.outPoint, tx5OutPoint);
fixture.DecrementBlocks();
BOOST_CHECK(pclaimTrie->empty());
BOOST_CHECK(fixture.queueEmpty());

View file

@ -93,8 +93,6 @@ BOOST_AUTO_TEST_CASE(claimtriebranching_normalization)
BOOST_CHECK(fixture.is_best_claim("normalizetest", tx1));
BOOST_CHECK(fixture.best_claim_effective_amount_equals("normalizetest", 3));
BOOST_CHECK(!pclaimTrie->find("normalizeTest"));
// Check equivalence of normalized claim names
BOOST_CHECK(fixture.is_best_claim("normalizetest", tx1)); // collapsed tx2
fixture.IncrementBlocks(1);
@ -192,7 +190,7 @@ BOOST_AUTO_TEST_CASE(claimtriecache_normalization)
CClaimValue lookupClaim;
std::string lookupName;
BOOST_CHECK(getClaimById(ClaimIdHash(tx2.GetHash(), 0), lookupName, &lookupClaim));
BOOST_CHECK(fixture.getClaimById(ClaimIdHash(tx2.GetHash(), 0), lookupName, lookupClaim));
CClaimValue nval1;
BOOST_CHECK(fixture.getInfoForName("amelie1", nval1));
// amélie is not found cause normalization still not appear
@ -219,12 +217,11 @@ BOOST_AUTO_TEST_CASE(claimtriecache_normalization)
BOOST_CHECK(ReadBlockFromDisk(block, pindex, Params().GetConsensus()));
BOOST_CHECK(g_chainstate.DisconnectBlock(block, pindex, coins, trieCache) == DisconnectResult::DISCONNECT_OK);
BOOST_CHECK(!trieCache.shouldNormalize());
BOOST_CHECK(!trieCache.spendClaim(name_normd, COutPoint(tx2.GetHash(), 0), currentHeight, amelieValidHeight));
BOOST_CHECK(trieCache.spendClaim(name_upper, COutPoint(tx2.GetHash(), 0), currentHeight, amelieValidHeight));
BOOST_CHECK(!trieCache.removeClaim(ClaimIdHash(tx2.GetHash(), 0), name_normd, amelieValidHeight));
BOOST_CHECK(trieCache.removeClaim(ClaimIdHash(tx2.GetHash(), 0), name_upper, amelieValidHeight));
BOOST_CHECK(!pclaimTrie->find(name));
BOOST_CHECK(trieCache.getInfoForName(name, nval1));
BOOST_CHECK(trieCache.addClaim(name, COutPoint(tx1.GetHash(), 0), ClaimIdHash(tx1.GetHash(), 0), CAmount(2), currentHeight + 1));
BOOST_CHECK(trieCache.addClaim(name, COutPoint(tx1.GetHash(), 0), ClaimIdHash(tx1.GetHash(), 0), CAmount(2), currentHeight + 1, {}));
BOOST_CHECK(trieCache.getInfoForName(name, nval1));
insertUndoType insertUndo;
claimQueueRowType expireUndo;
@ -233,10 +230,6 @@ BOOST_AUTO_TEST_CASE(claimtriecache_normalization)
std::vector<std::pair<std::string, int> > takeoverHeightUndo;
BOOST_CHECK(trieCache.incrementBlock(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo, takeoverHeightUndo));
BOOST_CHECK(trieCache.shouldNormalize());
// we cannot use getXXXForName cause they will normalized name
for (auto it = pclaimTrie->cbegin(); it != pclaimTrie->cend(); ++it)
BOOST_CHECK(it.key() != name);
}
BOOST_AUTO_TEST_CASE(undo_normalization_does_not_kill_claim_order)
@ -312,11 +305,11 @@ BOOST_AUTO_TEST_CASE(normalization_removal_test)
CClaimTrieCache cache(pclaimTrie);
int height = chainActive.Height() + 1;
cache.addClaim("AB", COutPoint(tx1.GetHash(), 0), ClaimIdHash(tx1.GetHash(), 0), 1, height);
cache.addClaim("Ab", COutPoint(tx2.GetHash(), 0), ClaimIdHash(tx2.GetHash(), 0), 2, height);
cache.addClaim("aB", COutPoint(tx3.GetHash(), 0), ClaimIdHash(tx3.GetHash(), 0), 3, height);
cache.addSupport("AB", COutPoint(sx1.GetHash(), 0), 1, ClaimIdHash(tx1.GetHash(), 0), height);
cache.addSupport("Ab", COutPoint(sx2.GetHash(), 0), 1, ClaimIdHash(tx2.GetHash(), 0), height);
cache.addClaim("AB", COutPoint(tx1.GetHash(), 0), ClaimIdHash(tx1.GetHash(), 0), 1, height, {});
cache.addClaim("Ab", COutPoint(tx2.GetHash(), 0), ClaimIdHash(tx2.GetHash(), 0), 2, height, {});
cache.addClaim("aB", COutPoint(tx3.GetHash(), 0), ClaimIdHash(tx3.GetHash(), 0), 3, height, {});
cache.addSupport("AB", COutPoint(sx1.GetHash(), 0), 1, ClaimIdHash(tx1.GetHash(), 0), height, {});
cache.addSupport("Ab", COutPoint(sx2.GetHash(), 0), 1, ClaimIdHash(tx2.GetHash(), 0), height, {});
insertUndoType insertUndo;
claimQueueRowType expireUndo;
insertUndoType insertSupportUndo;
@ -329,11 +322,12 @@ BOOST_AUTO_TEST_CASE(normalization_removal_test)
BOOST_CHECK(cache.getClaimsForName("ab").claimsNsupports[2].supports.size() == 1U);
BOOST_CHECK(cache.decrementBlock(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo));
BOOST_CHECK(cache.finalizeDecrement(takeoverHeightUndo));
BOOST_CHECK(cache.undoAddSupport("AB", COutPoint(sx1.GetHash(), 0), height));
BOOST_CHECK(cache.undoAddSupport("Ab", COutPoint(sx2.GetHash(), 0), height));
BOOST_CHECK(cache.undoAddClaim("AB", COutPoint(tx1.GetHash(), 0), height));
BOOST_CHECK(cache.undoAddClaim("Ab", COutPoint(tx2.GetHash(), 0), height));
BOOST_CHECK(cache.undoAddClaim("aB", COutPoint(tx3.GetHash(), 0), height));
std::string unused;
BOOST_CHECK(cache.removeSupport(COutPoint(sx1.GetHash(), 0), unused, height));
BOOST_CHECK(cache.removeSupport(COutPoint(sx2.GetHash(), 0), unused, height));
BOOST_CHECK(cache.removeClaim(ClaimIdHash(tx1.GetHash(), 0), unused, height));
BOOST_CHECK(cache.removeClaim(ClaimIdHash(tx2.GetHash(), 0), unused, height));
BOOST_CHECK(cache.removeClaim(ClaimIdHash(tx3.GetHash(), 0), unused, height));
BOOST_CHECK(cache.getClaimsForName("ab").claimsNsupports.size() == 0U);
}

View file

@ -1,293 +0,0 @@
#include <claimtrie.h>
#include <prefixtrie.h>
#include <random.h>
#include <boost/test/unit_test.hpp>
#include <test/claimtriefixture.h>
#include <test/test_bitcoin.h>
#include <chrono>
std::vector<std::string> random_strings(std::size_t count)
{
static auto& chrs = "0123456789"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
FastRandomContext frc(true);
std::unordered_set<std::string> strings;
strings.reserve(count);
while(strings.size() < count) {
auto length = frc.randrange(sizeof(chrs) - 2) + 1;
std::string s;
s.reserve(length);
while (length--)
s += chrs[frc.randrange(sizeof(chrs) - 1)];
strings.emplace(s);
}
std::vector<std::string> ret(strings.begin(), strings.end());
std::random_shuffle(ret.begin(), ret.end(), [&frc](std::size_t n) { return frc.randrange(n); });
return ret;
}
using namespace std;
BOOST_FIXTURE_TEST_SUITE(prefixtrie_tests, RegTestingSetup)
#ifndef MAC_OSX // can't find a random number generator that produces the same sequence on OSX
BOOST_AUTO_TEST_CASE(triehash_fuzzer_test)
{
ClaimTrieChainFixture fixture;
auto envClaims = std::getenv("TRIEHASH_FUZZER_CLAIMS");
auto envBlocks = std::getenv("TRIEHASH_FUZZER_BLOCKS");
const int claimsPerBlock = envClaims ? std::atoi(envClaims) : 100;
const int blocks = envBlocks ? std::atoi(envBlocks) : 13;
auto names = random_strings(blocks * claimsPerBlock);
FastRandomContext frc(true);
std::unordered_map<std::string, std::vector<CMutableTransaction>> existingClaims;
std::vector<CMutableTransaction> existingSupports;
std::string value(1024, 'c');
std::vector<CTransaction> cb {fixture.GetCoinbase()};
for (int i = 0; i < blocks; ++i) {
for (int j = 0; j < claimsPerBlock; ++j) {
auto name = names[i * claimsPerBlock + j];
auto supportFront = frc.randrange(4) == 0;
auto supportBack = frc.randrange(4) == 0;
auto removeClaim = frc.randrange(4) == 0;
auto removeSupport = frc.randrange(4) == 0;
auto hit = existingClaims.find(name);
if (supportFront && hit != existingClaims.end() && hit->second.size()) {
auto tx = fixture.MakeSupport(cb.back(), hit->second[frc.rand64() % hit->second.size()], name, 2);
existingSupports.push_back(tx);
cb.emplace_back(std::move(tx));
}
if (removeClaim && hit != existingClaims.end() && hit->second.size()) {
auto idx = frc.rand64() % hit->second.size();
fixture.Spend(hit->second[idx]);
hit->second.erase(hit->second.begin() + idx);
} else {
auto tx = fixture.MakeClaim(cb.back(), name, value, 2);
existingClaims[name].push_back(tx);
hit = existingClaims.find(name);
cb.emplace_back(std::move(tx));
}
if (supportBack && hit != existingClaims.end() && hit->second.size()) {
auto tx = fixture.MakeSupport(cb.back(), hit->second[frc.rand64() % hit->second.size()], name, 2);
existingSupports.push_back(tx);
cb.emplace_back(std::move(tx));
}
if (removeSupport && (i & 7) == 7 && !existingSupports.empty()) {
const auto tidx = frc.rand64() % existingSupports.size();
const auto tx = existingSupports[tidx];
fixture.Spend(tx);
existingSupports.erase(existingSupports.begin() + tidx);
}
if (cb.back().GetValueOut() < 10 || cb.size() > 40000) {
cb.clear();
cb.push_back(fixture.GetCoinbase());
}
}
fixture.IncrementBlocks(1);
if (blocks > 13 && i % 50 == 0) // travisCI needs some periodic output
std::cerr << "In triehash_fuzzer_test with " << fixture.getTotalNamesInTrie() << " names at block " << i << std::endl;
}
if (blocks == 1000 && claimsPerBlock == 100)
BOOST_CHECK_EQUAL(fixture.getMerkleHash().GetHex(), "28825257a129eef69cab87d6255c8359fc6dc083ca7f09222526e3a7971f382d");
else if (blocks == 13 && claimsPerBlock == 100)
BOOST_CHECK_EQUAL(fixture.getMerkleHash().GetHex(), "4e5984d6984f5f05d50e821e6228d56bcfbd16ca2093cd0308f6ff1c2bc8689a");
else
std::cerr << "Hash: " << fixture.getMerkleHash().GetHex() << std::endl;
}
#endif
BOOST_AUTO_TEST_CASE(insert_erase_test)
{
CPrefixTrie<std::string, CClaimTrieData> root;
BOOST_CHECK(root.empty());
BOOST_CHECK_EQUAL(root.height(), 0);
CClaimTrieData data;
data.insertClaim(CClaimValue{});
BOOST_CHECK(root.insert("abc", std::move(data)) != root.end());
BOOST_CHECK_EQUAL(root.height(), 1);
data.insertClaim(CClaimValue{});
BOOST_CHECK(root.insert("abd", std::move(data)) != root.end());
BOOST_CHECK_EQUAL(root.height(), 3);
// test expanding on ab
BOOST_CHECK(root.find("ab") != root.end());
data.insertClaim(CClaimValue{});
BOOST_CHECK(root.insert("a", std::move(data)) != root.end());
BOOST_CHECK_EQUAL(root.height(), 4);
// test ab again
BOOST_CHECK(root.find("ab") != root.end());
BOOST_CHECK(root.erase("abd"));
BOOST_CHECK(root.find("abd") == root.end());
BOOST_CHECK_EQUAL(root.height(), 2);
// collapsing of ab-c to abc so ab does not present any more
BOOST_CHECK(root.find("ab") == root.end());
// a has children so only data is erased
BOOST_CHECK(root.erase("a"));
BOOST_CHECK(root.find("a") == root.end());
// erasing abc will erase all node refers to a
BOOST_CHECK(root.erase("abc"));
BOOST_CHECK(root.empty());
BOOST_CHECK_EQUAL(root.height(), 0);
BOOST_CHECK(root.find("a") == root.end());
BOOST_CHECK(root.find("ab") == root.end());
BOOST_CHECK(root.find("abc") == root.end());
BOOST_CHECK(root.find("abd") == root.end());
}
BOOST_AUTO_TEST_CASE(iterate_test)
{
CPrefixTrie<std::string, CClaimTrieData> root;
BOOST_CHECK(root.empty());
BOOST_CHECK_EQUAL(root.height(), 0);
BOOST_CHECK(root.insert("abc", CClaimTrieData{}) != root.end());
BOOST_CHECK_EQUAL(root.height(), 1);
BOOST_CHECK(root.insert("abd", CClaimTrieData{}) != root.end());
BOOST_CHECK_EQUAL(root.height(), 3);
BOOST_CHECK(root.insert("tdb", CClaimTrieData{}) != root.end());
BOOST_CHECK_EQUAL(root.height(), 4);
for (auto it = root.begin(); it != root.end(); ++it) {
auto& key = it.key();
if (key.empty() ||
key == "ab" ||
key == "abc" ||
key == "abd" ||
key == "tdb") {
} else {
BOOST_CHECK(false); // should not happen
}
}
auto nodes = root.nodes("abd");
BOOST_CHECK_EQUAL(nodes.size(), 3);
BOOST_CHECK_EQUAL(nodes[0].key(), "");
BOOST_CHECK(nodes[0].hasChildren());
BOOST_CHECK_EQUAL(nodes[0].children().size(), 2);
// children of ""
for (auto& child : nodes[0].children()) {
auto& key = child.key();
if (key == "ab" || key == "tdb") {
} else {
BOOST_CHECK(false); // should not happen
}
}
BOOST_CHECK_EQUAL(nodes[1].key(), "ab");
BOOST_CHECK(nodes[1].hasChildren());
BOOST_CHECK_EQUAL(nodes[1].children().size(), 2);
// children of "ab"
for (auto& child : nodes[1].children()) {
auto& key = child.key();
if (key == "abc" || key == "abd") {
} else {
BOOST_CHECK(false); // should not happen
}
}
BOOST_CHECK_EQUAL(nodes[2].key(), "abd");
BOOST_CHECK(!nodes[2].hasChildren());
BOOST_CHECK_EQUAL(nodes[2].children().size(), 0);
}
BOOST_AUTO_TEST_CASE(erase_cleanup_test)
{
CPrefixTrie<std::string, CClaimTrieData> root;
CClaimTrieData data;
data.insertClaim(CClaimValue{});
BOOST_CHECK(root.insert("a", std::move(data)) != root.end());
data.insertClaim(CClaimValue{});
BOOST_CHECK(root.insert("adata", std::move(data)) != root.end());
data.insertClaim(CClaimValue{});
BOOST_CHECK(root.insert("adata2", std::move(data)) != root.end());
data.insertClaim(CClaimValue{});
BOOST_CHECK(root.insert("adota", std::move(data)) != root.end());
BOOST_CHECK_EQUAL(root.height(), 5);
BOOST_CHECK(root.erase("adata"));
BOOST_CHECK(root.erase("adata2"));
BOOST_CHECK_EQUAL(root.height(), 2);
}
BOOST_AUTO_TEST_CASE(add_many_nodes) {
// this if for testing performance and making sure erasure goes all the way to zero
CPrefixTrie<std::string, CClaimTrieData> trie;
std::size_t count = 100000; // bump this up to 1M for a better test
auto strings = random_strings(count);
auto s1 = std::chrono::high_resolution_clock::now();
for (std::size_t i = 0; i < count; i++) {
CClaimTrieData d; d.nHeightOfLastTakeover = i;
trie.insert(strings[i], std::move(d));
}
auto s2 = std::chrono::high_resolution_clock::now();
auto nodes = trie.height();
// run with --log_level=message to see these:
BOOST_TEST_MESSAGE("Nodes:" + std::to_string(nodes));
BOOST_TEST_MESSAGE("Insertion sec: " + std::to_string(std::chrono::duration_cast<std::chrono::duration<float> >(s2 - s1).count()));
auto s3 = std::chrono::high_resolution_clock::now();
for (std::size_t i = 0; i < count; i++) {
auto& value = trie.at(strings[i]);
BOOST_CHECK_EQUAL(value.nHeightOfLastTakeover, i);
}
auto s4 = std::chrono::high_resolution_clock::now();
BOOST_TEST_MESSAGE("Lookup sec: " + std::to_string(std::chrono::duration_cast<std::chrono::duration<float> >(s4 - s3).count()));
auto s5 = std::chrono::high_resolution_clock::now();
for (std::size_t i = 0; i < count; i++) {
auto value = trie.nodes(strings[i]);
BOOST_CHECK(!value.empty());
}
auto s6 = std::chrono::high_resolution_clock::now();
BOOST_TEST_MESSAGE("Parents sec: " + std::to_string(std::chrono::duration_cast<std::chrono::duration<float> >(s6 - s5).count()));
auto s7 = std::chrono::high_resolution_clock::now();
for (std::size_t i = 0; i < count; i++) {
trie.erase(strings[i]);
}
BOOST_CHECK_EQUAL(trie.height(), 0);
auto s8 = std::chrono::high_resolution_clock::now();
BOOST_TEST_MESSAGE("Deletion sec: " + std::to_string(std::chrono::duration_cast<std::chrono::duration<float> >(s8 - s7).count()));
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -56,12 +56,6 @@ std::ostream& operator<<(std::ostream& os, const COutPoint& point)
return os;
}
std::ostream& operator<<(std::ostream& os, const CClaimTrieData& data)
{
os << data.hash.ToString();
return os;
}
std::ostream& operator<<(std::ostream& os, const CClaimValue& claim)
{
os << "claim(" << claim.outPoint.ToString()

View file

@ -141,7 +141,4 @@ std::ostream& operator<<(std::ostream& os, const CClaimValue& claim);
struct CSupportValue;
std::ostream& operator<<(std::ostream& os, const CSupportValue& support);
struct CClaimTrieData;
std::ostream& operator<<(std::ostream& os, const CClaimTrieData& data);
#endif

View file

@ -1827,8 +1827,6 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
assert(trieCache.finalizeDecrement(blockUndo.takeoverHeightUndo));
auto merkleHash = trieCache.getMerkleHash();
if (merkleHash != pindex->pprev->hashClaimTrie) {
if (!trieCache.empty())
trieCache.dumpToLog(trieCache.find({}));
LogPrintf("Hash comparison failure at block %d\n", pindex->nHeight);
assert(merkleHash == pindex->pprev->hashClaimTrie);
}
@ -2316,8 +2314,6 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
if (trieCache.getMerkleHash() != block.hashClaimTrie)
{
if (!trieCache.empty()) // we could run checkConsistency here, but it would take a while
trieCache.dumpToLog(trieCache.find({}));
return state.DoS(100, error("ConnectBlock() : the merkle root of the claim trie does not match "
"(actual=%s vs block=%s on height=%d)", trieCache.getMerkleHash().GetHex(),
block.hashClaimTrie.GetHex(), pindex->nHeight), REJECT_INVALID, "bad-claim-merkle-hash");