dbmanager.h 6.23 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Copyright (c) 2014-2018 The Crown developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef DB_MANAGER_H
#define DB_MANAGER_H

#include "chainparams.h"
#include "clientversion.h"
#include "hash.h"
#include "streams.h"
#include "util.h"

#include <boost/filesystem.hpp>

/** 
*   Generic Dumping and Loading
*   ---------------------------
*/

template<typename T>
class DbManager
{
public:
Ashot's avatar
Ashot committed
25
26
27
    DbManager(std::string filename, std::string magicMessage);
    bool Load(T& objToLoad, bool fDryRun = false);
    bool Dump(const T& objToSave);
28

Ashot's avatar
Ashot committed
29
30
    enum ReadResult
    {
31
32
33
34
35
36
37
38
39
40
41
42
43
44
        Ok,
        FileError,
        HashReadError,
        IncorrectHash,
        IncorrectMagicMessage,
        IncorrectMagicNumber,
        IncorrectFormat
    };

private:
    bool Write(const T& objToSave);
    ReadResult Read(T& objToLoad, bool fDryRun = false);

private:
Ashot's avatar
Ashot committed
45
46
47
    boost::filesystem::path m_pathDb;
    std::string m_filename;
    std::string m_magicMessage;
48
49
50
};

template <typename T>
Ashot's avatar
Ashot committed
51
DbManager<T>::DbManager(std::string filename, std::string magicMessage)
52
{
Ashot's avatar
Ashot committed
53
54
55
    m_pathDb = GetDataDir() / filename;
    m_filename = filename;
    m_magicMessage = magicMessage;
56
57
58
59
60
61
62
63
64
}

template <typename T>
bool DbManager<T>::Write(const T& objToSave)
{
    int64_t nStart = GetTimeMillis();

    // serialize, checksum data up to that point, then append checksum
    CDataStream ssObj(SER_DISK, CLIENT_VERSION);
Ashot's avatar
Ashot committed
65
    ssObj << m_magicMessage; // specific magic message for this type of object
66
67
68
69
70
71
    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
Ashot's avatar
Ashot committed
72
    FILE *file = fopen(m_pathDb.string().c_str(), "wb");
73
74
    CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
    if (fileout.IsNull())
Ashot's avatar
Ashot committed
75
76
77
    {
        return error("%s: Failed to open file %s", __func__, m_pathDb.string());
    }
78
79

    // Write and commit header, data
Ashot's avatar
Ashot committed
80
81
    try
    {
82
83
        fileout << ssObj;
    }
Ashot's avatar
Ashot committed
84
85
    catch (std::exception &e)
    {
86
87
88
89
        return error("%s: Serialize or I/O error - %s", __func__, e.what());
    }
    fileout.fclose();

Ashot's avatar
Ashot committed
90
    LogPrintf("Written info to %s  %dms\n", m_filename, GetTimeMillis() - nStart);
91
92
93
94
95
96
97
98
99
100
    LogPrintf("     %s\n", objToSave.ToString());

    return true;
}

