Skip to content

Feat/multidid#1

Open
zachferland wants to merge 7 commits into
mainfrom
feat/multidid
Open

Feat/multidid#1
zachferland wants to merge 7 commits into
mainfrom
feat/multidid

Conversation

@zachferland

Copy link
Copy Markdown
Contributor

First go at writing anything in rust, timeboxed so a few todos, and need think thru a few parts, relied a lot on compiler feedback to just get running.

Based on js implementation here - https://github.com/ceramicnetwork/js-did/tree/main/packages/multidid

@oed

oed commented Apr 24, 2023

Copy link
Copy Markdown

Why would this not go into rust-ceramic? Seems unnecessary to have multiple repos for now?

@zachferland

Copy link
Copy Markdown
Contributor Author

yeah know we mentioned that before, just wasnt a lot of crates in there, so didnt seem to make a lot of sense, and doesnt share a lots of dependencies/build, but will be easy to move if needed

@oed

oed commented Apr 24, 2023

Copy link
Copy Markdown

What's the value of keeping it separate? Seems like it will be harder to manage? We will need to use this in Ceramic regardless

Thoughts @nathanielc ?

Comment thread Cargo.lock Outdated

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For libraries, you usually omit the lock files.

Comment thread Cargo.toml

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there's not multiple crates in a repo, you don't need to use workspace, and instead just a top level Cargo.toml with files under source.

Comment thread multidid/src/lib.rs
/**
* Multicodec Codes https://github.com/multiformats/multicodec/blob/master/table.csv
*/
const MULTIDID_CODEC: u32 = 0xd1d; // did

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a group of codecs, rust approach is usually to put them in an enum. That way they can be strongly typed, and easily shared by making the enum public https://github.com/3box/rust-ceramic/pull/3/files#diff-ecd9c86aee006d0568451cc27f6aba8ae777dee68cc03920d480ac80e04e5ac2R14

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example see how mulitbase does it here https://docs.rs/multibase/latest/multibase/enum.Base.html

Notice the from_code and code methods that allow conversion to an integer code.

A peak behind the scenes show they use a macro to implement the methods on each variant of the enum https://docs.rs/multibase/latest/src/multibase/base.rs.html While its a bit of an advanced technique its a good practice in this case and reads well.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tried doing this as enum now, will probably try to do as macro after, but havent gotten there yet, very verbose this way but nice to be properly typed

the did key codecs are both top level method (did:?) determinants, and method specific (did:key:?) determinants, making cross boundaries a bit, all other methods wont do this, didnt know if there was better way represent as enum here where did:key codecs need to be represented at top level and as subset

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If for some reason enums don't work, you can also create a stronger type.

pub type Codec = u32

then

const MULTIDID_CODEC: Codec = 0xd1d;

That way you can scope to only codec types, and not any u32.

Keep trying with enums though. If you still can't get it, let me know.

Comment thread multidid/src/lib.rs Outdated

impl Multidid {

pub fn new(code: u32, id: Vec<u8>, url: Vec<u8>) -> Result<Self, &'static str> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than static str, you can use thiserror or anyhow.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Danny and I are on the same page :)

Also in this case creating a Multidid cannot error so you can simply return Self instead of wrapping it in a Result type.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove a few instances where result not needed

used anyhow for now, will probably type errors in the future

Comment thread multidid/src/lib.rs Outdated
let method_id_offset = method_code_offset + &self.method_code.required_space();

let method_id_len;
if &self.method_code == &ANY_METHOD_CODE {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than unbound variables, you can use assigment from the block

let method_id_len = if &self.method_code = &ANY_METHOD_CODE {
  0
} else { ... }

This also works with early returns from different cases.

Comment thread multidid/src/lib.rs Outdated
if &method_code == &ANY_METHOD_CODE {
method_id_len = 0;
} else if &method_code == &PKH_METHOD_CODE {
return Err("Not implemented");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make this more verbose by saying "PKH Method is not implemented"

Comment thread multidid/src/lib.rs Outdated
return Err("No matching did method code found")
}

let mut method_id = vec![0;method_id_len as usize];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these can be simplifed with

let method_id = &bytes[method_id_offsert..url_len_offset].to_vec()

Comment thread multidid/src/lib.rs Outdated
}

// return bs58btc_str only
pub fn to_multibase(&self) -> Result<String, &'static str> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about a generic to_multibase that takes in a base as an argument?

Comment thread multidid/src/lib.rs Outdated
return Multidid::from_bytes(bytes);
}

