|
| 1 | +// Copyright (c) 2012-2018, The CryptoNote developers, The Bytecoin developers. |
| 2 | +// Licensed under the GNU Lesser General Public License. See LICENSE for details. |
| 3 | + |
| 4 | +#include "Archive.hpp" |
| 5 | +#include <boost/lexical_cast.hpp> |
| 6 | +#include <iostream> |
| 7 | +#include "CryptoNoteTools.hpp" |
| 8 | +#include "Currency.hpp" |
| 9 | +#include "common/StringTools.hpp" |
| 10 | +#include "common/Varint.hpp" |
| 11 | +#include "crypto/crypto.hpp" |
| 12 | +#include "platform/Time.hpp" |
| 13 | +#include "seria/BinaryInputStream.hpp" |
| 14 | +#include "seria/BinaryOutputStream.hpp" |
| 15 | + |
| 16 | +using namespace bytecoin; |
| 17 | +using namespace platform; |
| 18 | + |
| 19 | +static const std::string RECORDS_PREFIX = "r"; |
| 20 | +static const std::string HASHES_PREFIX = "h"; |
| 21 | + |
| 22 | +const std::string Archive::BLOCK("b"); |
| 23 | +const std::string Archive::TRANSACTION("t"); |
| 24 | +const std::string Archive::CHECKPOINT("c"); |
| 25 | + |
| 26 | +Archive::Archive(bool read_only, const std::string &path) : read_only(read_only) { |
| 27 | + try { |
| 28 | + m_db = std::make_unique<DB>(read_only, path); |
| 29 | + if (!m_db->get("$unique_id", unique_id)) { |
| 30 | + DB::Cursor cur = m_db->begin(std::string()); |
| 31 | + if (!cur.end()) |
| 32 | + throw std::runtime_error("Archive database format unknown version, please delete " + m_db->get_path()); |
| 33 | + unique_id = common::pod_to_hex(crypto::random_keypair().public_key); |
| 34 | + m_db->put("$unique_id", unique_id, true); |
| 35 | + std::cout << "Created archive with unique id: " << unique_id << std::endl; |
| 36 | + } |
| 37 | + DB::Cursor cur2 = m_db->rbegin(RECORDS_PREFIX); |
| 38 | + next_record_id = cur2.end() ? 0 : 1 + common::read_varint_sqlite4(cur2.get_suffix()); |
| 39 | + } catch (const std::exception &) { |
| 40 | + if (read_only) |
| 41 | + m_db = nullptr; |
| 42 | + else |
| 43 | + throw; |
| 44 | + } |
| 45 | +} |
| 46 | + |
| 47 | +// struct Record { |
| 48 | +// std::string type; |
| 49 | +// BinaryArray data; |
| 50 | +// Timestamp timestamp; |
| 51 | +//}; |
| 52 | + |
| 53 | +void Archive::add(const std::string &type, |
| 54 | + const BinaryArray &data, |
| 55 | + const Hash &hash, |
| 56 | + const std::string &source_address) { |
| 57 | + if (!m_db || read_only || source_address.empty()) |
| 58 | + return; |
| 59 | + std::cout << "Adding to archive: " << type << " hash=" << hash << " size=" << data.size() |
| 60 | + << " source_address=" << source_address << std::endl; |
| 61 | + auto hash_key = HASHES_PREFIX + DB::to_binary_key(hash.data, sizeof(hash.data)); |
| 62 | + DB::Value value; |
| 63 | + if (!m_db->get(hash_key, value)) |
| 64 | + m_db->put(hash_key, data, true); |
| 65 | + api::bytecoind::GetArchive::ArchiveRecord rec; |
| 66 | + rec.timestamp = now_unix_timestamp(&rec.timestamp_usec); |
| 67 | + rec.type = type; |
| 68 | + rec.hash = hash; |
| 69 | + rec.source_address = source_address; |
| 70 | + m_db->put(RECORDS_PREFIX + common::write_varint_sqlite4(next_record_id), seria::to_binary(rec), true); |
| 71 | + next_record_id += 1; |
| 72 | +} |
| 73 | + |
| 74 | +void Archive::db_commit() { |
| 75 | + if (!m_db || read_only) |
| 76 | + return; |
| 77 | + m_db->commit_db_txn(); |
| 78 | +} |
| 79 | + |
| 80 | +void Archive::read_archive(api::bytecoind::GetArchive::Request &&req, api::bytecoind::GetArchive::Response &resp) { |
| 81 | + if (req.archive_id != unique_id) { |
| 82 | + api::bytecoind::GetArchive::Error err; |
| 83 | + err.code = api::bytecoind::GetArchive::WRONG_ARCHIVE_ID; |
| 84 | + err.message = "Archive id changed"; |
| 85 | + err.archive_id = unique_id; |
| 86 | + throw err; |
| 87 | + } |
| 88 | + resp.from_record = req.from_record; |
| 89 | + if (resp.from_record > next_record_id) |
| 90 | + resp.from_record = next_record_id; |
| 91 | + if (req.max_count > api::bytecoind::GetArchive::Request::MAX_COUNT) |
| 92 | + req.max_count = api::bytecoind::GetArchive::Request::MAX_COUNT; |
| 93 | + if (!m_db) |
| 94 | + return; |
| 95 | + resp.records.reserve(static_cast<size_t>(req.max_count)); |
| 96 | + for (DB::Cursor cur = m_db->begin(RECORDS_PREFIX, common::write_varint_sqlite4(resp.from_record)); !cur.end(); |
| 97 | + cur.next()) { |
| 98 | + if (resp.records.size() >= req.max_count) |
| 99 | + break; |
| 100 | + api::bytecoind::GetArchive::ArchiveRecord rec; |
| 101 | + seria::from_binary(rec, cur.get_value_array()); |
| 102 | + resp.records.push_back(rec); |
| 103 | + std::string str_hash = common::pod_to_hex(rec.hash); |
| 104 | + const auto hash_key = HASHES_PREFIX + DB::to_binary_key(rec.hash.data, sizeof(rec.hash.data)); |
| 105 | + if (rec.type == BLOCK) { |
| 106 | + if (resp.blocks.count(str_hash) == 0) { |
| 107 | + BinaryArray data; |
| 108 | + invariant(m_db->get(hash_key, data), ""); |
| 109 | + api::bytecoind::GetArchive::ArchiveBlock &bl = resp.blocks[str_hash]; |
| 110 | + RawBlock raw_block; |
| 111 | + seria::from_binary(raw_block, data); |
| 112 | + Block block; |
| 113 | + invariant(block.from_raw_block(raw_block), ""); |
| 114 | + bl.raw_header = block.header; |
| 115 | + bl.raw_transactions.reserve(block.transactions.size()); |
| 116 | + for (size_t i = 0; i != block.transactions.size(); ++i) { |
| 117 | + bl.raw_transactions.push_back(static_cast<TransactionPrefix &>(block.transactions.at(i))); |
| 118 | + bl.transaction_binary_sizes.push_back(static_cast<uint32_t>(raw_block.transactions.at(i).size())); |
| 119 | + } |
| 120 | + bl.base_transaction_hash = get_transaction_hash(block.header.base_transaction); |
| 121 | + } |
| 122 | + } |
| 123 | + if (rec.type == TRANSACTION) { |
| 124 | + if (resp.transactions.count(str_hash) == 0) { |
| 125 | + BinaryArray data; |
| 126 | + invariant(m_db->get(hash_key, data), ""); |
| 127 | + TransactionPrefix &tr = resp.transactions[str_hash]; |
| 128 | + Transaction transaction; |
| 129 | + seria::from_binary(transaction, data); |
| 130 | + tr = static_cast<TransactionPrefix &>(transaction); |
| 131 | + } |
| 132 | + } |
| 133 | + if (rec.type == CHECKPOINT) { |
| 134 | + if (resp.checkpoints.count(str_hash) == 0) { |
| 135 | + BinaryArray data; |
| 136 | + invariant(m_db->get(hash_key, data), ""); |
| 137 | + SignedCheckPoint &ch = resp.checkpoints[str_hash]; |
| 138 | + seria::from_binary(ch, data); |
| 139 | + } |
| 140 | + } |
| 141 | + } |
| 142 | +} |
0 commit comments