diff --git a/active/0000-Unified-structured-data.md b/active/0000-Unified-structured-data.md index 79d4d23..d8eea26 100644 --- a/active/0000-Unified-structured-data.md +++ b/active/0000-Unified-structured-data.md @@ -125,3 +125,243 @@ Status quo is an option and realistic. 1. Size of StructuredData packet, it would be nice if it were perhaps 512Bytes to have the best chance to fit into a single UDP packet, although not guaranteed. Means a payload after serialisation of only a few hundred bytes (maybe less) 2. Version conflicts or out of order updates, will upper layers handle this via a wait condition? + +-------------------------------------------------------------------------------- +# Addendum + +# Summary + +[RFC Unified Structured Data](https://github.com/dirvine/rfcs/blob/unified-structured-data/proposed/0000-Unified-structured-data.md) introduces `StructuredData` as a fundamental type for the network. +This RFC explores in more detail the implications for applying this change for routing +and sentinel library. This is not the exact implementation as completed after Rust-3, but appended for reference. + +# Motivation + +For the motivation on introducing `Unified Structured Data` we refer back to +the parent RFC. The motivation for the current RFC is to establish a collective +understanding of the changes needed for routing and sentinel to +implementing the parent RFC. This RFC explicitly excludes the intend to change +the design of the actual `StructuredData` and any such discussions should be +posted on the parent RFC. + +# Detailed design + +For the design of the actual `StructuredData` we again refer to the parent RFC. + +## Types +In routing three new fundamental types will be introduced `PlainData`, +`ImmutableData`, `StructuredData` + +### Plain data + +```rust +struct PlainData { + key : NameType, + value : Vec +} +``` + +Routing will not perform any additional validation on the PlainData type. +Default routing behaviour applies to this type, which is briefly recapitulated +below. + +### Immutable data + +``` rust +struct ImmutableData { + tag_type : u8, + value : Vec +} + +impl ImmutableData { + pub fn new(tag_type: u8, value : Vec) -> ImmutableData {} + + pub fn name(&self) -> NameType { + (tag_type + 1) Hash iterations of self.value + } + + // add lifetime if needed + pub fn content(&self) -> &[u8] { + &self.value[..] + } +} +``` + +Routing considers `ImmutableData` valid when a requested `name` equals +`immutable_data.name()`. + +### Structured data + +We repeat the structure as defined in the parent RFC + +``` rust +struct StructuredData { + tag_type : u64, + identifier : NameType, + data : Vec, + owner_keys : Vec, + version : u64, + previous_owner_keys : vec + signature : Vec +} + +impl StructuredData { + pub fn new( + tag_type : u64, + identifier : NameType, + data : Vec, + owner_keys : Vec, + version : u64, + previous_owner_keys : vec + signature : Vec) -> Result { + // validate: + // 0. total size <= 100 kB + // 1. must always be owned + // 2. on version == 0, no signatures needed + // 3. if previous owners set: check signatures with majority + // of their keys + // 4. if no previous owners: check signatures with majority + // of their keys + construct!() + } + + pub fn name(&self) -> NameType { + SHA512(tag_type + identifier) + } + + pub fn content(&self) -> &[u8] {} + + pub fn add_signature(&mut self, + private_sign_key : &crypto::sign::SecretKey, + public_sign_key: &crypto::sign::PublicKey) -> Result {} + + pub fn is_valid_successor(&self, successor: &StructuredData) -> bool { + // see detailed discussion of logic below + } +} +``` + +- `data` Routing does not parse the `data` field; it is always considered +as serialised bytes. +- `version` Routing does not (currently) attempt to resolve concurreny issues; +as such `version` is unused at routing. Additionally routing has no knowledge +of the previously stored valid version. The version number is for the user. +- `tag_type` Routing does not attach meaning to the 8 byte `tag_type` and treats +all tag_types equal. +- the signature should sign in bytes that are concatenated +in the following order: `data`, `version`, `owner_keys` and +`previous_owner_keys` - purely as a matter of convention. + +Corresponding `get` functions for all data fields are implied. + +#### logic for valid succession of structured data + +The structured data is recursively validated strictly on the preceding version. +That is the validity of a `StructuredData` of version `n` can only depend +on the validity of the same `StructuredData` of version `n-1`. Here "same" +means "have the same name". For `StructuredData` of version `n = 0` the +validity is `true` (and hence ClientManagers can charge an account to create +new valid `StructuredData_v0`). + +The version number must strictly increase by one. +All structured data must be owned; transfer of ownership is discussed below. +A majority is 50%, as outlined in the parent RFC + +**case : previous owner field is empty** + +An empty `previous_owner_keys` vector implies that the `owner_keys` +of version `n` must match the `owner_keys` of version `n-1`. +The `previous_owner_keys` of version `n-1` is ignored. +In this case no transfer of ownership is executed. The signatures must +be validatable with a majority of the keys of the current owners of version `n`. + +**case : previous owner field is not empty** + +A non-empty `previous_owner_keys` vector implies that there is an intent to +transfer ownership. In this case the `previous_owner_keys` of version `n` +must match the `owner_keys` of version `n-1`. The signatures must be +validatable with a majority of the keys of `previous_owner_keys` of version `n`. + +#### efficiency consideration when validating the signatures + +As outlined above, a signed vector of public keys defines a fixed order of +the public keys that will be checked to validate the signatures (see above +on previous owners or not). On construction of a `StructuredData` +the `Vec` will be ordered to the fixed order defined by the +relevant `Vec`. + +Any validation effort will then in sequence iterate over the signatures, and +in sequence check the keys, starting from the same index. This is an +easy algorithm for minimizing the search for matching signatures. A better +algorithm can be suggested. + +## Impact on routing message + +Currently `RoutingMessage` has the following declaration with +an obligatory signature from the `Header::Source::fromNode` +```rust +pub struct RoutingMessage { + pub message_type: MessageTypeTag, + pub message_header: message_header::MessageHeader, + pub serialised_body: Vec, + pub signature : types::Signature +} +``` + +As every client connects to the network over a relocated relay node, +we can keep this obligatory signature and have it signed by the relay node. +This puts responsibility on the relay node when injecting client messages in +the network, and keeps consistency that the from_node on the network is always +the id of a relocated node, not the hash of a 32byte client PublicKey. + +RoutingClient will sign RoutingMessages with the generic unrelocated Id +it self-generates on construction. This generic Id is unrelated to the keys +for signing ownership of structured data; it is purely and internally used +by routing to identify client-relay connections, per session. + +## Conservative approach to MessageTypes + +Routing can reduce to the following message types; the main motivation +is to simplify the message handlers. However, we will first go for +a conservative approach and extend the existing paradigm. + +This requires updating `GetData`, `GetDataResponse`, `PutData`, `PutDataResponse`. +It leads to introducing `Post`, `PostResponse`, `Delete`, `DeleteResponse`. + +The corresponding handlers need to be updated/written, replacing `serialised_bytes` +with `UnifiedData`. + +``` rust +pub enum UnifiedData { + PlainData, + ImmutableData(u8), + StructuredData(u64) +} +``` + +## also update with UnifiedData enumeration +This list is not exhaustive; all previously `serialised bytes` +need to be updated to a `UnifiedData`. + +- `NameTypeAndId` +- `MessageAction` +- `MethodCall` +- `RoutingMembrane::Put`, `RoutingMembrane::Get`, etc +- `RoutingClient::Put`, `RoutingClient::Get`, etc +- `NodeInterface` +- `ClientInterface` + +# Drawbacks + +Why should we *not* do this? +(uncompleted) + +# Alternatives + +What other designs have been considered? What is the impact of not doing this? +(uncompleted) + +# Unresolved questions + +What parts of the design are still to be done? +(uncompleted)