Skip to content

Commit f888b94

Browse files
committed
THE checkpoint: prune orphaned on startup, verify against blockchain
TheCheckpointStore new methods: prune_unverified() — removes all checkpoints with status != verified verify_all(block_exists_fn) — checks each pending checkpoint against blockchain RPC, marks as verified (block in best chain) or orphaned remove() — delete single checkpoint On startup: prune_unverified() clears stale checkpoints from previous runs where blocks were orphaned (e.g., race condition with p2pool submitauxblock vs c2pool submitblock on same merged chain).
1 parent e8322d9 commit f888b94

2 files changed

Lines changed: 64 additions & 1 deletion

File tree

src/c2pool/c2pool_refactored.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1265,7 +1265,11 @@ int main(int argc, char* argv[]) {
12651265
<< " root=" << cp.the_state_root.GetHex().substr(0, 16) << "...";
12661266
}
12671267
);
1268-
LOG_INFO << "[THE] Checkpoint store: " << the_store->count() << " existing";
1268+
// On startup: prune checkpoints whose blocks are orphaned
1269+
size_t pruned = the_store->prune_unverified();
1270+
if (pruned > 0)
1271+
LOG_INFO << "[THE] Pruned " << pruned << " unverified checkpoints from previous runs";
1272+
LOG_INFO << "[THE] Checkpoint store: " << the_store->count() << " verified";
12691273
} else {
12701274
LOG_WARNING << "[Pool] Failed to open found blocks LevelDB at " << fblk_db_path;
12711275
}

src/c2pool/storage/the_checkpoint.hpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,65 @@ class TheCheckpointStore {
204204
return result;
205205
}
206206

207+
/// Remove a checkpoint
208+
bool remove(const std::string& chain, uint64_t block_height) {
209+
auto key = make_key(chain, block_height);
210+
return m_store.remove(key);
211+
}
212+
213+
/// Remove all checkpoints with status != 1 (verified)
214+
size_t prune_unverified() {
215+
auto keys = m_store.list_keys("thecp:", 10000);
216+
size_t removed = 0;
217+
for (const auto& key : keys) {
218+
std::vector<uint8_t> data;
219+
if (m_store.get(key, data)) {
220+
auto cp = TheCheckpoint::deserialize(data);
221+
if (cp.status != 1) { // not verified
222+
m_store.remove(key);
223+
++removed;
224+
}
225+
}
226+
}
227+
return removed;
228+
}
229+
230+
/// Verify all pending checkpoints against a block-exists callback.
231+
/// The callback returns true if the block hash is in the best chain.
232+
/// Returns number of checkpoints verified or marked orphaned.
233+
size_t verify_all(std::function<bool(const std::string& chain,
234+
const std::string& block_hash)> block_exists_fn) {
235+
auto keys = m_store.list_keys("thecp:", 10000);
236+
size_t checked = 0;
237+
for (const auto& key : keys) {
238+
std::vector<uint8_t> data;
239+
if (!m_store.get(key, data)) continue;
240+
auto cp = TheCheckpoint::deserialize(data);
241+
if (cp.status == 1 || cp.status == 2) continue; // already decided
242+
243+
bool exists = false;
244+
try {
245+
exists = block_exists_fn(cp.chain, cp.block_hash);
246+
} catch (...) {
247+
continue; // RPC error — skip
248+
}
249+
250+
if (exists) {
251+
cp.status = 1; // verified — block is in best chain
252+
LOG_INFO << "[THE] Checkpoint VERIFIED: " << cp.chain
253+
<< " height=" << cp.block_height;
254+
} else {
255+
cp.status = 2; // orphaned — block not in best chain
256+
LOG_WARNING << "[THE] Checkpoint ORPHANED: " << cp.chain
257+
<< " height=" << cp.block_height
258+
<< " hash=" << cp.block_hash;
259+
}
260+
m_store.put(key, cp.serialize());
261+
++checked;
262+
}
263+
return checked;
264+
}
265+
207266
size_t count() {
208267
return m_store.list_keys("thecp:", 10000).size();
209268
}

0 commit comments

Comments
 (0)