diff --git a/.daily_canary b/.daily_canary index 866d416cb..833da4fcc 100644 --- a/.daily_canary +++ b/.daily_canary @@ -1 +1 @@ -No no he's not dead, he's, he's restin'! +No no he's not dead, he's, he's resting! diff --git a/CHANGELOG.md b/CHANGELOG.md index a13febb2a..f80a396d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## Unreleased + +### Changed + +- The entry point for creation of C++ apps is now `make_user_endpoints()`. The old entry point `get_rpc_handler()` has been removed. + ## [2.0.0-rc1] ### Added diff --git a/doc/build_apps/api.rst b/doc/build_apps/api.rst index b93473d57..b3b4fa8f6 100644 --- a/doc/build_apps/api.rst +++ b/doc/build_apps/api.rst @@ -3,14 +3,14 @@ Developer API A CCF application is composed of the following: -- The :ref:`Application Entry Point ` which registers the application in CCF. +- The :ref:`Application Entry Point ` which creates the application in CCF. - A collection of :cpp:class:`endpoints ` handling HTTP requests and grouped in a single :cpp:class:`registry `. An :cpp:class:`endpoint ` reads and writes to the key-value store via the :ref:`Key-Value Store API `. - An optional set of :ref:`JavaScript FFI Plugins ` that can be registered to extend the built-in JavaScript API surface. Application Entry Point ----------------------- -.. doxygenfunction:: ccfapp::get_rpc_handler +.. doxygenfunction:: ccfapp::make_user_endpoints :project: CCF diff --git a/doc/build_apps/logging_cpp.rst b/doc/build_apps/logging_cpp.rst index edf596e17..ea0b36e34 100644 --- a/doc/build_apps/logging_cpp.rst +++ b/doc/build_apps/logging_cpp.rst @@ -19,8 +19,7 @@ The Logging application simply has: .. note:: - :cpp:class:`kv::Store` tables are essentially the only interface between CCF - and the application, and the sole mechanism for it to have state. + :cpp:class:`kv::Store` tables are the only interface between CCF and the replicated application, and the sole mechanism for it to have distributed state. The Logging application keeps its state in a pair of tables, one containing private encrypted logs and the other containing public unencrypted logs. Their type is defined as: @@ -44,10 +43,10 @@ The Logging application simply has: :lines: 1 :dedent: -RPC Handler ------------ +Application Endpoints +--------------------- -The type returned by :cpp:func:`ccfapp::get_rpc_handler()` should subclass :cpp:class:`ccf::RpcFrontend`, passing the base constructor a reference to an implementation of :cpp:class:`ccf::EndpointRegistry`: +The implementation of :cpp:func:`ccfapp::make_user_endpoints()` should return a subclass of :cpp:class:`ccf::endpoints::EndpointRegistry`, containing the endpoints that constitute the app. .. literalinclude:: ../../samples/apps/logging/logging.cpp :language: cpp @@ -75,7 +74,9 @@ Each function is installed as the handler for a specific HTTP resource, defined This example installs at ``"log/private", HTTP_POST``, so will be invoked for HTTP requests beginning :http:POST:`/app/log/private`. -The return value from ``make_endpoint`` is an ``Endpoint&`` object which can be used to alter how the handler is executed. For example, the handler for :http:POST:`/app/log/private` shown above sets a `schema` declaring the types of its request and response bodies. These will be used in calls to the :http:GET:`/app/api` endpoint to populate the relevant parts of the OpenAPI document. There are other endpoints installed for the URI path ``/app/log/private`` with different verbs, to handle :http:GET:`GET ` and :http:DELETE:`DELETE ` requests. Any other verbs, without an installed endpoint, will not be accepted - the framework will return a ``405 Method Not Allowed`` response. +The return value from ``make_endpoint`` is an ``Endpoint&`` object which can be used to alter how the handler is executed. For example, the handler for :http:POST:`/app/log/private` shown above sets a `schema` declaring the types of its request and response bodies. These will be used in calls to the :http:GET:`/app/api` endpoint to populate the relevant parts of the OpenAPI document. That OpenAPI document in turn is used to generate the entries in this documentation describing :http:POST:`/app/log/private`. + +There are other endpoints installed for the URI path ``/app/log/private`` with different verbs, to handle :http:GET:`GET ` and :http:DELETE:`DELETE ` requests. Requests with those verbs will be executed by the appropriate handler. Any other verbs, without an installed endpoint, will not be accepted - the framework will return a ``405 Method Not Allowed`` response. To process the raw body directly, a handler should use the general lambda signature which takes a single ``EndpointContext&`` parameter. Examples of this are also included in the logging sample app. For instance the ``log_record_text`` handler takes a raw string as the request body: @@ -162,7 +163,7 @@ The final piece is the definition of the endpoint itself, which uses an instance Default Endpoints ~~~~~~~~~~~~~~~~~ -The logging app sample exposes several built-in endpoints which are provided by the framework for convenience, such as ``/app/tx``, ``/app/commit``, and ``/app/user_id``. It is also possible to write an app which does not expose these endpoints, either to build a minimal user-facing API or to re-wrap this common functionality in your own format or authentication. A sample of this is provided in ``samples/apps/nobuiltins``. Whereas the logging app declares a registry inheriting from :cpp:class:`ccf::CommonEndpointRegistry`, this app inherits from :cpp:class:`ccf::BaseEndpointRegistry` which does not install any default endpoints: +The logging app sample exposes several built-in endpoints which are provided by the framework for convenience, such as :http:GET:`/app/tx`, :http:GET:`/app/commit`, and :http:GET:`/app/receipt`. It is also possible to write an app which does not expose these endpoints, either to build a minimal user-facing API or to re-wrap this common functionality in your own format or authentication. A sample of this is provided in ``samples/apps/nobuiltins``. Whereas the logging app declares a registry inheriting from :cpp:class:`ccf::CommonEndpointRegistry`, this app inherits from :cpp:class:`ccf::BaseEndpointRegistry` which does not install any default endpoints: .. literalinclude:: ../../samples/apps/nobuiltins/nobuiltins.cpp :language: cpp diff --git a/include/ccf/app_interface.h b/include/ccf/app_interface.h index cce1ed5d7..de9ffd566 100644 --- a/include/ccf/app_interface.h +++ b/include/ccf/app_interface.h @@ -2,36 +2,57 @@ // Licensed under the Apache 2.0 License. #pragma once +#include "ccf/ccf_deprecated.h" +#include "ccf/common_endpoint_registry.h" #include "ccf/js_plugin.h" +#include "ccf/node_context.h" #include #include +// Forward declarations, can be removed with deprecation namespace ccf { - // Forward declarations class RpcFrontend; +} - struct NetworkTables; +namespace kv +{ + class Store; } namespace ccfapp { - // Forward declaration - struct AbstractNodeContext; + CCF_DEPRECATED("Replace with make_user_endpoints") + std::shared_ptr get_rpc_handler( + kv::Store& store, AbstractNodeContext& context); +} +namespace ccf +{ + class UserEndpointRegistry : public CommonEndpointRegistry + { + public: + UserEndpointRegistry(ccfapp::AbstractNodeContext& context) : + CommonEndpointRegistry(get_actor_prefix(ActorsType::users), context) + {} + }; +} + +namespace ccfapp +{ // SNIPPET_START: app_interface - /** To be implemented by the application to be registered by CCF. + /** To be implemented by the application. Creates a collection of endpoints + * which will be exposed to callers under /app. * - * @param network Access to the network's replicated tables * @param context Access to node and host services * - * @return Shared pointer to the application handler instance + * @return Unique pointer to the endpoint registry instance */ - std::shared_ptr get_rpc_handler( - ccf::NetworkTables& network, AbstractNodeContext& context); + std::unique_ptr make_user_endpoints( + ccfapp::AbstractNodeContext& context); - /** To be implemented by the application to be registered by CCF. + /** To be implemented by the application. * * @return Vector of JavaScript FFI plugins */ diff --git a/include/ccf/user_frontend.h b/include/ccf/user_frontend.h deleted file mode 100644 index 163c2d0ba..000000000 --- a/include/ccf/user_frontend.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the Apache 2.0 License. -#pragma once - -#include "ccf/common_endpoint_registry.h" -#include "ccf/node_context.h" -#include "node/rpc/frontend.h" -#include "service/network_tables.h" - -namespace ccf -{ - class UserEndpointRegistry : public CommonEndpointRegistry - { - public: - UserEndpointRegistry(ccfapp::AbstractNodeContext& context) : - CommonEndpointRegistry(get_actor_prefix(ActorsType::users), context) - {} - }; - - class SimpleUserRpcFrontend : public RpcFrontend - { - protected: - UserEndpointRegistry common_handlers; - - public: - SimpleUserRpcFrontend( - kv::Store& tables, ccfapp::AbstractNodeContext& context) : - RpcFrontend(tables, common_handlers), - common_handlers(context) - {} - }; -} diff --git a/samples/apps/logging/logging.cpp b/samples/apps/logging/logging.cpp index 99b8d2725..efe759724 100644 --- a/samples/apps/logging/logging.cpp +++ b/samples/apps/logging/logging.cpp @@ -11,7 +11,6 @@ #include "ccf/historical_queries_adapter.h" #include "ccf/http_query.h" #include "ccf/indexing/strategies/seqnos_by_key_bucketed.h" -#include "ccf/user_frontend.h" #include "ccf/version.h" #include "node/tx_receipt.h" @@ -166,6 +165,13 @@ namespace loggingapp get_public_params_schema(nlohmann::json::parse(j_get_public_in)), get_public_result_schema(nlohmann::json::parse(j_get_public_out)) { + openapi_info.title = "CCF Sample Logging App"; + openapi_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."; + openapi_info.document_version = "1.7.0"; + index_per_public_key = std::make_shared( PUBLIC_RECORDS, context.get_lfs_access(), 10000, 20); context.get_indexing_strategies().install_strategy(index_per_public_key); @@ -1482,38 +1488,15 @@ namespace loggingapp ccf::UserEndpointRegistry::tick(elapsed, tx_count); } }; - - class Logger : public ccf::RpcFrontend - { - private: - LoggerHandlers logger_handlers; - - public: - Logger(ccf::NetworkTables& network, ccfapp::AbstractNodeContext& context) : - ccf::RpcFrontend(*network.tables, logger_handlers), - logger_handlers(context) - {} - - void open(std::optional identity = std::nullopt) override - { - ccf::RpcFrontend::open(identity); - logger_handlers.openapi_info.title = "CCF Sample Logging App"; - logger_handlers.openapi_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."; - logger_handlers.openapi_info.document_version = "1.7.0"; - } - }; } namespace ccfapp { // SNIPPET_START: app_interface - std::shared_ptr get_rpc_handler( - ccf::NetworkTables& nwt, ccfapp::AbstractNodeContext& context) + std::unique_ptr make_user_endpoints( + ccfapp::AbstractNodeContext& context) { - return make_shared(nwt, context); + return std::make_unique(context); } // SNIPPET_END: app_interface } diff --git a/samples/apps/nobuiltins/nobuiltins.cpp b/samples/apps/nobuiltins/nobuiltins.cpp index 9e42b1c01..b0897bdff 100644 --- a/samples/apps/nobuiltins/nobuiltins.cpp +++ b/samples/apps/nobuiltins/nobuiltins.cpp @@ -329,26 +329,13 @@ namespace nobuiltins .install(); } }; - - class NoBuiltinsFrontend : public ccf::RpcFrontend - { - private: - NoBuiltinsRegistry nbr; - - public: - NoBuiltinsFrontend( - ccf::NetworkTables& network, ccfapp::AbstractNodeContext& context) : - ccf::RpcFrontend(*network.tables, nbr), - nbr(context) - {} - }; } namespace ccfapp { - std::shared_ptr get_rpc_handler( - ccf::NetworkTables& nwt, ccfapp::AbstractNodeContext& context) + std::unique_ptr make_user_endpoints( + ccfapp::AbstractNodeContext& context) { - return std::make_shared(nwt, context); + return std::make_unique(context); } } diff --git a/src/apps/js_generic/js_generic.cpp b/src/apps/js_generic/js_generic.cpp index 057a40555..a00c0b67f 100644 --- a/src/apps/js_generic/js_generic.cpp +++ b/src/apps/js_generic/js_generic.cpp @@ -6,10 +6,10 @@ namespace ccfapp { - std::shared_ptr get_rpc_handler( - ccf::NetworkTables& network, ccfapp::AbstractNodeContext& context) + std::unique_ptr make_user_endpoints( + ccfapp::AbstractNodeContext& context) { - return get_rpc_handler_impl(network, context); + return make_user_endpoints_impl(context); } std::vector get_js_plugins() diff --git a/src/apps/js_generic/js_generic_base.cpp b/src/apps/js_generic/js_generic_base.cpp index 1896fa319..8619e6bdf 100644 --- a/src/apps/js_generic/js_generic_base.cpp +++ b/src/apps/js_generic/js_generic_base.cpp @@ -5,7 +5,6 @@ #include "ccf/crypto/key_wrap.h" #include "ccf/crypto/rsa_key_pair.h" #include "ccf/historical_queries_adapter.h" -#include "ccf/user_frontend.h" #include "ccf/version.h" #include "js/wrap.h" #include "kv/untyped_map.h" @@ -32,7 +31,6 @@ namespace ccfapp struct JSDynamicEndpoint : public ccf::endpoints::EndpointDefinition {}; - NetworkTables& network; ccfapp::AbstractNodeContext& context; metrics::Tracker metrics_tracker; @@ -471,9 +469,8 @@ namespace ccfapp } public: - JSHandlers(NetworkTables& network, AbstractNodeContext& context) : + JSHandlers(AbstractNodeContext& context) : UserEndpointRegistry(context), - network(network), context(context) { metrics_tracker.install_endpoint(*this); @@ -681,21 +678,10 @@ namespace ccfapp #pragma clang diagnostic pop - class JS : public ccf::RpcFrontend + std::unique_ptr make_user_endpoints_impl( + ccfapp::AbstractNodeContext& context) { - private: - JSHandlers js_handlers; - - public: - JS(NetworkTables& network, ccfapp::AbstractNodeContext& context) : - ccf::RpcFrontend(*network.tables, js_handlers), - js_handlers(network, context) - {} - }; - - std::shared_ptr get_rpc_handler_impl( - NetworkTables& network, ccfapp::AbstractNodeContext& context) - { - return make_shared(network, context); + return std::make_unique(context); } + } // namespace ccfapp diff --git a/src/apps/js_generic/js_generic_base.h b/src/apps/js_generic/js_generic_base.h index d3084be6a..ab1463dd2 100644 --- a/src/apps/js_generic/js_generic_base.h +++ b/src/apps/js_generic/js_generic_base.h @@ -6,6 +6,6 @@ namespace ccfapp { - std::shared_ptr get_rpc_handler_impl( - ccf::NetworkTables& network, ccfapp::AbstractNodeContext& context); + std::unique_ptr make_user_endpoints_impl( + ccfapp::AbstractNodeContext& context); } \ No newline at end of file diff --git a/src/apps/js_v8/js_v8.cpp b/src/apps/js_v8/js_v8.cpp index e958f357a..67d48b75e 100644 --- a/src/apps/js_v8/js_v8.cpp +++ b/src/apps/js_v8/js_v8.cpp @@ -6,10 +6,10 @@ namespace ccfapp { - std::shared_ptr get_rpc_handler( - ccf::NetworkTables& network, ccfapp::AbstractNodeContext& context) + std::unique_ptr make_user_endpoints( + ccfapp::AbstractNodeContext& context) { - return get_rpc_handler_impl(network, context); + return make_user_endpoints_impl(context); } std::vector get_js_plugins() diff --git a/src/apps/js_v8/js_v8_base.cpp b/src/apps/js_v8/js_v8_base.cpp index dd734e67d..219365ccd 100644 --- a/src/apps/js_v8/js_v8_base.cpp +++ b/src/apps/js_v8/js_v8_base.cpp @@ -5,11 +5,11 @@ #include "ccf/crypto/key_wrap.h" #include "ccf/crypto/rsa_key_pair.h" #include "ccf/historical_queries_adapter.h" -#include "ccf/user_frontend.h" #include "ccf/version.h" #include "kv/untyped_map.h" #include "kv_module_loader.h" #include "named_auth_policies.h" +#include "service/table_names.h" #include "tmpl/ccf_global.h" #include "tmpl/console_global.h" #include "tmpl/request.h" @@ -35,7 +35,6 @@ namespace ccfapp struct JSDynamicEndpoint : public ccf::endpoints::EndpointDefinition {}; - NetworkTables& network; ccfapp::AbstractNodeContext& node_context; ::metrics::Tracker metrics_tracker; @@ -301,9 +300,8 @@ namespace ccfapp } public: - V8Handlers(NetworkTables& network, AbstractNodeContext& context) : + V8Handlers(AbstractNodeContext& context) : UserEndpointRegistry(context), - network(network), node_context(context) { metrics_tracker.install_endpoint(*this); @@ -509,30 +507,14 @@ namespace ccfapp } }; - /** - * V8 Frontend for RPC calls - */ - class V8Frontend : public ccf::RpcFrontend - { - private: - V8Handlers handlers; - - public: - V8Frontend(NetworkTables& network, ccfapp::AbstractNodeContext& context) : - ccf::RpcFrontend(*network.tables, handlers), - handlers(network, context) - {} - }; - - /// Returns a new V8 Rpc Frontend - std::shared_ptr get_rpc_handler_impl( - NetworkTables& network, ccfapp::AbstractNodeContext& context) + /// Returns new V8 Endpoints + std::unique_ptr make_user_endpoints_impl( + ccfapp::AbstractNodeContext& context) { // V8 initialization needs to move to a more central place // once/if V8 is integrated into core CCF. v8_initialize(); - return make_shared(network, context); + return std::make_unique(context); } - } // namespace ccfapp diff --git a/src/apps/js_v8/js_v8_base.h b/src/apps/js_v8/js_v8_base.h index d3084be6a..ab1463dd2 100644 --- a/src/apps/js_v8/js_v8_base.h +++ b/src/apps/js_v8/js_v8_base.h @@ -6,6 +6,6 @@ namespace ccfapp { - std::shared_ptr get_rpc_handler_impl( - ccf::NetworkTables& network, ccfapp::AbstractNodeContext& context); + std::unique_ptr make_user_endpoints_impl( + ccfapp::AbstractNodeContext& context); } \ No newline at end of file diff --git a/src/apps/tpcc/app/tpcc.cpp b/src/apps/tpcc/app/tpcc.cpp index e03551fd4..0482b6541 100644 --- a/src/apps/tpcc/app/tpcc.cpp +++ b/src/apps/tpcc/app/tpcc.cpp @@ -3,7 +3,6 @@ #include "../tpcc_serializer.h" #include "apps/utils/metrics_tracker.h" #include "ccf/app_interface.h" -#include "ccf/user_frontend.h" #include "tpcc_setup.h" #include "tpcc_tables.h" #include "tpcc_transactions.h" @@ -145,22 +144,10 @@ namespace ccfapp } }; - class Tpcc : public ccf::RpcFrontend + std::unique_ptr make_user_endpoints( + ccfapp::AbstractNodeContext& context) { - private: - TpccHandlers tpcc_handlers; - - public: - Tpcc(kv::Store& store, AbstractNodeContext& context) : - RpcFrontend(store, tpcc_handlers), - tpcc_handlers(context) - {} - }; - - std::shared_ptr get_rpc_handler( - NetworkTables& nwt, AbstractNodeContext& context) - { - return make_shared(*nwt.tables, context); + return std::make_unique(context); } } diff --git a/src/enclave/enclave.h b/src/enclave/enclave.h index 8df3f80a2..df41597a7 100644 --- a/src/enclave/enclave.h +++ b/src/enclave/enclave.h @@ -17,6 +17,7 @@ #include "node/rpc/forwarder.h" #include "node/rpc/member_frontend.h" #include "node/rpc/node_frontend.h" +#include "node/rpc/user_frontend.h" #include "oe_init.h" #include "ringbuffer_logger.h" #include "rpc_map.h" @@ -164,7 +165,8 @@ namespace enclave network, *context, share_manager)); rpc_map->register_frontend( - ccfapp::get_rpc_handler(network, *context)); + std::make_unique( + network, ccfapp::make_user_endpoints(*context))); rpc_map->register_frontend( std::make_unique(network, *context)); diff --git a/src/node/rpc/test/frontend_test.cpp b/src/node/rpc/test/frontend_test.cpp index 90c04e977..59f75627d 100644 --- a/src/node/rpc/test/frontend_test.cpp +++ b/src/node/rpc/test/frontend_test.cpp @@ -8,7 +8,6 @@ #include "ccf/ds/logger.h" #include "ccf/json_handler.h" #include "ccf/serdes.h" -#include "ccf/user_frontend.h" #include "consensus/aft/request.h" #include "ds/files.h" #include "frontend_test_infra.h" @@ -34,6 +33,19 @@ std::atomic threading::ThreadMessaging::thread_count = 0; using namespace ccf; using namespace std; +class SimpleUserRpcFrontend : public RpcFrontend +{ +protected: + UserEndpointRegistry common_handlers; + +public: + SimpleUserRpcFrontend( + kv::Store& tables, ccfapp::AbstractNodeContext& context) : + RpcFrontend(tables, common_handlers), + common_handlers(context) + {} +}; + class BaseTestFrontend : public SimpleUserRpcFrontend { public: diff --git a/src/node/rpc/test/frontend_test_infra.h b/src/node/rpc/test/frontend_test_infra.h index ed2020132..6ac762818 100644 --- a/src/node/rpc/test/frontend_test_infra.h +++ b/src/node/rpc/test/frontend_test_infra.h @@ -7,12 +7,12 @@ #include "ccf/ds/logger.h" #include "ccf/serdes.h" #include "ccf/service/signed_req.h" -#include "ccf/user_frontend.h" #include "ds/files.h" #include "kv/test/null_encryptor.h" #include "kv/test/stub_consensus.h" #include "node/history.h" #include "node/rpc/member_frontend.h" +#include "node/rpc/user_frontend.h" #include "node_stub.h" #include "service/genesis_gen.h" diff --git a/src/node/rpc/user_frontend.h b/src/node/rpc/user_frontend.h new file mode 100644 index 000000000..0b9f04fbf --- /dev/null +++ b/src/node/rpc/user_frontend.h @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the Apache 2.0 License. +#pragma once + +#include "ccf/app_interface.h" +#include "node/network_state.h" +#include "node/rpc/frontend.h" + +namespace ccf +{ + class UserRpcFrontend : public RpcFrontend + { + protected: + std::unique_ptr endpoints; + + public: + UserRpcFrontend( + NetworkState& network, + std::unique_ptr&& endpoints_) : + RpcFrontend(*network.tables, *endpoints_), + endpoints(std::move(endpoints_)) + {} + }; +}