LevelDB.cpp 8.23 KB
Newer Older
kladkogex's avatar
kladkogex committed
1
/*
2
    Copyright (C) 2019-Present SKALE Labs
kladkogex's avatar
kladkogex committed
3

4
    This file is part of sgxwallet.
kladkogex's avatar
kladkogex committed
5

6
    sgxwallet is free software: you can redistribute it and/or modify
kladkogex's avatar
kladkogex committed
7 8 9 10
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

11
    sgxwallet is distributed in the hope that it will be useful,
kladkogex's avatar
kladkogex committed
12 13 14 15 16
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
17
    along with sgxwallet.  If not, see <https://www.gnu.org/licenses/>.
kladkogex's avatar
kladkogex committed
18 19 20 21 22 23 24 25 26

    @file LevelDB.cpp
    @author Stan Kladko
    @date 2019
*/

#include <stdexcept>
#include <memory>
#include <string>
27
#include <iostream>
kladkogex's avatar
kladkogex committed
28 29

#include "leveldb/db.h"
30
#include <jsonrpccpp/client.h>
kladkogex's avatar
kladkogex committed
31

kladkogex's avatar
kladkogex committed
32
#include "sgxwallet_common.h"
33
#include "SGXException.h"
kladkogex's avatar
kladkogex committed
34 35
#include "LevelDB.h"

36 37
#include "ServerInit.h"

Oleh Nikolaiev's avatar
Oleh Nikolaiev committed
38
#include "third_party/spdlog/spdlog.h"
39 40
#include "common.h"

kladkogex's avatar
kladkogex committed
41 42 43 44 45
using namespace leveldb;

static WriteOptions writeOptions;
static ReadOptions readOptions;

46 47 48 49 50 51 52 53
shared_ptr<string> LevelDB::readNewStyleValue(const string& value) {
    Json::Value key_data;
    Json::Reader reader;
    reader.parse(value.c_str(), key_data);

    return std::make_shared<string>(key_data["value"].asString());
}

54
std::shared_ptr<string> LevelDB::readString(const string &_key) {
kladkogex's avatar
kladkogex committed
55

56
    auto result = std::make_shared<string>();
kladkogex's avatar
kladkogex committed
57

kladko's avatar
kladko committed
58
    CHECK_STATE(db)
kladkogex's avatar
kladkogex committed
59

Oleh Nikolaiev's avatar
Oleh Nikolaiev committed
60
    auto status = db->Get(readOptions, _key, result.get());
61

kladkogex's avatar
kladkogex committed
62 63
    throwExceptionOnError(status);

64
    if (status.IsNotFound()) {
kladkogex's avatar
kladkogex committed
65
        return nullptr;
66
    }
kladkogex's avatar
kladkogex committed
67

68 69 70 71
    if (result->at(0) == '{') {
        return readNewStyleValue(*result);
    }

kladkogex's avatar
kladkogex committed
72 73 74
    return result;
}

75
void LevelDB::writeString(const string &_key, const string &_value) {
76 77 78 79 80 81
    Json::Value writerData;
    writerData["value"] = _value;
    writerData["timestamp"] = std::to_string(std::time(nullptr));

    Json::FastWriter fastWriter;
    std::string output = fastWriter.write(writerData);
kladkogex's avatar
kladkogex committed
82

83
    auto status = db->Put(writeOptions, Slice(_key), Slice(output));
kladkogex's avatar
kladkogex committed
84 85 86 87

    throwExceptionOnError(status);
}

Oleh Nikolaiev's avatar
Oleh Nikolaiev committed
88
void LevelDB::deleteDHDKGKey(const string &_key) {
89

90
    string full_key = "DKG_DH_KEY_" + _key;
91

92
    auto status = db->Delete(writeOptions, Slice(full_key));
93 94 95 96 97

    throwExceptionOnError(status);

}

98
void LevelDB::deleteTempNEK(const string &_key) {
99

kladko's avatar
kladko committed
100
    CHECK_STATE(_key.rfind("tmp_NEK", 0) == 0);
101 102 103 104 105 106

    auto status = db->Delete(writeOptions, Slice(_key));

    throwExceptionOnError(status);
}

