Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide toolkit instead of porting to application services? #429

Open
bendk opened this issue Jun 23, 2024 · 0 comments
Open

Provide toolkit instead of porting to application services? #429

bendk opened this issue Jun 23, 2024 · 0 comments

Comments

@bendk
Copy link

bendk commented Jun 23, 2024

I tried to port the remote settings client to application services last week and ran into number of small issues when trying to expose it via UniFFI:

  • UniFFI use Arc<dyn Trait>, while remote-settings-client used Box<dyn Trait>
  • UniFFI makes a sharp distinction between data types and interface types, but the Record class is a bit of a hybrid between the two: sometimes users will want to treat it as a data type and read the attachment metadata, while sometimes they will want to fetch the actually data which requires a &mut.
  • UniFFI requires that types and futures are Send since the foreign language might end up passing them between threads, but this doesn't work with Client since it stores Box<dyn Trait> fields and the traits don't require a Send bound.

Once I reached this point I stopped and started to consider other options. All of these issues are solvable, but they give me the feeling that creating a remote settings client for Rust consumers is a different problem than creating a UniFFI component. I wouldn't want to force other Rust consumers to make their trait implementations Send just for UniFFI compatibility.

Instead of trying to get the client into application-services, what if you defined a low-level toolkit of sorts that our current client could use to implement the complicated parts? For example, you could define something like the following interface for verification:

pub trait Verification {
    fn verify_nist384p_chain(...);
    fn verify_sha256_hash(...);
}

fn certificate_url(collection_metadata: serde_json::Value) -> String { ... }

/// Get the root hash for a server.  `RemoteSettingsServer` is an enum with prod, stage, dev, etc. 
fn get_root_hash(server: RemoteSettingServer) -> &str { ... }

fn verify_collection(
  verifier: impl Verification,
  root_hash: &str,
  certificate_data: String,
  collection_metadata: serde_json::Value,
  records: Vec<serde_json::Value>,
  timestamp: u64) -> bool { ... }

fn verify_attachment(
  verifier: impl Verification,
  root_hash: &str,
  certificate_data: String,
  attachment_hash: String,
  attachment_data: &[u8]) -> bool { ... }

I don't think that's exactly right, but hopefully it paints a picture. The idea is that it's a low-level, non-async, API for doing verification that could be adopted by our existing client. The client would still need to do some work, but these functions would make it easy to implement and hard to mess up the implementation. One nice upside is that this works with both a sync and async client.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant