Skip to content

Commit 66b1b47

Browse files
achamayouCopilot
andcommitted
Address Rust interface review feedback
Stabilize the C ABI, preserve Cargo dependency tracking, register Rust unit tests, enforce unwind panics, and clarify native application trust semantics. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent b4760dd commit 66b1b47

8 files changed

Lines changed: 120 additions & 93 deletions

File tree

CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,19 @@ if(BUILD_TESTS)
594594

595595
# Unit tests
596596
if(BUILD_UNIT_TESTS)
597+
add_test(
598+
NAME ccf_app_rust_test
599+
COMMAND
600+
${CMAKE_COMMAND} -E env --unset=CARGO_BUILD_TARGET "CARGO_NET_RETRY=10"
601+
"CARGO_HTTP_TIMEOUT=60" "CARGO_BUILD_RUSTC=${RUSTC}" ${CARGO} test
602+
--manifest-path ${CCF_DIR}/src/rust/ccf-app/Cargo.toml --target-dir
603+
${CMAKE_BINARY_DIR}/cargo/ccf-app-test --locked
604+
)
605+
set_tests_properties(
606+
ccf_app_rust_test
607+
PROPERTIES LABELS unit WORKING_DIRECTORY ${CCF_DIR}/src/rust/ccf-app
608+
)
609+
597610
add_test(
598611
NAME verify_uvm_attestation_and_endorsements
599612
COMMAND

cmake/ccf_app.cmake

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,7 @@ function(add_ccf_app name)
5353
endfunction()
5454

5555
function(add_ccf_rust_app name)
56-
cmake_parse_arguments(
57-
PARSE_ARGV 1
58-
PARSED_ARGS
59-
""
60-
"MANIFEST_PATH;PACKAGE"
61-
"DEPS"
62-
)
56+
cmake_parse_arguments(PARSE_ARGV 1 PARSED_ARGS "" "MANIFEST_PATH;PACKAGE" "")
6357

6458
if(NOT PARSED_ARGS_MANIFEST_PATH)
6559
message(FATAL_ERROR "add_ccf_rust_app requires MANIFEST_PATH")
@@ -95,14 +89,13 @@ function(add_ccf_rust_app name)
9589
${CARGO_TARGET_DIR}/${CARGO_PROFILE_DIR}/lib${RUST_LIB_NAME}.a
9690
)
9791

