2020-01-17 15:42:55 +01:00
|
|
|
// Copyright (c) 2014-2020 The Dash Core developers
|
2016-04-10 08:31:32 +02:00
|
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
2016-10-22 18:52:14 +02:00
|
|
|
|
2020-07-19 07:39:57 +02:00
|
|
|
#ifndef BITCOIN_FLAT_DATABASE_H
|
|
|
|
#define BITCOIN_FLAT_DATABASE_H
|
2016-04-10 08:31:32 +02:00
|
|
|
|
2020-03-19 23:46:56 +01:00
|
|
|
#include <chainparams.h>
|
|
|
|
#include <clientversion.h>
|
|
|
|
#include <fs.h>
|
|
|
|
#include <hash.h>
|
|
|
|
#include <streams.h>
|
|
|
|
#include <util.h>
|
2016-04-10 08:31:32 +02:00
|
|
|
|
|
|
|
/**
|
2016-04-13 19:49:47 +02:00
|
|
|
* Generic Dumping and Loading
|
|
|
|
* ---------------------------
|
2016-04-10 08:31:32 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
class CFlatDB
|
|
|
|
{
|
|
|
|
private:
|
2016-04-13 19:49:47 +02:00
|
|
|
|
|
|
|
enum ReadResult {
|
|
|
|
Ok,
|
|
|
|
FileError,
|
|
|
|
HashReadError,
|
|
|
|
IncorrectHash,
|
|
|
|
IncorrectMagicMessage,
|
|
|
|
IncorrectMagicNumber,
|
|
|
|
IncorrectFormat
|
|
|
|
};
|
|
|
|
|
2019-05-24 14:53:24 +02:00
|
|
|
fs::path pathDB;
|
2016-04-10 08:31:32 +02:00
|
|
|
std::string strFilename;
|
|
|
|
std::string strMagicMessage;
|
|
|
|
|
2016-04-13 19:49:47 +02:00
|
|
|
bool Write(const T& objToSave)
|
|
|
|
{
|
|
|
|
// LOCK(objToSave.cs);
|
|
|
|
|
|
|
|
int64_t nStart = GetTimeMillis();
|
|
|
|
|
|
|
|
// serialize, checksum data up to that point, then append checksum
|
|
|
|
CDataStream ssObj(SER_DISK, CLIENT_VERSION);
|
|
|
|
ssObj << strMagicMessage; // specific magic message for this type of object
|
|
|
|
ssObj << FLATDATA(Params().MessageStart()); // network specific magic number
|
|
|
|
ssObj << objToSave;
|
|
|
|
uint256 hash = Hash(ssObj.begin(), ssObj.end());
|
|
|
|
ssObj << hash;
|
|
|
|
|
|
|
|
// open output file, and associate with CAutoFile
|
|
|
|
FILE *file = fopen(pathDB.string().c_str(), "wb");
|
|
|
|
CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
|
|
|
|
if (fileout.IsNull())
|
2016-10-22 18:52:14 +02:00
|
|
|
return error("%s: Failed to open file %s", __func__, pathDB.string());
|
2016-04-13 19:49:47 +02:00
|
|
|
|
|
|
|
// Write and commit header, data
|
|
|
|
try {
|
|
|
|
fileout << ssObj;
|
|
|
|
}
|
|
|
|
catch (std::exception &e) {
|
2016-10-22 18:52:14 +02:00
|
|
|
return error("%s: Serialize or I/O error - %s", __func__, e.what());
|
2016-04-13 19:49:47 +02:00
|
|
|
}
|
|
|
|
fileout.fclose();
|
|
|
|
|
|
|
|
LogPrintf("Written info to %s %dms\n", strFilename, GetTimeMillis() - nStart);
|
|
|
|
LogPrintf(" %s\n", objToSave.ToString());
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
ReadResult Read(T& objToLoad, bool fDryRun = false)
|
|
|
|
{
|
|
|
|
//LOCK(objToLoad.cs);
|
|
|
|
|
|
|
|
int64_t nStart = GetTimeMillis();
|
|
|
|
// open input file, and associate with CAutoFile
|
|
|
|
FILE *file = fopen(pathDB.string().c_str(), "rb");
|
|
|
|
CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
|
|
|
|
if (filein.IsNull())
|
|
|
|
{
|
2016-10-22 18:52:14 +02:00
|
|
|
error("%s: Failed to open file %s", __func__, pathDB.string());
|
2016-04-13 19:49:47 +02:00
|
|
|
return FileError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// use file size to size memory buffer
|
2019-05-24 14:53:24 +02:00
|
|
|
int fileSize = fs::file_size(pathDB);
|
2016-04-13 19:49:47 +02:00
|
|
|
int dataSize = fileSize - sizeof(uint256);
|
|
|
|
// Don't try to resize to a negative number if file is small
|
|
|
|
if (dataSize < 0)
|
|
|
|
dataSize = 0;
|
|
|
|
std::vector<unsigned char> vchData;
|
|
|
|
vchData.resize(dataSize);
|
|
|
|
uint256 hashIn;
|
|
|
|
|
|
|
|
// read data and checksum from file
|
|
|
|
try {
|
2020-01-05 03:00:36 +01:00
|
|
|
filein.read((char *)vchData.data(), dataSize);
|
2016-04-13 19:49:47 +02:00
|
|
|
filein >> hashIn;
|
|
|
|
}
|
|
|
|
catch (std::exception &e) {
|
2016-10-22 18:52:14 +02:00
|
|
|
error("%s: Deserialize or I/O error - %s", __func__, e.what());
|
2016-04-13 19:49:47 +02:00
|
|
|
return HashReadError;
|
|
|
|
}
|
|
|
|
filein.fclose();
|
|
|
|
|
|
|
|
CDataStream ssObj(vchData, SER_DISK, CLIENT_VERSION);
|
|
|
|
|
|
|
|
// verify stored checksum matches input data
|
|
|
|
uint256 hashTmp = Hash(ssObj.begin(), ssObj.end());
|
|
|
|
if (hashIn != hashTmp)
|
|
|
|
{
|
2016-10-22 18:52:14 +02:00
|
|
|
error("%s: Checksum mismatch, data corrupted", __func__);
|
2016-04-13 19:49:47 +02:00
|
|
|
return IncorrectHash;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
unsigned char pchMsgTmp[4];
|
|
|
|
std::string strMagicMessageTmp;
|
|
|
|
try {
|
|
|
|
// de-serialize file header (file specific magic message) and ..
|
|
|
|
ssObj >> strMagicMessageTmp;
|
|
|
|
|
|
|
|
// ... verify the message matches predefined one
|
|
|
|
if (strMagicMessage != strMagicMessageTmp)
|
|
|
|
{
|
2016-10-22 18:52:14 +02:00
|
|
|
error("%s: Invalid magic message", __func__);
|
2016-04-13 19:49:47 +02:00
|
|
|
return IncorrectMagicMessage;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// de-serialize file header (network specific magic number) and ..
|
|
|
|
ssObj >> FLATDATA(pchMsgTmp);
|
|
|
|
|
|
|
|
// ... verify the network matches ours
|
|
|
|
if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
|
|
|
|
{
|
2016-10-22 18:52:14 +02:00
|
|
|
error("%s: Invalid network magic number", __func__);
|
2016-04-13 19:49:47 +02:00
|
|
|
return IncorrectMagicNumber;
|
|
|
|
}
|
|
|
|
|
|
|
|
// de-serialize data into T object
|
|
|
|
ssObj >> objToLoad;
|
|
|
|
}
|
|
|
|
catch (std::exception &e) {
|
|
|
|
objToLoad.Clear();
|
2016-10-22 18:52:14 +02:00
|
|
|
error("%s: Deserialize or I/O error - %s", __func__, e.what());
|
2016-04-13 19:49:47 +02:00
|
|
|
return IncorrectFormat;
|
|
|
|
}
|
|
|
|
|
|
|
|
LogPrintf("Loaded info from %s %dms\n", strFilename, GetTimeMillis() - nStart);
|
|
|
|
LogPrintf(" %s\n", objToLoad.ToString());
|
|
|
|
if(!fDryRun) {
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrintf("%s: Cleaning....\n", __func__);
|
2016-04-13 19:49:47 +02:00
|
|
|
objToLoad.CheckAndRemove();
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrintf(" %s\n", objToLoad.ToString());
|
2016-04-13 19:49:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
2016-04-10 08:31:32 +02:00
|
|
|
|
|
|
|
|
2016-04-13 19:49:47 +02:00
|
|
|
public:
|
|
|
|
CFlatDB(std::string strFilenameIn, std::string strMagicMessageIn)
|
|
|
|
{
|
|
|
|
pathDB = GetDataDir() / strFilenameIn;
|
|
|
|
strFilename = strFilenameIn;
|
|
|
|
strMagicMessage = strMagicMessageIn;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Load(T& objToLoad)
|
|
|
|
{
|
2016-05-20 02:21:16 +02:00
|
|
|
LogPrintf("Reading info from %s...\n", strFilename);
|
2016-04-13 19:49:47 +02:00
|
|
|
ReadResult readResult = Read(objToLoad);
|
|
|
|
if (readResult == FileError)
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrintf("Missing file %s, will try to recreate\n", strFilename);
|
2016-04-13 19:49:47 +02:00
|
|
|
else if (readResult != Ok)
|
|
|
|
{
|
|
|
|
LogPrintf("Error reading %s: ", strFilename);
|
|
|
|
if(readResult == IncorrectFormat)
|
|
|
|
{
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrintf("%s: Magic is ok but data has invalid format, will try to recreate\n", __func__);
|
2016-04-13 19:49:47 +02:00
|
|
|
}
|
|
|
|
else {
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrintf("%s: File format is unknown or invalid, please fix it manually\n", __func__);
|
2016-04-13 19:49:47 +02:00
|
|
|
// program should exit with an error
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Dump(T& objToSave)
|
|
|
|
{
|
|
|
|
int64_t nStart = GetTimeMillis();
|
|
|
|
|
|
|
|
LogPrintf("Verifying %s format...\n", strFilename);
|
2016-05-20 02:21:16 +02:00
|
|
|
T tmpObjToLoad;
|
|
|
|
ReadResult readResult = Read(tmpObjToLoad, true);
|
2016-04-13 19:49:47 +02:00
|
|
|
|
2016-05-20 02:21:16 +02:00
|
|
|
// there was an error and it was not an error on file opening => do not proceed
|
|
|
|
if (readResult == FileError)
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrintf("Missing file %s, will try to recreate\n", strFilename);
|
2016-05-20 02:21:16 +02:00
|
|
|
else if (readResult != Ok)
|
|
|
|
{
|
|
|
|
LogPrintf("Error reading %s: ", strFilename);
|
|
|
|
if(readResult == IncorrectFormat)
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrintf("%s: Magic is ok but data has invalid format, will try to recreate\n", __func__);
|
2016-05-20 02:21:16 +02:00
|
|
|
else
|
|
|
|
{
|
2016-10-22 18:52:14 +02:00
|
|
|
LogPrintf("%s: File format is unknown or invalid, please fix it manually\n", __func__);
|
2016-05-20 02:21:16 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2016-04-13 19:49:47 +02:00
|
|
|
|
2017-09-03 15:28:53 +02:00
|
|
|
LogPrintf("Writing info to %s...\n", strFilename);
|
2016-04-13 19:49:47 +02:00
|
|
|
Write(objToSave);
|
|
|
|
LogPrintf("%s dump finished %dms\n", strFilename, GetTimeMillis() - nStart);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-04-10 08:31:32 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2020-07-19 07:39:57 +02:00
|
|
|
#endif // BITCOIN_FLAT_DATABASE_H
|