-
Notifications
You must be signed in to change notification settings - Fork 37
Petname resolution and synchronization in spheres and gateways #253
Conversation
50ff040
to
ae6997c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking great! some early comments so far
|
||
/// Consume the [Sphere] and get a [Stream] that yields the [ChangelogIpld] | ||
/// for content slugs at each version of the sphere. | ||
pub fn into_link_changelog_stream( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Depending on future plans for these stream functions, they could maybe have an enum or query interface, e.g.
enum SphereMetaType {
Name,
Link
};
into_changelog_stream(self, SphereMetaType, Option<&Cid>)
or
struct ChangeLogQuery {
since: Option<&Cid>,
type: SphereMetaType,
}
into_changelog_stream(self, ChangeLogQuery);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We might have to manifest this as a macro helper. Changes here refer to the change in a VersionedMap
and are typed Changelog<K, V>
, so the method invocation needs to imply key and value types in addition to the field.
#[ffi_export] | ||
/// Assign a DID to a petname. This will overwrite a petname entry if one already exists | ||
/// with the given name (and reset the resolved CID, if any). | ||
pub fn ns_sphere_petname_set( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tuple-nature of the address book ((id, petname) => cid
) doesn't lead to obvious FFI patterns. We should document outcome if petname/did are NULL (implementation of course TBD). I don't think I'd expect the petname to be the "key" here, but changing DID or petname are both valid use cases. Wonder what it'd look like if e.g.
links = ns_sphere_links_get();
for (uint8_t i = 0; i < links.len; i++) {
ns_link* link = links.array[i];
printf("name: %s", ns_link_get_name(link));
printf("did: %s", ns_link_get_did(link));
ns_link_set_name(link, "alice");
}
Though may cause similar synchronization issues as separation of Fs from Context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that under the hood, names (like slugs) are keys in a HAMT. This data structure is convenient (and necessary) because it allows us operate on the data with map semantics, while also allowing us to avoid loading in the entire data structure just to read one key.
The implication here is that loading in the entire set of names could be quite expensive as a general purpose operation, especially if we are working on one key at a time. It's expensive in terms of memory allocation and it's expensive in terms of storage access because the key space of the "map" is effectively sharded across many blocks.
That having been said, even in cases where synchronization is not an issue, we would still want to mediate access to the list of petnames via FFI. And, on the Rust side we implement iteration over all petnames/slugs/etc. as Stream
s - which are effectively async generators - so that we can side-step the pathological case of loading the entire key space into memory at once.
So, I guess the takeaway here is that we need a FFI to access the full space of petnames.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well explained, thanks!
ae6997c
to
4add4d0
Compare
ff57c8e
to
772139c
Compare
5b15264
to
04e28fc
Compare
04e28fc
to
d74e0c0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handful of comments with a closer eye on the NS changes, and some small things like comments. Excellent, gigantic work!!
@@ -101,6 +98,7 @@ Type or paste the code here and press enter:"#, | |||
let cid = Cid::from_str(cid_string.trim()) | |||
.map_err(|_| anyhow!("Could not parse the authorization identity as a CID"))?; | |||
|
|||
debug!("A"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: remove or clarify
@@ -109,6 +107,7 @@ Type or paste the code here and press enter:"#, | |||
.authorized_by(Some(&Authorization::Cid(cid))) | |||
.build() | |||
.await?; | |||
debug!("B"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: remove or clarify
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explanation: b
if updated_names.len() == 0 { | ||
return Ok(None); | ||
} | ||
println!( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No println
in noosphere
imo, this should be rendered in orb
if needed.
There's plenty of existing println here though, let's kick off our rules to writing to stdout in a separate issue
if let Some(jwt) = last_known_record { | ||
context.adopt_petname(&name, &identity, &jwt).await?; | ||
} else { | ||
warn!("UH OH"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😨
}, | ||
can: SphereAction::Publish, | ||
}) | ||
.with_lifetime(120) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't this record expire in 2 minutes? We should have some constant, default lifetime duration somewhere
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SG, although worth noting that at this time we have no expiration of name records (at least, not one that we enforce).
Already filed #259 to discuss
config: DhtConfig, | ||
processor: DhtMessageProcessor, | ||
) -> Result<tokio::task::JoinHandle<Result<(), DhtError>>, DhtError> { | ||
// debug!("SPAWNING WITH VALIDATOR: {:?}", validator); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: comment
store: SphereDb<S>, | ||
dht_config: DHTConfig, | ||
dht_config: DhtConfig, | ||
validator: Option<V>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like a configurable/optional top-level validator (wanting a network of nodes to have relatively similar/consistent validators), but I don't have any other ideas in order to support this (and more pushing and pulling between static DhtConfig values and living instances. I'm glad you've spent some time in this module and the Dht*
counterparts for future discussions on some of the cleanup challenges ahead!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pasting my related comment from Discord:
I'm sympathetic to your position on configurable Validator. Ultimately, I don't think orb-ns will offer a configuration here. Nor would orb. And, it may prove to be an anti-pattern altogether.
And filed #268
@@ -12,7 +12,7 @@ use std::{convert::TryFrom, str, str::FromStr}; | |||
use ucan::{builder::UcanBuilder, crypto::KeyMaterial}; | |||
use ucan::{chain::ProofChain, crypto::did::DidParser, Ucan}; | |||
|
|||
/// An [NSRecord] is the internal representation of a mapping from a | |||
/// An [NsRecord] is the internal representation of a mapping from a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not ready to talk about how NsRecord
/NSRecord
is the only struct prefixed by NS
🙃
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😅
.build()? | ||
.sign() | ||
.await?; | ||
let _ = store.write_token(&delegation.encode()?).await?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kind of a bummer that store
is being passed around these utils only for this side-stepping-Ipfs hack here, now that store
is internal to the validator
@@ -22,17 +22,31 @@ where | |||
} | |||
} | |||
|
|||
impl<S> Clone for Validator<S> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this also implement Display like the AllowAllValidator?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, Display
is vestigial, I can remove that implementation
Alright folks, we're gonna land this one. |
This change pertains to #12 and started as an effort to close the loop on end-to-end name publishing and resolution. Unfortunately, one thing lead to another and the change came to embody a significant refactor of how we read from and write to spheres in general.
Publishing and resolving names
As of this change, we have implemented everything needed for a user to publish a version of their sphere to the Noosphere Name System, and for another user in the distance to resolve that published version by petname. We've also stubbed out the corresponding C FFI in preparation for #243.
Petname entries are added by a user to their own sphere. When the user sync's with their gateway, the gateway adopts the user's petname changes and kicks off a background job to resolve all the updated names from the Noosphere Name System. As those names are resolved, the gateway incorporates the resolved values into its address book. The next time a user sync's with the gateway after the resolutions are complete, the resolved values propagate back to their local device, and the sync strategy incorporates the new resolutions into the user's own address book.
Publishing happens on every push of the user's history to the sphere. It is technically optional to publish; it requires a specially crafted UCAN invocation, which is marked as optional in the body of the API request. But, we currently do it on every push anyway. When the user pushes, the gateway forwards the updated record to the Noosphere Name System in a background thread.
The
SphereContext
refactorThe rationale for the refactor is that previously we had only one domain where we were performing a lot of user-driven reads and writes:
SphereFs
. We had factored things such thatSphereFs
did internal accounting of pending mutation state to a sphere. This immediately became a problem when we moved to add reading and writing APIs for a different domain (petnames), becauseSphereFs
only dealt with the content part of a sphere.The new factoring discards
SphereFs
and replaces it with a series of traits that ship the same API, but that are implemented directly for containers such asArc<SphereContext<_, _>>
. So, where previously we used one construct as a handle to a sphere -SphereContext
- and a different one as a read/write API -SphereFs
- we now do everything withSphereContext
. New traits have been introduced to deal with petnames in a similar style to the traits that deal with content. And, all pending mutations are now "centrally" tracked by theSphereContext
itself, so there is no risk of forking history by writing to two different parts of the sphere at the same time.Fixes #246
Fixes #214
Fixes #215