diff --git a/CHANGELOG.md b/CHANGELOG.md index c0162c4dc0ee..96a9034b986b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ 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 + +### Added + +- Historical point query support has been added to JavaScript endpoints (#2285). + +### Changed + +- `"readonly"` has been replaced by `"mode"` in `app.json` in JavaScript apps (#2285). + ## [0.19.0] ### Changed diff --git a/cmake/quickjs.cmake b/cmake/quickjs.cmake index 0098d8a10b9b..a43b5bb91fc0 100644 --- a/cmake/quickjs.cmake +++ b/cmake/quickjs.cmake @@ -28,8 +28,10 @@ if("sgx" IN_LIST COMPILE_TARGETS) quickjs.enclave STATIC ${QUICKJS_SRC} ${CCF_DIR}/3rdparty/stub/time.c ) target_compile_options( - quickjs.enclave PUBLIC -nostdinc -DCONFIG_VERSION="${QUICKJS_VERSION}" - -DEMSCRIPTEN -DCONFIG_STACK_CHECK + quickjs.enclave + PUBLIC -nostdinc -DCONFIG_VERSION="${QUICKJS_VERSION}" -DEMSCRIPTEN + -DCONFIG_STACK_CHECK + PRIVATE $<$:-DDUMP_LEAKS> ) target_link_libraries(quickjs.enclave PUBLIC ${OE_TARGET_LIBC}) set_property(TARGET quickjs.enclave PROPERTY POSITION_INDEPENDENT_CODE ON) @@ -40,7 +42,9 @@ endif() add_library(quickjs.host STATIC ${QUICKJS_SRC}) target_compile_options( - quickjs.host PUBLIC -DCONFIG_VERSION="${QUICKJS_VERSION}" + quickjs.host + PUBLIC -DCONFIG_VERSION="${QUICKJS_VERSION}" + PRIVATE $<$:-DDUMP_LEAKS> ) add_san(quickjs.host) set_property(TARGET quickjs.host PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/samples/apps/forum/app.tmpl.json b/samples/apps/forum/app.tmpl.json index 499b941a7494..c68070a805a5 100644 --- a/samples/apps/forum/app.tmpl.json +++ b/samples/apps/forum/app.tmpl.json @@ -7,7 +7,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": [], - "readonly": false + "mode": "readwrite" }, "put": { "js_module": "build/PollControllerProxy.js", @@ -15,7 +15,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": [], - "readonly": false + "mode": "readwrite" }, "get": { "js_module": "build/PollControllerProxy.js", @@ -23,7 +23,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": [], - "readonly": true + "mode": "readonly" } }, "/polls": { @@ -33,7 +33,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": [], - "readonly": false + "mode": "readwrite" }, "put": { "js_module": "build/PollControllerProxy.js", @@ -41,7 +41,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": [], - "readonly": false + "mode": "readwrite" }, "get": { "js_module": "build/PollControllerProxy.js", @@ -49,7 +49,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": [], - "readonly": true + "mode": "readonly" } }, "/site": { @@ -59,7 +59,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": [], - "readonly": true + "mode": "readonly" } }, "/site/polls/create": { @@ -69,7 +69,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": [], - "readonly": true + "mode": "readonly" } }, "/site/opinions/submit": { @@ -79,7 +79,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": [], - "readonly": true + "mode": "readonly" } }, "/site/view": { @@ -89,7 +89,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": [], - "readonly": true + "mode": "readonly" } }, "/csv": { @@ -99,7 +99,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": [], - "readonly": true, + "mode": "readonly", "openapi_merge_patch": { "responses": { "200": { @@ -117,7 +117,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": [], - "readonly": false, + "mode": "readwrite", "openapi_merge_patch": { "requestBody": { "content": { diff --git a/samples/apps/forum/src/types/ccf.ts b/samples/apps/forum/src/types/ccf.ts index 4703ff5c392d..dbcf74bb9a84 100644 --- a/samples/apps/forum/src/types/ccf.ts +++ b/samples/apps/forum/src/types/ccf.ts @@ -48,6 +48,26 @@ export interface KVMap { export type KVMaps = { [key: string]: KVMap }; +export interface ProofElement { + left?: string; + right?: string; +} + +export type Proof = ProofElement[]; + +export interface Receipt { + signature: string; + root: string; + proof: Proof; + leaf: string; + nodeId: string; +} + +export interface HistoricalState { + transactionId: string; + receipt: Receipt; +} + interface WrapAlgoParams { name: string; } @@ -61,6 +81,11 @@ export interface AESKWPParams extends WrapAlgoParams { name: "AES-KWP"; } +export interface RsaOaepAESKWPParams extends WrapAlgoParams { + name: "RSA-OAEP-AES-KWP"; + label?: ArrayBuffer; +} + export interface CryptoKeyPair { privateKey: string; publicKey: string; @@ -80,6 +105,7 @@ export interface CCF { ): ArrayBuffer; kv: KVMaps; + historicalState?: HistoricalState; } export const ccf = globalThis.ccf as CCF; diff --git a/samples/apps/forum/tsoa-support/postprocess.js b/samples/apps/forum/tsoa-support/postprocess.js index f64602f14a44..2d3112565fe3 100644 --- a/samples/apps/forum/tsoa-support/postprocess.js +++ b/samples/apps/forum/tsoa-support/postprocess.js @@ -4,11 +4,11 @@ import SwaggerParser from "@apidevtools/swagger-parser"; import jsonmergepatch from "json-merge-patch"; // endpoint metadata defaults when first added to endpoints.json -const metadataDefaults = (readonly) => ({ +const metadataDefaults = (mode) => ({ forwarding_required: "always", execute_outside_consensus: "never", authn_policies: ["user_cert"], - readonly: readonly, + mode: mode, }); const distDir = "./dist"; @@ -116,8 +116,8 @@ const oldEndpoints = oldMetadata["endpoints"]; const newEndpoints = newMetadata["endpoints"]; for (const url in newEndpoints) { for (const method in newEndpoints[url]) { - const readonly = method == "get"; - Object.assign(newEndpoints[url][method], metadataDefaults(readonly)); + const mode = method == "get" ? "readonly" : "readwrite"; + Object.assign(newEndpoints[url][method], metadataDefaults(mode)); } } console.log(`Updating ${metadataPath} (if needed)`); diff --git a/samples/apps/logging/js/app.json b/samples/apps/logging/js/app.json index 2318ae0077dd..b6872fc0f5ee 100644 --- a/samples/apps/logging/js/app.json +++ b/samples/apps/logging/js/app.json @@ -7,7 +7,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": ["jwt", "user_cert"], - "readonly": true, + "mode": "readonly", "openapi": {} }, "post": { @@ -16,7 +16,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": ["jwt", "user_cert"], - "readonly": false, + "mode": "readwrite", "openapi": {} }, "delete": { @@ -25,7 +25,29 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": ["jwt", "user_cert"], - "readonly": false, + "mode": "readwrite", + "openapi": {} + } + }, + "/log/private/historical": { + "get": { + "js_module": "logging.js", + "js_function": "get_historical", + "forwarding_required": "never", + "execute_outside_consensus": "never", + "authn_policies": ["jwt", "user_cert"], + "mode": "historical", + "openapi": {} + } + }, + "/log/private/historical_receipt": { + "get": { + "js_module": "logging.js", + "js_function": "get_historical_with_receipt", + "forwarding_required": "never", + "execute_outside_consensus": "never", + "authn_policies": ["jwt", "user_cert"], + "mode": "historical", "openapi": {} } }, @@ -36,7 +58,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": ["jwt", "user_cert"], - "readonly": true, + "mode": "readonly", "openapi": {} }, "post": { @@ -45,7 +67,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": ["jwt", "user_cert"], - "readonly": false, + "mode": "readwrite", "openapi": {} }, "delete": { @@ -54,7 +76,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": ["jwt", "user_cert"], - "readonly": false, + "mode": "readwrite", "openapi": {} } } diff --git a/samples/apps/logging/js/src/logging.js b/samples/apps/logging/js/src/logging.js index 1ec039c13377..dfe995b5c7a6 100644 --- a/samples/apps/logging/js/src/logging.js +++ b/samples/apps/logging/js/src/logging.js @@ -29,6 +29,16 @@ export function get_private(request) { return get_record(ccf.kv["records"], id); } +export function get_historical(request) { + return get_private(request); +} + +export function get_historical_with_receipt(request) { + const result = get_private(request); + result.body.receipt = ccf.historicalState.receipt; + return result; +} + export function get_public(request) { const id = get_id_from_request_query(request); return get_record(ccf.kv["public:records"], id); diff --git a/samples/apps/logging/logging.cpp b/samples/apps/logging/logging.cpp index 65611ea4d15a..548721c72b78 100644 --- a/samples/apps/logging/logging.cpp +++ b/samples/apps/logging/logging.cpp @@ -546,30 +546,8 @@ namespace loggingapp kv::Consensus::View view, kv::Consensus::SeqNo seqno, std::string& error_reason) { - if (consensus == nullptr) - { - error_reason = "Node is not fully configured"; - return false; - } - - const auto tx_view = consensus->get_view(seqno); - const auto committed_seqno = consensus->get_committed_seqno(); - const auto committed_view = consensus->get_view(committed_seqno); - - const auto tx_status = ccf::evaluate_tx_status( - view, seqno, tx_view, committed_view, committed_seqno); - if (tx_status != ccf::TxStatus::Committed) - { - error_reason = fmt::format( - "Only committed transactions can be queried. Transaction {}.{} is " - "{}", - view, - seqno, - ccf::tx_status_to_str(tx_status)); - return false; - } - - return true; + return ccf::historical::is_tx_committed( + consensus, view, seqno, error_reason); }; make_endpoint( "log/private/historical", diff --git a/src/apps/js_generic/js_generic.cpp b/src/apps/js_generic/js_generic.cpp index 773007bbd2be..dab59e1bbe0a 100644 --- a/src/apps/js_generic/js_generic.cpp +++ b/src/apps/js_generic/js_generic.cpp @@ -610,7 +610,9 @@ namespace ccfapp JSValueConst this_val, JSAtom property) { - const auto property_name = JS_AtomToCString(ctx, property); + const auto property_name_c = JS_AtomToCString(ctx, property); + const std::string property_name(property_name_c); + JS_FreeCString(ctx, property_name_c); LOG_TRACE_FMT("Looking for kv map '{}'", property_name); const auto [security_domain, access_category] = @@ -807,6 +809,7 @@ namespace ccfapp { private: NetworkTables& network; + ccfapp::AbstractNodeContext& context; JSClassDef kv_class_def = {}; JSClassExoticMethods kv_exotic_methods = {}; @@ -817,7 +820,11 @@ namespace ccfapp metrics::Tracker metrics_tracker; - static JSValue create_ccf_obj(EndpointContext& args, JSContext* ctx) + static JSValue create_ccf_obj( + kv::Tx& tx, + const std::optional& transaction_id, + historical::TxReceiptPtr receipt, + JSContext* ctx) { auto ccf = JS_NewObject(ctx); @@ -861,9 +868,62 @@ namespace ccfapp JS_NewCFunction(ctx, ccfapp::js_wrap_key, "wrapKey", 3)); auto kv = JS_NewObjectClass(ctx, kv_class_id); - JS_SetOpaque(kv, &args.tx); + JS_SetOpaque(kv, &tx); JS_SetPropertyStr(ctx, ccf, "kv", kv); + // Historical queries + if (receipt) + { + auto state = JS_NewObject(ctx); + + ccf::TxID tx_id; + tx_id.seqno = static_cast(transaction_id.value().version); + tx_id.view = static_cast(transaction_id.value().term); + JS_SetPropertyStr( + ctx, + state, + "transactionId", + JS_NewString(ctx, tx_id.to_str().c_str())); + + ccf::GetReceipt::Out receipt_out; + receipt_out.from_receipt(receipt); + auto js_receipt = JS_NewObject(ctx); + JS_SetPropertyStr( + ctx, + js_receipt, + "signature", + JS_NewString(ctx, receipt_out.signature.c_str())); + JS_SetPropertyStr( + ctx, js_receipt, "root", JS_NewString(ctx, receipt_out.root.c_str())); + JS_SetPropertyStr( + ctx, js_receipt, "leaf", JS_NewString(ctx, receipt_out.leaf.c_str())); + JS_SetPropertyStr( + ctx, + js_receipt, + "nodeId", + JS_NewString(ctx, receipt_out.node_id.value().c_str())); + auto proof = JS_NewArray(ctx); + uint32_t i = 0; + for (auto& element : receipt_out.proof) + { + auto js_element = JS_NewObject(ctx); + auto is_left = element.left.has_value(); + JS_SetPropertyStr( + ctx, + js_element, + is_left ? "left" : "right", + JS_NewString( + ctx, (is_left ? element.left : element.right).value().c_str())); + JS_DefinePropertyValueUint32( + ctx, proof, i++, js_element, JS_PROP_C_W_E); + } + JS_SetPropertyStr(ctx, js_receipt, "proof", proof); + + JS_SetPropertyStr(ctx, state, "receipt", js_receipt); + + JS_SetPropertyStr(ctx, ccf, "historicalState", state); + } + return ccf; } @@ -877,12 +937,20 @@ namespace ccfapp return console; } - static void populate_global_obj(EndpointContext& args, JSContext* ctx) + static void populate_global_obj( + kv::Tx& tx, + const std::optional& transaction_id, + ccf::historical::TxReceiptPtr receipt, + JSContext* ctx) { auto global_obj = JS_GetGlobalObject(ctx); JS_SetPropertyStr(ctx, global_obj, "console", create_console_obj(ctx)); - JS_SetPropertyStr(ctx, global_obj, "ccf", create_ccf_obj(args, ctx)); + JS_SetPropertyStr( + ctx, + global_obj, + "ccf", + create_ccf_obj(tx, transaction_id, receipt, ctx)); JS_FreeValue(ctx, global_obj); } @@ -1043,6 +1111,48 @@ namespace ccfapp const std::string& method, const ccf::RESTVerb& verb, EndpointContext& args) + { + // Is this a historical endpoint? + auto endpoints = + args.tx.ro(ccf::Tables::ENDPOINTS); + auto info = endpoints->get(ccf::endpoints::EndpointKey{method, verb}); + + if ( + info.has_value() && + info.value().mode == ccf::endpoints::Mode::Historical) + { + auto is_tx_committed = [this]( + kv::Consensus::View view, + kv::Consensus::SeqNo seqno, + std::string& error_reason) { + return ccf::historical::is_tx_committed( + consensus, view, seqno, error_reason); + }; + + ccf::historical::adapter( + [this, &method, &verb]( + ccf::EndpointContext& args, ccf::historical::StatePtr state) { + auto tx = state->store->create_tx(); + auto tx_id = state->transaction_id; + auto receipt = state->receipt; + do_execute_request(method, verb, args, tx, tx_id, receipt); + }, + context.get_historical_state(), + is_tx_committed)(args); + } + else + { + do_execute_request(method, verb, args, args.tx, std::nullopt, nullptr); + } + } + + void do_execute_request( + const std::string& method, + const ccf::RESTVerb& verb, + EndpointContext& args, + kv::Tx& target_tx, + const std::optional& transaction_id, + ccf::historical::TxReceiptPtr receipt) { const auto local_method = method.substr(method.find_first_not_of('/')); @@ -1130,7 +1240,7 @@ namespace ccfapp JS_SetClassProto(ctx, body_class_id, body_proto); // Populate globalThis with console and ccf globals - populate_global_obj(args, ctx); + populate_global_obj(target_tx, transaction_id, receipt, ctx); // Compile module if (!handler_script.value().text.has_value()) @@ -1356,7 +1466,8 @@ namespace ccfapp public: JSHandlers(NetworkTables& network, AbstractNodeContext& context) : UserEndpointRegistry(context), - network(network) + network(network), + context(context) { JS_NewClassID(&kv_class_id); kv_exotic_methods.get_own_property = js_kv_lookup; diff --git a/src/node/historical_queries_adapter.h b/src/node/historical_queries_adapter.h index c9a374f3bcbd..ced0218ce104 100644 --- a/src/node/historical_queries_adapter.h +++ b/src/node/historical_queries_adapter.h @@ -49,6 +49,39 @@ namespace ccf::historical return tx_id_opt; } + static inline bool is_tx_committed( + kv::Consensus* consensus, + kv::Consensus::View view, + kv::Consensus::SeqNo seqno, + std::string& error_reason) + { + if (consensus == nullptr) + { + error_reason = "Node is not fully configured"; + return false; + } + + const auto tx_view = consensus->get_view(seqno); + const auto committed_seqno = consensus->get_committed_seqno(); + const auto committed_view = consensus->get_view(committed_seqno); + + const auto tx_status = ccf::evaluate_tx_status( + view, seqno, tx_view, committed_view, committed_seqno); + if (tx_status != ccf::TxStatus::Committed) + { + error_reason = fmt::format( + "Only committed transactions can be queried. Transaction {}.{} " + "is " + "{}", + view, + seqno, + ccf::tx_status_to_str(tx_status)); + return false; + } + + return true; + }; + #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-function" diff --git a/src/node/rpc/endpoint.h b/src/node/rpc/endpoint.h index 0d6e2ead0d21..d21083a617e6 100644 --- a/src/node/rpc/endpoint.h +++ b/src/node/rpc/endpoint.h @@ -39,11 +39,19 @@ namespace ccf Locally, Primary }; + + enum class Mode + { + ReadWrite, + ReadOnly, + Historical + }; } } MSGPACK_ADD_ENUM(ccf::endpoints::ForwardingRequired); MSGPACK_ADD_ENUM(ccf::endpoints::ExecuteOutsideConsensus); +MSGPACK_ADD_ENUM(ccf::endpoints::Mode); namespace ccf { @@ -61,10 +69,17 @@ namespace ccf {ExecuteOutsideConsensus::Locally, "locally"}, {ExecuteOutsideConsensus::Primary, "primary"}}); + DECLARE_JSON_ENUM( + Mode, + {{Mode::ReadWrite, "readwrite"}, + {Mode::ReadOnly, "readonly"}, + {Mode::Historical, "historical"}}); + using AuthnPolicies = std::vector>; struct EndpointProperties { + Mode mode = Mode::ReadWrite; ForwardingRequired forwarding_required = ForwardingRequired::Always; ExecuteOutsideConsensus execute_outside_consensus = ExecuteOutsideConsensus::Never; @@ -78,13 +93,15 @@ namespace ccf execute_outside_consensus, authn_policies, openapi, - openapi_hidden); + openapi_hidden, + mode); }; DECLARE_JSON_TYPE_WITH_OPTIONAL_FIELDS(EndpointProperties); DECLARE_JSON_REQUIRED_FIELDS( EndpointProperties, forwarding_required, authn_policies); - DECLARE_JSON_OPTIONAL_FIELDS(EndpointProperties, openapi, openapi_hidden); + DECLARE_JSON_OPTIONAL_FIELDS( + EndpointProperties, openapi, openapi_hidden, mode); struct EndpointDefinition { diff --git a/tests/e2e_logging.py b/tests/e2e_logging.py index a7e047565095..d2899ff90717 100644 --- a/tests/e2e_logging.py +++ b/tests/e2e_logging.py @@ -430,14 +430,9 @@ def test_historical_query(network, args): LOG.warning("Skipping historical queries in BFT") return network - if args.package == "liblogging": - network.txs.issue(network, number_txs=2) - network.txs.issue(network, number_txs=2, repeat=True) - network.txs.verify() - else: - LOG.warning( - f"Skipping {inspect.currentframe().f_code.co_name} as application is not C++" - ) + network.txs.issue(network, number_txs=2) + network.txs.issue(network, number_txs=2, repeat=True) + network.txs.verify() return network @@ -449,40 +444,34 @@ def test_historical_receipts(network, args): LOG.warning("Skipping historical queries in BFT") return network - if args.package == "liblogging": - primary, backups = network.find_nodes() - cert_path = os.path.join(primary.common_dir, f"{primary.local_node_id}.pem") - with open(cert_path) as c: - primary_cert = load_pem_x509_certificate( - c.read().encode("ascii"), default_backend() - ) + primary, backups = network.find_nodes() + cert_path = os.path.join(primary.common_dir, f"{primary.local_node_id}.pem") + with open(cert_path) as c: + primary_cert = load_pem_x509_certificate( + c.read().encode("ascii"), default_backend() + ) - TXS_COUNT = 5 - network.txs.issue(network, number_txs=5) - for idx in range(1, TXS_COUNT + 1): - for node in [primary, backups[0]]: - first_msg = network.txs.priv[idx][0] - first_receipt = network.txs.get_receipt( - node, idx, first_msg["seqno"], first_msg["view"] - ) - r = first_receipt.json()["receipt"] - assert r["root"] == ccf.receipt.root(r["leaf"], r["proof"]) - ccf.receipt.verify(r["root"], r["signature"], primary_cert) - - # receipt.verify() raises if it fails, but does not return anything - verified = True - try: - ccf.receipt.verify( - hashlib.sha256(b"").hexdigest(), r["signature"], primary_cert + TXS_COUNT = 5 + network.txs.issue(network, number_txs=5) + for idx in range(1, TXS_COUNT + 1): + for node in [primary, backups[0]]: + first_msg = network.txs.priv[idx][0] + first_receipt = network.txs.get_receipt( + node, idx, first_msg["seqno"], first_msg["view"] ) - except InvalidSignature: - verified = False - assert not verified - - else: - LOG.warning( - f"Skipping {inspect.currentframe().f_code.co_name} as application is not C++" + r = first_receipt.json()["receipt"] + assert r["root"] == ccf.receipt.root(r["leaf"], r["proof"]) + ccf.receipt.verify(r["root"], r["signature"], primary_cert) + + # receipt.verify() raises if it fails, but does not return anything + verified = True + try: + ccf.receipt.verify( + hashlib.sha256(b"").hexdigest(), r["signature"], primary_cert ) + except InvalidSignature: + verified = False + assert not verified return network diff --git a/tests/js-app-bundle/app.json b/tests/js-app-bundle/app.json index 4781104d8899..7854ddf2e75a 100644 --- a/tests/js-app-bundle/app.json +++ b/tests/js-app-bundle/app.json @@ -7,7 +7,7 @@ "forwarding_required": "never", "execute_outside_consensus": "locally", "authn_policies": ["user_cert"], - "readonly": true, + "mode": "readonly", "openapi": { "requestBody": { "required": true, @@ -80,7 +80,7 @@ "forwarding_required": "never", "execute_outside_consensus": "locally", "authn_policies": ["user_cert"], - "readonly": true, + "mode": "readonly", "openapi": { "parameters": [ { diff --git a/tests/js-authentication/app.json b/tests/js-authentication/app.json index fb8d57aa6e7e..8f04f88899ea 100644 --- a/tests/js-authentication/app.json +++ b/tests/js-authentication/app.json @@ -7,7 +7,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": ["jwt"], - "readonly": true, + "mode": "readonly", "openapi": {} } }, @@ -25,7 +25,7 @@ "jwt", "no_auth" ], - "readonly": true, + "mode": "readonly", "openapi": {} } } diff --git a/tests/js-content-types/app.json b/tests/js-content-types/app.json index 8becfce67e91..c1c39a1d0122 100644 --- a/tests/js-content-types/app.json +++ b/tests/js-content-types/app.json @@ -7,7 +7,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": ["user_cert"], - "readonly": true, + "mode": "readonly", "openapi": {} } }, @@ -18,7 +18,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": ["user_cert"], - "readonly": true, + "mode": "readonly", "openapi": {} } }, @@ -29,7 +29,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": ["user_cert"], - "readonly": true, + "mode": "readonly", "openapi": {} } }, @@ -40,7 +40,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": ["user_cert"], - "readonly": true, + "mode": "readonly", "openapi": {} } } diff --git a/tests/js-custom-authorization/app.json b/tests/js-custom-authorization/app.json index 3b2e68bdba3f..f29768ea0346 100644 --- a/tests/js-custom-authorization/app.json +++ b/tests/js-custom-authorization/app.json @@ -7,7 +7,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": ["user_cert"], - "readonly": true, + "mode": "readonly", "openapi": {} } } diff --git a/tests/js-limits/app.json b/tests/js-limits/app.json index 30277f63fd8d..7d5b872fdc5f 100644 --- a/tests/js-limits/app.json +++ b/tests/js-limits/app.json @@ -7,7 +7,7 @@ "forwarding_required": "never", "execute_outside_consensus": "never", "authn_policies": ["user_cert"], - "readonly": true, + "mode": "readonly", "openapi": {} } }, @@ -18,7 +18,7 @@ "forwarding_required": "never", "execute_outside_consensus": "never", "authn_policies": ["user_cert"], - "readonly": true, + "mode": "readonly", "openapi": {} } } diff --git a/tests/js-modules/basic-module-import/app.json b/tests/js-modules/basic-module-import/app.json index 04ecc87699e4..1c8248c23b87 100644 --- a/tests/js-modules/basic-module-import/app.json +++ b/tests/js-modules/basic-module-import/app.json @@ -7,7 +7,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": ["user_cert"], - "readonly": true, + "mode": "readonly", "openapi": {} } } diff --git a/tests/npm-app/app.json b/tests/npm-app/app.json index 9d66130e67b4..fe24f19667da 100644 --- a/tests/npm-app/app.json +++ b/tests/npm-app/app.json @@ -7,7 +7,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": [], - "readonly": true, + "mode": "readonly", "openapi": { "responses": { "200": { @@ -57,7 +57,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": ["user_cert"], - "readonly": true, + "mode": "readonly", "openapi": { "responses": { "200": { @@ -90,7 +90,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": ["user_cert"], - "readonly": true, + "mode": "readonly", "openapi": { "requestBody": { "required": true, @@ -125,7 +125,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": ["user_cert"], - "readonly": true, + "mode": "readonly", "openapi": { "requestBody": { "required": true, @@ -177,7 +177,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": ["user_cert"], - "readonly": true, + "mode": "readonly", "openapi": { "requestBody": { "required": true, @@ -218,7 +218,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": ["user_cert"], - "readonly": false, + "mode": "readonly", "openapi": { "responses": { "200": { @@ -257,7 +257,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": ["user_cert"], - "readonly": false, + "mode": "readonly", "openapi": { "requestBody": { "required": true, @@ -283,7 +283,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": ["user_cert"], - "readonly": false, + "mode": "readonly", "openapi": { "responses": { "200": { @@ -313,7 +313,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": ["user_cert"], - "readonly": false, + "mode": "readwrite", "openapi": { "responses": { "200": { @@ -350,7 +350,7 @@ "forwarding_required": "always", "execute_outside_consensus": "never", "authn_policies": ["user_cert"], - "readonly": false, + "mode": "readonly", "openapi": { "responses": { "200": { diff --git a/tests/npm-app/src/types/ccf.ts b/tests/npm-app/src/types/ccf.ts index ca2fb4cdbe3f..dbcf74bb9a84 100644 --- a/tests/npm-app/src/types/ccf.ts +++ b/tests/npm-app/src/types/ccf.ts @@ -48,6 +48,26 @@ export interface KVMap { export type KVMaps = { [key: string]: KVMap }; +export interface ProofElement { + left?: string; + right?: string; +} + +export type Proof = ProofElement[]; + +export interface Receipt { + signature: string; + root: string; + proof: Proof; + leaf: string; + nodeId: string; +} + +export interface HistoricalState { + transactionId: string; + receipt: Receipt; +} + interface WrapAlgoParams { name: string; } @@ -85,6 +105,7 @@ export interface CCF { ): ArrayBuffer; kv: KVMaps; + historicalState?: HistoricalState; } export const ccf = globalThis.ccf as CCF;