-
Notifications
You must be signed in to change notification settings - Fork 0
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
Feat/multidid #1
base: main
Are you sure you want to change the base?
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 ? |
Cargo.lock
Outdated
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.
For libraries, you usually omit the lock files.
Cargo.toml
Outdated
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.
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.
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
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.
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.
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
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.
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.
multidid/src/lib.rs
Outdated
|
||
impl Multidid { | ||
|
||
pub fn new(code: u32, id: Vec<u8>, url: Vec<u8>) -> Result<Self, &'static str> { |
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.
Rather than static str, you can use thiserror or anyhow.
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.
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.
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
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 { |
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.
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.
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"); |
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.
Make this more verbose by saying "PKH Method is not implemented"
multidid/src/lib.rs
Outdated
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.
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()
multidid/src/lib.rs
Outdated
} | ||
|
||
// return bs58btc_str only | ||
pub fn to_multibase(&self) -> Result<String, &'static str> { |
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.
How about a generic to_multibase
that takes in a base as an argument?
multidid/src/lib.rs
Outdated
return Multidid::from_bytes(bytes); | ||
} | ||
|
||
pub fn from_string(did: &str) -> Result<Multidid, &'static str> { |
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.
Instead of having this as a a function, implement the trait FromStr
.
It could go either way. Placing it in the |
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.
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.
multidid/Cargo.lock
Outdated
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.
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.
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.
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> { |
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.
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()) |
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.
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.
multidid/src/lib.rs
Outdated
]); | ||
|
||
let val = *km_codes_len.get(code).ok_or("Key not supported").unwrap(); | ||
return Ok(val) |
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, the last expression of a function is the return value so this line can be simply Ok(val)
, no need for the return
keyword.
multidid/src/lib.rs
Outdated
|
||
impl Multidid { | ||
|
||
pub fn new(code: u32, id: Vec<u8>, url: Vec<u8>) -> Result<Self, &'static str> { |
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.
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); | ||
} | ||
} |
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.
These tests are great!
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