-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
feat(dpp): add a convenience method to get the public key data for a private key depending on the key type #2214
feat(dpp): add a convenience method to get the public key data for a private key depending on the key type #2214
Conversation
…te key depending on the key type
WalkthroughThe changes introduce a new public method Changes
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
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.
Actionable comments posted: 4
🧹 Outside diff range and nitpick comments (1)
packages/rs-dpp/src/identity/identity_public_key/key_type.rs (1)
254-258
: Clarify the unsupported operation in the error messageThe error message returned for
KeyType::BIP13_SCRIPT_HASH
could be more descriptive. Providing additional context can help users understand why the operation isn't supported.Consider updating the error message:
return Err(ProtocolError::NotSupported( - "Converting a private key to a script hash is not supported".to_string(), + "Conversion from private key to script hash for BIP13_SCRIPT_HASH key type is not supported".to_string(), ));This change offers clearer information about the limitation.
KeyType::ECDSA_SECP256K1 => { | ||
let secp = Secp256k1::new(); | ||
let secret_key = | ||
dashcore::secp256k1::SecretKey::from_slice(private_key_bytes.as_slice()) | ||
.map_err(|e| ProtocolError::Generic(e.to_string()))?; | ||
let private_key = dashcore::PrivateKey::new(secret_key, network); | ||
|
||
Ok(private_key.public_key(&secp).to_bytes()) | ||
} |
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.
🛠️ Refactor suggestion
Refactor to avoid code duplication in ECDSA key types
The match arms for KeyType::ECDSA_SECP256K1
and KeyType::ECDSA_HASH160
share similar code for handling ECDSA keys. Consider extracting the common logic into a helper function to enhance maintainability and reduce duplication.
Create a helper function:
fn get_ecdsa_private_key(
private_key_bytes: &[u8],
network: Network,
) -> Result<dashcore::PrivateKey, ProtocolError> {
let secp = Secp256k1::new();
let secret_key = dashcore::secp256k1::SecretKey::from_slice(private_key_bytes)
.map_err(|e| ProtocolError::Generic(e.to_string()))?;
Ok(dashcore::PrivateKey::new(secret_key, network))
}
Update the match arms:
For KeyType::ECDSA_SECP256K1
:
let private_key = get_ecdsa_private_key(private_key_bytes, network)?;
let secp = Secp256k1::new();
Ok(private_key.public_key(&secp).to_bytes())
For KeyType::ECDSA_HASH160
:
let private_key = get_ecdsa_private_key(private_key_bytes, network)?;
let secp = Secp256k1::new();
Ok(ripemd160_sha256(private_key.public_key(&secp).to_bytes().as_slice()).to_vec())
This refactor eliminates redundancy and streamlines the code.
pub fn public_key_data_from_private_key_data( | ||
&self, | ||
private_key_bytes: Vec<u8>, | ||
network: Network, | ||
) -> Result<Vec<u8>, ProtocolError> { |
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.
🛠️ Refactor suggestion
Consider using a slice for the private_key_bytes
parameter
Currently, the method takes ownership of private_key_bytes
as a Vec<u8>
, but it doesn't modify it internally. Using a slice &[u8]
would avoid unnecessary cloning and improve efficiency.
Apply this diff to change the parameter type:
-pub fn public_key_data_from_private_key_data(
- &self,
- private_key_bytes: Vec<u8>,
- network: Network,
-) -> Result<Vec<u8>, ProtocolError> {
+pub fn public_key_data_from_private_key_data(
+ &self,
+ private_key_bytes: &[u8],
+ network: Network,
+) -> Result<Vec<u8>, ProtocolError> {
Adjust subsequent code by removing .as_slice()
calls since private_key_bytes
is already a slice.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
pub fn public_key_data_from_private_key_data( | |
&self, | |
private_key_bytes: Vec<u8>, | |
network: Network, | |
) -> Result<Vec<u8>, ProtocolError> { | |
pub fn public_key_data_from_private_key_data( | |
&self, | |
private_key_bytes: &[u8], | |
network: Network, | |
) -> Result<Vec<u8>, ProtocolError> { |
.g1_element() | ||
.expect("expected to get a public key from a bls private key") | ||
.to_bytes() | ||
.to_vec(); |
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.
Handle potential errors without using expect
Using .expect()
can lead to panics if g1_element()
returns an error. Instead, propagate the error using ?
to maintain robustness.
Apply this diff to handle the error properly:
-let public_key_bytes = private_key
- .g1_element()
- .expect("expected to get a public key from a bls private key")
- .to_bytes()
- .to_vec();
+let public_key_bytes = private_key
+ .g1_element()?
+ .to_bytes()
+ .to_vec();
Ensure that g1_element()
returns Result
and any errors are appropriately mapped to ProtocolError
.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
.g1_element() | |
.expect("expected to get a public key from a bls private key") | |
.to_bytes() | |
.to_vec(); | |
.g1_element()? | |
.to_bytes() | |
.to_vec(); |
let key_pair = ed25519_dalek::SigningKey::from_bytes( | ||
&private_key_bytes.as_slice().try_into().map_err(|_| { | ||
ProtocolError::InvalidVectorSizeError(InvalidVectorSizeError::new( | ||
32, | ||
private_key_bytes.len(), | ||
)) | ||
})?, | ||
); |
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.
💡 Codebase verification
Missing proper error handling for SigningKey::from_bytes
- In
packages/rs-dpp/src/identity/identity_public_key/key_type.rs
lines 244-251, theSigningKey::from_bytes
call does not handle potential errors using the?
operator or.map_err
.
🔗 Analysis chain
Ensure correct error handling for invalid key sizes
In the KeyType::EDDSA_25519_HASH160
arm, the conversion of private_key_bytes
to a fixed-size array could fail if the length is incorrect. Verify that the error handling accurately reflects the issue.
Run the following script to check if the from_bytes
method handles varying key sizes correctly:
This script simulates keys of different sizes to ensure that the error handling works as expected.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify that the ED25519 key handling correctly detects invalid key sizes.
# Test: Attempt to create a SigningKey with invalid key sizes.
# Expect: The method should return an error for sizes other than 32 bytes.
for size in 31 32 33; do
echo "Testing with key size: $size bytes"
python3 -c "
import os
key_bytes = os.urandom($size)
try:
from ed25519_dalek import SigningKey
SigningKey.from_bytes(key_bytes)
print('Key accepted')
except Exception as e:
print(f'Error: {e}')
"
done
Length of output: 1181
Script:
#!/bin/bash
# Description: Verify that all usages of SigningKey::from_bytes have proper error handling.
# Search for all instances of SigningKey::from_bytes in the codebase
ast-grep --lang rust --pattern $'SigningKey::from_bytes($_)' > signing_key_usages.txt
# Check each usage for error handling
echo "Verifying error handling for each SigningKey::from_bytes usage:"
while IFS= read -r line; do
# Extract file and line number
file=$(echo "$line" | awk '{print $1}')
code=$(echo "$line" | cut -d' ' -f2-)
# Check if error handling is present using regex
if echo "$code" | grep -qE '\?|\.map_err'; then
echo "[$file]: Proper error handling found."
else
echo "[$file]: Missing error handling for SigningKey::from_bytes."
fi
done < signing_key_usages.txt
Length of output: 220
Script:
#!/bin/bash
# Description: Verify that all usages of SigningKey::from_bytes in key_type.rs have proper error handling.
# Define the file to inspect
FILE_PATH="packages/rs-dpp/src/identity/identity_public_key/key_type.rs"
# Check if the file exists
if [ ! -f "$FILE_PATH" ]; then
echo "Error: File $FILE_PATH does not exist."
exit 1
fi
# Search for all instances of SigningKey::from_bytes in the specified file
echo "Searching for SigningKey::from_bytes usages in $FILE_PATH..."
matches=$(rg 'SigningKey::from_bytes' "$FILE_PATH")
# Check if any matches were found
if [ -z "$matches" ]; then
echo "No instances of SigningKey::from_bytes found in $FILE_PATH."
exit 0
fi
# Iterate over each match and verify error handling
echo "Verifying error handling for each SigningKey::from_bytes usage:"
echo "$matches" | while IFS= read -r line; do
# Extract the line number and code
line_number=$(echo "$line" | cut -d':' -f2)
code=$(echo "$line" | cut -d':' -f3-)
# Extract surrounding lines for context (2 lines before and after)
context=$(rg -C 2 "SigningKey::from_bytes" "$FILE_PATH" | grep -A2 "SigningKey::from_bytes" | grep -v "SigningKey::from_bytes")
# Check for error handling using '?' or '.map_err'
if echo "$code" | grep -qE '\?'; then
echo "Line $line_number: Proper error handling found using '?'."
elif echo "$code" | grep -qE '\.map_err'; then
echo "Line $line_number: Proper error handling found using '.map_err'."
else
echo "Line $line_number: **Missing** proper error handling for SigningKey::from_bytes."
fi
done
Length of output: 1731
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.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
- packages/rs-dpp/src/identity/identity_public_key/key_type.rs (2 hunks)
🧰 Additional context used
🔇 Additional comments (7)
packages/rs-dpp/src/identity/identity_public_key/key_type.rs (7)
17-17
: Import change looks goodThe addition of
InvalidVectorSizeError
to the import statement is appropriate, as it's used in the newpublic_key_data_from_private_key_data
method. This change enhances code clarity by explicitly importing the error type.
205-209
: Method signature and structure look goodThe method signature is well-defined, taking a slice of private key bytes and a network parameter. The return type
Result<Vec<u8>, ProtocolError>
is appropriate for this operation, allowing for error propagation.
211-218
: ECDSA_SECP256K1 implementation is correctThe implementation for ECDSA_SECP256K1 keys is well-structured and includes proper error handling. It correctly creates a
SecretKey
from the input bytes, constructs aPrivateKey
with the given network, and returns the corresponding public key bytes.
237-244
: ECDSA_HASH160 implementation is correctThe implementation for ECDSA_HASH160 keys is well-structured and includes proper error handling. It correctly creates a
SecretKey
from the input bytes, constructs aPrivateKey
with the given network, and applies theripemd160_sha256
hash function to the public key bytes.
245-262
: EDDSA_25519_HASH160 implementation is correct and well-handledThe implementation for EDDSA_25519_HASH160 keys is well-structured and includes proper error handling:
- The use of the
ed25519-dalek
feature flag is appropriate for conditional compilation.- The error handling for invalid input size using
InvalidVectorSizeError
is well-implemented.- The application of
ripemd160_sha256
to the verifying key bytes is correct.- The error handling for the
not(feature = "ed25519-dalek")
case is good, returning aNotSupported
error.
263-267
: BIP13_SCRIPT_HASH handling is appropriateThe implementation for BIP13_SCRIPT_HASH keys correctly returns a
NotSupported
error. This is an appropriate approach for a key type that doesn't support conversion from private to public key data.
205-269
: Overall implementation ofpublic_key_data_from_private_key_data
is well-structured and comprehensiveThe new method
public_key_data_from_private_key_data
is a valuable addition to theKeyType
enum. It provides a unified way to derive public key data from private key data for various key types. Key points:
- The method signature is well-defined, taking appropriate parameters and returning a
Result
type.- Implementation for each key type is handled separately, allowing for type-specific logic.
- Error handling is generally well-implemented, with appropriate use of
ProtocolError
variants.- Feature flags are used effectively to handle conditional compilation for BLS and EdDSA implementations.
- Unsupported key types (BIP13_SCRIPT_HASH) are handled explicitly.
The only suggested improvement is in the error handling for the BLS12_381 case, where
expect()
could be replaced withmap_err()
for more consistent error propagation.Overall, this implementation enhances the functionality of the
KeyType
enum and provides a robust method for deriving public key data.
KeyType::BLS12_381 => { | ||
#[cfg(feature = "bls-signatures")] | ||
{ | ||
let private_key = | ||
bls_signatures::PrivateKey::from_bytes(private_key_bytes, false) | ||
.map_err(|e| ProtocolError::Generic(e.to_string()))?; | ||
let public_key_bytes = private_key | ||
.g1_element() | ||
.expect("expected to get a public key from a bls private key") | ||
.to_bytes() | ||
.to_vec(); | ||
Ok(public_key_bytes) | ||
} | ||
#[cfg(not(feature = "bls-signatures"))] | ||
return Err(ProtocolError::NotSupported( | ||
"Converting a private key to a bls public key is not supported without the bls-signatures feature".to_string(), | ||
)); | ||
} |
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.
Improve error handling in BLS12_381 implementation
The BLS12_381 implementation is generally correct, but there's room for improvement in error handling:
-
The use of
expect()
when callingg1_element()
could lead to a panic. Consider usingmap_err()
to convert any error to aProtocolError
instead. -
The error handling for the
not(feature = "bls-signatures")
case is good, returning aNotSupported
error.
Consider replacing the expect()
call with proper error handling:
- let public_key_bytes = private_key
- .g1_element()
- .expect("expected to get a public key from a bls private key")
- .to_bytes()
- .to_vec();
+ let public_key_bytes = private_key
+ .g1_element()
+ .map_err(|e| ProtocolError::Generic(e.to_string()))?
+ .to_bytes()
+ .to_vec();
This change will propagate any errors from g1_element()
as a ProtocolError
instead of panicking.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
KeyType::BLS12_381 => { | |
#[cfg(feature = "bls-signatures")] | |
{ | |
let private_key = | |
bls_signatures::PrivateKey::from_bytes(private_key_bytes, false) | |
.map_err(|e| ProtocolError::Generic(e.to_string()))?; | |
let public_key_bytes = private_key | |
.g1_element() | |
.expect("expected to get a public key from a bls private key") | |
.to_bytes() | |
.to_vec(); | |
Ok(public_key_bytes) | |
} | |
#[cfg(not(feature = "bls-signatures"))] | |
return Err(ProtocolError::NotSupported( | |
"Converting a private key to a bls public key is not supported without the bls-signatures feature".to_string(), | |
)); | |
} | |
KeyType::BLS12_381 => { | |
#[cfg(feature = "bls-signatures")] | |
{ | |
let private_key = | |
bls_signatures::PrivateKey::from_bytes(private_key_bytes, false) | |
.map_err(|e| ProtocolError::Generic(e.to_string()))?; | |
let public_key_bytes = private_key | |
.g1_element() | |
.map_err(|e| ProtocolError::Generic(e.to_string()))? | |
.to_bytes() | |
.to_vec(); | |
Ok(public_key_bytes) | |
} | |
#[cfg(not(feature = "bls-signatures"))] | |
return Err(ProtocolError::NotSupported( | |
"Converting a private key to a bls public key is not supported without the bls-signatures feature".to_string(), | |
)); | |
} |
Issue being fixed or feature implemented
It wasn't convenient to get public key data when we knew the private key data and the key type.
What was done?
Added a convenience method to get the public key data for a private key depending on the key type
How Has This Been Tested?
Breaking Changes
Not breaking
Checklist:
For repository code-owners and collaborators only
Summary by CodeRabbit