Skip to content

Commit

Permalink
Moving JS registry to public headers (#6213)
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyashton authored Jun 3, 2024
1 parent 6251861 commit 2827d0d
Show file tree
Hide file tree
Showing 10 changed files with 772 additions and 707 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ 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).

## [5.0.0-dev17]

[5.0.0-dev17]: https://github.com/microsoft/CCF/releases/tag/ccf-5.0.0-dev17

### Added

- Moved JS registry to public header `ccf/js/registry.h`. Apps should subclass `ccf::js::DynamicJSEndpointRegistry` to get similar behaviour to the existing JS Generic app.

## [5.0.0-dev16]

[5.0.0-dev16]: https://github.com/microsoft/CCF/releases/tag/ccf-5.0.0-dev16
Expand Down
11 changes: 6 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ set(CCF_JS_SOURCES
${CCF_DIR}/src/js/extensions/ccf/node.cpp
${CCF_DIR}/src/js/extensions/ccf/rpc.cpp
${CCF_DIR}/src/js/extensions/ccf/request.cpp
${CCF_DIR}/src/js/registry.cpp
)

if(COMPILE_TARGET STREQUAL "sgx")
Expand Down Expand Up @@ -753,10 +754,10 @@ if(COMPILE_TARGET STREQUAL "sgx")
ccf.enclave
PUBLIC http_parser.enclave
sss.enclave
ccf_js.enclave
ccf_endpoints.enclave
ccfcrypto.enclave
ccf_kv.enclave
ccf_js.enclave
nghttp2.enclave
)

Expand Down Expand Up @@ -801,10 +802,10 @@ elseif(COMPILE_TARGET STREQUAL "snp")
-lgcc
http_parser.snp
sss.snp
ccf_js.snp
ccf_endpoints.snp
ccfcrypto.snp
ccf_kv.snp
ccf_js.snp
nghttp2.snp
${CMAKE_THREAD_LIBS_INIT}
)
Expand Down Expand Up @@ -852,10 +853,10 @@ elseif(COMPILE_TARGET STREQUAL "virtual")
-lgcc
http_parser.host
sss.host
ccf_js.host
ccf_endpoints.host
ccfcrypto.host
ccf_kv.host
ccf_js.host
nghttp2.host
${CMAKE_THREAD_LIBS_INIT}
)
Expand Down Expand Up @@ -1134,10 +1135,10 @@ if(BUILD_TESTS)
PRIVATE ${CMAKE_THREAD_LIBS_INIT}
http_parser.host
sss.host
ccf_js.host
ccf_endpoints.host
ccfcrypto.host
ccf_kv.host
ccf_js.host
)

add_unit_test(
Expand All @@ -1161,10 +1162,10 @@ if(BUILD_TESTS)
PRIVATE ${CMAKE_THREAD_LIBS_INIT}
http_parser.host
sss.host
ccf_js.host
ccf_endpoints.host
ccfcrypto.host
ccf_kv.host
ccf_js.host
)

