twisterp2pblockchainnetworkbittorrentmicrobloggingipv6social-networkdhtdecentralizedtwister-coretwisterarmyp2p-networktwister-servertwister-ipv6
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
489 lines
12 KiB
489 lines
12 KiB
// Copyright (c) 2009-2010 Satoshi Nakamoto |
|
// Distributed under the MIT/X11 software license, see the accompanying |
|
// file license.txt or http://www.opensource.org/licenses/mit-license.php. |
|
|
|
class CTransaction; |
|
class CTxIndex; |
|
class CDiskBlockIndex; |
|
class CDiskTxPos; |
|
class COutPoint; |
|
class CUser; |
|
class CReview; |
|
class CAddress; |
|
class CWalletTx; |
|
|
|
extern map<string, string> mapAddressBook; |
|
extern CCriticalSection cs_mapAddressBook; |
|
extern vector<unsigned char> vchDefaultKey; |
|
extern bool fClient; |
|
extern int nBestHeight; |
|
|
|
|
|
extern unsigned int nWalletDBUpdated; |
|
extern DbEnv dbenv; |
|
|
|
|
|
extern void DBFlush(bool fShutdown); |
|
|
|
|
|
|
|
|
|
class CDB |
|
{ |
|
protected: |
|
Db* pdb; |
|
string strFile; |
|
vector<DbTxn*> vTxn; |
|
bool fReadOnly; |
|
|
|
explicit CDB(const char* pszFile, const char* pszMode="r+"); |
|
~CDB() { Close(); } |
|
public: |
|
void Close(); |
|
private: |
|
CDB(const CDB&); |
|
void operator=(const CDB&); |
|
|
|
protected: |
|
template<typename K, typename T> |
|
bool Read(const K& key, T& value) |
|
{ |
|
if (!pdb) |
|
return false; |
|
|
|
// Key |
|
CDataStream ssKey(SER_DISK); |
|
ssKey.reserve(1000); |
|
ssKey << key; |
|
Dbt datKey(&ssKey[0], ssKey.size()); |
|
|
|
// Read |
|
Dbt datValue; |
|
datValue.set_flags(DB_DBT_MALLOC); |
|
int ret = pdb->get(GetTxn(), &datKey, &datValue, 0); |
|
memset(datKey.get_data(), 0, datKey.get_size()); |
|
if (datValue.get_data() == NULL) |
|
return false; |
|
|
|
// Unserialize value |
|
CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK); |
|
ssValue >> value; |
|
|
|
// Clear and free memory |
|
memset(datValue.get_data(), 0, datValue.get_size()); |
|
free(datValue.get_data()); |
|
return (ret == 0); |
|
} |
|
|
|
template<typename K, typename T> |
|
bool Write(const K& key, const T& value, bool fOverwrite=true) |
|
{ |
|
if (!pdb) |
|
return false; |
|
if (fReadOnly) |
|
assert(("Write called on database in read-only mode", false)); |
|
|
|
// Key |
|
CDataStream ssKey(SER_DISK); |
|
ssKey.reserve(1000); |
|
ssKey << key; |
|
Dbt datKey(&ssKey[0], ssKey.size()); |
|
|
|
// Value |
|
CDataStream ssValue(SER_DISK); |
|
ssValue.reserve(10000); |
|
ssValue << value; |
|
Dbt datValue(&ssValue[0], ssValue.size()); |
|
|
|
// Write |
|
int ret = pdb->put(GetTxn(), &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE)); |
|
|
|
// Clear memory in case it was a private key |
|
memset(datKey.get_data(), 0, datKey.get_size()); |
|
memset(datValue.get_data(), 0, datValue.get_size()); |
|
return (ret == 0); |
|
} |
|
|
|
template<typename K> |
|
bool Erase(const K& key) |
|
{ |
|
if (!pdb) |
|
return false; |
|
if (fReadOnly) |
|
assert(("Erase called on database in read-only mode", false)); |
|
|
|
// Key |
|
CDataStream ssKey(SER_DISK); |
|
ssKey.reserve(1000); |
|
ssKey << key; |
|
Dbt datKey(&ssKey[0], ssKey.size()); |
|
|
|
// Erase |
|
int ret = pdb->del(GetTxn(), &datKey, 0); |
|
|
|
// Clear memory |
|
memset(datKey.get_data(), 0, datKey.get_size()); |
|
return (ret == 0 || ret == DB_NOTFOUND); |
|
} |
|
|
|
template<typename K> |
|
bool Exists(const K& key) |
|
{ |
|
if (!pdb) |
|
return false; |
|
|
|
// Key |
|
CDataStream ssKey(SER_DISK); |
|
ssKey.reserve(1000); |
|
ssKey << key; |
|
Dbt datKey(&ssKey[0], ssKey.size()); |
|
|
|
// Exists |
|
int ret = pdb->exists(GetTxn(), &datKey, 0); |
|
|
|
// Clear memory |
|
memset(datKey.get_data(), 0, datKey.get_size()); |
|
return (ret == 0); |
|
} |
|
|
|
Dbc* GetCursor() |
|
{ |
|
if (!pdb) |
|
return NULL; |
|
Dbc* pcursor = NULL; |
|
int ret = pdb->cursor(NULL, &pcursor, 0); |
|
if (ret != 0) |
|
return NULL; |
|
return pcursor; |
|
} |
|
|
|
int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue, unsigned int fFlags=DB_NEXT) |
|
{ |
|
// Read at cursor |
|
Dbt datKey; |
|
if (fFlags == DB_SET || fFlags == DB_SET_RANGE || fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) |
|
{ |
|
datKey.set_data(&ssKey[0]); |
|
datKey.set_size(ssKey.size()); |
|
} |
|
Dbt datValue; |
|
if (fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) |
|
{ |
|
datValue.set_data(&ssValue[0]); |
|
datValue.set_size(ssValue.size()); |
|
} |
|
datKey.set_flags(DB_DBT_MALLOC); |
|
datValue.set_flags(DB_DBT_MALLOC); |
|
int ret = pcursor->get(&datKey, &datValue, fFlags); |
|
if (ret != 0) |
|
return ret; |
|
else if (datKey.get_data() == NULL || datValue.get_data() == NULL) |
|
return 99999; |
|
|
|
// Convert to streams |
|
ssKey.SetType(SER_DISK); |
|
ssKey.clear(); |
|
ssKey.write((char*)datKey.get_data(), datKey.get_size()); |
|
ssValue.SetType(SER_DISK); |
|
ssValue.clear(); |
|
ssValue.write((char*)datValue.get_data(), datValue.get_size()); |
|
|
|
// Clear and free memory |
|
memset(datKey.get_data(), 0, datKey.get_size()); |
|
memset(datValue.get_data(), 0, datValue.get_size()); |
|
free(datKey.get_data()); |
|
free(datValue.get_data()); |
|
return 0; |
|
} |
|
|
|
DbTxn* GetTxn() |
|
{ |
|
if (!vTxn.empty()) |
|
return vTxn.back(); |
|
else |
|
return NULL; |
|
} |
|
|
|
public: |
|
bool TxnBegin() |
|
{ |
|
if (!pdb) |
|
return false; |
|
DbTxn* ptxn = NULL; |
|
int ret = dbenv.txn_begin(GetTxn(), &ptxn, DB_TXN_NOSYNC); |
|
if (!ptxn || ret != 0) |
|
return false; |
|
vTxn.push_back(ptxn); |
|
return true; |
|
} |
|
|
|
bool TxnCommit() |
|
{ |
|
if (!pdb) |
|
return false; |
|
if (vTxn.empty()) |
|
return false; |
|
int ret = vTxn.back()->commit(0); |
|
vTxn.pop_back(); |
|
return (ret == 0); |
|
} |
|
|
|
bool TxnAbort() |
|
{ |
|
if (!pdb) |
|
return false; |
|
if (vTxn.empty()) |
|
return false; |
|
int ret = vTxn.back()->abort(); |
|
vTxn.pop_back(); |
|
return (ret == 0); |
|
} |
|
|
|
bool ReadVersion(int& nVersion) |
|
{ |
|
nVersion = 0; |
|
return Read(string("version"), nVersion); |
|
} |
|
|
|
bool WriteVersion(int nVersion) |
|
{ |
|
return Write(string("version"), nVersion); |
|
} |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CTxDB : public CDB |
|
{ |
|
public: |
|
CTxDB(const char* pszMode="r+") : CDB(!fClient ? "blkindex.dat" : NULL, pszMode) { } |
|
private: |
|
CTxDB(const CTxDB&); |
|
void operator=(const CTxDB&); |
|
public: |
|
bool ReadTxIndex(uint256 hash, CTxIndex& txindex); |
|
bool UpdateTxIndex(uint256 hash, const CTxIndex& txindex); |
|
bool AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight); |
|
bool EraseTxIndex(const CTransaction& tx); |
|
bool ContainsTx(uint256 hash); |
|
bool ReadOwnerTxes(uint160 hash160, int nHeight, vector<CTransaction>& vtx); |
|
bool ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex); |
|
bool ReadDiskTx(uint256 hash, CTransaction& tx); |
|
bool ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex); |
|
bool ReadDiskTx(COutPoint outpoint, CTransaction& tx); |
|
bool WriteBlockIndex(const CDiskBlockIndex& blockindex); |
|
bool EraseBlockIndex(uint256 hash); |
|
bool ReadHashBestChain(uint256& hashBestChain); |
|
bool WriteHashBestChain(uint256 hashBestChain); |
|
bool ReadBestInvalidWork(CBigNum& bnBestInvalidWork); |
|
bool WriteBestInvalidWork(CBigNum bnBestInvalidWork); |
|
bool LoadBlockIndex(); |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
class CAddrDB : public CDB |
|
{ |
|
public: |
|
CAddrDB(const char* pszMode="r+") : CDB("addr.dat", pszMode) { } |
|
private: |
|
CAddrDB(const CAddrDB&); |
|
void operator=(const CAddrDB&); |
|
public: |
|
bool WriteAddress(const CAddress& addr); |
|
bool LoadAddresses(); |
|
}; |
|
|
|
bool LoadAddresses(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
class CKeyPool |
|
{ |
|
public: |
|
int64 nTime; |
|
vector<unsigned char> vchPubKey; |
|
|
|
CKeyPool() |
|
{ |
|
nTime = GetTime(); |
|
} |
|
|
|
CKeyPool(const vector<unsigned char>& vchPubKeyIn) |
|
{ |
|
nTime = GetTime(); |
|
vchPubKey = vchPubKeyIn; |
|
} |
|
|
|
IMPLEMENT_SERIALIZE |
|
( |
|
if (!(nType & SER_GETHASH)) |
|
READWRITE(nVersion); |
|
READWRITE(nTime); |
|
READWRITE(vchPubKey); |
|
) |
|
}; |
|
|
|
|
|
|
|
|
|
class CWalletDB : public CDB |
|
{ |
|
public: |
|
CWalletDB(const char* pszMode="r+") : CDB("wallet.dat", pszMode) { } |
|
private: |
|
CWalletDB(const CWalletDB&); |
|
void operator=(const CWalletDB&); |
|
public: |
|
bool ReadName(const string& strAddress, string& strName) |
|
{ |
|
strName = ""; |
|
return Read(make_pair(string("name"), strAddress), strName); |
|
} |
|
|
|
bool WriteName(const string& strAddress, const string& strName) |
|
{ |
|
CRITICAL_BLOCK(cs_mapAddressBook) |
|
mapAddressBook[strAddress] = strName; |
|
nWalletDBUpdated++; |
|
return Write(make_pair(string("name"), strAddress), strName); |
|
} |
|
|
|
bool EraseName(const string& strAddress) |
|
{ |
|
// This should only be used for sending addresses, never for receiving addresses, |
|
// receiving addresses must always have an address book entry if they're not change return. |
|
CRITICAL_BLOCK(cs_mapAddressBook) |
|
mapAddressBook.erase(strAddress); |
|
nWalletDBUpdated++; |
|
return Erase(make_pair(string("name"), strAddress)); |
|
} |
|
|
|
bool ReadTx(uint256 hash, CWalletTx& wtx) |
|
{ |
|
return Read(make_pair(string("tx"), hash), wtx); |
|
} |
|
|
|
bool WriteTx(uint256 hash, const CWalletTx& wtx) |
|
{ |
|
nWalletDBUpdated++; |
|
return Write(make_pair(string("tx"), hash), wtx); |
|
} |
|
|
|
bool EraseTx(uint256 hash) |
|
{ |
|
nWalletDBUpdated++; |
|
return Erase(make_pair(string("tx"), hash)); |
|
} |
|
|
|
bool ReadKey(const vector<unsigned char>& vchPubKey, CPrivKey& vchPrivKey) |
|
{ |
|
vchPrivKey.clear(); |
|
return Read(make_pair(string("key"), vchPubKey), vchPrivKey); |
|
} |
|
|
|
bool WriteKey(const vector<unsigned char>& vchPubKey, const CPrivKey& vchPrivKey) |
|
{ |
|
nWalletDBUpdated++; |
|
return Write(make_pair(string("key"), vchPubKey), vchPrivKey, false); |
|
} |
|
|
|
bool ReadDefaultKey(vector<unsigned char>& vchPubKey) |
|
{ |
|
vchPubKey.clear(); |
|
return Read(string("defaultkey"), vchPubKey); |
|
} |
|
|
|
bool WriteDefaultKey(const vector<unsigned char>& vchPubKey) |
|
{ |
|
vchDefaultKey = vchPubKey; |
|
nWalletDBUpdated++; |
|
return Write(string("defaultkey"), vchPubKey); |
|
} |
|
|
|
template<typename T> |
|
bool ReadSetting(const string& strKey, T& value) |
|
{ |
|
return Read(make_pair(string("setting"), strKey), value); |
|
} |
|
|
|
template<typename T> |
|
bool WriteSetting(const string& strKey, const T& value) |
|
{ |
|
nWalletDBUpdated++; |
|
return Write(make_pair(string("setting"), strKey), value); |
|
} |
|
|
|
bool LoadWallet(); |
|
protected: |
|
void ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool); |
|
void KeepKey(int64 nIndex); |
|
static void ReturnKey(int64 nIndex); |
|
friend class CReserveKey; |
|
public: |
|
vector<unsigned char> GetKeyFromKeyPool(); |
|
}; |
|
|
|
bool LoadWallet(bool& fFirstRunRet); |
|
void BackupWallet(const string& strDest); |
|
|
|
inline bool SetAddressBookName(const string& strAddress, const string& strName) |
|
{ |
|
return CWalletDB().WriteName(strAddress, strName); |
|
} |
|
|
|
class CReserveKey |
|
{ |
|
protected: |
|
int64 nIndex; |
|
vector<unsigned char> vchPubKey; |
|
public: |
|
CReserveKey() |
|
{ |
|
nIndex = -1; |
|
} |
|
|
|
~CReserveKey() |
|
{ |
|
if (!fShutdown) |
|
ReturnKey(); |
|
} |
|
|
|
vector<unsigned char> GetReservedKey() |
|
{ |
|
if (nIndex == -1) |
|
{ |
|
CKeyPool keypool; |
|
CWalletDB().ReserveKeyFromKeyPool(nIndex, keypool); |
|
vchPubKey = keypool.vchPubKey; |
|
} |
|
assert(!vchPubKey.empty()); |
|
return vchPubKey; |
|
} |
|
|
|
void KeepKey() |
|
{ |
|
if (nIndex != -1) |
|
CWalletDB().KeepKey(nIndex); |
|
nIndex = -1; |
|
vchPubKey.clear(); |
|
} |
|
|
|
void ReturnKey() |
|
{ |
|
if (nIndex != -1) |
|
CWalletDB::ReturnKey(nIndex); |
|
nIndex = -1; |
|
vchPubKey.clear(); |
|
} |
|
};
|
|
|