107
void LevelDB::deleteKey(const string &_key) {
108 109 110 111 112 113 114

    auto status = db->Delete(writeOptions, Slice(_key));

    throwExceptionOnError(status);

}

kladkogex's avatar
kladkogex committed
115 116 117
void LevelDB::throwExceptionOnError(Status _status) {
    if (_status.IsNotFound())
        return;
kladkogex's avatar
kladkogex committed
118

kladkogex's avatar
kladkogex committed
119
    if (!_status.ok()) {
120
        throw SGXException(COULD_NOT_ACCESS_DATABASE, ("Could not access database database:" + _status.ToString()).c_str());
kladkogex's avatar
kladkogex committed
121 122 123 124
    }
}

uint64_t LevelDB::visitKeys(LevelDB::KeyVisitor *_visitor, uint64_t _maxKeysToVisit) {
kladko's avatar
kladko committed
125 126 127

    CHECK_STATE(_visitor);

kladkogex's avatar
kladkogex committed
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
    uint64_t readCounter = 0;

    leveldb::Iterator *it = db->NewIterator(readOptions);
    for (it->SeekToFirst(); it->Valid(); it->Next()) {
        _visitor->visitDBKey(it->key().data());
        readCounter++;
        if (readCounter >= _maxKeysToVisit) {
            break;
        }
    }

    delete it;

    return readCounter;
}

144
std::vector<string> LevelDB::writeKeysToVector1(uint64_t _maxKeysToVisit){
145
  uint64_t readCounter = 0;
146
  std::vector<string> keys;
147 148 149

  leveldb::Iterator *it = db->NewIterator(readOptions);
  for (it->SeekToFirst(); it->Valid(); it->Next()) {
150
    string cur_key(it->key().data(), it->key().size());
151 152 153 154 155 156 157 158 159 160 161 162
    keys.push_back(cur_key);
    readCounter++;
    if (readCounter >= _maxKeysToVisit) {
      break;
    }
  }

  delete it;

  return keys;
}

kladko's avatar
kladko committed
163
void LevelDB::writeDataUnique(const string & name, const string &value) {
kladko's avatar
kladko committed
164
  if (readString(name)) {
kladko's avatar
kladko committed
165
    spdlog::debug("Name {} already exists", name);
166
    throw SGXException(KEY_SHARE_ALREADY_EXISTS, "Data with this name already exists");
167 168
  }

169
  writeString(name, value);
170 171
}

Oleh Nikolaiev's avatar
Oleh Nikolaiev committed
172 173
pair<stringstream, uint64_t> LevelDB::getAllKeys() {
    stringstream keysInfo;
174

175 176 177 178 179 180 181 182 183 184 185
    leveldb::Iterator *it = db->NewIterator(readOptions);
    uint64_t counter = 0;
    for (it->SeekToFirst(); it->Valid(); it->Next()) {
        ++counter;
        string key = it->key().ToString();
        string value;
        if (it->value().ToString()[0] == '{') {
            // new style keys
            Json::Value key_data;
            Json::Reader reader;
            reader.parse(it->value().ToString().c_str(), key_data);
186 187

            string timestamp_to_date_command = "date -d @" + key_data["timestamp"].asString();
188
            value = " VALUE: " + key_data["value"].asString() + ", TIMESTAMP: " + exec(timestamp_to_date_command.c_str()) + '\n';
189 190 191 192
        } else {
            // old style keys
            value = " VALUE: " + it->value().ToString();
        }
Oleh Nikolaiev's avatar
Oleh Nikolaiev committed
193
        keysInfo << "KEY: " << key << ',' << value;
194 195
    }

Oleh Nikolaiev's avatar
Oleh Nikolaiev committed
196
    return {std::move(keysInfo), counter};
197 198
}