add_unit_test(
Expand Down
File renamed without changes.
File renamed without changes.
98 changes: 98 additions & 0 deletions include/ccf/js/registry.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.

// CCF
#include "ccf/app_interface.h"
#include "ccf/endpoint.h"
#include "ccf/js/bundle.h"
#include "ccf/js/core/context.h"
#include "ccf/js/interpreter_cache_interface.h"
#include "ccf/tx.h"
#include "ccf/tx_id.h"

#include <charconv>
#define FMT_HEADER_ONLY
#include <fmt/format.h>

namespace ccf::js
{
struct CustomJSEndpoint : public ccf::endpoints::Endpoint
{};

// By subclassing DynamicJSEndpointRegistry, an application gains the
// ability to execute custom JavaScript endpoints, and exposes the ability to
// install them via install_custom_endpoints(). The JavaScript code for these
// endpoints is stored in the internal KV store under a namespace configured
// in the second argument to the constructor. Calling
// install_custom_endpoints() is logically equivalent to passing a set_js_app
// proposal in governance, and the payload format is currently identical,
// except the controlling logic resides in the application space.
//
// Known limitations:
//
// No auditability yet, COSE Sign1 auth is recommended, but the signature is
// not stored.
// No support for historical endpoints yet.
// No support for import from external modules.
//
// Additional functionality compared to set_js_app:
//
// The KV namespace can be private, to keep the application confidential if
// desired.
class DynamicJSEndpointRegistry : public ccf::UserEndpointRegistry
{
private:
std::shared_ptr<ccf::js::AbstractInterpreterCache> interpreter_cache =
nullptr;
std::string modules_map;
std::string metadata_map;
std::string interpreter_flush_map;
std::string modules_quickjs_version_map;
std::string modules_quickjs_bytecode_map;

using PreExecutionHook = std::function<void(ccf::js::core::Context&)>;

void do_execute_request(
const CustomJSEndpoint* endpoint,
ccf::endpoints::EndpointContext& endpoint_ctx,
const std::optional<PreExecutionHook>& pre_exec_hook = std::nullopt);

void execute_request(
const CustomJSEndpoint* endpoint,
ccf::endpoints::EndpointContext& endpoint_ctx);

void execute_request_locally_committed(
const CustomJSEndpoint* endpoint,
ccf::endpoints::CommandEndpointContext& endpoint_ctx,
const ccf::TxID& tx_id);

public:
DynamicJSEndpointRegistry(
ccfapp::AbstractNodeContext& context,
const std::string& kv_prefix = "public:custom_endpoints");

/**
* Call this to populate the KV with JS endpoint definitions, so they can
* later be dispatched to.
*/
void install_custom_endpoints(
ccf::endpoints::EndpointContext& ctx,
const ccf::js::BundleWrapper& wrapper);

/// \defgroup Overrides for base EndpointRegistry functions, looking up JS
/// endpoints before delegating to base implementation.
///@{
ccf::endpoints::EndpointDefinitionPtr find_endpoint(
kv::Tx& tx, ccf::RpcContext& rpc_ctx) override;

void execute_endpoint(
ccf::endpoints::EndpointDefinitionPtr e,
ccf::endpoints::EndpointContext& endpoint_ctx) override;

void execute_endpoint_locally_committed(
ccf::endpoints::EndpointDefinitionPtr e,
ccf::endpoints::CommandEndpointContext& endpoint_ctx,
const ccf::TxID& tx_id) override;
///@}
};
}
37 changes: 10 additions & 27 deletions samples/apps/basic/basic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,50 +6,33 @@
#include "ccf/common_auth_policies.h"
#include "ccf/ds/hash.h"
#include "ccf/http_query.h"
#include "ccf/js/registry.h"
#include "ccf/json_handler.h"
#include "ccf/version.h"

#include <charconv>
#define FMT_HEADER_ONLY
#include <fmt/format.h>

// Custom Endpoints
#include "custom_endpoints/registry.h"

using namespace nlohmann;

namespace basicapp
{
using RecordsMap = kv::Map<std::string, std::vector<uint8_t>>;
static constexpr auto PRIVATE_RECORDS = "records";

// By subclassing CustomJSEndpointRegistry, this application gains the ability
// execute custom JavaScript endpoints, and exposes the ability to install
// them via install_custom_endpoints().
// This sample also adds a PUT /app/custom_endpoints that enables a user
// for which user_data["isAdmin"] is true to install custom JavaScript
// endpoints. The JavaScript code for these endpoints is stored in the
// internal KV store under a namespace configured in the second argument to
// the constructor. PUT /app/custom_endpoints is logically
// equivalent to passing a set_js_app proposal in governance, except the
// application resides in the application space.
//
// Known limitations:
//
// No auditability yet, COSE Sign1 auth is mandated, but the signature is not
// stored.
// No support for historical endpoints yet.
// No support for import from external modules.
//
// Additional functionality compared to set_js_app:
//
// The KV namespace can be private, to keep the application confidential if
// desired.
class BasicHandlers : public basicapp::CustomJSEndpointRegistry
// This sample shows the features of DynamicJSEndpointRegistry. This sample
// adds a PUT /app/custom_endpoints, which calls install_custom_endpoints(),
// after first authenticating the caller (user_data["isAdmin"] is true), to
// install custom JavaScript endpoints.
// PUT /app/custom_endpoints is logically equivalent to passing a set_js_app
// proposal in governance, except the application resides in the application
// space.
class BasicHandlers : public ccf::js::DynamicJSEndpointRegistry
{
public:
BasicHandlers(ccfapp::AbstractNodeContext& context) :
basicapp::CustomJSEndpointRegistry(
ccf::js::DynamicJSEndpointRegistry(
context,
"public:custom_endpoints" // Internal KV space will be under
// public:custom_endpoints.*
Expand Down
Loading

0 comments on commit 2827d0d

Please sign in to comment.