Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
a1bac08
Aggressive rebase
eddyashton Nov 26, 2025
7c0075f
Juggling files, simple cleanups
eddyashton Nov 26, 2025
5db82d1
Paused
eddyashton Nov 28, 2025
072734f
Merge branch 'main' of https://github.com/microsoft/CCF into respond_…
eddyashton Dec 3, 2025
3139e99
Merge branch 'main' of https://github.com/microsoft/CCF into respond_…
eddyashton Dec 3, 2025
0cbd31e
Tidy tweaks
eddyashton Dec 9, 2025
845345b
Merge branch 'main' of https://github.com/microsoft/CCF into respond_…
eddyashton Dec 10, 2025
ad9ed1c
Lol tidy
eddyashton Dec 10, 2025
9b18cc3
Add a consensus committed function, like locally_committed
eddyashton Dec 11, 2025
0addac0
Merge branch 'main' of https://github.com/microsoft/CCF into respond_…
eddyashton Dec 12, 2025
d26dd7c
Remove some unnecessary diffs
eddyashton Dec 12, 2025
ceb20df
Distinct FinalTxStatus
eddyashton Dec 12, 2025
1f2a8d8
Distinct blocking endpoints
eddyashton Dec 12, 2025
c9a16c5
Merge branch 'main' of https://github.com/microsoft/CCF into respond_…
eddyashton Dec 16, 2025
d37bc86
Merge branch 'main' of https://github.com/microsoft/CCF into respond_…
eddyashton Jan 6, 2026
19f90f1
Merge fixup
eddyashton Jan 7, 2026
90adb87
Settle for a rubbish time-delta measuring initial test?
eddyashton Jan 7, 2026
718df9d
Merge branch 'main' of https://github.com/microsoft/CCF into respond_…
eddyashton Jan 8, 2026
513648e
Brief CHANGELOG
eddyashton Jan 8, 2026
19dae2e
How does this keep happening
eddyashton Jan 8, 2026
1f99471
How does this keep happening
eddyashton Jan 8, 2026
4f1df95
Schema bump
eddyashton Jan 8, 2026
0b7fad9
Plausible improvements
eddyashton Jan 8, 2026
f83eaac
Evaluate tx status via consensus
eddyashton Jan 12, 2026
d66a4a0
WIP
eddyashton Jan 12, 2026
782d28f
Restore other tests
eddyashton Jan 13, 2026
a66bac9
Merge branch 'respond_at_commit' of https://github.com/eddyashton/CCF…
eddyashton Jan 13, 2026
9bbcf6c
Format and tidy
eddyashton Jan 13, 2026
83ebcbf
Ok fair
eddyashton Jan 13, 2026
d521643
Skip blocking on HTTP/2
eddyashton Jan 13, 2026
d900799
recursive_mutex
eddyashton Jan 13, 2026
1b37e45
Merge branch 'main' of https://github.com/microsoft/CCF into respond_…
eddyashton Jan 13, 2026
5c57275
Revert recursive_mutex
eddyashton Jan 13, 2026
605b68d
Merge branch 'main' of https://github.com/microsoft/CCF into respond_…
eddyashton Jan 23, 2026
2e80f8c
Unpickle - trigger_callbacks takes a ViewHistory, rather than calling…
eddyashton Jan 23, 2026
e6a8226
non discard
eddyashton Jan 23, 2026
87cb0f9
Turn exceptions thrown from commit callback into session termination
eddyashton Jan 23, 2026
e56cc6b
Merge branch 'main' of https://github.com/microsoft/CCF into respond_…
eddyashton Jan 23, 2026
d812bd0
Merge branch 'main' of https://github.com/microsoft/CCF into respond_…
eddyashton Feb 24, 2026
1ac9043
Don't repeat uint8_t
eddyashton Feb 26, 2026
dfaaaaa
Merge branch 'main' into respond_at_commit
achamayou Feb 27, 2026
7968e0e
Merge branch 'main' into respond_at_commit
achamayou Mar 5, 2026
b8cefa9
Merge branch 'main' into respond_at_commit
achamayou Mar 6, 2026
bab29d5
Merge branch 'main' of https://github.com/microsoft/CCF into respond_…
eddyashton Mar 12, 2026
c81325d
camaaaan
eddyashton Mar 12, 2026
3102b05
Merge branch 'main' into respond_at_commit
achamayou Mar 12, 2026
17603c2
Merge branch 'main' of https://github.com/microsoft/CCF into respond_…
eddyashton Mar 18, 2026
4574016
Remove recursive mutex (invoke callbacks _outside_ of lock), and cach…
eddyashton Mar 18, 2026
1fe4247
Merge branch 'main' of https://github.com/microsoft/CCF into respond_…
eddyashton Mar 18, 2026
7952b0d
I like to std::move it std::move it
eddyashton Mar 18, 2026
1db0907
Merge branch 'main' into respond_at_commit
achamayou Mar 18, 2026
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Added