98-
file(GLOB_RECURSE RUST_APP_SOURCES CONFIGURE_DEPENDS ${MANIFEST_DIR}/src/*.rs)
99-
10092
set(
10193
RUSTFLAGS
10294
"$ENV{RUSTFLAGS} --remap-path-prefix=${MANIFEST_DIR}=APP --remap-path-prefix=${CCF_DIR}=CCF --remap-path-prefix=$ENV{HOME}/.cargo=CARGO"
10395
)
104-
add_custom_command(
105-
OUTPUT ${RUST_APP_LIB}
96+
add_custom_target(
97+
cargo-build_${name}
98+
BYPRODUCTS ${RUST_APP_LIB}
10699
COMMAND ${CMAKE_COMMAND} -E make_directory ${CARGO_TARGET_DIR}
107100
COMMAND
108101
${CMAKE_COMMAND} -E env --unset=CARGO_BUILD_TARGET
@@ -112,16 +105,10 @@ function(add_ccf_rust_app name)
112105
${PARSED_ARGS_PACKAGE} --manifest-path ${MANIFEST_PATH} --target-dir
113106
${CARGO_TARGET_DIR} ${CARGO_PROFILE_FLAG} --locked
114107
WORKING_DIRECTORY ${MANIFEST_DIR}
115-
DEPENDS
116-
${MANIFEST_PATH}
117-
${MANIFEST_DIR}/Cargo.lock
118-
${RUST_APP_SOURCES}
119-
${PARSED_ARGS_DEPS}
120108
COMMENT "Building Rust CCF application ${name}"
121109
USES_TERMINAL
122110
VERBATIM
123111
)
124-
add_custom_target(cargo-build_${name} DEPENDS ${RUST_APP_LIB})
125112

126113
if(EXISTS "${CCF_DIR}/src/rust/app_bridge.cpp")
127114
set(RUST_BRIDGE_SOURCE "${CCF_DIR}/src/rust/app_bridge.cpp")

cmake/gersemi_definitions.cmake

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,7 @@ function(add_ccf_app name)
1616
endfunction()
1717

1818
function(add_ccf_rust_app name)
19-
cmake_parse_arguments(
20-
PARSE_ARGV 1
21-
PARSED_ARGS
22-
""
23-
"MANIFEST_PATH;PACKAGE"
24-
"DEPS"
25-
)
19+
cmake_parse_arguments(PARSE_ARGV 1 PARSED_ARGS "" "MANIFEST_PATH;PACKAGE" "")
2620
endfunction()
2721

2822
function(add_ccf_static_library name)

doc/build_apps/example_rust.rst

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,10 @@ which depends on the source-tree ``src/rust/ccf-app`` crate or the installed
3131
3232
The helper maps CMake ``Debug`` builds to Cargo's development profile and all
3333
other build types to Cargo's release profile. It also links the generic C++ ABI
34-
bridge, launcher, and CCF libraries. Cargo sources, the manifest, and the lock
35-
file are build dependencies. The application should commit ``Cargo.lock`` and
36-
pin a Rust toolchain for reproducible builds.
34+
bridge, launcher, and CCF libraries. Cargo is invoked on every build and decides
35+
whether the crate is up to date, so Rust source edits do not require CMake to be
36+
reconfigured. The application should commit ``Cargo.lock`` and pin a Rust
37+
toolchain for reproducible builds.
3738

3839
The complete records example is in :ccf_repo:`samples/apps/basic_rust`. It
3940
exports a registration function with ``ccf_app::export_app!`` and registers
@@ -47,8 +48,10 @@ retry a read-write handler when a transaction conflicts, so handlers should be
4748
deterministic and should not perform non-transactional side effects.
4849

4950
Request, response, transaction, and map values borrow the callback context and
50-
cannot be retained. Rust panics are caught at the ABI boundary and become HTTP
51-
500 errors. C++ exceptions are also contained by the bridge.
51+
cannot be retained. The SDK requires Rust's ``unwind`` panic strategy so that
52+
panics are caught at the ABI boundary and become HTTP 500 errors. Builds using
53+
``panic = "abort"`` are rejected. C++ exceptions are also contained by the
54+
bridge.
5255

5356
KV values and keys
5457
------------------
@@ -59,8 +62,10 @@ common interface without prescribing a wire format.
5962

6063
Map names retain the standard CCF security semantics. Names beginning with
6164
``public:`` are written to the ledger in plaintext. All other application map
62-
names, such as the sample's ``records`` map, are private and encrypted. The
63-
framework continues to enforce reserved governance and internal map namespaces.
65+
names, such as the sample's ``records`` map, are private and encrypted. Like
66+
native C++ applications, native Rust applications are trusted code: raw map
67+
access does not enforce the namespace restrictions applied to JavaScript
68+
applications for reserved governance and internal maps.
6469

6570
Read-only handlers receive only ``ReadOnlyMap``, so write operations are
6671
not available at compile time. Errors returned by a handler use the normal CCF

include/ccf/rust_ffi.h

Lines changed: 62 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -12,88 +12,112 @@ extern "C"
1212

1313
static const uint32_t CCF_RUST_ABI_VERSION = 1;
1414

15-
typedef struct ccf_rust_registry ccf_rust_registry;
16-
typedef struct ccf_rust_endpoint_context ccf_rust_endpoint_context;
17-
18-
typedef struct ccf_rust_slice
15+
struct ccf_rust_registry;
16+
struct ccf_rust_endpoint_context;
17+
struct ccf_rust_slice
1918
{
2019
const uint8_t* data;
2120
size_t len;
22-
} ccf_rust_slice;
21+
};
2322

24-
typedef enum ccf_rust_result
25-
{
26-
CCF_RUST_OK = 0,
27-
CCF_RUST_NOT_FOUND = 1,
28-
CCF_RUST_INVALID_ARGUMENT = 2,
29-
CCF_RUST_READ_ONLY = 3,
30-
CCF_RUST_INTERNAL_ERROR = 4
31-
} ccf_rust_result;
32-
33-
typedef enum ccf_rust_auth
34-
{
35-
CCF_RUST_AUTH_NONE = 0,
36-
CCF_RUST_AUTH_USER_CERT = 1
37-
} ccf_rust_auth;
23+
#ifdef __cplusplus
24+
using ccf_rust_result = int32_t;
25+
using ccf_rust_auth = int32_t;
26+
using ccf_rust_endpoint_callback =
27+
ccf_rust_result (*)(void* user_data, ccf_rust_endpoint_context* ctx);
28+
using ccf_rust_drop_callback = void (*)(void* user_data);
29+
#else
30+
typedef struct ccf_rust_registry ccf_rust_registry;
31+
typedef struct ccf_rust_endpoint_context ccf_rust_endpoint_context;
32+
typedef struct ccf_rust_slice ccf_rust_slice;
33+
typedef int32_t ccf_rust_result;
34+
typedef int32_t ccf_rust_auth;
35+
typedef ccf_rust_result (*ccf_rust_endpoint_callback)(
36+
void* user_data, ccf_rust_endpoint_context* ctx);
37+
typedef void (*ccf_rust_drop_callback)(void* user_data);
38+
#endif
3839

39-
typedef int (*ccf_rust_endpoint_callback)(
40-
void* user_data, ccf_rust_endpoint_context* ctx);
41-
typedef void (*ccf_rust_drop_callback)(void* user_data);
40+
#ifdef __cplusplus
41+
inline constexpr ccf_rust_result CCF_RUST_OK = 0;
42+
inline constexpr ccf_rust_result CCF_RUST_NOT_FOUND = 1;
43+
inline constexpr ccf_rust_result CCF_RUST_INVALID_ARGUMENT = 2;
44+
inline constexpr ccf_rust_result CCF_RUST_READ_ONLY = 3;
45+
inline constexpr ccf_rust_result CCF_RUST_INTERNAL_ERROR = 4;
46+
47+
inline constexpr ccf_rust_auth CCF_RUST_AUTH_NONE = 0;
48+
inline constexpr ccf_rust_auth CCF_RUST_AUTH_USER_CERT = 1;
49+
#else
50+
enum
51+
{
52+
CCF_RUST_OK = 0,
53+
CCF_RUST_NOT_FOUND = 1,
54+
CCF_RUST_INVALID_ARGUMENT = 2,
55+
CCF_RUST_READ_ONLY = 3,
56+
CCF_RUST_INTERNAL_ERROR = 4
57+
};
58+
59+
enum
60+
{
61+
CCF_RUST_AUTH_NONE = 0,
62+
CCF_RUST_AUTH_USER_CERT = 1
63+
};
64+
#endif
4265

4366
uint32_t ccf_rust_get_abi_version(void);
4467

45-
int ccf_rust_register_endpoint(
68+
ccf_rust_result ccf_rust_register_endpoint(
4669
ccf_rust_registry* registry,
4770
ccf_rust_slice path,
4871
ccf_rust_slice method,
4972
ccf_rust_auth auth,
50-
int read_only,
73+
int32_t read_only,
5174
ccf_rust_endpoint_callback callback,
5275
ccf_rust_drop_callback drop,
5376
void* user_data);
5477

55-
int ccf_rust_request_body(
78+
ccf_rust_result ccf_rust_request_body(
5679
ccf_rust_endpoint_context* ctx, ccf_rust_slice* body);
57-
int ccf_rust_request_query(
80+
ccf_rust_result ccf_rust_request_query(
5881
ccf_rust_endpoint_context* ctx, ccf_rust_slice* query);
59-
int ccf_rust_request_path_param(
82+
ccf_rust_result ccf_rust_request_path_param(
6083
ccf_rust_endpoint_context* ctx, ccf_rust_slice name, ccf_rust_slice* value);
61-
int ccf_rust_request_header(
84+
ccf_rust_result ccf_rust_request_header(
6285
ccf_rust_endpoint_context* ctx, ccf_rust_slice name, ccf_rust_slice* value);
6386

64-
int ccf_rust_response_status(ccf_rust_endpoint_context* ctx, uint16_t status);
65-
int ccf_rust_response_header(
87+
ccf_rust_result ccf_rust_response_status(
88+
ccf_rust_endpoint_context* ctx, uint16_t status);
89+
ccf_rust_result ccf_rust_response_header(
6690
ccf_rust_endpoint_context* ctx, ccf_rust_slice name, ccf_rust_slice value);
67-
int ccf_rust_response_body(
91+
ccf_rust_result ccf_rust_response_body(
6892
ccf_rust_endpoint_context* ctx, ccf_rust_slice body);
69-
int ccf_rust_response_error(
93+
ccf_rust_result ccf_rust_response_error(
7094
ccf_rust_endpoint_context* ctx,
7195
uint16_t status,
7296
ccf_rust_slice code,
7397
ccf_rust_slice message);
7498

75-
int ccf_rust_kv_get(
99+
ccf_rust_result ccf_rust_kv_get(
76100
ccf_rust_endpoint_context* ctx,
77101
ccf_rust_slice map_name,
78102
ccf_rust_slice key,
79103
ccf_rust_slice* value);
80-
int ccf_rust_kv_has(
104+
ccf_rust_result ccf_rust_kv_has(
81105
ccf_rust_endpoint_context* ctx,
82106
ccf_rust_slice map_name,
83107
ccf_rust_slice key,
84-
int* present);
85-
int ccf_rust_kv_put(
108+
int32_t* present);
109+
ccf_rust_result ccf_rust_kv_put(
86110
ccf_rust_endpoint_context* ctx,
87111
ccf_rust_slice map_name,
88112
ccf_rust_slice key,
89113
ccf_rust_slice value);
90-
int ccf_rust_kv_remove(
114+
ccf_rust_result ccf_rust_kv_remove(
91115
ccf_rust_endpoint_context* ctx,
92116
ccf_rust_slice map_name,
93117
ccf_rust_slice key);
94118

95119
uint32_t ccf_rust_app_abi_version(void);
96-
int ccf_rust_app_register(ccf_rust_registry* registry);
120+
ccf_rust_result ccf_rust_app_register(ccf_rust_registry* registry);
97121

98122
#ifdef __cplusplus
99123
}

samples/apps/basic_rust/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,4 @@ add_ccf_rust_app(
1515
basic_rust
1616
MANIFEST_PATH ${CMAKE_CURRENT_LIST_DIR}/Cargo.toml
1717
PACKAGE ccf-basic-rust
18-
DEPS ${CCF_DIR}/src/rust/ccf-app/src/lib.rs
1918
)

0 commit comments

Comments
 (0)