From ca3085b3bc8b1ec97eb58f8116fad19fda397c49 Mon Sep 17 00:00:00 2001 From: Eddy Ashton Date: Thu, 13 Jul 2023 09:06:52 +0000 Subject: [PATCH 1/5] Add getVersionOfPreviousWrite --- js/ccf-app/src/kv.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/js/ccf-app/src/kv.ts b/js/ccf-app/src/kv.ts index f68faf778e4f..c15f7ce96511 100644 --- a/js/ccf-app/src/kv.ts +++ b/js/ccf-app/src/kv.ts @@ -45,7 +45,7 @@ export class TypedKvMap { private kv: KvMap, private kt: DataConverter, private vt: DataConverter, - ) {} + ) { } has(key: K): boolean { return this.kv.has(this.kt.encode(key)); @@ -56,6 +56,10 @@ export class TypedKvMap { return v === undefined ? undefined : this.vt.decode(v); } + getVersionOfPreviousWrite(key: K): number | undefined { + return this.kv.getVersionOfPreviousWrite(this.kt.encode(key)); + } + set(key: K, value: V): TypedKvMap { this.kv.set(this.kt.encode(key), this.vt.encode(value)); return this; From 7f1e51c4991f9265d46f4a5bb66db82dcc146779 Mon Sep 17 00:00:00 2001 From: Eddy Ashton Date: Thu, 13 Jul 2023 09:10:28 +0000 Subject: [PATCH 2/5] Add new endpoint, consistent embedding of test path --- js/ccf-app/src/global.ts | 14 ++++++------ js/ccf-app/src/kv.ts | 8 +++---- tests/js-modules/modules.py | 17 ++++++++------- tests/npm-app/app.json | 31 +++++++++++++++++++++++++++ tests/npm-app/src/endpoints/log.ts | 34 ++++++++++++++++++++++++------ 5 files changed, 78 insertions(+), 26 deletions(-) diff --git a/js/ccf-app/src/global.ts b/js/ccf-app/src/global.ts index 6489839003b6..47d5f6c66def 100644 --- a/js/ccf-app/src/global.ts +++ b/js/ccf-app/src/global.ts @@ -44,7 +44,7 @@ export interface KvMap { delete(key: ArrayBuffer): void; clear(): void; forEach( - callback: (value: ArrayBuffer, key: ArrayBuffer, kvmap: KvMap) => void, + callback: (value: ArrayBuffer, key: ArrayBuffer, kvmap: KvMap) => void ): void; size: number; } @@ -326,7 +326,7 @@ export interface CCFCrypto { sign( algorithm: SigningAlgorithm, key: string, - plaintext: ArrayBuffer, + plaintext: ArrayBuffer ): ArrayBuffer; /** @@ -343,7 +343,7 @@ export interface CCFCrypto { algorithm: SigningAlgorithm, key: string, signature: ArrayBuffer, - plaintext: ArrayBuffer, + plaintext: ArrayBuffer ): boolean; /** @@ -384,7 +384,7 @@ export interface CCFCrypto { wrapKey( key: ArrayBuffer, wrappingKey: ArrayBuffer, - wrapAlgo: WrapAlgoParams, + wrapAlgo: WrapAlgoParams ): ArrayBuffer; /** @@ -582,7 +582,7 @@ export interface CCFHistorical { handle: number, startSeqno: number, endSeqno: number, - secondsUntilExpiry: number, + secondsUntilExpiry: number ): HistoricalState[] | null; /** Drop cached states for the given handle. @@ -648,7 +648,7 @@ export interface CCF { wrapKey( key: ArrayBuffer, wrappingKey: ArrayBuffer, - wrapAlgo: WrapAlgoParams, + wrapAlgo: WrapAlgoParams ): ArrayBuffer; /** @@ -736,6 +736,6 @@ export interface OpenEnclave { verifyOpenEnclaveEvidence( format: string | undefined, evidence: ArrayBuffer, - endorsements?: ArrayBuffer, + endorsements?: ArrayBuffer ): EvidenceClaims; } diff --git a/js/ccf-app/src/kv.ts b/js/ccf-app/src/kv.ts index c15f7ce96511..692af4a681bf 100644 --- a/js/ccf-app/src/kv.ts +++ b/js/ccf-app/src/kv.ts @@ -44,8 +44,8 @@ export class TypedKvMap { constructor( private kv: KvMap, private kt: DataConverter, - private vt: DataConverter, - ) { } + private vt: DataConverter + ) {} has(key: K): boolean { return this.kv.has(this.kt.encode(key)); @@ -80,7 +80,7 @@ export class TypedKvMap { this.kv.forEach(function ( raw_v: ArrayBuffer, raw_k: ArrayBuffer, - table: KvMap, + table: KvMap ) { callback(vt.decode(raw_v), kt.decode(raw_k), typedMap); }); @@ -107,7 +107,7 @@ export class TypedKvMap { export function typedKv( nameOrMap: string | KvMap, kt: DataConverter, - vt: DataConverter, + vt: DataConverter ) { const kvMap = typeof nameOrMap === "string" ? ccf.kv[nameOrMap] : nameOrMap; return new TypedKvMap(kvMap, kt, vt); diff --git a/tests/js-modules/modules.py b/tests/js-modules/modules.py index b945956e8d05..04b2a6dbf18c 100644 --- a/tests/js-modules/modules.py +++ b/tests/js-modules/modules.py @@ -787,6 +787,7 @@ def test_npm_app(network, args): r = c.get("/app/log?id=42") assert r.status_code == http.HTTPStatus.OK, r.status_code body = r.body.json() + assert body["id"] == 42, r.body assert body["msg"] == "Hello!", r.body r = c.post("/app/log?id=42", {"msg": "Saluton!"}) @@ -1105,15 +1106,15 @@ def run(args): args.nodes, args.binary_dir, args.debug_nodes, args.perf_nodes, pdb=args.pdb ) as network: network.start_and_open(args) - network = test_module_import(network, args) - network = test_bytecode_cache(network, args) - network = test_app_bundle(network, args) - network = test_dynamic_endpoints(network, args) - network = test_set_js_runtime(network, args) + # network = test_module_import(network, args) + # network = test_bytecode_cache(network, args) + # network = test_app_bundle(network, args) + # network = test_dynamic_endpoints(network, args) + # network = test_set_js_runtime(network, args) network = test_npm_app(network, args) - network = test_js_execution_time(network, args) - network = test_js_exception_output(network, args) - network = test_user_cose_authentication(network, args) + # network = test_js_execution_time(network, args) + # network = test_js_exception_output(network, args) + # network = test_user_cose_authentication(network, args) if __name__ == "__main__": diff --git a/tests/npm-app/app.json b/tests/npm-app/app.json index 6ca670991ce5..576bd0cc239a 100644 --- a/tests/npm-app/app.json +++ b/tests/npm-app/app.json @@ -722,6 +722,37 @@ } } }, + "/log/version": { + "get": { + "js_module": "endpoints/log.js", + "js_function": "getLogItemVersion", + "forwarding_required": "sometimes", + "authn_policies": ["user_cert"], + "mode": "readonly", + "openapi": { + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "properties": { + "id": { + "type": "number" + }, + "version": { + "type": "number" + } + }, + "type": "object" + } + } + } + } + } + } + } + }, "/rpc/apply_writes": { "post": { "js_module": "endpoints/rpc.js", diff --git a/tests/npm-app/src/endpoints/log.ts b/tests/npm-app/src/endpoints/log.ts index f3227e1440b8..e2a64e75ff30 100644 --- a/tests/npm-app/src/endpoints/log.ts +++ b/tests/npm-app/src/endpoints/log.ts @@ -1,16 +1,36 @@ import * as ccfapp from "@microsoft/ccf-app"; +type LogContent = string; + interface LogItem { - msg: string; + msg: LogContent; } interface LogEntry extends LogItem { id: number; } -const logMap = ccfapp.typedKv("log", ccfapp.uint32, ccfapp.json()); +interface LogVersion { + id: number; + version: number; +} + +const logMap = ccfapp.typedKv("log", ccfapp.uint32, ccfapp.json()); -export function getLogItem(request: ccfapp.Request): ccfapp.Response { +export function getLogItem(request: ccfapp.Request): ccfapp.Response { + const id = parseInt(request.query.split("=")[1]); + if (!logMap.has(id)) { + return { + statusCode: 404, + }; + } + return { + body: { id: id, msg: logMap.get(id) }, + }; +} +export function getLogItemVersion( + request: ccfapp.Request +): ccfapp.Response { const id = parseInt(request.query.split("=")[1]); if (!logMap.has(id)) { return { @@ -18,22 +38,22 @@ export function getLogItem(request: ccfapp.Request): ccfapp.Response { }; } return { - body: logMap.get(id), + body: { id: id, version: logMap.getVersionOfPreviousWrite(id) }, }; } export function setLogItem(request: ccfapp.Request): ccfapp.Response { const id = parseInt(request.query.split("=")[1]); - logMap.set(id, request.body.json()); + logMap.set(id, request.body.json().msg); return {}; } export function getAllLogItems( - request: ccfapp.Request, + request: ccfapp.Request ): ccfapp.Response> { let items: Array = []; logMap.forEach(function (item, id) { - items.push({ id: id, msg: item.msg }); + items.push({ id: id, msg: item }); }); return { body: items, From 9b742db04595d9ddc36ce665ca132d1baed22639 Mon Sep 17 00:00:00 2001 From: Eddy Ashton Date: Thu, 13 Jul 2023 09:21:48 +0000 Subject: [PATCH 3/5] Test new TS getVersion wrapper --- tests/js-modules/modules.py | 42 ++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/tests/js-modules/modules.py b/tests/js-modules/modules.py index 04b2a6dbf18c..d9ec9e082cfd 100644 --- a/tests/js-modules/modules.py +++ b/tests/js-modules/modules.py @@ -781,6 +781,9 @@ def test_npm_app(network, args): r = c.get("/app/log?id=42") assert r.status_code == http.HTTPStatus.NOT_FOUND, r.status_code + r = c.get("/app/log/version?id=42") + assert r.status_code == http.HTTPStatus.NOT_FOUND, r.status_code + r = c.post("/app/log?id=42", {"msg": "Hello!"}) assert r.status_code == http.HTTPStatus.OK, r.status_code @@ -790,11 +793,29 @@ def test_npm_app(network, args): assert body["id"] == 42, r.body assert body["msg"] == "Hello!", r.body + r = c.get("/app/log/version?id=42") + assert r.status_code == http.HTTPStatus.OK, r.status_code + v0 = r.body.json()["version"] + r = c.post("/app/log?id=42", {"msg": "Saluton!"}) assert r.status_code == http.HTTPStatus.OK, r.status_code + + r = c.get("/app/log/version?id=42") + assert r.status_code == http.HTTPStatus.OK, r.status_code + v1 = r.body.json()["version"] + assert v1 > v0 + + r = c.get("/app/log/version?id=43") + assert r.status_code == http.HTTPStatus.NOT_FOUND, r.status_code + r = c.post("/app/log?id=43", {"msg": "Bonjour!"}) assert r.status_code == http.HTTPStatus.OK, r.status_code + r = c.get("/app/log/version?id=43") + assert r.status_code == http.HTTPStatus.OK, r.status_code + v2 = r.body.json()["version"] + assert v2 > v1 + r = c.get("/app/log/all") assert r.status_code == http.HTTPStatus.OK, r.status_code body = r.body.json() @@ -803,6 +824,11 @@ def test_npm_app(network, args): assert {"id": 42, "msg": "Saluton!"} in body, body assert {"id": 43, "msg": "Bonjour!"} in body, body + r = c.get("/app/log/version?id=42") + assert r.status_code == http.HTTPStatus.OK, r.status_code + v3 = r.body.json()["version"] + assert v3 == v1 + test_apply_writes(c) r = c.get("/app/jwt") @@ -1106,15 +1132,15 @@ def run(args): args.nodes, args.binary_dir, args.debug_nodes, args.perf_nodes, pdb=args.pdb ) as network: network.start_and_open(args) - # network = test_module_import(network, args) - # network = test_bytecode_cache(network, args) - # network = test_app_bundle(network, args) - # network = test_dynamic_endpoints(network, args) - # network = test_set_js_runtime(network, args) + network = test_module_import(network, args) + network = test_bytecode_cache(network, args) + network = test_app_bundle(network, args) + network = test_dynamic_endpoints(network, args) + network = test_set_js_runtime(network, args) network = test_npm_app(network, args) - # network = test_js_execution_time(network, args) - # network = test_js_exception_output(network, args) - # network = test_user_cose_authentication(network, args) + network = test_js_execution_time(network, args) + network = test_js_exception_output(network, args) + network = test_user_cose_authentication(network, args) if __name__ == "__main__": From edd0120f7d08cbcc11b9a24fb54c45ad1a85483c Mon Sep 17 00:00:00 2001 From: Eddy Ashton Date: Thu, 13 Jul 2023 09:30:24 +0000 Subject: [PATCH 4/5] Revert formatting regressions --- js/ccf-app/src/global.ts | 14 +++++++------- js/ccf-app/src/kv.ts | 6 +++--- tests/npm-app/src/endpoints/log.ts | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/js/ccf-app/src/global.ts b/js/ccf-app/src/global.ts index 47d5f6c66def..6489839003b6 100644 --- a/js/ccf-app/src/global.ts +++ b/js/ccf-app/src/global.ts @@ -44,7 +44,7 @@ export interface KvMap { delete(key: ArrayBuffer): void; clear(): void; forEach( - callback: (value: ArrayBuffer, key: ArrayBuffer, kvmap: KvMap) => void + callback: (value: ArrayBuffer, key: ArrayBuffer, kvmap: KvMap) => void, ): void; size: number; } @@ -326,7 +326,7 @@ export interface CCFCrypto { sign( algorithm: SigningAlgorithm, key: string, - plaintext: ArrayBuffer + plaintext: ArrayBuffer, ): ArrayBuffer; /** @@ -343,7 +343,7 @@ export interface CCFCrypto { algorithm: SigningAlgorithm, key: string, signature: ArrayBuffer, - plaintext: ArrayBuffer + plaintext: ArrayBuffer, ): boolean; /** @@ -384,7 +384,7 @@ export interface CCFCrypto { wrapKey( key: ArrayBuffer, wrappingKey: ArrayBuffer, - wrapAlgo: WrapAlgoParams + wrapAlgo: WrapAlgoParams, ): ArrayBuffer; /** @@ -582,7 +582,7 @@ export interface CCFHistorical { handle: number, startSeqno: number, endSeqno: number, - secondsUntilExpiry: number + secondsUntilExpiry: number, ): HistoricalState[] | null; /** Drop cached states for the given handle. @@ -648,7 +648,7 @@ export interface CCF { wrapKey( key: ArrayBuffer, wrappingKey: ArrayBuffer, - wrapAlgo: WrapAlgoParams + wrapAlgo: WrapAlgoParams, ): ArrayBuffer; /** @@ -736,6 +736,6 @@ export interface OpenEnclave { verifyOpenEnclaveEvidence( format: string | undefined, evidence: ArrayBuffer, - endorsements?: ArrayBuffer + endorsements?: ArrayBuffer, ): EvidenceClaims; } diff --git a/js/ccf-app/src/kv.ts b/js/ccf-app/src/kv.ts index 692af4a681bf..662ebf267252 100644 --- a/js/ccf-app/src/kv.ts +++ b/js/ccf-app/src/kv.ts @@ -44,7 +44,7 @@ export class TypedKvMap { constructor( private kv: KvMap, private kt: DataConverter, - private vt: DataConverter + private vt: DataConverter, ) {} has(key: K): boolean { @@ -80,7 +80,7 @@ export class TypedKvMap { this.kv.forEach(function ( raw_v: ArrayBuffer, raw_k: ArrayBuffer, - table: KvMap + table: KvMap, ) { callback(vt.decode(raw_v), kt.decode(raw_k), typedMap); }); @@ -107,7 +107,7 @@ export class TypedKvMap { export function typedKv( nameOrMap: string | KvMap, kt: DataConverter, - vt: DataConverter + vt: DataConverter, ) { const kvMap = typeof nameOrMap === "string" ? ccf.kv[nameOrMap] : nameOrMap; return new TypedKvMap(kvMap, kt, vt); diff --git a/tests/npm-app/src/endpoints/log.ts b/tests/npm-app/src/endpoints/log.ts index e2a64e75ff30..c451528cfc62 100644 --- a/tests/npm-app/src/endpoints/log.ts +++ b/tests/npm-app/src/endpoints/log.ts @@ -29,7 +29,7 @@ export function getLogItem(request: ccfapp.Request): ccfapp.Response { }; } export function getLogItemVersion( - request: ccfapp.Request + request: ccfapp.Request, ): ccfapp.Response { const id = parseInt(request.query.split("=")[1]); if (!logMap.has(id)) { @@ -49,7 +49,7 @@ export function setLogItem(request: ccfapp.Request): ccfapp.Response { } export function getAllLogItems( - request: ccfapp.Request + request: ccfapp.Request, ): ccfapp.Response> { let items: Array = []; logMap.forEach(function (item, id) { From 0e11ecf1a27042062d2244be2b0d61c5cb8fff52 Mon Sep 17 00:00:00 2001 From: Eddy Ashton Date: Fri, 14 Jul 2023 08:25:50 +0000 Subject: [PATCH 5/5] Add CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74422391d01f..b921b7388c4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. [4.0.5]: https://github.com/microsoft/CCF/releases/tag/ccf-4.0.5 - Debug logging is now available in non-SGX builds by default, and controlled by a run-time CLI argument (`--enclave-log-level`). On SGX this remains a build-time decision (#5375). +- Added `getVersionOfPreviousWrite` to TypeScript `TypedKvMap` interface (#5451). ## [4.0.4]