@derec-alliance/react-native 0.0.7 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +394 -6
- package/android/src/main/jniLibs/arm64-v8a/libderec_library.a +0 -0
- package/android/src/main/jniLibs/armeabi-v7a/libderec_library.a +0 -0
- package/android/src/main/jniLibs/x86_64/libderec_library.a +0 -0
- package/cpp/Convert.cpp +9 -0
- package/cpp/Convert.h +8 -2
- package/cpp/Primitives.cpp +113 -25
- package/cpp/StoreCallbacks.cpp +119 -20
- package/cpp/StoreCallbacks.h +8 -1
- package/cpp/derec_ffi.h +83 -15
- package/ios/DeRecFFI.xcframework/ios-arm64/Headers/derec_ffi.h +83 -15
- package/ios/DeRecFFI.xcframework/ios-arm64/libderec_library.a +0 -0
- package/ios/DeRecFFI.xcframework/ios-arm64_x86_64-simulator/Headers/derec_ffi.h +83 -15
- package/ios/DeRecFFI.xcframework/ios-arm64_x86_64-simulator/libderec_library.a +2 -2
- package/lib/errors.d.ts +11 -1
- package/lib/errors.js +63 -1
- package/lib/primitives.d.ts +2 -2
- package/lib/primitives.js +3 -1
- package/lib/protocol.d.ts +102 -6
- package/lib/protocol.js +158 -7
- package/lib/types.d.ts +426 -28
- package/lib/types.js +19 -0
- package/package.json +1 -1
- package/src/errors.ts +66 -1
- package/src/primitives.ts +13 -2
- package/src/protocol.ts +166 -8
- package/src/types.ts +432 -29
package/cpp/Primitives.cpp
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
|
|
4
4
|
#include "Primitives.h"
|
|
5
5
|
|
|
6
|
+
#include <cmath>
|
|
6
7
|
#include <cstring>
|
|
8
|
+
#include <limits>
|
|
7
9
|
#include <string>
|
|
8
10
|
#include <vector>
|
|
9
11
|
|
|
@@ -118,17 +120,79 @@ jsi::Value toUint8ArrayVal(jsi::Runtime& rt, const std::vector<uint8_t>& bytes)
|
|
|
118
120
|
}
|
|
119
121
|
|
|
120
122
|
uint64_t asU64(jsi::Runtime& rt, const jsi::Value& value) {
|
|
123
|
+
auto reject = [&]() -> uint64_t {
|
|
124
|
+
throw jsi::JSError(rt, "expected an integer in 0..=18446744073709551615");
|
|
125
|
+
};
|
|
121
126
|
if (value.isBigInt()) {
|
|
122
|
-
|
|
127
|
+
jsi::BigInt big = value.getBigInt(rt);
|
|
128
|
+
return big.isUint64(rt) ? big.getUint64(rt) : reject();
|
|
123
129
|
}
|
|
124
|
-
|
|
130
|
+
if (!value.isNumber()) {
|
|
131
|
+
return reject();
|
|
132
|
+
}
|
|
133
|
+
double number = value.getNumber();
|
|
134
|
+
// 18446744073709551616.0 is 2^64, the first double above `uint64_t`'s range.
|
|
135
|
+
if (!(number >= 0.0) || number >= 18446744073709551616.0 || std::trunc(number) != number) {
|
|
136
|
+
return reject();
|
|
137
|
+
}
|
|
138
|
+
return static_cast<uint64_t>(number);
|
|
125
139
|
}
|
|
126
140
|
|
|
127
141
|
namespace {
|
|
128
142
|
|
|
143
|
+
/// Read an integer losslessly: a `bigint` or `number` holding an integer in
|
|
144
|
+
/// `0..=max`. Anything else throws `<name> expects <expected>`, so a value is
|
|
145
|
+
/// never truncated, rounded, or wrapped on its way to the core.
|
|
146
|
+
uint64_t asBoundedInteger(jsi::Runtime& rt,
|
|
147
|
+
const char* name,
|
|
148
|
+
const jsi::Value& element,
|
|
149
|
+
uint64_t max,
|
|
150
|
+
const std::string& expected) {
|
|
151
|
+
auto reject = [&]() -> uint64_t {
|
|
152
|
+
throw jsi::JSError(rt, std::string(name) + " expects " + expected);
|
|
153
|
+
};
|
|
154
|
+
if (element.isBigInt()) {
|
|
155
|
+
jsi::BigInt big = element.getBigInt(rt);
|
|
156
|
+
if (!big.isUint64(rt)) {
|
|
157
|
+
return reject();
|
|
158
|
+
}
|
|
159
|
+
uint64_t number = big.getUint64(rt);
|
|
160
|
+
return number <= max ? number : reject();
|
|
161
|
+
}
|
|
162
|
+
if (!element.isNumber()) {
|
|
163
|
+
return reject();
|
|
164
|
+
}
|
|
165
|
+
double number = element.getNumber();
|
|
166
|
+
// 18446744073709551616.0 is 2^64, the first double above `uint64_t`'s range.
|
|
167
|
+
if (!(number >= 0.0) || number >= 18446744073709551616.0 || std::trunc(number) != number) {
|
|
168
|
+
return reject();
|
|
169
|
+
}
|
|
170
|
+
auto integer = static_cast<uint64_t>(number);
|
|
171
|
+
return integer <= max ? integer : reject();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/// Read one element of an integer array with `asBoundedInteger`.
|
|
175
|
+
uint64_t asArrayInteger(jsi::Runtime& rt,
|
|
176
|
+
const char* name,
|
|
177
|
+
const jsi::Value& element,
|
|
178
|
+
uint64_t max,
|
|
179
|
+
const char* range) {
|
|
180
|
+
return asBoundedInteger(rt, name, element, max, std::string("an array of integers in ") + range);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/// Read the scalar argument `argument` with `asBoundedInteger`.
|
|
184
|
+
uint64_t asScalarInteger(jsi::Runtime& rt,
|
|
185
|
+
const char* name,
|
|
186
|
+
const char* argument,
|
|
187
|
+
const jsi::Value& value,
|
|
188
|
+
uint64_t max) {
|
|
189
|
+
return asBoundedInteger(rt, name, value, max,
|
|
190
|
+
std::string(argument) + " to be an integer in 0..=" + std::to_string(max));
|
|
191
|
+
}
|
|
192
|
+
|
|
129
193
|
/// Read a JS array of `bigint`/`number` elements as a `uint64_t` vector.
|
|
130
194
|
/// Used for `protect_secret`'s `channels` argument.
|
|
131
|
-
std::vector<uint64_t> asU64Array(jsi::Runtime& rt, const jsi::Value& value) {
|
|
195
|
+
std::vector<uint64_t> asU64Array(jsi::Runtime& rt, const char* name, const jsi::Value& value) {
|
|
132
196
|
std::vector<uint64_t> out;
|
|
133
197
|
if (value.isNull() || value.isUndefined()) {
|
|
134
198
|
return out;
|
|
@@ -137,14 +201,17 @@ std::vector<uint64_t> asU64Array(jsi::Runtime& rt, const jsi::Value& value) {
|
|
|
137
201
|
size_t length = array.size(rt);
|
|
138
202
|
out.reserve(length);
|
|
139
203
|
for (size_t i = 0; i < length; ++i) {
|
|
140
|
-
out.push_back(
|
|
204
|
+
out.push_back(asArrayInteger(rt, name, array.getValueAtIndex(rt, i),
|
|
205
|
+
std::numeric_limits<uint64_t>::max(),
|
|
206
|
+
"0..=18446744073709551615"));
|
|
141
207
|
}
|
|
142
208
|
return out;
|
|
143
209
|
}
|
|
144
210
|
|
|
145
211
|
/// Read a JS array of `number` elements as a `uint32_t` vector. Used for
|
|
146
|
-
/// `
|
|
147
|
-
|
|
212
|
+
/// `protect_secret`'s `weights` and
|
|
213
|
+
/// `produce_store_share_request_message`'s `keep_list` arguments.
|
|
214
|
+
std::vector<uint32_t> asU32Array(jsi::Runtime& rt, const char* name, const jsi::Value& value) {
|
|
148
215
|
std::vector<uint32_t> out;
|
|
149
216
|
if (value.isNull() || value.isUndefined()) {
|
|
150
217
|
return out;
|
|
@@ -153,7 +220,9 @@ std::vector<uint32_t> asU32Array(jsi::Runtime& rt, const jsi::Value& value) {
|
|
|
153
220
|
size_t length = array.size(rt);
|
|
154
221
|
out.reserve(length);
|
|
155
222
|
for (size_t i = 0; i < length; ++i) {
|
|
156
|
-
out.push_back(static_cast<uint32_t>(
|
|
223
|
+
out.push_back(static_cast<uint32_t>(
|
|
224
|
+
asArrayInteger(rt, name, array.getValueAtIndex(rt, i),
|
|
225
|
+
std::numeric_limits<uint32_t>::max(), "0..=4294967295")));
|
|
157
226
|
}
|
|
158
227
|
return out;
|
|
159
228
|
}
|
|
@@ -336,9 +405,11 @@ jsi::Value createContactMessage(jsi::Runtime& rt,
|
|
|
336
405
|
size_t count) {
|
|
337
406
|
requireArgs(rt, "create_contact_message", count, 5);
|
|
338
407
|
uint64_t channelId = asU64(rt, args[0]);
|
|
339
|
-
auto contactMode = static_cast<int32_t>(
|
|
408
|
+
auto contactMode = static_cast<int32_t>(asScalarInteger(
|
|
409
|
+
rt, "create_contact_message", "contact_mode", args[1], std::numeric_limits<int32_t>::max()));
|
|
340
410
|
ByteView transportProtocols = asBytes(rt, args[2]);
|
|
341
|
-
auto hasNonce = static_cast<uint32_t>(
|
|
411
|
+
auto hasNonce = static_cast<uint32_t>(asScalarInteger(
|
|
412
|
+
rt, "create_contact_message", "has_nonce", args[3], std::numeric_limits<uint32_t>::max()));
|
|
342
413
|
uint64_t nonce = asU64(rt, args[4]);
|
|
343
414
|
CreateContactMessageResult result = create_contact_message(
|
|
344
415
|
channelId, contactMode, transportProtocols.ptr, transportProtocols.len,
|
|
@@ -412,7 +483,8 @@ jsi::Value decodeMessageJson(jsi::Runtime& rt,
|
|
|
412
483
|
const jsi::Value* args,
|
|
413
484
|
size_t count) {
|
|
414
485
|
requireArgs(rt, "decode_message_json", count, 2);
|
|
415
|
-
auto kind = static_cast<int32_t>(
|
|
486
|
+
auto kind = static_cast<int32_t>(asScalarInteger(
|
|
487
|
+
rt, "decode_message_json", "kind", args[0], std::numeric_limits<int32_t>::max()));
|
|
416
488
|
ByteView proto = asBytes(rt, args[1]);
|
|
417
489
|
DeRecMessageJsonResult result =
|
|
418
490
|
derec_decode_message_json(kind, proto.ptr, proto.len);
|
|
@@ -432,7 +504,8 @@ jsi::Value encodeMessageJson(jsi::Runtime& rt,
|
|
|
432
504
|
const jsi::Value* args,
|
|
433
505
|
size_t count) {
|
|
434
506
|
requireArgs(rt, "encode_message_json", count, 2);
|
|
435
|
-
auto kind = static_cast<int32_t>(
|
|
507
|
+
auto kind = static_cast<int32_t>(asScalarInteger(
|
|
508
|
+
rt, "encode_message_json", "kind", args[0], std::numeric_limits<int32_t>::max()));
|
|
436
509
|
ByteView json = asBytes(rt, args[1]);
|
|
437
510
|
DeRecMessageJsonResult result =
|
|
438
511
|
derec_encode_message_json(kind, json.ptr, json.len);
|
|
@@ -453,7 +526,8 @@ jsi::Value producePairRequestMessage(jsi::Runtime& rt,
|
|
|
453
526
|
const jsi::Value* args,
|
|
454
527
|
size_t count) {
|
|
455
528
|
requireArgs(rt, "produce_pair_request_message", count, 5);
|
|
456
|
-
auto senderKind = static_cast<int32_t>(
|
|
529
|
+
auto senderKind = static_cast<int32_t>(asScalarInteger(
|
|
530
|
+
rt, "produce_pair_request_message", "sender_kind", args[0], std::numeric_limits<int32_t>::max()));
|
|
457
531
|
ByteView transportProtocols = asBytes(rt, args[1]);
|
|
458
532
|
ByteView contactMessage = asBytes(rt, args[2]);
|
|
459
533
|
ByteView communicationInfo = asBytes(rt, args[3]);
|
|
@@ -796,7 +870,8 @@ jsi::Value produceGetShareRequestMessage(jsi::Runtime& rt,
|
|
|
796
870
|
requireArgs(rt, "produce_get_share_request_message", count, 5);
|
|
797
871
|
uint64_t channelId = asU64(rt, args[0]);
|
|
798
872
|
uint64_t secretId = asU64(rt, args[1]);
|
|
799
|
-
auto version = static_cast<uint32_t>(
|
|
873
|
+
auto version = static_cast<uint32_t>(asScalarInteger(
|
|
874
|
+
rt, "produce_get_share_request_message", "version", args[2], std::numeric_limits<uint32_t>::max()));
|
|
800
875
|
ByteView sharedKey = asBytes(rt, args[3]);
|
|
801
876
|
ByteView replyTo = asBytes(rt, args[4]);
|
|
802
877
|
ProduceGetShareRequestMessageResult result = produce_get_share_request_message(
|
|
@@ -886,7 +961,8 @@ jsi::Value recoverFromShareResponses(jsi::Runtime& rt,
|
|
|
886
961
|
requireArgs(rt, "recover_from_share_responses", count, 3);
|
|
887
962
|
ByteView responses = asBytes(rt, args[0]);
|
|
888
963
|
uint64_t secretId = asU64(rt, args[1]);
|
|
889
|
-
auto version = static_cast<uint32_t>(
|
|
964
|
+
auto version = static_cast<uint32_t>(asScalarInteger(
|
|
965
|
+
rt, "recover_from_share_responses", "version", args[2], std::numeric_limits<uint32_t>::max()));
|
|
890
966
|
RecoverFromShareResponsesResult result = recover_from_share_responses(
|
|
891
967
|
responses.ptr, responses.len, secretId, version);
|
|
892
968
|
if (result.error.code != 0) {
|
|
@@ -897,20 +973,29 @@ jsi::Value recoverFromShareResponses(jsi::Runtime& rt,
|
|
|
897
973
|
}
|
|
898
974
|
|
|
899
975
|
/// `protect_secret(secretId: bigint, secretData: ArrayBuffer, channels:
|
|
900
|
-
/// (bigint|number)[],
|
|
976
|
+
/// (bigint|number)[], weights: number[], threshold: number, version:
|
|
977
|
+
/// number) -> ArrayBuffer`
|
|
901
978
|
jsi::Value protectSecret(jsi::Runtime& rt,
|
|
902
979
|
const jsi::Value&,
|
|
903
980
|
const jsi::Value* args,
|
|
904
981
|
size_t count) {
|
|
905
|
-
requireArgs(rt, "protect_secret", count,
|
|
982
|
+
requireArgs(rt, "protect_secret", count, 6);
|
|
906
983
|
uint64_t secretId = asU64(rt, args[0]);
|
|
907
984
|
ByteView secretData = asBytes(rt, args[1]);
|
|
908
|
-
std::vector<uint64_t> channels = asU64Array(rt, args[2]);
|
|
909
|
-
|
|
910
|
-
|
|
985
|
+
std::vector<uint64_t> channels = asU64Array(rt, "protect_secret", args[2]);
|
|
986
|
+
std::vector<uint32_t> weights = asU32Array(rt, "protect_secret", args[3]);
|
|
987
|
+
if (channels.size() != weights.size()) {
|
|
988
|
+
throw jsi::JSError(rt,
|
|
989
|
+
"protect_secret: channels and weights must have "
|
|
990
|
+
"the same length");
|
|
991
|
+
}
|
|
992
|
+
auto threshold = static_cast<size_t>(asScalarInteger(
|
|
993
|
+
rt, "protect_secret", "threshold", args[4], std::numeric_limits<size_t>::max()));
|
|
994
|
+
auto version = static_cast<uint32_t>(asScalarInteger(
|
|
995
|
+
rt, "protect_secret", "version", args[5], std::numeric_limits<uint32_t>::max()));
|
|
911
996
|
ProtectSecretResult result = protect_secret(
|
|
912
997
|
secretId, secretData.ptr, secretData.len, channels.data(),
|
|
913
|
-
channels.size(), threshold, version);
|
|
998
|
+
weights.data(), channels.size(), threshold, version);
|
|
914
999
|
if (result.error.code != 0) {
|
|
915
1000
|
throwDeRecError(rt, result.error);
|
|
916
1001
|
}
|
|
@@ -928,10 +1013,11 @@ jsi::Value produceStoreShareRequestMessage(jsi::Runtime& rt,
|
|
|
928
1013
|
size_t count) {
|
|
929
1014
|
requireArgs(rt, "produce_store_share_request_message", count, 8);
|
|
930
1015
|
uint64_t channelId = asU64(rt, args[0]);
|
|
931
|
-
auto version = static_cast<uint32_t>(
|
|
1016
|
+
auto version = static_cast<uint32_t>(asScalarInteger(
|
|
1017
|
+
rt, "produce_store_share_request_message", "version", args[1], std::numeric_limits<uint32_t>::max()));
|
|
932
1018
|
uint64_t secretId = asU64(rt, args[2]);
|
|
933
1019
|
ByteView committedShare = asBytes(rt, args[3]);
|
|
934
|
-
std::vector<uint32_t> keepList = asU32Array(rt, args[4]);
|
|
1020
|
+
std::vector<uint32_t> keepList = asU32Array(rt, "produce_store_share_request_message", args[4]);
|
|
935
1021
|
ByteView description = asBytes(rt, args[5]);
|
|
936
1022
|
ByteView sharedKey = asBytes(rt, args[6]);
|
|
937
1023
|
ByteView replyTo = asBytes(rt, args[7]);
|
|
@@ -1035,7 +1121,8 @@ jsi::Value processStoreShareResponseMessage(jsi::Runtime& rt,
|
|
|
1035
1121
|
const jsi::Value* args,
|
|
1036
1122
|
size_t count) {
|
|
1037
1123
|
requireArgs(rt, "process_store_share_response_message", count, 2);
|
|
1038
|
-
auto version = static_cast<uint32_t>(
|
|
1124
|
+
auto version = static_cast<uint32_t>(asScalarInteger(
|
|
1125
|
+
rt, "process_store_share_response_message", "version", args[0], std::numeric_limits<uint32_t>::max()));
|
|
1039
1126
|
ByteView responseProto = asBytes(rt, args[1]);
|
|
1040
1127
|
ProcessStoreShareResponseMessageResult result =
|
|
1041
1128
|
process_store_share_response_message(version, responseProto.ptr,
|
|
@@ -1159,7 +1246,8 @@ jsi::Value produceVerifyShareRequestMessage(jsi::Runtime& rt,
|
|
|
1159
1246
|
requireArgs(rt, "produce_verify_share_request_message", count, 5);
|
|
1160
1247
|
uint64_t channelId = asU64(rt, args[0]);
|
|
1161
1248
|
uint64_t secretId = asU64(rt, args[1]);
|
|
1162
|
-
auto version = static_cast<uint32_t>(
|
|
1249
|
+
auto version = static_cast<uint32_t>(asScalarInteger(
|
|
1250
|
+
rt, "produce_verify_share_request_message", "version", args[2], std::numeric_limits<uint32_t>::max()));
|
|
1163
1251
|
ByteView sharedKey = asBytes(rt, args[3]);
|
|
1164
1252
|
ByteView replyTo = asBytes(rt, args[4]);
|
|
1165
1253
|
ProduceVerifyShareRequestMessageResult result =
|
|
@@ -1321,7 +1409,7 @@ void installPrimitives(jsi::Runtime& rt, jsi::Object& host) {
|
|
|
1321
1409
|
produceGetShareResponseMessage);
|
|
1322
1410
|
bind(rt, host, "extract_get_share_response", 2, extractGetShareResponse);
|
|
1323
1411
|
bind(rt, host, "recover_from_share_responses", 3, recoverFromShareResponses);
|
|
1324
|
-
bind(rt, host, "protect_secret",
|
|
1412
|
+
bind(rt, host, "protect_secret", 6, protectSecret);
|
|
1325
1413
|
bind(rt, host, "produce_store_share_request_message", 8,
|
|
1326
1414
|
produceStoreShareRequestMessage);
|
|
1327
1415
|
bind(rt, host, "extract_store_share_request", 2, extractStoreShareRequest);
|
package/cpp/StoreCallbacks.cpp
CHANGED
|
@@ -27,7 +27,7 @@ static_assert(sizeof(ChannelStoreCallbacks) == 9 * sizeof(void*),
|
|
|
27
27
|
"ChannelStoreCallbacks layout changed; update the bindings");
|
|
28
28
|
static_assert(sizeof(SecretStoreCallbacks) == 6 * sizeof(void*),
|
|
29
29
|
"SecretStoreCallbacks layout changed; update the bindings");
|
|
30
|
-
static_assert(sizeof(ShareStoreCallbacks) ==
|
|
30
|
+
static_assert(sizeof(ShareStoreCallbacks) == 11 * sizeof(void*),
|
|
31
31
|
"ShareStoreCallbacks layout changed; update the bindings");
|
|
32
32
|
static_assert(sizeof(UserSecretStoreCallbacks) == 5 * sizeof(void*),
|
|
33
33
|
"UserSecretStoreCallbacks layout changed; update the bindings");
|
|
@@ -245,6 +245,21 @@ CallResult toShareListResult(jsi::Runtime& rt, const jsi::Value& value) {
|
|
|
245
245
|
text += "{\"secret_id\":\"" + secretIdStr + "\",\"version\":" + std::to_string(version) +
|
|
246
246
|
",\"bytes\":";
|
|
247
247
|
appendByteArrayJson(text, bytes.ptr, bytes.len);
|
|
248
|
+
// `subshares` and `threshold`: `null`/`undefined` is none and is omitted,
|
|
249
|
+
// as Rust omits it; anything else must be a `u32` to cross as one.
|
|
250
|
+
for (const char* field : {"subshares", "threshold"}) {
|
|
251
|
+
jsi::Value value = entry.getProperty(rt, field);
|
|
252
|
+
if (value.isNull() || value.isUndefined()) continue;
|
|
253
|
+
if (!value.isNumber()) {
|
|
254
|
+
return CallResult{kBackendFailure, {}};
|
|
255
|
+
}
|
|
256
|
+
double number = value.asNumber();
|
|
257
|
+
if (!(number >= 0 && number <= 4294967295.0) ||
|
|
258
|
+
number != static_cast<double>(static_cast<uint32_t>(number))) {
|
|
259
|
+
return CallResult{kBackendFailure, {}};
|
|
260
|
+
}
|
|
261
|
+
text += std::string(",\"") + field + "\":" + std::to_string(static_cast<uint32_t>(number));
|
|
262
|
+
}
|
|
248
263
|
text += '}';
|
|
249
264
|
}
|
|
250
265
|
text += ']';
|
|
@@ -354,6 +369,38 @@ CallResult toKeepListResult(jsi::Runtime& rt, const jsi::Value& value) {
|
|
|
354
369
|
return CallResult{0, std::vector<uint8_t>(text.begin(), text.end())};
|
|
355
370
|
}
|
|
356
371
|
|
|
372
|
+
/// Reads the `refusal` property a `ShareStore.save`/`recordVerification`
|
|
373
|
+
/// throws, or rejects its promise with, to refuse the call — see
|
|
374
|
+
/// `shareStoreRefusal` in `src/types.ts`. Maps the wire string to the FFI
|
|
375
|
+
/// return code `library/src/interop/ffi/error.rs` names
|
|
376
|
+
/// `DEREC_SHARE_STORE_REFUSED_*`; a missing or unknown property is an
|
|
377
|
+
/// ordinary backend failure, never any other interpretation.
|
|
378
|
+
int32_t shareRefusalCode(jsi::Runtime& rt, const jsi::Value& value) {
|
|
379
|
+
if (!value.isObject()) {
|
|
380
|
+
return kBackendFailure;
|
|
381
|
+
}
|
|
382
|
+
jsi::Object obj = value.asObject(rt);
|
|
383
|
+
if (!obj.hasProperty(rt, "refusal")) {
|
|
384
|
+
return kBackendFailure;
|
|
385
|
+
}
|
|
386
|
+
jsi::Value refusal = obj.getProperty(rt, "refusal");
|
|
387
|
+
if (!refusal.isString()) {
|
|
388
|
+
return kBackendFailure;
|
|
389
|
+
}
|
|
390
|
+
std::string reason = refusal.asString(rt).utf8(rt);
|
|
391
|
+
if (reason == "too_frequent") return DEREC_SHARE_STORE_REFUSED_TOO_FREQUENT;
|
|
392
|
+
if (reason == "size_limit_exceeded") return DEREC_SHARE_STORE_REFUSED_SIZE_LIMIT_EXCEEDED;
|
|
393
|
+
if (reason == "rejected") return DEREC_SHARE_STORE_REFUSED_REJECTED;
|
|
394
|
+
return kBackendFailure;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/// `ResultConverter` form of `shareRefusalCode`, passed as `ShareStore.save`/
|
|
398
|
+
/// `recordVerification`'s `settleFromPromise` error converter and used
|
|
399
|
+
/// directly on a synchronous throw.
|
|
400
|
+
CallResult toShareRefusalResult(jsi::Runtime& rt, const jsi::Value& value) {
|
|
401
|
+
return CallResult{shareRefusalCode(rt, value), {}};
|
|
402
|
+
}
|
|
403
|
+
|
|
357
404
|
/// `UserSecretStore.loadLatest(): Promise<UserSecrets | null | undefined>`.
|
|
358
405
|
CallResult toUserSecretsResult(jsi::Runtime& rt, const jsi::Value& value) {
|
|
359
406
|
if (value.isNull() || value.isUndefined()) {
|
|
@@ -811,13 +858,59 @@ extern "C" int32_t shareStoreSave(void* userData, uint64_t secretId, uint64_t ch
|
|
|
811
858
|
jsi::Value(rt, jsi::String::createFromUtf8(rt, decoded.secretId)));
|
|
812
859
|
shareObj.setProperty(rt, "version", jsi::Value(static_cast<int>(decoded.version)));
|
|
813
860
|
shareObj.setProperty(rt, "bytes", toUint8ArrayVal(rt, decoded.bytes));
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
861
|
+
if (decoded.subshares) {
|
|
862
|
+
shareObj.setProperty(rt, "subshares", jsi::Value(static_cast<double>(*decoded.subshares)));
|
|
863
|
+
}
|
|
864
|
+
if (decoded.threshold) {
|
|
865
|
+
shareObj.setProperty(rt, "threshold", jsi::Value(static_cast<double>(*decoded.threshold)));
|
|
866
|
+
}
|
|
867
|
+
// A refusal thrown synchronously (rather than via a rejected promise)
|
|
868
|
+
// never reaches `settleFromPromise`'s `onErr`, so it is read here,
|
|
869
|
+
// before it would otherwise fall into `JsCallbackBridge::callSync`'s
|
|
870
|
+
// generic backend-failure catch.
|
|
871
|
+
try {
|
|
872
|
+
jsi::Value promise = method.callWithThis(rt, *store, idVal(rt, secretId), idVal(rt, channelId),
|
|
873
|
+
jsi::Value(rt, shareObj));
|
|
874
|
+
keepAlive->settleFromPromise(rt, std::move(promise), settle, toVoidResult, toShareRefusalResult);
|
|
875
|
+
} catch (jsi::JSError& e) {
|
|
876
|
+
settle(toShareRefusalResult(rt, e.value()));
|
|
877
|
+
}
|
|
817
878
|
});
|
|
818
879
|
return result.code;
|
|
819
880
|
}
|
|
820
881
|
|
|
882
|
+
/// `ShareStore.recordVerification(secretId, channelId, version) -> void`
|
|
883
|
+
///
|
|
884
|
+
/// Called once per accepted verification challenge, after the challenged
|
|
885
|
+
/// share is found and before the helper answers — see
|
|
886
|
+
/// `DeRecShareStore::record_verification` on the Rust side. A refusal
|
|
887
|
+
/// (thrown, or a rejected promise, tagged with `shareStoreRefusal`'s
|
|
888
|
+
/// `refusal` property) maps to the matching FFI return code exactly as
|
|
889
|
+
/// `ShareStore.save` does; any other failure is an ordinary backend failure.
|
|
890
|
+
extern "C" int32_t shareStoreRecordVerification(void* userData, uint64_t secretId, uint64_t channelId,
|
|
891
|
+
uint32_t version) {
|
|
892
|
+
auto* self = static_cast<StoreBindings*>(userData);
|
|
893
|
+
// Kept alive past this call so the lambda handed to `callSync` — which the
|
|
894
|
+
// JavaScript CallInvoker's queue may still be holding after this object's
|
|
895
|
+
// owner has released its own reference — has somewhere safe to run.
|
|
896
|
+
auto keepAlive = self->shared_from_this();
|
|
897
|
+
CallResult result = keepAlive->bridge().callSync(
|
|
898
|
+
[keepAlive, secretId, channelId, version](std::function<void(CallResult)> settle) {
|
|
899
|
+
jsi::Runtime& rt = keepAlive->runtime();
|
|
900
|
+
auto store = keepAlive->shareStore();
|
|
901
|
+
auto method = store->getPropertyAsFunction(rt, "recordVerification");
|
|
902
|
+
try {
|
|
903
|
+
jsi::Value promise = method.callWithThis(rt, *store, idVal(rt, secretId), idVal(rt, channelId),
|
|
904
|
+
jsi::Value(static_cast<double>(version)));
|
|
905
|
+
keepAlive->settleFromPromise(rt, std::move(promise), settle, toVoidResult,
|
|
906
|
+
toShareRefusalResult);
|
|
907
|
+
} catch (jsi::JSError& e) {
|
|
908
|
+
settle(toShareRefusalResult(rt, e.value()));
|
|
909
|
+
}
|
|
910
|
+
});
|
|
911
|
+
return result.code;
|
|
912
|
+
}
|
|
913
|
+
|
|
821
914
|
/// `ShareStore.removeChannel(secretId, channelId) -> void`
|
|
822
915
|
extern "C" int32_t shareStoreRemoveChannel(void* userData, uint64_t secretId, uint64_t channelId) {
|
|
823
916
|
auto* self = static_cast<StoreBindings*>(userData);
|
|
@@ -1110,7 +1203,7 @@ StoreBindings::StoreBindings(jsi::Runtime& rt, JsCallbackBridge& bridge,
|
|
|
1110
1203
|
|
|
1111
1204
|
void StoreBindings::settleFromPromise(jsi::Runtime& rt, jsi::Value value,
|
|
1112
1205
|
std::function<void(CallResult)> settle,
|
|
1113
|
-
ResultConverter convert) {
|
|
1206
|
+
ResultConverter convert, ResultConverter convertError) {
|
|
1114
1207
|
// A store may legitimately return a plain value; only route through
|
|
1115
1208
|
// `then` when the result actually is a thenable.
|
|
1116
1209
|
if (!value.isObject()) {
|
|
@@ -1150,13 +1243,16 @@ void StoreBindings::settleFromPromise(jsi::Runtime& rt, jsi::Value value,
|
|
|
1150
1243
|
|
|
1151
1244
|
auto onErr = jsi::Function::createFromHostFunction(
|
|
1152
1245
|
rt, jsi::PropNameID::forAscii(rt, "onErr"), 1,
|
|
1153
|
-
[shared](jsi::Runtime
|
|
1246
|
+
[shared, convertError](jsi::Runtime& rt, const jsi::Value&, const jsi::Value* args,
|
|
1247
|
+
size_t count) -> jsi::Value {
|
|
1154
1248
|
try {
|
|
1155
|
-
|
|
1249
|
+
CallResult result = convertError != nullptr
|
|
1250
|
+
? (count > 0 ? convertError(rt, args[0])
|
|
1251
|
+
: convertError(rt, jsi::Value::undefined()))
|
|
1252
|
+
: CallResult{kBackendFailure, {}};
|
|
1253
|
+
(*shared)(std::move(result));
|
|
1156
1254
|
} catch (...) {
|
|
1157
|
-
|
|
1158
|
-
// guard uniformly with `onOk` in case a future `settle`
|
|
1159
|
-
// implementation grows a fallible step.
|
|
1255
|
+
(*shared)(CallResult{kBackendFailure, {}});
|
|
1160
1256
|
}
|
|
1161
1257
|
return jsi::Value::undefined();
|
|
1162
1258
|
});
|
|
@@ -1198,16 +1294,17 @@ std::shared_ptr<StoreBindings> StoreBindings::create(jsi::Runtime& rt, jsi::Obje
|
|
|
1198
1294
|
storeFreeBuffer, // free_buffer
|
|
1199
1295
|
};
|
|
1200
1296
|
bindings->shareCallbacks_ = ShareStoreCallbacks{
|
|
1201
|
-
self,
|
|
1202
|
-
shareStoreLoad,
|
|
1203
|
-
shareStoreLoadMany,
|
|
1204
|
-
shareStoreLoadAll,
|
|
1205
|
-
shareStoreLatestVersion,
|
|
1206
|
-
shareStoreSave,
|
|
1207
|
-
shareStoreRemoveChannel,
|
|
1208
|
-
shareStoreRemoveVersions,
|
|
1209
|
-
shareStoreKeepList,
|
|
1210
|
-
|
|
1297
|
+
self, // user_data
|
|
1298
|
+
shareStoreLoad, // load
|
|
1299
|
+
shareStoreLoadMany, // load_many
|
|
1300
|
+
shareStoreLoadAll, // load_all
|
|
1301
|
+
shareStoreLatestVersion, // latest_version
|
|
1302
|
+
shareStoreSave, // save
|
|
1303
|
+
shareStoreRemoveChannel, // remove_channel
|
|
1304
|
+
shareStoreRemoveVersions, // remove_versions
|
|
1305
|
+
shareStoreKeepList, // keep_list
|
|
1306
|
+
shareStoreRecordVerification, // record_verification
|
|
1307
|
+
storeFreeBuffer, // free_buffer
|
|
1211
1308
|
};
|
|
1212
1309
|
bindings->userSecretCallbacks_ = UserSecretStoreCallbacks{
|
|
1213
1310
|
self, // user_data
|
|
@@ -1255,6 +1352,8 @@ std::shared_ptr<StoreBindings> StoreBindings::create(jsi::Runtime& rt, jsi::Obje
|
|
|
1255
1352
|
requireField(rt, bindings->shareCallbacks_.remove_channel, "ShareStoreCallbacks.remove_channel");
|
|
1256
1353
|
requireField(rt, bindings->shareCallbacks_.remove_versions, "ShareStoreCallbacks.remove_versions");
|
|
1257
1354
|
requireField(rt, bindings->shareCallbacks_.keep_list, "ShareStoreCallbacks.keep_list");
|
|
1355
|
+
requireField(rt, bindings->shareCallbacks_.record_verification,
|
|
1356
|
+
"ShareStoreCallbacks.record_verification");
|
|
1258
1357
|
requireField(rt, bindings->shareCallbacks_.free_buffer, "ShareStoreCallbacks.free_buffer");
|
|
1259
1358
|
|
|
1260
1359
|
requireField(rt, bindings->userSecretCallbacks_.user_data, "UserSecretStoreCallbacks.user_data");
|
package/cpp/StoreCallbacks.h
CHANGED
|
@@ -82,8 +82,15 @@ class StoreBindings : public std::enable_shared_from_this<StoreBindings> {
|
|
|
82
82
|
/// through `convert`, dispatching via `then` only when the value is
|
|
83
83
|
/// actually thenable. `convert` runs on the JavaScript thread, with `rt`
|
|
84
84
|
/// live, before `settle` is invoked — never after.
|
|
85
|
+
///
|
|
86
|
+
/// `convertError`, when non-null, replaces the default "any rejection is a
|
|
87
|
+
/// backend failure" handling: it runs on the rejected value instead, so a
|
|
88
|
+
/// caller that needs to read something off that value (e.g.
|
|
89
|
+
/// `ShareStore.save`'s `refusal` tag) can. Left null, a rejection settles
|
|
90
|
+
/// with `kBackendFailure` exactly as before.
|
|
85
91
|
void settleFromPromise(facebook::jsi::Runtime& rt, facebook::jsi::Value value,
|
|
86
|
-
std::function<void(CallResult)> settle, ResultConverter convert
|
|
92
|
+
std::function<void(CallResult)> settle, ResultConverter convert,
|
|
93
|
+
ResultConverter convertError = nullptr);
|
|
87
94
|
|
|
88
95
|
private:
|
|
89
96
|
StoreBindings(facebook::jsi::Runtime& rt, JsCallbackBridge& bridge,
|