template <typename T>
typename DbManager<T>::ReadResult DbManager<T>::Read(T& objToLoad, bool fDryRun)
{
    int64_t nStart = GetTimeMillis();
    // open input file, and associate with CAutoFile
Ashot's avatar
Ashot committed
101
    FILE *file = fopen(m_pathDb.string().c_str(), "rb");
102
103
104
    CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
    if (filein.IsNull())
    {
Ashot's avatar
Ashot committed
105
        error("%s: Failed to open file %s", __func__, m_pathDb.string());
106
107
108
109
        return FileError;
    }

    // use file size to size memory buffer
Ashot's avatar
Ashot committed
110
    int fileSize = boost::filesystem::file_size(m_pathDb);
111
112
113
114
    int dataSize = fileSize - sizeof(uint256);
    // Don't try to resize to a negative number if file is small
    if (dataSize < 0)
        dataSize = 0;
Ashot's avatar
Ashot committed
115
    std::vector<unsigned char> vchData(dataSize);
116
117
118
    uint256 hashIn;

    // read data and checksum from file
Ashot's avatar
Ashot committed
119
120
    try
    {
121
122
123
        filein.read((char *)&vchData[0], dataSize);
        filein >> hashIn;
    }
Ashot's avatar
Ashot committed
124
125
    catch (std::exception &e)
    {
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
        error("%s: Deserialize or I/O error - %s", __func__, e.what());
        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)
    {
        error("%s: Checksum mismatch, data corrupted", __func__);
        return IncorrectHash;
    }

    unsigned char pchMsgTmp[4];
    std::string strMagicMessageTmp;
Ashot's avatar
Ashot committed
143
144
    try
    {
145
146
147
148
        // de-serialize file header (file specific magic message) and ..
        ssObj >> strMagicMessageTmp;

        // ... verify the message matches predefined one
Ashot's avatar
Ashot committed
149
        if (m_magicMessage != strMagicMessageTmp)
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
        {
            error("%s: Invalid magic message", __func__);
            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)))
        {
            error("%s: Invalid network magic number", __func__);
            return IncorrectMagicNumber;
        }

        // de-serialize data into T object
        ssObj >> objToLoad;
    }
Ashot's avatar
Ashot committed
168
169
    catch (std::exception &e)
    {
170
171
172
173
174
        objToLoad.Clear();
        error("%s: Deserialize or I/O error - %s", __func__, e.what());
        return IncorrectFormat;
    }

Ashot's avatar
Ashot committed
175
    LogPrintf("Loaded info from %s  %dms\n", m_filename, GetTimeMillis() - nStart);
176
    LogPrintf("     %s\n", objToLoad.ToString());
Ashot's avatar
Ashot committed
177
178
    if(!fDryRun)
    {
179
180
181
182
183
184
185
186
187
        LogPrintf("%s: Cleaning....\n", __func__);
        objToLoad.CheckAndRemove();
        LogPrintf("     %s\n", objToLoad.ToString());
    }

    return Ok;
}

template <typename T>
Ashot's avatar
Ashot committed
188
bool DbManager<T>::Load(T& objToLoad, bool fDryRun)
189
{
Ashot's avatar
Ashot committed
190
191
    LogPrintf("Reading info from %s...\n", m_filename);
    ReadResult readResult = Read(objToLoad, fDryRun);
192
    if (readResult == FileError)
Ashot's avatar
Ashot committed
193
194
195
    {
        LogPrintf("Missing file %s, will try to recreate\n", m_filename);
    }
196
197
    else if (readResult != Ok)
    {
Ashot's avatar
Ashot committed
198
        LogPrintf("Error reading %s: ", m_filename);
199
200
201
202
        if(readResult == IncorrectFormat)
        {
            LogPrintf("%s: Magic is ok but data has invalid format, will try to recreate\n", __func__);
        }
Ashot's avatar
Ashot committed
203
204
        else
        {
205
206
207
208
209
210
211
212
213
            LogPrintf("%s: File format is unknown or invalid, please fix it manually\n", __func__);
            // program should exit with an error
            return false;
        }
    }
    return true;
}

template <typename T>
Ashot's avatar
Ashot committed
214
bool DbManager<T>::Dump(const T& objToSave)
215
216
217
{
    int64_t nStart = GetTimeMillis();

Ashot's avatar
Ashot committed
218
    LogPrintf("Verifying %s format...\n", m_filename);
219
    T tmpObjToLoad;
Ashot's avatar
Ashot committed
220
    if (!Load(tmpObjToLoad, true))
221
    {
Ashot's avatar
Ashot committed
222
        return false;
223
    }
Ashot's avatar
Ashot committed
224
    LogPrintf("Writing info to %s...\n", m_filename);
225
    Write(objToSave);
Ashot's avatar
Ashot committed
226
    LogPrintf("%s dump finished  %dms\n", m_filename, GetTimeMillis() - nStart);
227
228
229
230
231

    return true;
}

#endif