199
pair<string, uint64_t> LevelDB::getLatestCreatedKey() {
200 201
    leveldb::Iterator *it = db->NewIterator(readOptions);

202
    int64_t latest_timestamp = 0;
203 204 205 206 207 208 209 210
    string latest_created_key_name = "";
    for (it->SeekToFirst(); it->Valid(); it->Next()) {
        if (it->value().ToString()[0] == '{') {
            // new style keys
            Json::Value key_data;
            Json::Reader reader;
            reader.parse(it->value().ToString().c_str(), key_data);

211 212
            if (std::stoi(key_data["timestamp"].asString()) > latest_timestamp) {
                latest_timestamp = std::stoi(key_data["timestamp"].asString());
213 214 215 216 217 218 219 220
                latest_created_key_name = it->key().ToString();
            }
        } else {
            // old style keys
            // assuming server has at least one new-style key created
            continue;
        }
    }
221

222
    return {latest_created_key_name, latest_timestamp};
223 224
}

225

226
LevelDB::LevelDB(string &filename) {
kladkogex's avatar
kladkogex committed
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
    leveldb::Options options;
    options.create_if_missing = true;

    if (!leveldb::DB::Open(options, filename, (leveldb::DB **) &db).ok()) {
        throw std::runtime_error("Unable to open levelDB database");
    }

    if (db == nullptr) {
        throw std::runtime_error("Null levelDB object");
    }
}

LevelDB::~LevelDB() {
}

242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
const std::shared_ptr<LevelDB> &LevelDB::getLevelDb() {
    CHECK_STATE(levelDb)
    return levelDb;
}

const std::shared_ptr<LevelDB> &LevelDB::getCsrDb() {
    CHECK_STATE(csrDb)
    return csrDb;
}

const std::shared_ptr<LevelDB> &LevelDB::getCsrStatusDb() {
    CHECK_STATE(csrStatusDb)
    return csrStatusDb;
}


std::shared_ptr<LevelDB> LevelDB::levelDb = nullptr;

std::shared_ptr<LevelDB> LevelDB::csrDb = nullptr;
kladkogex's avatar
kladkogex committed
261

262
std::shared_ptr<LevelDB> LevelDB::csrStatusDb = nullptr;
kladkogex's avatar
kladkogex committed
263

kladko's avatar
kladko committed
264
string LevelDB::sgx_data_folder;
265 266 267

bool LevelDB::isInited = false;

kladko's avatar
kladko committed
268
void LevelDB::initDataFolderAndDBs() {
269 270 271 272 273
    CHECK_STATE(!isInited)
    isInited = true;

    spdlog::info("Initing wallet database ... ");

kladko's avatar
kladko committed
274
    char cwd[PATH_MAX];
275

kladko's avatar
kladko committed
276
    if (getcwd(cwd, sizeof(cwd)) == NULL) {
277 278
        spdlog::error("Could not get current working directory.");
        throw SGXException(COULD_NOT_GET_WORKING_DIRECTORY, "Could not get current working directory.");
kladko's avatar
kladko committed
279 280 281 282 283 284
    }

    sgx_data_folder = string(cwd) + "/" + SGXDATA_FOLDER;

    struct stat info;
    if (stat(sgx_data_folder.c_str(), &info) !=0 ){
285 286 287 288
        spdlog::info("sgx_data folder does not exist. Creating ...");

        if (system(("mkdir " + sgx_data_folder).c_str()) == 0){
            spdlog::info("Successfully created sgx_data folder");
kladko's avatar
kladko committed
289 290
        }
        else{
291 292
            spdlog::error("Could not create sgx_data folder.");
            throw SGXException(ERROR_CREATING_SGX_DATA_FOLDER, "Could not create sgx_data folder.");
kladko's avatar
kladko committed
293 294 295
        }
    }

296 297
    spdlog::info("Opening wallet databases");

kladko's avatar
kladko committed
298
    auto dbName = sgx_data_folder +  WALLETDB_NAME;
299 300
    levelDb = make_shared<LevelDB>(dbName);

kladko's avatar
kladko committed
301
    auto csr_dbname = sgx_data_folder + "CSR_DB";
302 303
    csrDb = make_shared<LevelDB>(csr_dbname);

kladko's avatar
kladko committed
304
    auto csr_status_dbname = sgx_data_folder + "CSR_STATUS_DB";
305 306
    csrStatusDb = make_shared<LevelDB>(csr_status_dbname);

307
    spdlog::info("Successfully opened databases");
308
}
kladko's avatar
kladko committed
309 310 311 312

const string &LevelDB::getSgxDataFolder() {
    return sgx_data_folder;
}