Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ template <typename Store, typename HashingPolicy> class ContentAddressedAppendOn
using UnwindBlockCallback = std::function<void(TypedResponse<UnwindResponse>&)>;
using FinalizeBlockCallback = EmptyResponseCallback;
using GetBlockForIndexCallback = std::function<void(TypedResponse<BlockForIndexResponse>&)>;
using CheckpointCallback = EmptyResponseCallback;
using CheckpointCallback = std::function<void(TypedResponse<CheckpointResponse>&)>;
using CheckpointCommitCallback = EmptyResponseCallback;
using CheckpointRevertCallback = EmptyResponseCallback;

Expand Down Expand Up @@ -254,8 +254,11 @@ template <typename Store, typename HashingPolicy> class ContentAddressedAppendOn
void checkpoint(const CheckpointCallback& on_completion);
void commit_checkpoint(const CheckpointCommitCallback& on_completion);
void revert_checkpoint(const CheckpointRevertCallback& on_completion);
void commit_all_checkpoints(const CheckpointCommitCallback& on_completion);
void revert_all_checkpoints(const CheckpointRevertCallback& on_completion);
void commit_all_checkpoints_to(const CheckpointCommitCallback& on_completion);
void revert_all_checkpoints_to(const CheckpointRevertCallback& on_completion);
void commit_to_depth(uint32_t target_depth, const CheckpointCommitCallback& on_completion);
void revert_to_depth(uint32_t target_depth, const CheckpointRevertCallback& on_completion);
uint32_t checkpoint_depth() const;

