1919
2020namespace ccf ::js
2121{
22+ static constexpr auto default_js_registry_kv_prefix =
23+ " public:custom_endpoints" ;
24+
2225 struct CustomJSEndpoint : public ccf ::endpoints::Endpoint
2326 {};
2427
25- // By subclassing DynamicJSEndpointRegistry , an application gains the
28+ // By subclassing BaseDynamicJSEndpointRegistry , an application gains the
2629 // ability to execute custom JavaScript endpoints, and exposes the ability to
2730 // install them via install_custom_endpoints(). The JavaScript code for these
2831 // endpoints is stored in the internal KV store under a namespace configured
@@ -31,31 +34,14 @@ namespace ccf::js
3134 // proposal in governance, and the payload format is currently identical,
3235 // except the controlling logic resides in the application space.
3336 //
34- // Known limitations:
35- //
36- // No auditability yet, COSE Sign1 auth is recommended, but the signature is
37- // not stored.
38- // No support for historical endpoints yet.
39- // No support for import from external modules.
40- //
4137 // Additional functionality compared to set_js_app:
42- //
43- // The KV namespace can be private, to keep the application confidential if
38+ // - The KV namespace can be private, to keep the application confidential if
4439 // desired.
45- class DynamicJSEndpointRegistry : public ccf ::UserEndpointRegistry
40+ class BaseDynamicJSEndpointRegistry : public ccf ::UserEndpointRegistry
4641 {
4742 private:
4843 std::shared_ptr<ccf::js::AbstractInterpreterCache> interpreter_cache =
4944 nullptr ;
50- std::string modules_map;
51- std::string metadata_map;
52- std::string interpreter_flush_map;
53- std::string modules_quickjs_version_map;
54- std::string modules_quickjs_bytecode_map;
55- std::string runtime_options_map;
56- std::string recent_actions_map;
57- std::string audit_input_map;
58- std::string audit_info_map;
5945
6046 ccf::js::NamespaceRestriction namespace_restriction;
6147
@@ -75,10 +61,18 @@ namespace ccf::js
7561 ccf::endpoints::CommandEndpointContext& endpoint_ctx,
7662 const ccf::TxID& tx_id);
7763
64+ protected:
65+ std::string modules_map;
66+ std::string metadata_map;
67+ std::string interpreter_flush_map;
68+ std::string modules_quickjs_version_map;
69+ std::string modules_quickjs_bytecode_map;
70+ std::string runtime_options_map;
71+
7872 public:
79- DynamicJSEndpointRegistry (
73+ BaseDynamicJSEndpointRegistry (
8074 ccf::AbstractNodeContext& context,
81- const std::string& kv_prefix = " public:custom_endpoints " );
75+ const std::string& kv_prefix = default_js_registry_kv_prefix );
8276
8377 /* *
8478 * Call this to populate the KV with JS endpoint definitions, so they can
@@ -133,29 +127,6 @@ namespace ccf::js
133127 */
134128 ccf::ApiResult get_js_runtime_options_v1 (
135129 ccf::JSRuntimeOptions& options, ccf::kv::ReadOnlyTx& tx);
136-
137- /* *
138- * Record action details by storing them in KV maps using a common format,
139- * for the purposes of offline audit using the ledger.
140- */
141- ccf::ApiResult record_action_for_audit_v1 (
142- ccf::kv::Tx& tx,
143- ccf::ActionFormat format,
144- const std::string& user_id,
145- const std::string& action_name,
146- const std::vector<uint8_t >& action_body);
147-
148- /* *
149- * Check an action is not being replayed, by looking it up
150- * in the history of recent actions. To place an upper bound on the history
151- * size, an authenticated timestamp (@p created_at) is required.
152- */
153- ccf::ApiResult check_action_not_replayed_v1 (
154- ccf::kv::Tx& tx,
155- uint64_t created_at,
156- const std::span<const uint8_t > action,
157- ccf::InvalidArgsReason& reason);
158-
159130 // / \defgroup Overrides for base EndpointRegistry functions, looking up JS
160131 // / endpoints before delegating to base implementation.
161132 // /@{
@@ -172,6 +143,9 @@ namespace ccf::js
172143 const ccf::TxID& tx_id) override ;
173144
174145 void build_api (nlohmann::json& document, ccf::kv::ReadOnlyTx& tx) override ;
146+
147+ std::set<RESTVerb> get_allowed_verbs (
148+ ccf::kv::Tx&, const ccf::RpcContext& rpc_ctx) override ;
175149 // /@}
176150
177151 virtual ccf::js::extensions::Extensions get_extensions (
@@ -180,4 +154,48 @@ namespace ccf::js
180154 return {};
181155 };
182156 };
157+
158+ // Extends BaseDynamicJSEndpointRegistry with methods for making actions
159+ // auditable and preventing replay. These should be used if apps are not
160+ // deployed through governance, to ensure that app-modification is safely and
161+ // clearly tracked in the ledger history
162+ class DynamicJSEndpointRegistry : public BaseDynamicJSEndpointRegistry
163+ {
164+ protected:
165+ std::string recent_actions_map;
166+ std::string audit_input_map;
167+ std::string audit_info_map;
168+
169+ public:
170+ DynamicJSEndpointRegistry (
171+ ccf::AbstractNodeContext& context,
172+ const std::string& kv_prefix = default_js_registry_kv_prefix) :
173+ BaseDynamicJSEndpointRegistry (context, kv_prefix),
174+ recent_actions_map (fmt::format(" {}.recent_actions" , kv_prefix)),
175+ audit_input_map (fmt::format(" {}.audit.input" , kv_prefix)),
176+ audit_info_map (fmt::format(" {}.audit.info" , kv_prefix))
177+ {}
178+
179+ /* *
180+ * Record action details by storing them in KV maps using a common format,
181+ * for the purposes of offline audit using the ledger.
182+ */
183+ ccf::ApiResult record_action_for_audit_v1 (
184+ ccf::kv::Tx& tx,
185+ ccf::ActionFormat format,
186+ const std::string& user_id,
187+ const std::string& action_name,
188+ const std::vector<uint8_t >& action_body);
189+
190+ /* *
191+ * Check an action is not being replayed, by looking it up
192+ * in the history of recent actions. To place an upper bound on the history
193+ * size, an authenticated timestamp (@p created_at) is required.
194+ */
195+ ccf::ApiResult check_action_not_replayed_v1 (
196+ ccf::kv::Tx& tx,
197+ uint64_t created_at,
198+ const std::span<const uint8_t > action,
199+ ccf::InvalidArgsReason& reason);
200+ };
183201}
0 commit comments