wasm work

This commit is contained in:
John Smith 2023-05-16 13:28:09 -04:00
parent 46e67d7b0c
commit 10af290e2f
5 changed files with 346 additions and 18 deletions

View File

@ -380,6 +380,42 @@ Future<T> processFutureJson<T>(
});
}
Future<T?> processFutureOptJson<T>(
T Function(dynamic) jsonConstructor, Future<dynamic> future) {
return future.then((value) {
final list = value as List<dynamic>;
switch (list[0] as int) {
case messageErr:
{
throw VeilidAPIExceptionInternal("Internal API Error: ${list[1]}");
}
case messageOkJson:
{
if (list[1] == null) {
return null;
}
var ret = jsonDecode(list[1] as String);
return jsonConstructor(ret);
}
case messageErrJson:
{
throw VeilidAPIException.fromJson(jsonDecode(list[1]));
}
default:
{
throw VeilidAPIExceptionInternal(
"Unexpected async return message type: ${list[0]}");
}
}
}).catchError((e) {
// Wrap all other errors in VeilidAPIExceptionInternal
throw VeilidAPIExceptionInternal(e.toString());
}, test: (e) {
// Pass errors that are already VeilidAPIException through without wrapping
return e is! VeilidAPIException;
});
}
Future<void> processFutureVoid(Future<dynamic> future) {
return future.then((value) {
final list = value as List<dynamic>;
@ -967,9 +1003,9 @@ class VeilidCryptoSystemFFI implements VeilidCryptoSystem {
final nativeEncodedData = base64UrlNoPadEncode(data).toNativeUtf8();
final nativeSignature = jsonEncode(signature).toNativeUtf8();
final recvPort = ReceivePort("crypto_sign");
final recvPort = ReceivePort("crypto_verify");
final sendPort = recvPort.sendPort;
_ffi._cryptoSign(sendPort.nativePort, _kind, nativeKey, nativeEncodedData,
_ffi._cryptoVerify(sendPort.nativePort, _kind, nativeKey, nativeEncodedData,
nativeSignature);
return processFutureVoid(recvPort.first);
}

View File

@ -77,6 +77,216 @@ class VeilidRoutingContextJS implements VeilidRoutingContext {
return _wrapApiPromise(js_util.callMethod(wasm,
"routing_context_app_message", [_ctx.id, target, encodedMessage]));
}
@override
Future<DHTRecordDescriptor> createDHTRecord(
CryptoKind kind, DHTSchema schema) async {
return DHTRecordDescriptor.fromJson(jsonDecode(await _wrapApiPromise(js_util
.callMethod(wasm, "routing_context_create_dht_record",
[_ctx.id, kind, jsonEncode(schema)]))));
}
@override
Future<DHTRecordDescriptor> openDHTRecord(
TypedKey key, KeyPair? writer) async {
return DHTRecordDescriptor.fromJson(jsonDecode(await _wrapApiPromise(js_util
.callMethod(wasm, "routing_context_open_dht_record", [
_ctx.id,
jsonEncode(key),
writer != null ? jsonEncode(writer) : null
]))));
}
@override
Future<void> closeDHTRecord(TypedKey key) {
return _wrapApiPromise(js_util.callMethod(
wasm, "routing_context_close_dht_record", [_ctx.id, jsonEncode(key)]));
}
@override
Future<void> deleteDHTRecord(TypedKey key) {
return _wrapApiPromise(js_util.callMethod(
wasm, "routing_context_delete_dht_record", [_ctx.id, jsonEncode(key)]));
}
@override
Future<ValueData?> getDHTValue(
TypedKey key, int subkey, bool forceRefresh) async {
final opt = await _wrapApiPromise(js_util.callMethod(
wasm,
"routing_context_get_dht_value",
[_ctx.id, jsonEncode(key), subkey, forceRefresh]));
return opt == null ? null : ValueData.fromJson(jsonDecode(opt));
}
@override
Future<ValueData?> setDHTValue(
TypedKey key, int subkey, Uint8List data) async {
final opt = await _wrapApiPromise(js_util.callMethod(
wasm,
"routing_context_set_dht_value",
[_ctx.id, jsonEncode(key), subkey, base64UrlNoPadEncode(data)]));
return opt == null ? null : ValueData.fromJson(jsonDecode(opt));
}
@override
Future<Timestamp> watchDHTValues(TypedKey key, ValueSubkeyRange subkeys,
Timestamp expiration, int count) async {
final ts = await _wrapApiPromise(js_util.callMethod(
wasm, "routing_context_watch_dht_values", [
_ctx.id,
jsonEncode(key),
jsonEncode(subkeys),
expiration.toString(),
count
]));
return Timestamp.fromString(ts);
}
@override
Future<bool> cancelDHTWatch(TypedKey key, ValueSubkeyRange subkeys) {
return _wrapApiPromise(js_util.callMethod(
wasm,
"routing_context_cancel_dht_watch",
[_ctx.id, jsonEncode(key), jsonEncode(subkeys)]));
}
}
// JS implementation of VeilidCryptoSystem
class VeilidCryptoSystemJS implements VeilidCryptoSystem {
final CryptoKind _kind;
final VeilidJS _js;
VeilidCryptoSystemJS._(this._js, this._kind);
@override
CryptoKind kind() {
return _kind;
}
@override
Future<SharedSecret> cachedDH(PublicKey key, SecretKey secret) async {
return SharedSecret.fromJson(jsonDecode(await _wrapApiPromise(js_util
.callMethod(wasm, "crypto_cached_dh",
[_kind, jsonEncode(key), jsonEncode(secret)]))));
}
@override
Future<SharedSecret> computeDH(PublicKey key, SecretKey secret) async {
return SharedSecret.fromJson(jsonDecode(await _wrapApiPromise(js_util
.callMethod(wasm, "crypto_compute_dh",
[_kind, jsonEncode(key), jsonEncode(secret)]))));
}
@override
Future<Nonce> randomNonce() async {
return Nonce.fromJson(jsonDecode(await _wrapApiPromise(
js_util.callMethod(wasm, "crypto_random_nonce", [_kind]))));
}
@override
Future<SharedSecret> randomSharedSecret() async {
return SharedSecret.fromJson(jsonDecode(await _wrapApiPromise(
js_util.callMethod(wasm, "crypto_random_shared_secret", [_kind]))));
}
@override
Future<KeyPair> generateKeyPair() async {
return KeyPair.fromJson(jsonDecode(await _wrapApiPromise(
js_util.callMethod(wasm, "crypto_generate_key_pair", [_kind]))));
}
@override
Future<HashDigest> generateHash(Uint8List data) async {
return HashDigest.fromJson(jsonDecode(await _wrapApiPromise(js_util
.callMethod(wasm, "crypto_generate_hash",
[_kind, base64UrlNoPadEncode(data)]))));
}
@override
Future<bool> validateKeyPair(PublicKey key, SecretKey secret) {
return _wrapApiPromise(js_util.callMethod(wasm, "crypto_validate_key_pair",
[_kind, jsonEncode(key), jsonEncode(secret)]));
}
@override
Future<bool> validateHash(Uint8List data, HashDigest hash) {
return _wrapApiPromise(js_util.callMethod(wasm, "crypto_validate_hash",
[_kind, base64UrlNoPadEncode(data), jsonEncode(hash)]));
}
@override
Future<CryptoKeyDistance> distance(CryptoKey key1, CryptoKey key2) async {
return CryptoKeyDistance.fromJson(jsonDecode(await _wrapApiPromise(js_util
.callMethod(wasm, "crypto_distance",
[_kind, jsonEncode(key1), jsonEncode(key2)]))));
}
@override
Future<Signature> sign(
PublicKey key, SecretKey secret, Uint8List data) async {
return Signature.fromJson(jsonDecode(await _wrapApiPromise(js_util
.callMethod(wasm, "crypto_sign", [
_kind,
jsonEncode(key),
jsonEncode(secret),
base64UrlNoPadEncode(data)
]))));
}
@override
Future<void> verify(PublicKey key, Uint8List data, Signature signature) {
return _wrapApiPromise(js_util.callMethod(wasm, "crypto_verify", [
_kind,
jsonEncode(key),
base64UrlNoPadEncode(data),
jsonEncode(signature),
]));
}
@override
Future<int> aeadOverhead() {
return _wrapApiPromise(
js_util.callMethod(wasm, "crypto_aead_overhead", [_kind]));
}
@override
Future<Uint8List> decryptAead(Uint8List body, Nonce nonce,
SharedSecret sharedSecret, Uint8List? associatedData) async {
return base64UrlNoPadDecode(
await _wrapApiPromise(js_util.callMethod(wasm, "crypto_decrypt_aead", [
_kind,
base64UrlNoPadEncode(body),
jsonEncode(nonce),
jsonEncode(sharedSecret),
associatedData != null ? base64UrlNoPadEncode(associatedData) : null
])));
}
@override
Future<Uint8List> encryptAead(Uint8List body, Nonce nonce,
SharedSecret sharedSecret, Uint8List? associatedData) async {
return base64UrlNoPadDecode(
await _wrapApiPromise(js_util.callMethod(wasm, "crypto_encrypt_aead", [
_kind,
base64UrlNoPadEncode(body),
jsonEncode(nonce),
jsonEncode(sharedSecret),
associatedData != null ? base64UrlNoPadEncode(associatedData) : null
])));
}
@override
Future<Uint8List> cryptNoAuth(
Uint8List body, Nonce nonce, SharedSecret sharedSecret) async {
return base64UrlNoPadDecode(await _wrapApiPromise(js_util.callMethod(
wasm, "crypto_crypt_no_auth", [
_kind,
base64UrlNoPadEncode(body),
jsonEncode(nonce),
jsonEncode(sharedSecret)
])));
}
}
class _TDBT {
@ -257,6 +467,50 @@ class VeilidJS implements Veilid {
js_util.callMethod(wasm, "shutdown_veilid_core", []));
}
@override
List<CryptoKind> validCryptoKinds() {
return jsonDecode(js_util.callMethod(wasm, "valid_crypto_kinds", []));
}
@override
Future<VeilidCryptoSystem> getCryptoSystem(CryptoKind kind) async {
if (!validCryptoKinds().contains(kind)) {
throw VeilidAPIExceptionGeneric("unsupported cryptosystem");
}
return VeilidCryptoSystemJS._(this, kind);
}
@override
Future<VeilidCryptoSystem> bestCryptoSystem() async {
return VeilidCryptoSystemJS._(
this, js_util.callMethod(wasm, "best_crypto_kind", []));
}
@override
Future<List<TypedKey>> verifySignatures(List<TypedKey> nodeIds,
Uint8List data, List<TypedSignature> signatures) async {
return jsonListConstructor(TypedKey.fromJson)(jsonDecode(
await _wrapApiPromise(js_util.callMethod(wasm, "verify_signatures", [
jsonEncode(nodeIds),
base64UrlNoPadEncode(data),
jsonEncode(signatures)
]))));
}
@override
Future<List<TypedSignature>> generateSignatures(
Uint8List data, List<TypedKeyPair> keyPairs) async {
return jsonListConstructor(TypedSignature.fromJson)(jsonDecode(
await _wrapApiPromise(js_util.callMethod(wasm, "generate_signatures",
[base64UrlNoPadEncode(data), jsonEncode(keyPairs)]))));
}
@override
Future<TypedKeyPair> generateKeyPair(CryptoKind kind) async {
return TypedKeyPair.fromJson(jsonDecode(await _wrapApiPromise(
js_util.callMethod(wasm, "generate_key_pair", [kind]))));
}
@override
Future<VeilidRoutingContext> routingContext() async {
int id =
@ -266,9 +520,8 @@ class VeilidJS implements Veilid {
@override
Future<RouteBlob> newPrivateRoute() async {
Map<String, dynamic> blobJson = jsonDecode(await _wrapApiPromise(
js_util.callMethod(wasm, "new_private_route", [])));
return RouteBlob.fromJson(blobJson);
return RouteBlob.fromJson(jsonDecode(await _wrapApiPromise(
js_util.callMethod(wasm, "new_private_route", []))));
}
@override
@ -277,10 +530,9 @@ class VeilidJS implements Veilid {
var stabilityString = jsonEncode(stability);
var sequencingString = jsonEncode(sequencing);
Map<String, dynamic> blobJson = jsonDecode(await _wrapApiPromise(js_util
return RouteBlob.fromJson(jsonDecode(await _wrapApiPromise(js_util
.callMethod(
wasm, "new_private_route", [stabilityString, sequencingString])));
return RouteBlob.fromJson(blobJson);
wasm, "new_private_route", [stabilityString, sequencingString]))));
}
@override
@ -315,6 +567,11 @@ class VeilidJS implements Veilid {
return _wrapApiPromise(js_util.callMethod(wasm, "delete_table_db", [name]));
}
@override
Timestamp now() {
return Timestamp.fromString(js_util.callMethod(wasm, "now", []));
}
@override
Future<String> debug(String command) async {
return await _wrapApiPromise(js_util.callMethod(wasm, "debug", [command]));

View File

@ -566,7 +566,7 @@ pub extern "C" fn routing_context_delete_dht_record(port: i64, id: u32, key: Ffi
#[no_mangle]
pub extern "C" fn routing_context_get_dht_value(port: i64, id: u32, key: FfiStr, subkey: u32, force_refresh: bool) {
let key: veilid_core::TypedKey = veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap();
DartIsolateWrapper::new(port).spawn_result_json(async move {
DartIsolateWrapper::new(port).spawn_result_opt_json(async move {
let routing_context = {
let rc = ROUTING_CONTEXTS.lock();
let Some(routing_context) = rc.get(&id) else {
@ -591,7 +591,7 @@ pub extern "C" fn routing_context_set_dht_value(port: i64, id: u32, key: FfiStr,
)
.unwrap();
DartIsolateWrapper::new(port).spawn_result_json(async move {
DartIsolateWrapper::new(port).spawn_result_opt_json(async move {
let routing_context = {
let rc = ROUTING_CONTEXTS.lock();
let Some(routing_context) = rc.get(&id) else {
@ -1252,12 +1252,12 @@ pub extern "C" fn crypto_verify(port: i64, kind: u32, key: FfiStr, data: FfiStr,
let signature: veilid_core::Signature =
veilid_core::deserialize_opt_json(signature.into_opt_string()).unwrap();
DartIsolateWrapper::new(port).spawn_result_json(async move {
DartIsolateWrapper::new(port).spawn_result(async move {
let veilid_api = get_veilid_api().await?;
let crypto = veilid_api.crypto()?;
let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_verify", "kind", kind.to_string()))?;
let out = csv.verify(&key, &data, &signature)?;
APIResult::Ok(out)
csv.verify(&key, &data, &signature)?;
APIRESULT_VOID
});
}

View File

@ -52,6 +52,17 @@ impl DartIsolateWrapper {
});
}
pub fn spawn_result_opt_json<F, T, E>(self, future: F)
where
F: Future<Output = Result<Option<T>, E>> + Send + 'static,
T: Serialize + Debug,
E: Serialize + Debug,
{
spawn(async move {
self.result_opt_json(future.await);
});
}
pub fn result<T: IntoDart + Debug, E: Serialize + Debug>(self, result: Result<T, E>) -> bool {
match result {
Ok(v) => self.ok(v),
@ -67,6 +78,16 @@ impl DartIsolateWrapper {
Err(e) => self.err_json(e),
}
}
pub fn result_opt_json<T: Serialize + Debug, E: Serialize + Debug>(
self,
result: Result<Option<T>, E>,
) -> bool {
match result {
Ok(Some(v)) => self.ok_json(v),
Ok(None) => self.ok(()),
Err(e) => self.err_json(e),
}
}
pub fn ok<T: IntoDart>(self, value: T) -> bool {
self.isolate
.post(vec![MESSAGE_OK.into_dart(), value.into_dart()])

View File

@ -59,6 +59,12 @@ fn take_veilid_api() -> Result<veilid_core::VeilidAPI, veilid_core::VeilidAPIErr
pub fn to_json<T: Serialize + Debug>(val: T) -> JsValue {
JsValue::from_str(&serialize_json(val))
}
pub fn to_opt_json<T: Serialize + Debug>(val: Option<T>) -> JsValue {
match val {
Some(v) => JsValue::from_str(&serialize_json(v)),
None => JsValue::UNDEFINED,
}
}
pub fn to_jsvalue<T>(val: T) -> JsValue
where
@ -113,6 +119,14 @@ where
future_to_promise(future.map(|res| res.map(|v| to_json(v)).map_err(|e| to_json(e))))
}
pub fn wrap_api_future_opt_json<F, T>(future: F) -> Promise
where
F: Future<Output = APIResult<Option<T>>> + 'static,
T: Serialize + Debug + 'static,
{
future_to_promise(future.map(|res| res.map(|v| to_opt_json(v)).map_err(|e| to_json(e))))
}
pub fn wrap_api_future_plain<F, T>(future: F) -> Promise
where
F: Future<Output = APIResult<T>> + 'static,
@ -489,7 +503,7 @@ pub fn routing_context_get_dht_value(
force_refresh: bool,
) -> Promise {
let key: veilid_core::TypedKey = veilid_core::deserialize_json(&key).unwrap();
wrap_api_future_json(async move {
wrap_api_future_opt_json(async move {
let routing_context = {
let rc = (*ROUTING_CONTEXTS).borrow();
let Some(routing_context) = rc.get(&id) else {
@ -511,7 +525,7 @@ pub fn routing_context_set_dht_value(id: u32, key: String, subkey: u32, data: St
.decode(&data.as_bytes())
.unwrap();
wrap_api_future_json(async move {
wrap_api_future_opt_json(async move {
let routing_context = {
let rc = (*ROUTING_CONTEXTS).borrow();
let Some(routing_context) = rc.get(&id) else {
@ -1181,14 +1195,14 @@ pub fn crypto_verify(kind: u32, key: String, data: String, signature: String) ->
.unwrap();
let signature: veilid_core::Signature = veilid_core::deserialize_json(&signature).unwrap();
wrap_api_future_json(async move {
wrap_api_future_void(async move {
let veilid_api = get_veilid_api()?;
let crypto = veilid_api.crypto()?;
let csv = crypto.get(kind).ok_or_else(|| {
veilid_core::VeilidAPIError::invalid_argument("crypto_verify", "kind", kind.to_string())
})?;
let out = csv.verify(&key, &data, &signature)?;
APIResult::Ok(out)
csv.verify(&key, &data, &signature)?;
APIRESULT_UNDEFINED
})
}