Added redis backend support for storage alternative to empower nextgen observer

This commit is contained in:
Matt Hess
2025-12-22 18:55:27 +00:00
parent e68704dc43
commit 33bb2a81a6
11 changed files with 1243 additions and 19 deletions
+114 -18
View File
@@ -34,6 +34,9 @@
#include "json_parsers.h"
#include "crypto.h"
#include "hardforks/hardforks.h"
#ifdef WITH_REDIS
#include "redis_storage.h"
#endif
#include <fstream>
#if !defined(_MSC_VER) || !defined(__cppcheck__)
@@ -3298,51 +3301,143 @@ void SideChain::clear_checkpoints()
void SideChain::save_checkpoints() const
{
const std::string path = DATA_DIR + "p2pool_checkpoints.dat";
#ifdef WITH_REDIS
ReadLock lock(m_checkpointsLock);
if (m_checkpoints.empty()) {
get_redis_storage().del("checkpoints");
return;
}
// Build binary buffer matching file format
std::vector<uint8_t> buf;
const uint32_t version = 1;
const uint32_t count = static_cast<uint32_t>(m_checkpoints.size());
buf.insert(buf.end(), reinterpret_cast<const uint8_t*>(&version),
reinterpret_cast<const uint8_t*>(&version) + sizeof(version));
buf.insert(buf.end(), reinterpret_cast<const uint8_t*>(&count),
reinterpret_cast<const uint8_t*>(&count) + sizeof(count));
for (const Checkpoint& cp : m_checkpoints) {
buf.insert(buf.end(), reinterpret_cast<const uint8_t*>(&cp.height),
reinterpret_cast<const uint8_t*>(&cp.height) + sizeof(cp.height));
buf.insert(buf.end(), cp.id.h, cp.id.h + HASH_SIZE);
buf.insert(buf.end(), reinterpret_cast<const uint8_t*>(&cp.cumulative_difficulty),
reinterpret_cast<const uint8_t*>(&cp.cumulative_difficulty) + sizeof(cp.cumulative_difficulty));
}
if (!get_redis_storage().set("checkpoints", buf.data(), buf.size())) {
LOGWARN(1, "Failed to save checkpoints to Redis");
return;
}
LOGINFO(3, "Saved " << count << " checkpoints to Redis");
#else
const std::string path = DATA_DIR + "p2pool_checkpoints.dat";
ReadLock lock(m_checkpointsLock);
if (m_checkpoints.empty()) {
// No checkpoints to save, remove old file if exists
remove(path.c_str());
return;
}
std::ofstream f(path, std::ios::binary);
if (!f.is_open()) {
LOGWARN(1, "Failed to save checkpoints to " << path);
return;
}
// Write version marker for future compatibility
const uint32_t version = 1;
f.write(reinterpret_cast<const char*>(&version), sizeof(version));
// Write checkpoint count
const uint32_t count = static_cast<uint32_t>(m_checkpoints.size());
f.write(reinterpret_cast<const char*>(&count), sizeof(count));
// Write each checkpoint
for (const Checkpoint& cp : m_checkpoints) {
f.write(reinterpret_cast<const char*>(&cp.height), sizeof(cp.height));
f.write(reinterpret_cast<const char*>(cp.id.h), HASH_SIZE);
f.write(reinterpret_cast<const char*>(&cp.cumulative_difficulty), sizeof(cp.cumulative_difficulty));
}
f.close();
LOGINFO(3, "Saved " << count << " checkpoints to " << path);
#endif
}
void SideChain::load_checkpoints()
{
#ifdef WITH_REDIS
std::vector<uint8_t> buf;
if (!get_redis_storage().get("checkpoints", buf)) {
LOGINFO(3, "No checkpoints found in Redis (normal for first run)");
return;
}
if (buf.size() < 8) {
LOGWARN(1, "Checkpoint data too small, ignoring");
return;
}
size_t offset = 0;
// Read version
uint32_t version = *reinterpret_cast<const uint32_t*>(buf.data() + offset);
offset += sizeof(version);
if (version != 1) {
LOGWARN(1, "Unknown checkpoint version " << version << ", ignoring");
return;
}
// Read count
uint32_t count = *reinterpret_cast<const uint32_t*>(buf.data() + offset);
offset += sizeof(count);
if (count > 100) {
LOGWARN(1, "Suspicious checkpoint count " << count << ", ignoring");
return;
}
WriteLock lock(m_checkpointsLock);
m_checkpoints.clear();
for (uint32_t i = 0; i < count; ++i) {
if (offset + sizeof(uint64_t) + HASH_SIZE + sizeof(difficulty_type) > buf.size()) {
LOGWARN(1, "Checkpoint data corrupted at entry " << i << ", discarding");
m_checkpoints.clear();
return;
}
Checkpoint cp;
memcpy(&cp.height, buf.data() + offset, sizeof(cp.height));
offset += sizeof(cp.height);
memcpy(cp.id.h, buf.data() + offset, HASH_SIZE);
offset += HASH_SIZE;
memcpy(&cp.cumulative_difficulty, buf.data() + offset, sizeof(cp.cumulative_difficulty));
offset += sizeof(cp.cumulative_difficulty);
m_checkpoints.push_back(cp);
}
LOGINFO(1, "Loaded " << count << " checkpoints from Redis");
if (!m_checkpoints.empty()) {
LOGINFO(1, "Latest anchor point: height " << m_checkpoints.back().height <<
", id " << m_checkpoints.back().id);
m_checkpointsNeedValidation = true;
}
#else
const std::string path = DATA_DIR + "p2pool_checkpoints.dat";
std::ifstream f(path, std::ios::binary);
if (!f.is_open()) {
LOGINFO(3, "No checkpoint file found at " << path << " (normal for first run)");
return;
}
// Read version
uint32_t version = 0;
f.read(reinterpret_cast<char*>(&version), sizeof(version));
@@ -3350,42 +3445,43 @@ void SideChain::load_checkpoints()
LOGWARN(1, "Unknown checkpoint file version " << version << ", ignoring");
return;
}
// Read checkpoint count
uint32_t count = 0;
f.read(reinterpret_cast<char*>(&count), sizeof(count));
if (count > 100) {
LOGWARN(1, "Checkpoint file has suspicious count " << count << ", ignoring");
return;
}
WriteLock lock(m_checkpointsLock);
m_checkpoints.clear();
for (uint32_t i = 0; i < count; ++i) {
Checkpoint cp;
f.read(reinterpret_cast<char*>(&cp.height), sizeof(cp.height));
f.read(reinterpret_cast<char*>(cp.id.h), HASH_SIZE);
f.read(reinterpret_cast<char*>(&cp.cumulative_difficulty), sizeof(cp.cumulative_difficulty));
if (f.fail()) {
LOGWARN(1, "Checkpoint file corrupted at entry " << i << ", discarding");
m_checkpoints.clear();
return;
}
m_checkpoints.push_back(cp);
}
f.close();
LOGINFO(1, "Loaded " << count << " checkpoints from " << path);
if (!m_checkpoints.empty()) {
LOGINFO(1, "Latest anchor point: height " << m_checkpoints.back().height <<
LOGINFO(1, "Latest anchor point: height " << m_checkpoints.back().height <<
", id " << m_checkpoints.back().id);
m_checkpointsNeedValidation = true;
}
#endif
}
bool SideChain::validate_loaded_checkpoints()