mirror of
https://github.com/XRPLF/clio.git
synced 2026-07-31 02:50:20 +00:00
58
src/data/impl/InputFile.cpp
Normal file
58
src/data/impl/InputFile.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2025, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "data/impl/InputFile.hpp"
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <ios>
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace data::impl {
|
||||
|
||||
InputFile::InputFile(std::string const& path) : file_(path, std::ios::binary | std::ios::in)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
InputFile::isOpen() const
|
||||
{
|
||||
return file_.is_open();
|
||||
}
|
||||
|
||||
bool
|
||||
InputFile::readRaw(char* data, size_t size)
|
||||
{
|
||||
file_.read(data, size);
|
||||
shasum_.update(data, size);
|
||||
return not file_.fail();
|
||||
}
|
||||
|
||||
ripple::uint256
|
||||
InputFile::hash() const
|
||||
{
|
||||
auto sum = shasum_;
|
||||
return std::move(sum).finalize();
|
||||
}
|
||||
|
||||
} // namespace data::impl
|
||||
57
src/data/impl/InputFile.hpp
Normal file
57
src/data/impl/InputFile.hpp
Normal file
@@ -0,0 +1,57 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2025, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/Shasum.hpp"
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
|
||||
namespace data::impl {
|
||||
|
||||
class InputFile {
|
||||
std::ifstream file_;
|
||||
util::Sha256sum shasum_;
|
||||
|
||||
public:
|
||||
InputFile(std::string const& path);
|
||||
|
||||
bool
|
||||
isOpen() const;
|
||||
|
||||
template <typename T>
|
||||
bool
|
||||
read(T& t)
|
||||
{
|
||||
return readRaw(reinterpret_cast<char*>(&t), sizeof(T));
|
||||
}
|
||||
|
||||
bool
|
||||
readRaw(char* data, size_t size);
|
||||
|
||||
ripple::uint256
|
||||
hash() const;
|
||||
};
|
||||
} // namespace data::impl
|
||||
210
src/data/impl/LedgerCacheFile.cpp
Normal file
210
src/data/impl/LedgerCacheFile.cpp
Normal file
@@ -0,0 +1,210 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2025, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "data/impl/LedgerCacheFile.hpp"
|
||||
|
||||
#include "data/LedgerCache.hpp"
|
||||
#include "data/Types.hpp"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace data::impl {
|
||||
|
||||
using Hash = ripple::uint256;
|
||||
using Separator = std::array<char, 16>;
|
||||
static constexpr Separator kSEPARATOR = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
namespace {
|
||||
|
||||
std::expected<std::pair<ripple::uint256, LedgerCache::CacheEntry>, std::string>
|
||||
readCacheEntry(InputFile& file, size_t i)
|
||||
{
|
||||
ripple::uint256 key;
|
||||
if (not file.readRaw(reinterpret_cast<char*>(key.data()), ripple::base_uint<256>::bytes)) {
|
||||
return std::unexpected(fmt::format("Failed to read key at index {}", i));
|
||||
}
|
||||
|
||||
uint32_t seq{};
|
||||
if (not file.read(seq)) {
|
||||
return std::unexpected(fmt::format("Failed to read sequence at index {}", i));
|
||||
}
|
||||
|
||||
size_t blobSize{};
|
||||
if (not file.read(blobSize)) {
|
||||
return std::unexpected(fmt::format("Failed to read blob size at index {}", i));
|
||||
}
|
||||
|
||||
Blob blob(blobSize);
|
||||
if (not file.readRaw(reinterpret_cast<char*>(blob.data()), blobSize)) {
|
||||
return std::unexpected(fmt::format("Failed to read blob data at index {}", i));
|
||||
}
|
||||
return std::make_pair(key, LedgerCache::CacheEntry{.seq = seq, .blob = std::move(blob)});
|
||||
}
|
||||
|
||||
std::expected<void, std::string>
|
||||
verifySeparator(Separator const& s)
|
||||
{
|
||||
if (not std::ranges::all_of(s, [](char c) { return c == 0; })) {
|
||||
return std::unexpected{"Separator verification failed - data corruption detected"};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
LedgerCacheFile::LedgerCacheFile(std::string path) : path_(std::move(path))
|
||||
{
|
||||
}
|
||||
|
||||
std::expected<void, std::string>
|
||||
LedgerCacheFile::write(DataView dataView)
|
||||
{
|
||||
auto const newFilePath = fmt::format("{}.new", path_);
|
||||
auto file = OutputFile{newFilePath};
|
||||
if (not file.isOpen()) {
|
||||
return std::unexpected{fmt::format("Couldn't open file: {}", newFilePath)};
|
||||
}
|
||||
|
||||
Header const header{
|
||||
.latestSeq = dataView.latestSeq, .mapSize = dataView.map.size(), .deletedSize = dataView.deleted.size()
|
||||
};
|
||||
file.write(header);
|
||||
file.write(kSEPARATOR);
|
||||
|
||||
for (auto const& [k, v] : dataView.map) {
|
||||
file.write(k.data(), decltype(k)::bytes);
|
||||
file.write(v.seq);
|
||||
file.write(v.blob.size());
|
||||
file.writeRaw(reinterpret_cast<char const*>(v.blob.data()), v.blob.size());
|
||||
}
|
||||
file.write(kSEPARATOR);
|
||||
|
||||
for (auto const& [k, v] : dataView.deleted) {
|
||||
file.write(k.data(), decltype(k)::bytes);
|
||||
file.write(v.seq);
|
||||
file.write(v.blob.size());
|
||||
file.writeRaw(reinterpret_cast<char const*>(v.blob.data()), v.blob.size());
|
||||
}
|
||||
file.write(kSEPARATOR);
|
||||
auto const hash = file.hash();
|
||||
file.write(hash.data(), decltype(hash)::bytes);
|
||||
|
||||
try {
|
||||
std::filesystem::rename(newFilePath, path_);
|
||||
} catch (std::exception const& e) {
|
||||
return std::unexpected{fmt::format("Error moving cache file from {} to {}: {}", newFilePath, path_, e.what())};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::expected<LedgerCacheFile::Data, std::string>
|
||||
LedgerCacheFile::read(uint32_t minLatestSequence)
|
||||
{
|
||||
try {
|
||||
auto file = InputFile{path_};
|
||||
if (not file.isOpen()) {
|
||||
return std::unexpected{fmt::format("Couldn't open file: {}", path_)};
|
||||
}
|
||||
|
||||
Data result;
|
||||
|
||||
Header header{};
|
||||
if (not file.read(header)) {
|
||||
return std::unexpected{"Error reading cache header"};
|
||||
}
|
||||
if (header.version != kVERSION) {
|
||||
return std::unexpected{
|
||||
fmt::format("Cache has wrong version: expected {} found {}", kVERSION, header.version)
|
||||
};
|
||||
}
|
||||
if (header.latestSeq < minLatestSequence) {
|
||||
return std::unexpected{fmt::format("Latest sequence ({}) in the cache file is too low.", header.latestSeq)};
|
||||
}
|
||||
result.latestSeq = header.latestSeq;
|
||||
|
||||
Separator separator{};
|
||||
if (not file.readRaw(separator.data(), separator.size())) {
|
||||
return std::unexpected{"Error reading cache header"};
|
||||
}
|
||||
if (auto verificationResult = verifySeparator(separator); not verificationResult.has_value()) {
|
||||
return std::unexpected{std::move(verificationResult).error()};
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < header.mapSize; ++i) {
|
||||
auto cacheEntryExpected = readCacheEntry(file, i);
|
||||
if (not cacheEntryExpected.has_value()) {
|
||||
return std::unexpected{std::move(cacheEntryExpected).error()};
|
||||
}
|
||||
// Using insert with hint here to decrease insert operation complexity to the amortized constant instead of
|
||||
// logN
|
||||
result.map.insert(result.map.end(), std::move(cacheEntryExpected).value());
|
||||
}
|
||||
|
||||
if (not file.readRaw(separator.data(), separator.size())) {
|
||||
return std::unexpected{"Error reading separator"};
|
||||
}
|
||||
if (auto verificationResult = verifySeparator(separator); not verificationResult.has_value()) {
|
||||
return std::unexpected{std::move(verificationResult).error()};
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < header.deletedSize; ++i) {
|
||||
auto cacheEntryExpected = readCacheEntry(file, i);
|
||||
if (not cacheEntryExpected.has_value()) {
|
||||
return std::unexpected{std::move(cacheEntryExpected).error()};
|
||||
}
|
||||
result.deleted.insert(result.deleted.end(), std::move(cacheEntryExpected).value());
|
||||
}
|
||||
|
||||
if (not file.readRaw(separator.data(), separator.size())) {
|
||||
return std::unexpected{"Error reading separator"};
|
||||
}
|
||||
if (auto verificationResult = verifySeparator(separator); not verificationResult.has_value()) {
|
||||
return std::unexpected{std::move(verificationResult).error()};
|
||||
}
|
||||
|
||||
auto const dataHash = file.hash();
|
||||
ripple::uint256 hashFromFile{};
|
||||
if (not file.readRaw(reinterpret_cast<char*>(hashFromFile.data()), decltype(hashFromFile)::bytes)) {
|
||||
return std::unexpected{"Error reading hash"};
|
||||
}
|
||||
|
||||
if (dataHash != hashFromFile) {
|
||||
return std::unexpected{"Hash file corruption detected"};
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (std::exception const& e) {
|
||||
return std::unexpected{fmt::format(" Error reading cache file: {}", e.what())};
|
||||
} catch (...) {
|
||||
return std::unexpected{fmt::format(" Error reading cache file")};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace data::impl
|
||||
70
src/data/impl/LedgerCacheFile.hpp
Normal file
70
src/data/impl/LedgerCacheFile.hpp
Normal file
@@ -0,0 +1,70 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2025, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "data/LedgerCache.hpp"
|
||||
#include "data/impl/InputFile.hpp"
|
||||
#include "data/impl/OutputFile.hpp"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
namespace data::impl {
|
||||
|
||||
class LedgerCacheFile {
|
||||
public:
|
||||
struct Header {
|
||||
uint32_t version = kVERSION;
|
||||
uint32_t latestSeq{};
|
||||
uint64_t mapSize{};
|
||||
uint64_t deletedSize{};
|
||||
};
|
||||
|
||||
private:
|
||||
static constexpr uint32_t kVERSION = 1;
|
||||
|
||||
std::string path_;
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
struct DataBase {
|
||||
uint32_t latestSeq{0};
|
||||
T map;
|
||||
T deleted;
|
||||
};
|
||||
|
||||
using DataView = DataBase<LedgerCache::CacheMap const&>;
|
||||
using Data = DataBase<LedgerCache::CacheMap>;
|
||||
|
||||
LedgerCacheFile(std::string path);
|
||||
|
||||
std::expected<void, std::string>
|
||||
write(DataView dataView);
|
||||
|
||||
std::expected<Data, std::string>
|
||||
read(uint32_t minLatestSequence);
|
||||
};
|
||||
|
||||
} // namespace data::impl
|
||||
62
src/data/impl/OutputFile.cpp
Normal file
62
src/data/impl/OutputFile.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2025, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "data/impl/OutputFile.hpp"
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <ios>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace data::impl {
|
||||
|
||||
OutputFile::OutputFile(std::string const& path) : file_(path, std::ios::binary | std::ios::out)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
OutputFile::isOpen() const
|
||||
{
|
||||
return file_.is_open();
|
||||
}
|
||||
|
||||
void
|
||||
OutputFile::writeRaw(char const* data, size_t size)
|
||||
{
|
||||
writeToFile(data, size);
|
||||
}
|
||||
|
||||
void
|
||||
OutputFile::writeToFile(char const* data, size_t size)
|
||||
{
|
||||
file_.write(data, size);
|
||||
shasum_.update(data, size);
|
||||
}
|
||||
|
||||
ripple::uint256
|
||||
OutputFile::hash() const
|
||||
{
|
||||
auto sum = shasum_;
|
||||
return std::move(sum).finalize();
|
||||
}
|
||||
|
||||
} // namespace data::impl
|
||||
68
src/data/impl/OutputFile.hpp
Normal file
68
src/data/impl/OutputFile.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2025, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/Shasum.hpp"
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
namespace data::impl {
|
||||
|
||||
class OutputFile {
|
||||
std::ofstream file_;
|
||||
util::Sha256sum shasum_;
|
||||
|
||||
public:
|
||||
OutputFile(std::string const& path);
|
||||
|
||||
bool
|
||||
isOpen() const;
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
write(T&& data)
|
||||
{
|
||||
writeRaw(reinterpret_cast<char const*>(&data), sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
write(T const* data, size_t const size)
|
||||
{
|
||||
writeRaw(reinterpret_cast<char const*>(data), size);
|
||||
}
|
||||
|
||||
void
|
||||
writeRaw(char const* data, size_t size);
|
||||
|
||||
ripple::uint256
|
||||
hash() const;
|
||||
|
||||
private:
|
||||
void
|
||||
writeToFile(char const* data, size_t size);
|
||||
};
|
||||
|
||||
} // namespace data::impl
|
||||
Reference in New Issue
Block a user