protected:
using ReadTransaction = typename Store::ReadTransaction;
Expand Down Expand Up @@ -1002,7 +1005,11 @@ void ContentAddressedAppendOnlyTree<Store, HashingPolicy>::rollback(const Rollba
template <typename Store, typename HashingPolicy>
void ContentAddressedAppendOnlyTree<Store, HashingPolicy>::checkpoint(const CheckpointCallback& on_completion)
{
auto job = [=, this]() { execute_and_report([=, this]() { store_->checkpoint(); }, on_completion); };
auto job = [=, this]() {
execute_and_report<CheckpointResponse>(
[=, this](TypedResponse<CheckpointResponse>& response) { response.inner.depth = store_->checkpoint(); },
on_completion);
};
workers_->enqueue(job);
}

Expand All @@ -1023,21 +1030,46 @@ void ContentAddressedAppendOnlyTree<Store, HashingPolicy>::revert_checkpoint(
}

template <typename Store, typename HashingPolicy>
void ContentAddressedAppendOnlyTree<Store, HashingPolicy>::commit_all_checkpoints(
void ContentAddressedAppendOnlyTree<Store, HashingPolicy>::commit_all_checkpoints_to(
const CheckpointCommitCallback& on_completion)
{
auto job = [=, this]() { execute_and_report([=, this]() { store_->commit_all_checkpoints(); }, on_completion); };
auto job = [=, this]() { execute_and_report([=, this]() { store_->commit_all_checkpoints_to(); }, on_completion); };
workers_->enqueue(job);
}

template <typename Store, typename HashingPolicy>
void ContentAddressedAppendOnlyTree<Store, HashingPolicy>::revert_all_checkpoints(
void ContentAddressedAppendOnlyTree<Store, HashingPolicy>::revert_all_checkpoints_to(
const CheckpointRevertCallback& on_completion)
{
auto job = [=, this]() { execute_and_report([=, this]() { store_->revert_all_checkpoints(); }, on_completion); };
auto job = [=, this]() { execute_and_report([=, this]() { store_->revert_all_checkpoints_to(); }, on_completion); };
workers_->enqueue(job);
}

template <typename Store, typename HashingPolicy>
void ContentAddressedAppendOnlyTree<Store, HashingPolicy>::commit_to_depth(
uint32_t target_depth, const CheckpointCommitCallback& on_completion)
{
auto job = [=, this]() {
execute_and_report([=, this]() { store_->commit_to_depth(target_depth); }, on_completion);
};
workers_->enqueue(job);
}

template <typename Store, typename HashingPolicy>
void ContentAddressedAppendOnlyTree<Store, HashingPolicy>::revert_to_depth(
uint32_t target_depth, const CheckpointRevertCallback& on_completion)
{
auto job = [=, this]() {
execute_and_report([=, this]() { store_->revert_to_depth(target_depth); }, on_completion);
};
workers_->enqueue(job);
}

template <typename Store, typename HashingPolicy>
uint32_t ContentAddressedAppendOnlyTree<Store, HashingPolicy>::checkpoint_depth() const
{
return store_->checkpoint_depth();
}
template <typename Store, typename HashingPolicy>
void ContentAddressedAppendOnlyTree<Store, HashingPolicy>::remove_historic_block(
const block_number_t& blockNumber, const RemoveHistoricBlockCallback& on_completion)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2171,7 +2171,7 @@ TEST_F(PersistedContentAddressedAppendOnlyTreeTest, can_checkpoint_and_revert_fo
commit_checkpoint_tree(tree, false);
}

TEST_F(PersistedContentAddressedAppendOnlyTreeTest, can_commit_all_checkpoints)
TEST_F(PersistedContentAddressedAppendOnlyTreeTest, can_commit_all_checkpoints_to)
{
constexpr size_t depth = 10;
uint32_t blockSize = 16;
Expand Down Expand Up @@ -2223,7 +2223,7 @@ TEST_F(PersistedContentAddressedAppendOnlyTreeTest, can_commit_all_checkpoints)
commit_checkpoint_tree(tree, false);
}

TEST_F(PersistedContentAddressedAppendOnlyTreeTest, can_revert_all_checkpoints)
TEST_F(PersistedContentAddressedAppendOnlyTreeTest, can_revert_all_checkpoints_to)
{
constexpr size_t depth = 10;
uint32_t blockSize = 16;
Expand Down Expand Up @@ -2274,3 +2274,95 @@ TEST_F(PersistedContentAddressedAppendOnlyTreeTest, can_revert_all_checkpoints)
revert_checkpoint_tree(tree, false);
commit_checkpoint_tree(tree, false);
}

TEST_F(PersistedContentAddressedAppendOnlyTreeTest, can_commit_to_depth)
{
constexpr size_t depth = 10;
uint32_t blockSize = 16;
std::string name = random_string();
ThreadPoolPtr pool = make_thread_pool(1);
LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);

{
std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
TreeType tree(std::move(store), pool);
std::vector<fr> values = create_values(blockSize);
add_values(tree, values);
commit_tree(tree);
}

std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
TreeType tree(std::move(store), pool);

// Capture initial state
fr_sibling_path initial_path = get_sibling_path(tree, 0);

// Depth 1
checkpoint_tree(tree);
add_values(tree, create_values(blockSize));
fr_sibling_path after_depth1_path = get_sibling_path(tree, 0);

// Depth 2
checkpoint_tree(tree);
add_values(tree, create_values(blockSize));

// Depth 3
checkpoint_tree(tree);
add_values(tree, create_values(blockSize));
fr_sibling_path after_depth3_path = get_sibling_path(tree, 0);

// Commit depths 3 and 2 into depth 1, leaving depth at 1
commit_tree_to_depth(tree, 1);

// Data from all depths should be present
check_sibling_path(tree, 0, after_depth3_path);

// Revert depth 1 — should go back to initial state
revert_checkpoint_tree(tree);
check_sibling_path(tree, 0, initial_path);
}

TEST_F(PersistedContentAddressedAppendOnlyTreeTest, can_revert_to_depth)
{
constexpr size_t depth = 10;
uint32_t blockSize = 16;
std::string name = random_string();
ThreadPoolPtr pool = make_thread_pool(1);
LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);

{
std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
TreeType tree(std::move(store), pool);
std::vector<fr> values = create_values(blockSize);
add_values(tree, values);
commit_tree(tree);
}

std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
TreeType tree(std::move(store), pool);

// Depth 1
checkpoint_tree(tree);
add_values(tree, create_values(blockSize));
fr_sibling_path after_depth1_path = get_sibling_path(tree, 0);

// Depth 2
checkpoint_tree(tree);
add_values(tree, create_values(blockSize));

// Depth 3
checkpoint_tree(tree);
add_values(tree, create_values(blockSize));

// Revert depths 3 and 2, leaving depth at 1
revert_tree_to_depth(tree, 1);

// Should be back to after depth 1 state
check_sibling_path(tree, 0, after_depth1_path);

// Depth 1 still active — commit it
commit_checkpoint_tree(tree);

// Should still have depth 1 data
check_sibling_path(tree, 0, after_depth1_path);
}
Original file line number Diff line number Diff line change
Expand Up @@ -191,11 +191,14 @@ template <typename LeafValueType> class ContentAddressedCachedTreeStore {

std::optional<block_number_t> find_block_for_index(const index_t& index, ReadTransaction& tx) const;

void checkpoint();
uint32_t checkpoint();
void revert_checkpoint();
void commit_checkpoint();
void revert_all_checkpoints();
void commit_all_checkpoints();
void revert_all_checkpoints_to();
void commit_all_checkpoints_to();
void commit_to_depth(uint32_t depth);
void revert_to_depth(uint32_t depth);
uint32_t checkpoint_depth() const;

private:
using Cache = ContentAddressedCache<LeafValueType>;
Expand Down Expand Up @@ -276,10 +279,10 @@ ContentAddressedCachedTreeStore<LeafValueType>::ContentAddressedCachedTreeStore(
// These checkpoint apis modify the cache's internal state.
// They acquire the mutex to prevent races with concurrent read/write operations (e.g., when C++ AVM simulation
// runs on a worker thread while TypeScript calls revert_checkpoint from a timeout handler).
template <typename LeafValueType> void ContentAddressedCachedTreeStore<LeafValueType>::checkpoint()
template <typename LeafValueType> uint32_t ContentAddressedCachedTreeStore<LeafValueType>::checkpoint()
{
std::unique_lock lock(mtx_);
cache_.checkpoint();
return cache_.checkpoint();
}

template <typename LeafValueType> void ContentAddressedCachedTreeStore<LeafValueType>::revert_checkpoint()
Expand All @@ -294,18 +297,36 @@ template <typename LeafValueType> void ContentAddressedCachedTreeStore<LeafValue
cache_.commit();
}

template <typename LeafValueType> void ContentAddressedCachedTreeStore<LeafValueType>::revert_all_checkpoints()
template <typename LeafValueType> void ContentAddressedCachedTreeStore<LeafValueType>::revert_all_checkpoints_to()
{
std::unique_lock lock(mtx_);
cache_.revert_all();
}

template <typename LeafValueType> void ContentAddressedCachedTreeStore<LeafValueType>::commit_all_checkpoints()
template <typename LeafValueType> void ContentAddressedCachedTreeStore<LeafValueType>::commit_all_checkpoints_to()
{
std::unique_lock lock(mtx_);
cache_.commit_all();
}

template <typename LeafValueType> void ContentAddressedCachedTreeStore<LeafValueType>::commit_to_depth(uint32_t depth)
{
std::unique_lock lock(mtx_);
cache_.commit_to_depth(depth);
}

template <typename LeafValueType> void ContentAddressedCachedTreeStore<LeafValueType>::revert_to_depth(uint32_t depth)
{
std::unique_lock lock(mtx_);
cache_.revert_to_depth(depth);
}

template <typename LeafValueType> uint32_t ContentAddressedCachedTreeStore<LeafValueType>::checkpoint_depth() const
{
std::unique_lock lock(mtx_);
return cache_.depth();
}

template <typename LeafValueType>
index_t ContentAddressedCachedTreeStore<LeafValueType>::constrain_tree_size_to_only_committed(
const RequestContext& requestContext, ReadTransaction& tx) const
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,14 @@ template <typename LeafValueType> class ContentAddressedCache {
ContentAddressedCache& operator=(ContentAddressedCache&& other) noexcept = default;
bool operator==(const ContentAddressedCache& other) const = default;

void checkpoint();
uint32_t checkpoint();
void revert();
void commit();
void revert_all();
void commit_all();
void commit_to_depth(uint32_t depth);
void revert_to_depth(uint32_t depth);
uint32_t depth() const;

void reset(uint32_t depth);
std::pair<bool, index_t> find_low_value(const uint256_t& new_leaf_key,
Expand Down Expand Up @@ -126,9 +129,10 @@ template <typename LeafValueType> ContentAddressedCache<LeafValueType>::ContentA
reset(depth);
}

template <typename LeafValueType> void ContentAddressedCache<LeafValueType>::checkpoint()
template <typename LeafValueType> uint32_t ContentAddressedCache<LeafValueType>::checkpoint()
{
journals_.emplace_back(Journal(meta_));
return static_cast<uint32_t>(journals_.size());
}

template <typename LeafValueType> void ContentAddressedCache<LeafValueType>::revert()
Expand Down Expand Up @@ -240,6 +244,31 @@ template <typename LeafValueType> void ContentAddressedCache<LeafValueType>::rev
revert();
}
}
template <typename LeafValueType> uint32_t ContentAddressedCache<LeafValueType>::depth() const
{
return static_cast<uint32_t>(journals_.size());
}

template <typename LeafValueType> void ContentAddressedCache<LeafValueType>::commit_to_depth(uint32_t target_depth)
{
if (target_depth >= journals_.size()) {
throw std::runtime_error("Invalid depth for commit_to_depth");
}
while (journals_.size() > target_depth) {
commit();
}
}

template <typename LeafValueType> void ContentAddressedCache<LeafValueType>::revert_to_depth(uint32_t target_depth)
{
if (target_depth >= journals_.size()) {
throw std::runtime_error("Invalid depth for revert_to_depth");
}
while (journals_.size() > target_depth) {
revert();
}
}

template <typename LeafValueType> void ContentAddressedCache<LeafValueType>::reset(uint32_t depth)
{
nodes_ = std::unordered_map<fr, NodePayload>();
Expand Down
Loading
Loading