Developer API¶
A CCF application is composed of the following:
The Application Entry Point which creates the application in CCF.
A collection of
endpoints
handling HTTP requests and grouped in a singleregistry
. Anendpoint
reads and writes to the key-value store via the Key-Value Store API.An optional set of JavaScript FFI Plugins that can be registered to extend the built-in JavaScript API surface.
Application Entry Point¶
-
std::unique_ptr<ccf::endpoints::EndpointRegistry> ccf::make_user_endpoints(ccf::AbstractNodeContext &context)¶
To be implemented by the application. Creates a collection of endpoints which will be exposed to callers under /app.
- Parameters:
context – Access to node and host services
- Returns:
Unique pointer to the endpoint registry instance
Application Endpoint Registration¶
-
struct Endpoint : public ccf::endpoints::EndpointDefinition¶
An Endpoint represents a user-defined resource that can be invoked by authorised users via HTTP requests, over TLS. An Endpoint is accessible at a specific verb and URI, e.g. POST /app/accounts or GET /app/records.
Endpoints can read from and mutate the state of the replicated key-value store.
A CCF application is a collection of Endpoints recorded in the application’s EndpointRegistry.
Subclassed by ccf::endpoints::PathTemplatedEndpoint, ccf::js::CustomJSEndpoint
Public Functions
-
Endpoint &set_openapi_description(const std::string &description)¶
Set the OpenAPI description for the endpoint.
- Returns:
This Endpoint for further modification
-
Endpoint &set_openapi_summary(const std::string &summary)¶
Set the OpenAPI summary for the endpoint.
- Returns:
This Endpoint for further modification
-
Endpoint &set_openapi_deprecated(bool is_deprecated)¶
Set the endpoint as deprecated.
- Returns:
This Endpoint for further modification
-
Endpoint &set_openapi_deprecated_replaced(const std::string &deprecation_version, const std::string &replacement)¶
Set the endpoint as deprecated and overwrites the description to include deprecation version and point to a replacement endpoint.
- Returns:
This Endpoint for further modification
Whether the endpoint should be omitted from the OpenAPI document.
- Returns:
This Endpoint for further modification
-
Endpoint &set_params_schema(const nlohmann::json &j)¶
Sets the JSON schema that the request parameters must comply with.
- Parameters:
j – Request parameters JSON schema
- Returns:
This Endpoint for further modification
-
Endpoint &set_result_schema(const nlohmann::json &j, std::optional<http_status> status = std::nullopt)¶
Sets the JSON schema that the request response must comply with.
- Parameters:
j – Request response JSON schema
status – Request response status code
- Returns:
This Endpoint for further modification
-
template<typename In, typename Out>
inline Endpoint &set_auto_schema(std::optional<http_status> status = std::nullopt)¶ Sets the schema that the request and response bodies should comply with. These are used to populate the generated OpenAPI document, but do not introduce any constraints on the actual types that are parsed or produced by the handling functor.
Note
See
DECLARE_JSON_
serialisation macros for serialising user-defined data structures.- Template Parameters:
In – Request body JSON-serialisable data structure
Out – Response body JSON-serialisable data structure
- Parameters:
status – Response status code
- Returns:
This Endpoint for further modification
-
template<typename T>
inline Endpoint &set_auto_schema(std::optional<http_status> status = std::nullopt)¶ Sets schemas for request and response bodies using typedefs within T.
See also
Note
T
data structure should contain two nestedIn
andOut
structures for request parameters and response format, respectively.- Template Parameters:
T – Type containing
In
andOut
typedefs with JSON-schema description specialisations- Parameters:
status – Request response status code
- Returns:
This Endpoint for further modification
-
template<typename T>
inline Endpoint &add_query_parameter(const std::string ¶m_name, QueryParamPresence presence = RequiredParameter)¶ Add OpenAPI documentation for a query parameter which can be passed to this endpoint.
- Template Parameters:
T – Type with appropriate
ds::json
specialisations to generate a JSON schema description- Parameters:
param_name – Name to be used for the query parameter to this Endpoint
presence – Enum value indicating whether this parameter is required or optional
- Returns:
This Endpoint for further modification
-
Endpoint &set_forwarding_required(ForwardingRequired fr)¶
Overrides whether a Endpoint is always forwarded, or whether it is safe to sometimes execute on followers.
- Parameters:
fr – Enum value with desired status
- Returns:
This Endpoint for further modification
-
struct Installer¶
Subclassed by ccf::endpoints::EndpointRegistry
-
Endpoint &set_openapi_description(const std::string &description)¶
-
class EndpointRegistry : public ccf::endpoints::Endpoint::Installer¶
The EndpointRegistry records the user-defined endpoints for a given CCF application.
This is the abstract base for several more complete registrys. For a versioned API wrapping access to common CCF properties, see
BaseEndpointRegistry
. For implementation of several common endpoints seeCommonEndpointRegistry
.Subclassed by ccf::BaseEndpointRegistry
Public Functions
-
virtual void install(Endpoint &endpoint) override¶
Install the given endpoint, using its method and verb
If an implementation is already installed for this method and verb, it will be replaced.
- Parameters:
endpoint – Endpoint object describing the new resource to install
-
void set_default(EndpointFunction f, const AuthnPolicies &ap)¶
Set a default EndpointFunction
The default EndpointFunction is only invoked if no specific EndpointFunction was found.
- Parameters:
f – Method implementation
ap – Authentication policy
-
virtual void install(Endpoint &endpoint) override¶
-
class CommonEndpointRegistry : public ccf::BaseEndpointRegistry¶
Subclassed by ccf::ACMERpcEndpoints, ccf::GovEndpointRegistry, ccf::NodeEndpoints, ccf::UserEndpointRegistry
-
struct EndpointMetricsEntry¶
Public Members
-
std::string path¶
Endpoint path.
-
std::string method¶
Endpoint method.
-
size_t calls = 0¶
Number of calls since node start.
-
size_t errors = 0¶
Number of errors (4xx) since node start.
-
size_t failures = 0¶
Number of failures (5xx) since node start.
-
size_t retries = 0¶
Number of transaction retries caused by conflicts since node start
-
std::string path¶
-
struct EndpointMetrics¶
Public Members
-
std::vector<EndpointMetricsEntry> metrics¶
Metrics for all endpoints in the frontend.
-
std::vector<EndpointMetricsEntry> metrics¶
-
class BaseEndpointRegistry : public ccf::endpoints::EndpointRegistry¶
Extends the basic ccf::endpoints::EndpointRegistry with helper API methods for retrieving core CCF properties.
The API methods are versioned with a
_vN
suffix. App developers should use the latest version which provides the values they need. Note that theN
in these versions is specific to each method name, and is not related to a specific CCF release version. These APIs will be stable and supported for several CCF releases.The methods have a consistent calling pattern, taking their arguments first and setting results to the later out-parameters, passed by reference. All return an ApiResult, with value OK if the call succeeded.
Subclassed by ccf::CommonEndpointRegistry, nobuiltins::NoBuiltinsRegistry
Public Functions
-
ApiResult get_view_history_v1(std::vector<ccf::TxID> &history, ccf::View since = 1)¶
Get the history of the consensus view changes.
Returns the history of view changes since the given view, which defaults to the start of time.
A view change is characterised by the first sequence number in the new view.
-
ApiResult get_view_history_v2(std::vector<ccf::TxID> &history, ccf::View since, ccf::InvalidArgsReason &reason)¶
Get the history of the consensus view changes.
Returns the history of view changes since the given view, which defaults to the start of time.
A view change is characterised by the first sequence number in the new view.
-
ApiResult get_status_for_txid_v1(ccf::View view, ccf::SeqNo seqno, ccf::TxStatus &tx_status)¶
Get the status of a transaction by ID, provided as a view+seqno pair.
Note that this value is the node’s local understanding of the status of that transaction in the network at call time. For a given TxID, the initial status is always UNKNOWN, and eventually becomes COMMITTED or INVALID. See the documentation section titled “Verifying Transactions” for more detail.
COMMITTED INVALID [Final statuses]UNKNOWN [Initial status] v ^ PENDING v v
This status is not sampled atomically per handler: if this is called multiple times in a transaction handler, later calls may see more up to date values than earlier calls. Once a final state (COMMITTED or INVALID) has been reached, no further changes are possible.
See also
-
ApiResult get_last_committed_txid_v1(ccf::View &view, ccf::SeqNo &seqno)¶
Get the ID of latest transaction known to be committed.
-
ApiResult generate_openapi_document_v1(ccf::kv::ReadOnlyTx &tx, const std::string &title, const std::string &description, const std::string &document_version, nlohmann::json &document)¶
Generate an OpenAPI document describing the currently installed endpoints.
The document is compatible with OpenAPI version 3.0.0 - the _v1 suffix describes the version of this API, not the returned document format. Similarly, the document_version argument should be used to version the returned document itself as the set of endpoints or their APIs change, it does not affect the OpenAPI version of the format of the document.
-
ApiResult get_quote_for_this_node_v1(ccf::kv::ReadOnlyTx &tx, QuoteInfo "e_info)¶
Get a quote attesting to the hardware this node is running on.
-
ApiResult get_quotes_for_all_trusted_nodes_v1(ccf::kv::ReadOnlyTx &tx, std::map<NodeId, QuoteInfo> "es)¶
Get quotes attesting to the hardware that each node in the service is running on.
-
ApiResult get_view_for_seqno_v1(ccf::SeqNo seqno, ccf::View &view)¶
Get the view associated with a given seqno, to construct a valid TxID.
-
ApiResult get_user_data_v1(ccf::kv::ReadOnlyTx &tx, const UserId &user_id, nlohmann::json &user_data)¶
Get the user data associated with a given user id.
-
ApiResult get_member_data_v1(ccf::kv::ReadOnlyTx &tx, const MemberId &member_id, nlohmann::json &member_data)¶
Get the member data associated with a given member id.
-
ApiResult get_user_cert_v1(ccf::kv::ReadOnlyTx &tx, const UserId &user_id, ccf::crypto::Pem &user_cert_pem)¶
Get the certificate (PEM) of a given user id.
-
ApiResult get_member_cert_v1(ccf::kv::ReadOnlyTx &tx, const MemberId &member_id, ccf::crypto::Pem &member_cert_pem)¶
Get the certificate (PEM) of a given member id.
-
ApiResult get_view_history_v1(std::vector<ccf::TxID> &history, ccf::View since = 1)¶
RPC Context¶
-
class RpcContext¶
Describes the currently executing RPC.
Subclassed by ccf::RpcContextImpl
Public Functions
-
virtual std::shared_ptr<SessionContext> get_session_context() const = 0¶
Return information about the persistent session which this request was received on. Allows correlation between multiple requests coming from the same long-lived session.
-
virtual const PathParams &get_request_path_params() = 0¶
Returns a map of all PathParams parsed out of the original query path. For instance if this endpoint was installed at
/foo/{name}/{age}
, and the request path/foo/bob/42
, this would return the map: {“name”: “bob”, “age”: “42”}
-
virtual const http::HeaderMap &get_request_headers() const = 0¶
Returns map of all headers found in the request.
-
virtual std::optional<std::string> get_request_header(const std::string_view &name) const = 0¶
Returns value associated with named header, or nullopt of this header was not present.
-
virtual const std::string &get_request_url() const = 0¶
Returns full URL provided in request, rather than split into path + query.
-
virtual void set_claims_digest(ccf::ClaimsDigest::Digest &&digest) = 0¶
Sets the application claims digest associated with this transaction. This digest is used to construct the Merkle tree leaf representing this transaction. This allows a transaction to make specific, separately-revealable claims in each transaction, without being bound to the transaction serialisation format or what is stored in the KV. The digest will be included in receipts issued for that transaction.
-
virtual std::shared_ptr<SessionContext> get_session_context() const = 0¶
Authentication¶
Policies¶
-
static std::shared_ptr<EmptyAuthnPolicy> ccf::empty_auth_policy = std::make_shared<EmptyAuthnPolicy>()¶
Perform no authentication
-
static std::shared_ptr<UserCertAuthnPolicy> ccf::user_cert_auth_policy = std::make_shared<UserCertAuthnPolicy>()¶
Authenticate using TLS session identity, and
public:ccf.gov.users.certs
table
-
static std::shared_ptr<MemberCertAuthnPolicy> ccf::member_cert_auth_policy = std::make_shared<MemberCertAuthnPolicy>()¶
Authenticate using TLS session identity, and
public:ccf.gov.members.certs
table
-
static std::shared_ptr<AnyCertAuthnPolicy> ccf::any_cert_auth_policy = std::make_shared<AnyCertAuthnPolicy>()¶
Authenticate using TLS session identity, but do not check the certificate against any table, and let the application decide
-
static std::shared_ptr<MemberCOSESign1AuthnPolicy> ccf::member_cose_sign1_auth_policy = std::make_shared<MemberCOSESign1AuthnPolicy>()¶
Authenticate using COSE Sign1 payloads, and
public:ccf.gov.members.certs
table
-
static std::shared_ptr<UserCOSESign1AuthnPolicy> ccf::user_cose_sign1_auth_policy = std::make_shared<UserCOSESign1AuthnPolicy>()¶
Authenticate using COSE Sign1 payloads, and
public:ccf.gov.users.certs
table
-
static std::shared_ptr<JwtAuthnPolicy> ccf::jwt_auth_policy = std::make_shared<JwtAuthnPolicy>()¶
Authenticate using JWT, validating the token using the
public:ccf.gov.jwt.public_signing_keys_metadata
table
-
class TypedUserCOSESign1AuthnPolicy : public ccf::UserCOSESign1AuthnPolicy¶
Typed User COSE Sign1 Authentication Policy
Extends UserCOSESign1AuthPolicy, to require that a specific message type is present in the corresponding protected header.
Identities¶
-
struct UserCertAuthnIdentity : public ccf::AuthnIdentity¶
Public Members
-
UserId user_id¶
CCF user ID
-
UserId user_id¶
-
struct MemberCertAuthnIdentity : public ccf::AuthnIdentity¶
Public Members
-
MemberId member_id¶
CCF member ID
-
MemberId member_id¶
-
struct AnyCertAuthnIdentity : public ccf::AuthnIdentity¶
-
struct UserCOSESign1AuthnIdentity : public ccf::COSESign1AuthnIdentity¶
-
struct MemberCOSESign1AuthnIdentity : public ccf::COSESign1AuthnIdentity¶
-
struct JwtAuthnIdentity : public ccf::AuthnIdentity¶
Supporting Types¶
-
enum class ccf::TxStatus¶
Describes the status of a transaction, as seen by this node.
Values:
-
enumerator Unknown¶
This node has not received this transaction, and knows nothing about it
-
enumerator Pending¶
This node has this transaction locally, but has not yet heard that the transaction has been committed by the distributed consensus
-
enumerator Committed¶
This node has seen that this transaction is committed, it is an irrevocable and durable part of the service’s transaction history
-
enumerator Invalid¶
This node knows that the given transaction cannot be committed. This may mean there has been a view change, and a previously pending transaction has been lost (the original request should be resubmitted and will be given a new Transaction ID). This also describes IDs which are known to be impossible given the currently committed IDs
-
enumerator Unknown¶
-
using ccf::View = uint64_t¶
Transactions occur within a fixed View. Each View generally spans a range of transactions, though empty Views are also possible. The View is advanced by the consensus protocol during election of a new leader, and a single leader is assigned in each View. View and Term are synonymous.
-
using ccf::SeqNo = uint64_t¶
Each transaction is assigned a unique incrementing SeqNo, maintained across View transitions. This matches the order in which transactions are applied, where a higher SeqNo means that a transaction executed later. SeqNos are unique during normal operation, but around elections it is possible for distinct transactions in separate Views to have the same SeqNo. Only one of these transactions will ever commit, and the others are ephemeral.
-
struct TxID¶
-
enum class ccf::ApiResult¶
Lists the possible return codes from the versioned APIs in
ccf::BaseEndpointRegistry
Values:
-
enumerator OK¶
Call was successful, results can be used
-
enumerator Uninitialised¶
The node is not yet initialised, and doesn’t have access to the service needed to answer this call. Should only be returned if the API is called too early, during node construction.
-
enumerator InvalidArgs¶
One of the arguments passed to the function is invalid. It may be outside the range of known values, or not be in the expected format.
-
enumerator NotFound¶
The requsted value was not found.
-
enumerator InternalError¶
General error not covered by the cases above. Generally means that an unexpected exception was thrown during execution.
-
enumerator OK¶
Historical Queries¶
-
ccf::endpoints::EndpointFunction ccf::historical::adapter_v3(const HandleHistoricalQuery &f, ccf::AbstractNodeContext &node_context, const CheckHistoricalTxStatus &available, const TxIDExtractor &extractor = txid_from_header)¶
-
class AbstractStateCache : public ccf::AbstractNodeSubSystem¶
Stores the progress of historical query requests.
A request will generally need to be made multiple times (with the same handle and range) before the response is available, as the historical state is asynchronously retrieved from the ledger and then validated. If the same handle is used for a new range, the state for the old range will be discarded. State is also discarded after the handle’s expiry duration, or when drop_cached_states is called for a given handle. The management of requests (how many unique handles are concurrently active, how they are correlated across HTTP requests, how the active quota is divided between callers) is left to the calling system.
Subclassed by ccf::historical::StateCache
Public Functions
-
virtual void set_default_expiry_duration(ExpiryDuration seconds_until_expiry) = 0¶
Set the default time after which a request’s state will be deleted, and will not be accessible without retrieving it again from the ledger. Any call to get_store_XXX which doesn’t pass an explicit seconds_until_expiry will reset the timer to this default duration.
-
virtual void set_soft_cache_limit(CacheSize cache_limit) = 0¶
Set the cache limit (in bytes) to evict least recently used requests from the cache after its size grows beyond this limit. The limit is not strict. It is estimated based on serialized states’ sizes approximation and is checked once per tick, and so it can overflow for a short time.
-
virtual ccf::kv::ReadOnlyStorePtr get_store_at(RequestHandle handle, ccf::SeqNo seqno, ExpiryDuration seconds_until_expiry) = 0¶
Retrieve a Store containing the state written at the given seqno.
See
get_store_range
for a description of the caching behaviour. This is equivalent to get_store_at(handle, seqno, seqno), but returns nullptr if the state is currently unavailable.
-
virtual ccf::kv::ReadOnlyStorePtr get_store_at(RequestHandle handle, ccf::SeqNo seqno) = 0¶
Same as
get_store_at
but uses default expiry value.See also
-
virtual StatePtr get_state_at(RequestHandle handle, ccf::SeqNo seqno, ExpiryDuration seconds_until_expiry) = 0¶
Retrieve a full state at a given seqno, including the Store, the TxID assigned by consensus, and an offline-verifiable receipt for the Tx.
-
virtual StatePtr get_state_at(RequestHandle handle, ccf::SeqNo seqno) = 0¶
Same as
get_state_at
but uses default expiry value.See also
-
virtual std::vector<ccf::kv::ReadOnlyStorePtr> get_store_range(RequestHandle handle, ccf::SeqNo start_seqno, ccf::SeqNo end_seqno, ExpiryDuration seconds_until_expiry) = 0¶
Retrieve a range of Stores containing the state written at the given indices.
If this is not currently available, this function returns an empty vector and begins fetching the ledger entry asynchronously. This will generally be true for the first call for a given seqno, and it may take some time to completely fetch and validate. The call should be repeated later with the same arguments to retrieve the requested entries. This state is kept until it is deleted for one of the following reasons:
A call to
drop_cached_states
seconds_until_expiry
seconds elapse without calling this functionThis handle is used to request a different seqno or range
The range is inclusive of both start_seqno and end_seqno. If a non-empty vector is returned, it will always contain the full requested range; the vector will be of length (end_seqno - start_seqno + 1) and will contain no nullptrs.
-
virtual std::vector<ccf::kv::ReadOnlyStorePtr> get_store_range(RequestHandle handle, ccf::SeqNo start_seqno, ccf::SeqNo end_seqno) = 0¶
Same as
get_store_range
but uses default expiry value.See also
-
virtual bool drop_cached_states(RequestHandle handle) = 0¶
Drop state for the given handle.
May be used to free up space once a historical query has been resolved, more aggressively than waiting for the states to expire.
-
virtual void set_default_expiry_duration(ExpiryDuration seconds_until_expiry) = 0¶
-
struct State¶
-
class Receipt¶
Subclassed by ccf::ProofReceipt, ccf::SignatureReceipt
Indexing¶
-
class Strategy¶
The base class for all indexing strategies.
Sub-class this and override handle_committed_transaction to implement your own indexing strategy. Create an instance of this on each node, and then install it with context.get_indexing_strategies().install_strategy(). It will then be given each committed transaction shortly after commit. You should build some aggregate/summary from these transactions, and return that to endpoint handlers in an efficient format.
Subclassed by ccf::indexing::strategies::VisitEachEntryInMap, loggingapp::CommittedRecords
HTTP Accept Header Matching¶
-
struct AcceptHeaderField¶
-
inline std::vector<AcceptHeaderField> ccf::http::parse_accept_header(std::string s)¶
COSE¶
-
struct InArray¶
-
std::vector<uint8_t> ccf::cose::edit::set_unprotected_header(const std::span<const uint8_t> &cose_input, const desc::Type &descriptor)¶
Set the unprotected header of a COSE_Sign1 message, according to a descriptor.
Useful to add a proof to a signature to turn it into a receipt, to add a receipt to a signed statement to turn it into a transparent statement, or simply to strip the unprotected header from a COSE Sign1.
- Parameters:
cose_input – The COSE_Sign1 message to edit.
descriptor – An object describing whether and how to set the unprotected header.