enable full safety selection through api
This commit is contained in:
parent
fde70610cc
commit
f1292694a2
@ -235,6 +235,33 @@ fn get_public_key(text: &str) -> Option<PublicKey> {
|
||||
PublicKey::from_str(text).ok()
|
||||
}
|
||||
|
||||
fn get_dht_key(
|
||||
routing_table: RoutingTable,
|
||||
) -> impl FnOnce(&str) -> Option<(TypedKey, Option<SafetySelection>)> {
|
||||
move |text| {
|
||||
// Safety selection
|
||||
let (text, ss) = if let Some((first, second)) = text.split_once('+') {
|
||||
let ss = get_safety_selection(second, routing_table.clone())?;
|
||||
(first, Some(ss))
|
||||
} else {
|
||||
(text, None)
|
||||
};
|
||||
if text.len() == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let key = if let Some(key) = get_public_key(text) {
|
||||
TypedKey::new(best_crypto_kind(), key)
|
||||
} else if let Some(key) = get_typed_key(text) {
|
||||
key
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
Some((key, ss))
|
||||
}
|
||||
}
|
||||
|
||||
fn get_node_ref(routing_table: RoutingTable) -> impl FnOnce(&str) -> Option<NodeRef> {
|
||||
move |text| {
|
||||
let (text, mods) = text
|
||||
@ -923,14 +950,31 @@ impl VeilidAPI {
|
||||
return Ok(out);
|
||||
}
|
||||
async fn debug_record_get(&self, args: Vec<String>) -> VeilidAPIResult<String> {
|
||||
let storage_manager = self.storage_manager()?;
|
||||
let netman = self.network_manager()?;
|
||||
let routing_table = netman.routing_table();
|
||||
|
||||
let key = get_debug_argument_at(&args, 1, "debug_record_get", "key", get_typed_key)?;
|
||||
let subkeys =
|
||||
get_debug_argument_at(&args, 2, "debug_record_subkeys", "subkeys", get_string)?;
|
||||
let (key, ss) = get_debug_argument_at(
|
||||
&args,
|
||||
1,
|
||||
"debug_record_get",
|
||||
"key",
|
||||
get_dht_key(routing_table),
|
||||
)?;
|
||||
let subkeys = get_debug_argument_at(&args, 2, "debug_record_get", "subkeys", get_string)?;
|
||||
|
||||
// let rc = self.routing_context();
|
||||
// Get routing context with optional privacy
|
||||
let rc = self.routing_context();
|
||||
let rc = if let Some(ss) = ss {
|
||||
let rcp = match rc.with_custom_privacy(ss) {
|
||||
Err(e) => return Ok(format!("Can't use safety selection: {}", e)),
|
||||
Ok(v) => v,
|
||||
};
|
||||
rcp
|
||||
} else {
|
||||
rc
|
||||
};
|
||||
|
||||
// Do a record get
|
||||
return Ok("TODO".to_owned());
|
||||
}
|
||||
|
||||
@ -958,7 +1002,8 @@ impl VeilidAPI {
|
||||
entries [dead|reliable]
|
||||
entry <node>
|
||||
nodeinfo
|
||||
config [key [new value]]
|
||||
config [configkey [new value]]
|
||||
txtrecord
|
||||
purge <buckets|connections|routes>
|
||||
attach
|
||||
detach
|
||||
@ -975,8 +1020,9 @@ impl VeilidAPI {
|
||||
test <route>
|
||||
record list <local|remote>
|
||||
purge <local|remote> [bytes]
|
||||
get <key> <subkeys>
|
||||
get <dhtkey> <subkeys>
|
||||
|
||||
<configkey> is: dot path like network.protocol.udp.enabled
|
||||
<destination> is:
|
||||
* direct: <node>[+<safety>][<modifiers>]
|
||||
* relay: <relay>@<target>[+<safety>][<modifiers>]
|
||||
@ -988,6 +1034,7 @@ impl VeilidAPI {
|
||||
<protocoltype> is: udp|tcp|ws|wss
|
||||
<addresstype> is: ipv4|ipv6
|
||||
<routingdomain> is: public|local
|
||||
<dhtkey> is: <key>[+<safety>]
|
||||
<subkeys> is:
|
||||
* a number: 2
|
||||
* a comma-separated inclusive range list: 1..=3,5..=8
|
||||
|
@ -243,12 +243,12 @@ impl JsonRequestProcessor {
|
||||
.map(|new_rc| self.add_routing_context(new_rc)),
|
||||
),
|
||||
},
|
||||
RoutingContextRequestOp::WithCustomPrivacy { stability } => {
|
||||
RoutingContextRequestOp::WithCustomPrivacy { safety_selection } => {
|
||||
RoutingContextResponseOp::WithCustomPrivacy {
|
||||
result: to_json_api_result(
|
||||
routing_context
|
||||
.clone()
|
||||
.with_custom_privacy(stability)
|
||||
.with_custom_privacy(safety_selection)
|
||||
.map(|new_rc| self.add_routing_context(new_rc)),
|
||||
),
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ pub enum RoutingContextRequestOp {
|
||||
Release,
|
||||
WithPrivacy,
|
||||
WithCustomPrivacy {
|
||||
stability: Stability,
|
||||
safety_selection: SafetySelection,
|
||||
},
|
||||
WithSequencing {
|
||||
sequencing: Sequencing,
|
||||
|
@ -46,24 +46,22 @@ impl RoutingContext {
|
||||
}
|
||||
|
||||
pub fn with_privacy(self) -> VeilidAPIResult<Self> {
|
||||
self.with_custom_privacy(Stability::default())
|
||||
}
|
||||
|
||||
pub fn with_custom_privacy(self, stability: Stability) -> VeilidAPIResult<Self> {
|
||||
let config = self.api.config()?;
|
||||
let c = config.get();
|
||||
|
||||
self.with_custom_privacy(SafetySelection::Safe(SafetySpec {
|
||||
preferred_route: None,
|
||||
hop_count: c.network.rpc.default_route_hop_count as usize,
|
||||
stability: Stability::default(),
|
||||
sequencing: Sequencing::default(),
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn with_custom_privacy(self, safety_selection: SafetySelection) -> VeilidAPIResult<Self> {
|
||||
Ok(Self {
|
||||
api: self.api.clone(),
|
||||
inner: Arc::new(Mutex::new(RoutingContextInner {})),
|
||||
unlocked_inner: Arc::new(RoutingContextUnlockedInner {
|
||||
safety_selection: SafetySelection::Safe(SafetySpec {
|
||||
preferred_route: None,
|
||||
hop_count: c.network.rpc.default_route_hop_count as usize,
|
||||
stability,
|
||||
sequencing: self.sequencing(),
|
||||
}),
|
||||
}),
|
||||
unlocked_inner: Arc::new(RoutingContextUnlockedInner { safety_selection }),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ abstract class DHTSchema {
|
||||
default:
|
||||
{
|
||||
throw VeilidAPIExceptionInternal(
|
||||
"Invalid VeilidAPIException type: ${json['kind']}");
|
||||
"Invalid DHTSchema type: ${json['kind']}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -196,6 +196,7 @@ class ValueData {
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
/// Stability
|
||||
|
||||
enum Stability {
|
||||
@ -228,6 +229,80 @@ enum Sequencing {
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
/// SafetySelection
|
||||
|
||||
abstract class SafetySelection {
|
||||
factory SafetySelection.fromJson(dynamic json) {
|
||||
var m = json as Map<String, dynamic>;
|
||||
if (m.containsKey("Unsafe")) {
|
||||
return SafetySelectionUnsafe(
|
||||
sequencing: Sequencing.fromJson(m["Unsafe"]));
|
||||
} else if (m.containsKey("Safe")) {
|
||||
return SafetySelectionSafe(safetySpec: SafetySpec.fromJson(m["Safe"]));
|
||||
} else {
|
||||
throw VeilidAPIExceptionInternal("Invalid SafetySelection");
|
||||
}
|
||||
}
|
||||
Map<String, dynamic> toJson();
|
||||
}
|
||||
|
||||
class SafetySelectionUnsafe implements SafetySelection {
|
||||
final Sequencing sequencing;
|
||||
//
|
||||
SafetySelectionUnsafe({
|
||||
required this.sequencing,
|
||||
});
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {'Unsafe': sequencing.toJson()};
|
||||
}
|
||||
}
|
||||
|
||||
class SafetySelectionSafe implements SafetySelection {
|
||||
final SafetySpec safetySpec;
|
||||
//
|
||||
SafetySelectionSafe({
|
||||
required this.safetySpec,
|
||||
});
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {'Safe': safetySpec.toJson()};
|
||||
}
|
||||
}
|
||||
|
||||
/// Options for safety routes (sender privacy)
|
||||
class SafetySpec {
|
||||
final String? preferredRoute;
|
||||
final int hopCount;
|
||||
final Stability stability;
|
||||
final Sequencing sequencing;
|
||||
//
|
||||
SafetySpec({
|
||||
this.preferredRoute,
|
||||
required this.hopCount,
|
||||
required this.stability,
|
||||
required this.sequencing,
|
||||
});
|
||||
|
||||
SafetySpec.fromJson(dynamic json)
|
||||
: preferredRoute = json['preferred_route'],
|
||||
hopCount = json['hop_count'],
|
||||
stability = Stability.fromJson(json['stability']),
|
||||
sequencing = Sequencing.fromJson(json['sequencing']);
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'preferred_route': preferredRoute,
|
||||
'hop_count': hopCount,
|
||||
'stability': stability.toJson(),
|
||||
'sequencing': sequencing.toJson()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
/// RouteBlob
|
||||
class RouteBlob {
|
||||
@ -251,7 +326,7 @@ class RouteBlob {
|
||||
abstract class VeilidRoutingContext {
|
||||
// Modifiers
|
||||
VeilidRoutingContext withPrivacy();
|
||||
VeilidRoutingContext withCustomPrivacy(Stability stability);
|
||||
VeilidRoutingContext withCustomPrivacy(SafetySelection safetySelection);
|
||||
VeilidRoutingContext withSequencing(Sequencing sequencing);
|
||||
|
||||
// App call/message
|
||||
|
@ -595,9 +595,9 @@ class VeilidRoutingContextFFI implements VeilidRoutingContext {
|
||||
}
|
||||
|
||||
@override
|
||||
VeilidRoutingContextFFI withCustomPrivacy(Stability stability) {
|
||||
VeilidRoutingContextFFI withCustomPrivacy(SafetySelection safetySelection) {
|
||||
final newId = _ctx.ffi._routingContextWithCustomPrivacy(
|
||||
_ctx.id, jsonEncode(stability).toNativeUtf8());
|
||||
_ctx.id, jsonEncode(safetySelection).toNativeUtf8());
|
||||
return VeilidRoutingContextFFI._(_Ctx(newId, _ctx.ffi));
|
||||
}
|
||||
|
||||
|
@ -45,11 +45,11 @@ class VeilidRoutingContextJS implements VeilidRoutingContext {
|
||||
}
|
||||
|
||||
@override
|
||||
VeilidRoutingContextJS withCustomPrivacy(Stability stability) {
|
||||
VeilidRoutingContextJS withCustomPrivacy(SafetySelection safetySelection) {
|
||||
final newId = js_util.callMethod(
|
||||
wasm,
|
||||
"routing_context_with_custom_privacy",
|
||||
[_ctx.id, jsonEncode(stability)]);
|
||||
[_ctx.id, jsonEncode(safetySelection)]);
|
||||
|
||||
return VeilidRoutingContextJS._(_Ctx(newId, _ctx.js));
|
||||
}
|
||||
|
@ -410,15 +410,15 @@ pub extern "C" fn routing_context_with_privacy(id: u32) -> u32 {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn routing_context_with_custom_privacy(id: u32, stability: FfiStr) -> u32 {
|
||||
let stability: veilid_core::Stability =
|
||||
veilid_core::deserialize_opt_json(stability.into_opt_string()).unwrap();
|
||||
pub extern "C" fn routing_context_with_custom_privacy(id: u32, safety_selection: FfiStr) -> u32 {
|
||||
let safety_selection: veilid_core::SafetySelection =
|
||||
veilid_core::deserialize_opt_json(safety_selection.into_opt_string()).unwrap();
|
||||
|
||||
let rc = ROUTING_CONTEXTS.lock();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return 0;
|
||||
};
|
||||
let Ok(routing_context) = routing_context.clone().with_custom_privacy(stability) else {
|
||||
let Ok(routing_context) = routing_context.clone().with_custom_privacy(safety_selection) else {
|
||||
return 0;
|
||||
};
|
||||
let new_id = add_routing_context(routing_context);
|
||||
|
@ -16,14 +16,28 @@ from .conftest import server_info
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_routing_contexts(api_connection: veilid.VeilidAPI):
|
||||
rc = await api_connection.new_routing_context()
|
||||
async with rc:
|
||||
pass
|
||||
|
||||
rc = await api_connection.new_routing_context()
|
||||
async with rc:
|
||||
rcp = await rc.with_privacy(release=False)
|
||||
async with rcp:
|
||||
rcps = await rcp.with_sequencing(veilid.Sequencing.ENSURE_ORDERED, release=False)
|
||||
async with rcps:
|
||||
rcpscp = await rcps.with_custom_privacy(veilid.Stability.RELIABLE, release=False)
|
||||
await rcpscp.release()
|
||||
pass
|
||||
|
||||
rc = await (await api_connection.new_routing_context()).with_sequencing(veilid.Sequencing.ENSURE_ORDERED)
|
||||
async with rc:
|
||||
pass
|
||||
|
||||
rc = await (await api_connection.new_routing_context()).with_custom_privacy(
|
||||
veilid.SafetySelection.safe(
|
||||
veilid.SafetySpec(None, 2, veilid.Stability.RELIABLE, veilid.Sequencing.ENSURE_ORDERED)
|
||||
))
|
||||
await rc.release()
|
||||
|
||||
rc = await (await api_connection.new_routing_context()).with_custom_privacy(veilid.SafetySelection.unsafe(veilid.Sequencing.ENSURE_ORDERED))
|
||||
await rc.release()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
@ -27,7 +27,7 @@ class RoutingContext(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def with_custom_privacy(self, stability: types.Stability, release = True) -> Self:
|
||||
async def with_custom_privacy(self, safety_selection: types.SafetySelection, release = True) -> Self:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
|
@ -16,7 +16,7 @@ from .state import VeilidState, VeilidUpdate
|
||||
from .types import (CryptoKey, CryptoKeyDistance, CryptoKind,
|
||||
DHTRecordDescriptor, DHTSchema, HashDigest, KeyPair,
|
||||
NewPrivateRouteResult, Nonce, OperationId, PublicKey,
|
||||
RouteId, SecretKey, Sequencing, SharedSecret, Signature,
|
||||
RouteId, SafetySelection, SecretKey, Sequencing, SharedSecret, Signature,
|
||||
Stability, Timestamp, TypedKey, TypedKeyPair,
|
||||
TypedSignature, ValueData, ValueSubkey, VeilidJSONEncoder,
|
||||
VeilidVersion, urlsafe_b64decode_no_pad)
|
||||
@ -459,14 +459,14 @@ class _JsonRoutingContext(RoutingContext):
|
||||
await self.release()
|
||||
return self.__class__(self.api, new_rc_id)
|
||||
|
||||
async def with_custom_privacy(self, stability: Stability, release = True) -> Self:
|
||||
async def with_custom_privacy(self, safety_selection: SafetySelection, release = True) -> Self:
|
||||
new_rc_id = raise_api_result(
|
||||
await self.api.send_ndjson_request(
|
||||
Operation.ROUTING_CONTEXT,
|
||||
validate=validate_rc_op,
|
||||
rc_id=self.rc_id,
|
||||
rc_op=RoutingContextOperation.WITH_CUSTOM_PRIVACY,
|
||||
stability=stability,
|
||||
safety_selection=safety_selection,
|
||||
)
|
||||
)
|
||||
if release:
|
||||
|
@ -224,7 +224,7 @@
|
||||
"type": "object",
|
||||
"required": [
|
||||
"rc_op",
|
||||
"stability"
|
||||
"safety_selection"
|
||||
],
|
||||
"properties": {
|
||||
"rc_op": {
|
||||
@ -233,8 +233,8 @@
|
||||
"WithCustomPrivacy"
|
||||
]
|
||||
},
|
||||
"stability": {
|
||||
"$ref": "#/definitions/Stability"
|
||||
"safety_selection": {
|
||||
"$ref": "#/definitions/SafetySelection"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1566,6 +1566,77 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"SafetySelection": {
|
||||
"description": "The choice of safety route to include in compiled routes",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Don't use a safety route, only specify the sequencing preference",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Unsafe"
|
||||
],
|
||||
"properties": {
|
||||
"Unsafe": {
|
||||
"$ref": "#/definitions/Sequencing"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Use a safety route and parameters specified by a SafetySpec",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Safe"
|
||||
],
|
||||
"properties": {
|
||||
"Safe": {
|
||||
"$ref": "#/definitions/SafetySpec"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"SafetySpec": {
|
||||
"description": "Options for safety routes (sender privacy)",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"hop_count",
|
||||
"sequencing",
|
||||
"stability"
|
||||
],
|
||||
"properties": {
|
||||
"hop_count": {
|
||||
"description": "must be greater than 0",
|
||||
"type": "integer",
|
||||
"format": "uint",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"preferred_route": {
|
||||
"description": "preferred safety route set id if it still exists",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"sequencing": {
|
||||
"description": "prefer connection-oriented sequenced protocols",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Sequencing"
|
||||
}
|
||||
]
|
||||
},
|
||||
"stability": {
|
||||
"description": "prefer reliability over speed",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Stability"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Sequencing": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
|
@ -67,6 +67,9 @@ class DHTSchemaKind(StrEnum):
|
||||
DFLT = "DFLT"
|
||||
SMPL = "SMPL"
|
||||
|
||||
class SafetySelectionKind(StrEnum):
|
||||
UNSAFE = "Unsafe"
|
||||
SAFE = "Safe"
|
||||
|
||||
####################################################################
|
||||
|
||||
@ -357,7 +360,7 @@ class ValueData:
|
||||
|
||||
def __lt__(self, other):
|
||||
if other is None:
|
||||
return true
|
||||
return True
|
||||
if self.data < other.data:
|
||||
return True
|
||||
if self.data > other.data:
|
||||
@ -383,3 +386,61 @@ class ValueData:
|
||||
|
||||
def to_json(self) -> dict:
|
||||
return self.__dict__
|
||||
|
||||
|
||||
####################################################################
|
||||
|
||||
class SafetySpec:
|
||||
preferred_route: Optional[RouteId]
|
||||
hop_count: int
|
||||
stability: Stability
|
||||
sequencing: Sequencing
|
||||
|
||||
def __init__(self, preferred_route: Optional[RouteId], hop_count: int, stability: Stability, sequencing: Sequencing):
|
||||
self.preferred_route = preferred_route
|
||||
self.hop_count = hop_count
|
||||
self.stability = stability
|
||||
self.sequencing = sequencing
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, j: dict) -> Self:
|
||||
return cls(RouteId(j["preferred_route"]) if "preferred_route" in j else None,
|
||||
j["hop_count"],
|
||||
Stability(j["stability"]),
|
||||
Sequencing(j["sequencing"]))
|
||||
|
||||
def to_json(self) -> dict:
|
||||
return self.__dict__
|
||||
|
||||
class SafetySelection:
|
||||
kind: SafetySelectionKind
|
||||
|
||||
def __init__(self, kind: SafetySelectionKind, **kwargs):
|
||||
self.kind = kind
|
||||
for k, v in kwargs.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
@classmethod
|
||||
def unsafe(cls, sequencing: Sequencing) -> Self:
|
||||
return cls(SafetySelectionKind.UNSAFE, sequencing=sequencing)
|
||||
|
||||
@classmethod
|
||||
def safe(cls, safety_spec: SafetySpec) -> Self:
|
||||
return cls(SafetySelectionKind.SAFE, safety_spec=safety_spec)
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, j: dict) -> Self:
|
||||
if "Safe" in j:
|
||||
return cls.safe(SafetySpec.from_json(j["Safe"]))
|
||||
elif "Unsafe" in j:
|
||||
return cls.unsafe(Sequencing(j["Unsafe"]))
|
||||
raise Exception("Invalid SafetySelection")
|
||||
|
||||
def to_json(self) -> dict:
|
||||
if self.kind == SafetySelectionKind.UNSAFE:
|
||||
return {"Unsafe": self.sequencing }
|
||||
elif self.kind == SafetySelectionKind.SAFE:
|
||||
return {"Safe": self.safety_spec.to_json() }
|
||||
else:
|
||||
raise Exception("Invalid SafetySelection")
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user