Feat/multidid#1
Conversation
|
Why would this not go into rust-ceramic? Seems unnecessary to have multiple repos for now? |
|
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 |
|
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 ? |
There was a problem hiding this comment.
For libraries, you usually omit the lock files.
There was a problem hiding this comment.
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.
| /** | ||
| * Multicodec Codes https://github.com/multiformats/multicodec/blob/master/table.csv | ||
| */ | ||
| const MULTIDID_CODEC: u32 = 0xd1d; // did |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
|
|
||
| impl Multidid { | ||
|
|
||
| pub fn new(code: u32, id: Vec<u8>, url: Vec<u8>) -> Result<Self, &'static str> { |
There was a problem hiding this comment.
Rather than static str, you can use thiserror or anyhow.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
remove a few instances where result not needed
used anyhow for now, will probably type errors in the future
| let method_id_offset = method_code_offset + &self.method_code.required_space(); | ||
|
|
||
| let method_id_len; | ||
| if &self.method_code == &ANY_METHOD_CODE { |
There was a problem hiding this comment.
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.
| if &method_code == &ANY_METHOD_CODE { | ||
| method_id_len = 0; | ||
| } else if &method_code == &PKH_METHOD_CODE { | ||
| return Err("Not implemented"); |
There was a problem hiding this comment.
Make this more verbose by saying "PKH Method is not implemented"
| return Err("No matching did method code found") | ||
| } | ||
|
|
||
| let mut method_id = vec![0;method_id_len as usize]; |
There was a problem hiding this comment.
I think these can be simplifed with
let method_id = &bytes[method_id_offsert..url_len_offset].to_vec()
| } | ||
|
|
||
| // return bs58btc_str only | ||
| pub fn to_multibase(&self) -> Result<String, &'static str> { |
There was a problem hiding this comment.
How about a generic to_multibase that takes in a base as an argument?
| return Multidid::from_bytes(bytes); | ||
| } | ||
|
|
||
| pub fn from_string(did: &str) -> Result<Multidid, &'static str> { |
There was a problem hiding this comment.
Instead of having this as a a function, implement the trait FromStr.
|
It could go either way. Placing it in the |
nathanielc
left a comment
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Same here, do not commit the Cargo.lock file for libraries.
| /** | ||
| * Multicodec Codes https://github.com/multiformats/multicodec/blob/master/table.csv | ||
| */ | ||
| const MULTIDID_CODEC: u32 = 0xd1d; // did |
There was a problem hiding this comment.
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.
| 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> { |
There was a problem hiding this comment.
|
|
||
| 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()) |
There was a problem hiding this comment.
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.
| ]); | ||
|
|
||
| let val = *km_codes_len.get(code).ok_or("Key not supported").unwrap(); | ||
| return Ok(val) |
There was a problem hiding this comment.
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.
|
|
||
| impl Multidid { | ||
|
|
||
| pub fn new(code: u32, id: Vec<u8>, url: Vec<u8>) -> Result<Self, &'static str> { |
There was a problem hiding this comment.
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.
| let mdid = Multidid::from_string(did_str).unwrap(); | ||
| assert_eq!(mdid.to_string().unwrap(), did_str); | ||
| } | ||
| } No newline at end of file |
|
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. |
|
thanks @dbcfd and @nathanielc for reviewing! going to go through these shortly, and covered some during rust hours |
|
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 |
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