pub fn from_string(did: &str) -> Result<Multidid, &'static str> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of having this as a a function, implement the trait FromStr.

@nathanielc

Copy link
Copy Markdown

It could go either way. Placing it in the rust-ceramic repo would be easiest for now as it means we can leverage the same CI jobs etc and not have to manage as many repos. However its also not too much effort to copy/paste the CI jobs from rust-ceramic or rust-dag-jose repos

@nathanielc nathanielc left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks great. Generally improving the error handling with anyhow or thiserror would be good.

Generally speaking I prefer thiserror for libraries as it allows for clear communication about the ways in which the library can error. However as a first pass using anyhow error is good enough if there are not clear use cases for differentiating different kinds of errors.

Comment thread multidid/Cargo.lock Outdated

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, do not commit the Cargo.lock file for libraries.

Comment thread multidid/src/lib.rs
/**
* Multicodec Codes https://github.com/multiformats/multicodec/blob/master/table.csv
*/
const MULTIDID_CODEC: u32 = 0xd1d; // did

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example see how mulitbase does it here https://docs.rs/multibase/latest/multibase/enum.Base.html

Notice the from_code and code methods that allow conversion to an integer code.

A peak behind the scenes show they use a macro to implement the methods on each variant of the enum https://docs.rs/multibase/latest/src/multibase/base.rs.html While its a bit of an advanced technique its a good practice in this case and reads well.

Comment thread multidid/src/lib.rs Outdated
const RSA_CODE: u32 = 0x1205; // rsa-pub
const KEY_CODES: [u32; 8] = [SECP256K1_CODE, BLS12_381_G2_CODE, X25519_CODE, ED25519_CODE, P256_CODE, P384_CODE, P521_CODE, RSA_CODE];

fn key_method_code_len(code: &u32, key_prefix: &[u8]) -> Result<u16, &'static str> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would look into the anyhow crate that defines a very generic error type. It makes it easy to return errors from functions without having to use static strings as error messages or build your own type.

As an aside if you do need to build your own error type I typically use thiserror to do so.

Comment thread multidid/src/lib.rs Outdated

fn key_method_code_len(code: &u32, key_prefix: &[u8]) -> Result<u16, &'static str> {
if code == &RSA_CODE {
return Ok(*rsa_code_len(key_prefix).unwrap())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you change the return of this function to use the anyhow::Result error type you can then use the ? syntax to check for an error and return it instead of using .unwrap().

This code would then become:

 return Ok(*rsa_code_len(key_prefix)?)

The difference is now an error is returned from the function instead of panicing the current Rust thread.

Comment thread multidid/src/lib.rs Outdated
]);

let val = *km_codes_len.get(code).ok_or("Key not supported").unwrap();
return Ok(val)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, the last expression of a function is the return value so this line can be simply Ok(val), no need for the return keyword.

Comment thread multidid/src/lib.rs Outdated

impl Multidid {

pub fn new(code: u32, id: Vec<u8>, url: Vec<u8>) -> Result<Self, &'static str> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Danny and I are on the same page :)

Also in this case creating a Multidid cannot error so you can simply return Self instead of wrapping it in a Result type.

Comment thread multidid/src/lib.rs
let mdid = Multidid::from_string(did_str).unwrap();
assert_eq!(mdid.to_string().unwrap(), did_str);
}
} No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests are great!

@oed

oed commented Apr 24, 2023

Copy link
Copy Markdown

Also fwiw, I think it's unlikely that we will implement all the functionality that we have in js-did because spruce has a bunch of rust DID stuff already.

@zachferland

Copy link
Copy Markdown
Contributor Author

thanks @dbcfd and @nathanielc for reviewing! going to go through these shortly, and covered some during rust hours

@zachferland

Copy link
Copy Markdown
Contributor Author

Covered almost all comments, all made sense, some parts I will revisit in future when time and/or get more familiar with rust.

Aim to merge/close for now, unless any standout issues

@zachferland zachferland requested review from dbcfd and nathanielc May 2, 2023 21:47
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

Successfully merging this pull request may close these issues.

4 participants