Skip to content

Commit 7ee977b

Browse files
authored
Prefix frontend dispatch (#501)
* wip * WIP * wip * More prefixing * More prefixing * Prefixes * Formatting * Format * p * Revert cert changes * Fix error cases * wip * Pass new args to frontend process everywhere * Move detect_pack up to jsonrpc.h * Automatically prefix with sni * Extract common unpack_rpc * Don't give unpacked to pbft (yet) * Minor test fixes * Forward readonly_hint all the way * WIP * PoC: RPCContext holds everything, pre-parsed * Push even more details into RPCContext * Distinguish SessionContext * Start extending PoC into frontend_test * Combine and standardise tests * More frontend_test fixes * frontend_tests pass * Begin update of membervoting_test, remove process_json * Next test case passes * Churn * Progress * Most of membervoting_test * No more process_json * Fix luageneric_test * All compile * Re-use parsing logic in RPCEndpoint * Doh * Improve logging, serialise method for forwarded tx * Remove some unnecessary changes * Store the correct thing in voting_history * Remove more dead code * Update PBFT compilation paths * Improve logging * PR feedback * Upgrade to LOG_DEBUG * Pass actor with create request * Don't reparse * Reparse where necessary * non-None default prefices * Juggle lines * Add prefix in docs
1 parent 3e6b069 commit 7ee977b

File tree

22 files changed

+876
-843
lines changed

22 files changed

+876
-843
lines changed

sphinx/source/developers/demo.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ The response to each transaction is printed at the ``DEBUG`` logging level, and
5353
.. code-block:: json
5454
5555
{
56-
"method": "LOG_record",
56+
"method": "users/LOG_record",
5757
"params": {
5858
"id": 42,
5959
"msg": "Hello world"
@@ -65,7 +65,7 @@ There should be a corresponding entry in the Python output, similar to:
6565

6666
.. code-block:: text
6767
68-
| INFO | infra.jsonrpc:request:192 - #0 LOG_record {'id': 42, 'msg': 'Hello world'}
68+
| INFO | infra.jsonrpc:request:192 - #0 users/LOG_record {'id': 42, 'msg': 'Hello world'}
6969
| DEBUG | infra.jsonrpc:response:209 - #0 {'id': 0, 'result': True, 'error': None, 'jsonrpc': '2.0', 'commit': 5, 'term': 2, 'global_commit': 4}
7070
7171
The ``e2e`` test script takes several additional parameters, documented by passing ``-h`` on the command line. To debug a node it may be useful to increase the node's verbosity by altering the ``--log-level`` option [#log_location]_, or to attach a debugger to a node at launch with the ``--debug-nodes`` option. If passed the ``--network-only`` option the script will keep the network alive, rather than closing immediately after transactions have completed, allowing additional transactions to be sent manually.

sphinx/source/developers/example.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ The Logging application implements a trivial protocol, made up of four transacti
2121
{
2222
"jsonrpc": "2.0",
2323
"id": 0,
24-
"method": "LOG_record",
24+
"method": "users/LOG_record",
2525
"params": {
2626
"id": 42,
2727
"msg": "A sample private log message"
@@ -37,7 +37,7 @@ The Logging application implements a trivial protocol, made up of four transacti
3737
{
3838
"jsonrpc": "2.0",
3939
"id": 1,
40-
"method": "LOG_get",
40+
"method": "users/LOG_get",
4141
"params": {
4242
"id": 42
4343
}
@@ -52,7 +52,7 @@ The Logging application implements a trivial protocol, made up of four transacti
5252
{
5353
"jsonrpc": "2.0",
5454
"id": 2,
55-
"method": "LOG_record_pub",
55+
"method": "users/LOG_record_pub",
5656
"params": {
5757
"id": 100,
5858
"msg": "A sample public log message"
@@ -68,7 +68,7 @@ The Logging application implements a trivial protocol, made up of four transacti
6868
{
6969
"jsonrpc": "2.0",
7070
"id": 3,
71-
"method": "LOG_get_pub",
71+
"method": "users/LOG_get_pub",
7272
"params": {
7373
"id": 100
7474
}

sphinx/source/users/issue_commands.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Issuing Commands
22
================
33

4-
Clients communicate with CCF using framed :term:`JSON-RPC` over :term:`TLS`.
4+
Clients communicate with CCF using framed :term:`JSON-RPC` over :term:`TLS`. The ``method`` must be prefixed with the name of the target frontend (``"users"`` or ``"members"``), separated from the intended ``method`` with a single ``/``.
55

66
Users can issue business transactions to CCF using the ``client`` command-line utility built with CCF. For example, to record a message at a specific id with the :ref:`Example App`:
77

@@ -10,7 +10,7 @@ Users can issue business transactions to CCF using the ``client`` command-line u
1010
$ cat request.json
1111
{
1212
"id": 0,
13-
"method": "LOG_record",
13+
"method": "users/LOG_record",
1414
"jsonrpc": "2.0",
1515
"params":
1616
{
@@ -51,7 +51,7 @@ To guarantee that their request is successfully committed to the ledger, a user
5151
$ cat get_commit.json
5252
{
5353
"id": 0,
54-
"method": "getCommit",
54+
"method": "users/getCommit",
5555
"jsonrpc": "2.0",
5656
"params":
5757
{

src/apps/luageneric/luageneric_test.cpp

Lines changed: 80 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -128,15 +128,17 @@ template <typename F, typename K, typename V>
128128
void check_store_load(F frontend, K k, V v)
129129
{
130130
const Cert u0 = {0};
131-
enclave::RPCContext rpc_ctx(0, u0);
131+
const enclave::SessionContext user_session(enclave::InvalidSessionId, u0);
132132

133133
// store
134-
const auto pc0 = make_pc("store", {{"k", k}, {"v", v}});
135-
check_success(frontend->process(rpc_ctx, pc0), true);
134+
const auto store_packed = make_pc("store", {{"k", k}, {"v", v}});
135+
const auto store_ctx = enclave::make_rpc_context(user_session, store_packed);
136+
check_success(frontend->process(store_ctx).value(), true);
136137

137138
// load and check that we get the right result
138-
const auto pc1 = make_pc("load", {{"k", k}});
139-
check_success(frontend->process(rpc_ctx, pc1), v);
139+
const auto load_packed = make_pc("load", {{"k", k}});
140+
const auto load_ctx = enclave::make_rpc_context(user_session, load_packed);
141+
check_success(frontend->process(load_ctx).value(), v);
140142
}
141143

142144
TEST_CASE("simple lua apps")
@@ -152,7 +154,7 @@ TEST_CASE("simple lua apps")
152154
auto frontend = init_frontend(network, gen, notifier, 1, 3);
153155
set_lua_logger();
154156
const Cert u0 = {0};
155-
enclave::RPCContext rpc_ctx(0, u0);
157+
const enclave::SessionContext user_session(enclave::InvalidSessionId, u0);
156158

157159
SUBCASE("missing lua arg")
158160
{
@@ -170,9 +172,10 @@ TEST_CASE("simple lua apps")
170172
set_handler(network, "missing", {missing});
171173

172174
// call "missing"
173-
const auto pc = make_pc("missing", {});
174-
const auto response =
175-
check_error(frontend->process(rpc_ctx, pc), CCFErrorCodes::SCRIPT_ERROR);
175+
const auto packed = make_pc("missing", {});
176+
const auto rpc_ctx = enclave::make_rpc_context(user_session, packed);
177+
const auto response = check_error(
178+
frontend->process(rpc_ctx).value(), CCFErrorCodes::SCRIPT_ERROR);
176179
const auto error_msg = response[ERR][MESSAGE].get<string>();
177180
CHECK(error_msg.find("THIS_KEY_DOESNT_EXIST") != string::npos);
178181
}
@@ -187,8 +190,9 @@ TEST_CASE("simple lua apps")
187190

188191
// call "echo" function with "hello"
189192
const string verb = "hello";
190-
const auto pc = make_pc("echo", {{"verb", verb}});
191-
check_success(frontend->process(rpc_ctx, pc), verb);
193+
const auto packed = make_pc("echo", {{"verb", verb}});
194+
const auto rpc_ctx = enclave::make_rpc_context(user_session, packed);
195+
check_success(frontend->process(rpc_ctx).value(), verb);
192196
}
193197

194198
SUBCASE("store/load different types in generic table")
@@ -225,9 +229,10 @@ TEST_CASE("simple lua apps")
225229
);
226230

227231
// (3) attempt to read non-existing key (set of integers)
228-
const auto pc = make_pc("load", {{"k", set{5, 6, 7}}});
232+
const auto packed = make_pc("load", {{"k", set{5, 6, 7}}});
233+
const auto rpc_ctx = enclave::make_rpc_context(user_session, packed);
229234
check_error(
230-
frontend->process(rpc_ctx, pc), StandardErrorCodes::INVALID_PARAMS);
235+
frontend->process(rpc_ctx).value(), StandardErrorCodes::INVALID_PARAMS);
231236
}
232237

233238
SUBCASE("access gov tables")
@@ -250,17 +255,20 @@ TEST_CASE("simple lua apps")
250255
set_handler(network, "put_member", {put_member});
251256

252257
// (1) read out members table
253-
const auto pc = make_pc("get_members", {});
258+
const auto packed = make_pc("get_members", {});
259+
const auto get_ctx = enclave::make_rpc_context(user_session, packed);
254260
// expect to see 3 members in state active
255261
map<string, MemberInfo> expected = {{"0", {{}, MemberStatus::ACTIVE}},
256262
{"1", {{}, MemberStatus::ACTIVE}},
257263
{"2", {{}, MemberStatus::ACTIVE}}};
258-
check_success(frontend->process(rpc_ctx, pc), expected);
264+
check_success(frontend->process(get_ctx).value(), expected);
259265

260266
// (2) try to write to members table
261-
const auto pc1 = make_pc(
267+
const auto put_packed = make_pc(
262268
"put_member", {{"k", 99}, {"v", MemberInfo{{}, MemberStatus::ACTIVE}}});
263-
check_error(frontend->process(rpc_ctx, pc1), CCFErrorCodes::SCRIPT_ERROR);
269+
const auto put_ctx = enclave::make_rpc_context(user_session, put_packed);
270+
check_error(
271+
frontend->process(put_ctx).value(), CCFErrorCodes::SCRIPT_ERROR);
264272
}
265273
}
266274

@@ -277,7 +285,7 @@ TEST_CASE("simple bank")
277285
auto frontend = init_frontend(network, gen, notifier, 1, 3);
278286
set_lua_logger();
279287
const Cert u0 = {0};
280-
enclave::RPCContext rpc_ctx(0, u0);
288+
const enclave::SessionContext user_session(enclave::InvalidSessionId, u0);
281289

282290
constexpr auto create_method = "SB_create";
283291
constexpr auto create = R"xxx(
@@ -336,37 +344,52 @@ TEST_CASE("simple bank")
336344
set_handler(network, transfer_method, {transfer});
337345

338346
{
339-
const auto pc = make_pc(create_method, {{"dst", 1}, {"amt", 123}});
340-
check_success<bool>(frontend->process(rpc_ctx, pc), true);
341-
342-
const auto pc1 = make_pc(read_method, {{"account", 1}});
343-
check_success(frontend->process(rpc_ctx, pc1), 123);
347+
const auto create_packed =
348+
make_pc(create_method, {{"dst", 1}, {"amt", 123}});
349+
const auto create_ctx =
350+
enclave::make_rpc_context(user_session, create_packed);
351+
check_success<bool>(frontend->process(create_ctx).value(), true);
352+
353+
const auto read_packed = make_pc(read_method, {{"account", 1}});
354+
const auto read_ctx = enclave::make_rpc_context(user_session, read_packed);
355+
check_success(frontend->process(read_ctx).value(), 123);
344356
}
345357

346358
{
347-
const auto pc = make_pc(create_method, {{"dst", 2}, {"amt", 999}});
348-
check_success<bool>(frontend->process(rpc_ctx, pc), true);
349-
350-
const auto pc1 = make_pc(read_method, {{"account", 2}});
351-
check_success(frontend->process(rpc_ctx, pc1), 999);
359+
const auto create_packed =
360+
make_pc(create_method, {{"dst", 2}, {"amt", 999}});
361+
const auto create_ctx =
362+
enclave::make_rpc_context(user_session, create_packed);
363+
check_success<bool>(frontend->process(create_ctx).value(), true);
364+
365+
const auto read_packed = make_pc(read_method, {{"account", 2}});
366+
const auto read_ctx = enclave::make_rpc_context(user_session, read_packed);
367+
check_success(frontend->process(read_ctx).value(), 999);
352368
}
353369

354370
{
355-
const auto pc = make_pc(read_method, {{"account", 3}});
371+
const auto read_packed = make_pc(read_method, {{"account", 3}});
372+
const auto read_ctx = enclave::make_rpc_context(user_session, read_packed);
356373
check_error(
357-
frontend->process(rpc_ctx, pc), StandardErrorCodes::INVALID_PARAMS);
374+
frontend->process(read_ctx).value(), StandardErrorCodes::INVALID_PARAMS);
358375
}
359376

360377
{
361-
const auto pc =
378+
const auto transfer_packed =
362379
make_pc(transfer_method, {{"src", 1}, {"dst", 2}, {"amt", 5}});
363-
check_success<bool>(frontend->process(rpc_ctx, pc), true);
364-
365-
const auto pc1 = make_pc(read_method, {{"account", 1}});
366-
check_success(frontend->process(rpc_ctx, pc1), 123 - 5);
367-
368-
const auto pc2 = make_pc(read_method, {{"account", 2}});
369-
check_success(frontend->process(rpc_ctx, pc2), 999 + 5);
380+
const auto transfer_ctx =
381+
enclave::make_rpc_context(user_session, transfer_packed);
382+
check_success<bool>(frontend->process(transfer_ctx).value(), true);
383+
384+
const auto read1_packed = make_pc(read_method, {{"account", 1}});
385+
const auto read1_ctx =
386+
enclave::make_rpc_context(user_session, read1_packed);
387+
check_success(frontend->process(read1_ctx).value(), 123 - 5);
388+
389+
const auto read2_packed = make_pc(read_method, {{"account", 2}});
390+
const auto read2_ctx =
391+
enclave::make_rpc_context(user_session, read2_packed);
392+
check_success(frontend->process(read2_ctx).value(), 999 + 5);
370393
}
371394
}
372395

@@ -383,7 +406,7 @@ TEST_CASE("pre-populated environment")
383406
auto frontend = init_frontend(network, gen, notifier, 1, 3);
384407
set_lua_logger();
385408
const Cert u0 = {0};
386-
enclave::RPCContext rpc_ctx(0, u0);
409+
const enclave::SessionContext user_session(enclave::InvalidSessionId, u0);
387410

388411
{
389412
constexpr auto log_trace_method = "log_trace";
@@ -395,8 +418,9 @@ TEST_CASE("pre-populated environment")
395418
set_handler(network, log_trace_method, {log_trace});
396419

397420
{
398-
const auto pc = make_pc(log_trace_method, {});
399-
check_success(frontend->process(rpc_ctx, pc), true);
421+
const auto packed = make_pc(log_trace_method, {});
422+
const auto rpc_ctx = enclave::make_rpc_context(user_session, packed);
423+
check_success(frontend->process(rpc_ctx).value(), true);
400424
}
401425

402426
constexpr auto log_debug_method = "log_debug";
@@ -408,8 +432,9 @@ TEST_CASE("pre-populated environment")
408432
set_handler(network, log_debug_method, {log_debug});
409433

410434
{
411-
const auto pc = make_pc(log_debug_method, {});
412-
check_success(frontend->process(rpc_ctx, pc), true);
435+
const auto packed = make_pc(log_debug_method, {});
436+
const auto rpc_ctx = enclave::make_rpc_context(user_session, packed);
437+
check_success(frontend->process(rpc_ctx).value(), true);
413438
}
414439

415440
constexpr auto log_info_method = "log_info";
@@ -421,8 +446,9 @@ TEST_CASE("pre-populated environment")
421446
set_handler(network, log_info_method, {log_info});
422447

423448
{
424-
const auto pc = make_pc(log_info_method, {});
425-
check_success(frontend->process(rpc_ctx, pc), true);
449+
const auto packed = make_pc(log_info_method, {});
450+
const auto rpc_ctx = enclave::make_rpc_context(user_session, packed);
451+
check_success(frontend->process(rpc_ctx).value(), true);
426452
}
427453

428454
constexpr auto log_fail_method = "log_fail";
@@ -434,8 +460,9 @@ TEST_CASE("pre-populated environment")
434460
set_handler(network, log_fail_method, {log_fail});
435461

436462
{
437-
const auto pc = make_pc(log_fail_method, {});
438-
check_success(frontend->process(rpc_ctx, pc), true);
463+
const auto packed = make_pc(log_fail_method, {});
464+
const auto rpc_ctx = enclave::make_rpc_context(user_session, packed);
465+
check_success(frontend->process(rpc_ctx).value(), true);
439466
}
440467

441468
constexpr auto log_fatal_method = "log_fatal";
@@ -446,9 +473,10 @@ TEST_CASE("pre-populated environment")
446473
set_handler(network, log_fatal_method, {log_fatal});
447474

448475
{
449-
const auto pc = make_pc(log_fatal_method, {});
476+
const auto packed = make_pc(log_fatal_method, {});
477+
const auto rpc_ctx = enclave::make_rpc_context(user_session, packed);
450478
check_error(
451-
frontend->process(rpc_ctx, pc),
479+
frontend->process(rpc_ctx).value(),
452480
jsonrpc::StandardErrorCodes::INTERNAL_ERROR);
453481
}
454482
}
@@ -487,9 +515,10 @@ TEST_CASE("pre-populated environment")
487515
using EBT = jsonrpc::ErrorBaseType;
488516
using StdEC = jsonrpc::StandardErrorCodes;
489517
using CCFEC = jsonrpc::CCFErrorCodes;
490-
const auto pc = make_pc(invalid_params_method, {});
518+
const auto packed = make_pc(invalid_params_method, {});
519+
const auto rpc_ctx = enclave::make_rpc_context(user_session, packed);
491520
const Response<std::vector<EBT>> r =
492-
json::from_msgpack(frontend->process(rpc_ctx, pc));
521+
json::from_msgpack(frontend->process(rpc_ctx).value());
493522

494523
std::vector<EBT> expected;
495524
expected.push_back(EBT(StdEC::PARSE_ERROR));

src/clients/rpc_tls_client.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "tls_client.h"
66

7+
#include <fmt/format_header_only.h>
78
#include <nlohmann/json.hpp>
89
#include <optional>
910

@@ -56,10 +57,12 @@ class RpcTlsClient : public TlsClient
5657
nlohmann::json json_rpc(
5758
const std::string& method, const nlohmann::json& params)
5859
{
60+
const auto prefixed_method = fmt::format("{}/{}", get_sni(), method);
61+
5962
nlohmann::json j;
6063
j["jsonrpc"] = "2.0";
6164
j["id"] = id++;
62-
j["method"] = method;
65+
j["method"] = prefixed_method;
6366
j["params"] = params;
6467
return j;
6568
}

src/clients/tls_client.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,4 +163,9 @@ class TlsClient
163163
read(b);
164164
return true;
165165
}
166+
167+
std::string get_sni() const
168+
{
169+
return sni;
170+
}
166171
};

src/consensus/pbft/pbftconfig.h

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,15 @@ namespace pbft
115115

116116
auto frontend = handler.value();
117117

118-
enclave::RPCContext ctx(
119-
enclave::InvalidSessionId,
120-
request.caller_id,
121-
ccf::ActorsType(request.actor),
122-
request.caller_cert);
123-
124-
auto rep = frontend->process_pbft(ctx, request.request);
118+
// TODO: Should serialise context directly, rather than reconstructing
119+
const enclave::SessionContext session(
120+
enclave::InvalidSessionId, request.caller_id, request.caller_cert);
121+
auto ctx = enclave::make_rpc_context(session, request.request);
122+
ctx.actor = (ccf::ActorsType)request.actor;
123+
const auto n = ctx.method.find_last_of('/');
124+
ctx.method = ctx.method.substr(n + 1, ctx.method.size());
125+
126+
auto rep = frontend->process_pbft(ctx);
125127

126128
static_assert(sizeof(info.merkle_root) == sizeof(crypto::Sha256Hash));
127129
std::copy(

0 commit comments

Comments
 (0)