- Experimental self-healing-open protocol for automatically transitioning-to-open during a disaster recovery without operator intervention. (#7189)
- Added support for endpoints which only respond once their content is committed by the consensus protocol (#7562).

### Changed

Expand Down
82 changes: 81 additions & 1 deletion doc/schemas/app_openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@
"info": {
"description": "This CCF sample app implements a simple logging application, securely recording messages at client-specified IDs. It demonstrates most of the features available to CCF apps.",
"title": "CCF Sample Logging App",
"version": "2.8.0"
"version": "2.8.1"
},
"openapi": "3.0.0",
"paths": {
Expand Down Expand Up @@ -375,6 +375,86 @@
}
}
},
"/app/log/blocking/private": {
"get": {
"operationId": "GetAppLogBlockingPrivate",
"parameters": [
{
"in": "query",
"name": "id",
"required": true,
"schema": {
"$ref": "#/components/schemas/uint64"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/LoggingGet__Out"
}
}
},
"description": "Default response description"
},
"default": {
"$ref": "#/components/responses/default"
}
},
"security": [
{
"jwt": []
},
{
"user_cose_sign1": []
}
],
"x-ccf-forwarding": {
"$ref": "#/components/x-ccf-forwarding/sometimes"
}
},
"post": {
"operationId": "PostAppLogBlockingPrivate",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/LoggingRecord__In"
}
}
},
"description": "Auto-generated request body schema"
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/boolean"
}
}
},
"description": "Default response description"
},
"default": {
"$ref": "#/components/responses/default"
}
},
"security": [
{
"jwt": []
},
{
"user_cose_sign1": []
}
],
"x-ccf-forwarding": {
"$ref": "#/components/x-ccf-forwarding/always"
}
}
},
"/app/log/cose_signed_content": {
"post": {
"operationId": "PostAppLogCoseSignedContent",
Expand Down
12 changes: 11 additions & 1 deletion include/ccf/endpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,9 +259,16 @@ namespace ccf::endpoints
{
// Functor which is invoked to process requests for this Endpoint
EndpointFunction func;
// Functor which is invoked to modify the response post commit.

// Functor which is invoked to modify the response after it is locally
// committed (ie - assigned a transaction ID)
LocallyCommittedEndpointFunction locally_committed_func;

// Functor which is invoked to modify the response after it reaches a
// terminal consensus state (ie - it is either globally committed, or
// invalidated)
ConsensusCommittedEndpointFunction consensus_committed_func;

struct Installer
{
virtual void install(Endpoint&) = 0;
Expand Down Expand Up @@ -488,6 +495,9 @@ namespace ccf::endpoints
Endpoint& set_locally_committed_function(
const LocallyCommittedEndpointFunction& lcf);

Endpoint& set_consensus_committed_function(
const ConsensusCommittedEndpointFunction& ccf_);

void install();
};

Expand Down
6 changes: 6 additions & 0 deletions include/ccf/endpoint_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#pragma once

#include "ccf/endpoints/authentication/authentication_types.h"
#include "ccf/tx_status.h"

#include <functional>
#include <memory>
Expand Down Expand Up @@ -65,6 +66,11 @@ namespace ccf::endpoints
using LocallyCommittedEndpointFunction =
std::function<void(CommandEndpointContext& ctx, const ccf::TxID& txid)>;

using ConsensusCommittedEndpointFunction = std::function<void(
std::shared_ptr<ccf::RpcContext> rpc_ctx,
const ccf::TxID& txid,
ccf::FinalTxStatus status)>;

// Read-only endpoints can only get values from the kv, they cannot write
struct ReadOnlyEndpointContext : public CommandEndpointContext
{
Expand Down
5 changes: 5 additions & 0 deletions include/ccf/endpoint_registry.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ namespace ccf::endpoints
void default_locally_committed_func(
CommandEndpointContext& ctx, const TxID& tx_id);

void default_respond_on_commit_func(
std::shared_ptr<ccf::RpcContext> rpc_ctx,
const TxID& tx_id,
ccf::FinalTxStatus status);

template <typename T>
inline bool get_path_param(
const ccf::PathParams& params,
Expand Down
8 changes: 8 additions & 0 deletions include/ccf/tx_status.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ namespace ccf
Invalid,
};

// Contains only the terminal values of TxStatus
enum class FinalTxStatus : std::underlying_type_t<TxStatus>
{
Committed =
static_cast<std::underlying_type_t<TxStatus>>(TxStatus::Committed),
Invalid = static_cast<std::underlying_type_t<TxStatus>>(TxStatus::Invalid),
};

constexpr char const* tx_status_to_str(TxStatus status)
{
switch (status)
Expand Down
23 changes: 22 additions & 1 deletion samples/apps/logging/logging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ namespace loggingapp
"recording messages at client-specified IDs. It demonstrates most of "
"the features available to CCF apps.";

openapi_info.document_version = "2.8.0";
openapi_info.document_version = "2.8.1";
};

void init_handlers() override
Expand Down Expand Up @@ -561,6 +561,16 @@ namespace loggingapp
.install();
// SNIPPET_END: install_record

make_endpoint(
Comment thread
achamayou marked this conversation as resolved.
"/log/blocking/private",
HTTP_POST,
ccf::json_adapter(record),
auth_policies)
.set_auto_schema<LoggingRecord::In, bool>()
.set_consensus_committed_function(
Comment thread
eddyashton marked this conversation as resolved.
ccf::endpoints::default_respond_on_commit_func)
.install();

auto add_txid_in_body_put = [](auto& ctx, const auto& tx_id) {
static constexpr auto CCF_TX_ID = "x-ms-ccf-transaction-id";
ctx.rpc_ctx->set_response_header(CCF_TX_ID, tx_id.to_str());
Expand Down Expand Up @@ -667,6 +677,17 @@ namespace loggingapp
.install();
// SNIPPET_END: install_get

make_read_only_endpoint(
"/log/blocking/private",
HTTP_GET,
ccf::json_read_only_adapter(get),
auth_policies)
.set_auto_schema<void, LoggingGet::Out>()
.add_query_parameter<size_t>("id")
.set_consensus_committed_function(
ccf::endpoints::default_respond_on_commit_func)
.install();

make_read_only_endpoint(
"/log/private/backup",
HTTP_GET,
Expand Down
13 changes: 7 additions & 6 deletions src/consensus/aft/impl/state.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ namespace aft
LOG_DEBUG_FMT("Resulting views: {}", fmt::join(views, ", "));
}

ccf::View view_at(ccf::kv::Version idx)
[[nodiscard]] ccf::View view_at(ccf::kv::Version idx) const
{
auto it = upper_bound(views.begin(), views.end(), idx);

Expand All @@ -68,7 +68,7 @@ namespace aft
return (it - views.begin());
}

ccf::kv::Version start_of_view(ccf::View view)
[[nodiscard]] ccf::kv::Version start_of_view(ccf::View view) const
{
if (view > views.size() || view == InvalidView)
{
Expand All @@ -85,7 +85,7 @@ namespace aft
return tentative;
}

ccf::kv::Version end_of_view(ccf::View view)
[[nodiscard]] ccf::kv::Version end_of_view(ccf::View view) const
{
// If this view has no start (potentially because it contains no
// transactions), then it can't have an end
Expand All @@ -104,14 +104,15 @@ namespace aft
return views[view] - 1;
}

std::vector<ccf::kv::Version> get_history_until(
ccf::kv::Version idx = std::numeric_limits<ccf::kv::Version>::max())
[[nodiscard]] std::vector<ccf::kv::Version> get_history_until(
ccf::kv::Version idx = std::numeric_limits<ccf::kv::Version>::max()) const
{
return {views.begin(), std::upper_bound(views.begin(), views.end(), idx)};
}

// view should be non-zero as views start at one in here
std::vector<ccf::kv::Version> get_history_since(uint64_t view)
[[nodiscard]] std::vector<ccf::kv::Version> get_history_since(
uint64_t view) const
{
if (view > views.size())
{
Expand Down
19 changes: 18 additions & 1 deletion src/consensus/aft/raft.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "ds/serialized.h"
#include "impl/state.h"
#include "kv/kv_types.h"
#include "node/commit_callback_subsystem.h"
#include "node/node_client.h"
#include "node/node_to_node.h"
#include "node/node_types.h"
Expand Down Expand Up @@ -183,6 +184,8 @@ namespace aft
// Used to remove retired nodes from store
std::unique_ptr<ccf::RetiredNodeCleanup> retired_node_cleanup;

std::shared_ptr<ccf::CommitCallbackSubsystem> commit_callbacks;

size_t entry_size_not_limited = 0;
size_t entry_count = 0;
Index entries_batch_size = 20;
Expand Down Expand Up @@ -214,6 +217,8 @@ namespace aft
std::shared_ptr<ccf::NodeToNode> channels_,
std::shared_ptr<aft::State> state_,
std::shared_ptr<ccf::NodeClient> rpc_request_context_,
std::shared_ptr<ccf::CommitCallbackSubsystem>
commit_callbacks_subsystem_ = nullptr,
bool public_only_ = false) :
store(std::move(store_)),

Expand All @@ -228,6 +233,7 @@ namespace aft
node_client(std::move(rpc_request_context_)),
retired_node_cleanup(
std::make_unique<ccf::RetiredNodeCleanup>(node_client)),
commit_callbacks(std::move(commit_callbacks_subsystem_)),

public_only(public_only_),

Expand All @@ -236,7 +242,12 @@ namespace aft

ledger(std::move(ledger_)),
channels(std::move(channels_))
{}
{
if (commit_callbacks != nullptr)
{
commit_callbacks->set_consensus(this);
}
}

~Aft() override = default;

Expand Down Expand Up @@ -2602,6 +2613,12 @@ namespace aft
store->compact(idx);
ledger->commit(idx);

if (commit_callbacks != nullptr)
{
const auto term = get_term_internal(idx);
commit_callbacks->trigger_callbacks({term, idx}, state->view_history);
}

RAFT_DEBUG_FMT("Commit on {}: {}", state->node_id, idx);

// Examine each configuration that is followed by a globally committed
Expand Down
6 changes: 6 additions & 0 deletions src/enclave/enclave.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "interface.h"
#include "js/interpreter_cache.h"
#include "kv/ledger_chunker.h"
#include "node/commit_callback_subsystem.h"
#include "node/historical_queries.h"
#include "node/network_state.h"
#include "node/node_state.h"
Expand Down Expand Up @@ -151,6 +152,10 @@ namespace ccf
context->install_subsystem(
std::make_shared<ccf::AbstractCOSESignaturesConfigSubsystem>(*node));

auto commit_callbacks = std::make_shared<ccf::CommitCallbackSubsystem>();
context->install_subsystem(commit_callbacks);
rpcsessions->set_commit_callbacks_subsystem(commit_callbacks);

LOG_TRACE_FMT("Creating RPC actors / ffi");
rpc_map->register_frontend<ccf::ActorsType::members>(
std::make_unique<ccf::MemberRpcFrontend>(network, *context));
Expand All @@ -168,6 +173,7 @@ namespace ccf
rpc_map,
rpcsessions,
indexer,
commit_callbacks,
sig_tx_interval,
sig_ms_interval);
}
Expand Down
Loading