-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Migrates PrivateKey and PublicKey implementation to Rust #4347
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
Conversation
Binary size comparison➡️ aarch64-apple-ios: - 13.93 MB
+ 13.96 MB +40 KB➡️ aarch64-apple-ios-sim: - 13.93 MB
+ 13.97 MB +40 KB➡️ aarch64-linux-android: - 18.35 MB
+ 18.41 MB +70 KB➡️ armv7-linux-androideabi: - 15.36 MB
+ 15.41 MB +47 KB➡️ wasm32-unknown-emscripten: - 13.06 MB
+ 13.11 MB +53 KB |
satoshiotomakan
left a comment
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.
Please also add extensive tests to tw_keypair/src/zilliqa_schnorr/mod.rs. Take a look at other curve implementations, it's highly important to cover it well as it's critical section for us
| PublicKey PrivateKey::getPublicKey(TWPublicKeyType type) const { | ||
| auto* pubkey = Rust::tw_private_key_get_public_key_by_type(_impl.get(), static_cast<uint32_t>(type)); | ||
| if (pubkey == nullptr) { | ||
| return PublicKey(Data(), 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.
It's not good to return a PublicKey initialized with an empty data in case of an error, even if PublicKey(Data(), type) constructor throws an exception.
Please throw an exception directly from this function
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.
It's not good to return a PublicKey initialized with an empty data in case of an error, even if
PublicKey(Data(), type)constructor throws an exception. Please throw an exception directly from this function
For backward compatibility:
wallet-core/src/PrivateKey.cpp
Line 164 in 4b63b63
| PublicKey PrivateKey::getPublicKey(TWPublicKeyType type) const { |
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 it's legacy, and we should improve it. If either PrivateKey or PublicKey instances created, it means that they are valid, and can be used later in the code.
I really hope this line is never reached in prod
wallet-core/src/PrivateKey.cpp
Line 222 in 4b63b63
| return PublicKey(result, type); |
src/PrivateKey.cpp
Outdated
| // graphene adds 31 to the recovery id | ||
| result[0] += 31; |
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.
Let's move this logic to Rust
src/PublicKey.cpp
Outdated
| PublicKey::PublicKey(std::shared_ptr<TW::Rust::TWPublicKey> _impl) | ||
| : _impl(_impl) { | ||
| auto pubkeyData = Rust::tw_public_key_data(_impl.get()); | ||
| bytes = Data(pubkeyData.data, pubkeyData.data + pubkeyData.size); |
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 bytes filed should be replaced with bytes() method to avoid confusion as the pubkey bytes also stored in Rust's PublicKey
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
bytesfiled should be replaced withbytes()method to avoid confusion as the pubkey bytes also stored in Rust'sPublicKey
This is for consistency to avoid breaking the API further.
src/PublicKey.cpp
Outdated
| if (messageDigest.size() < PrivateKey::_size) { | ||
| throw std::invalid_argument("digest too short"); | ||
| } |
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.
Let's move all these conditions above to Rust
|
|
||
| // TODO uncomment when nist256p1 Rust implementation is enabled. | ||
| // EXPECT_EQ("00d134120000c40900000000000050c3000000000000fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a4d02e9001496f688657b95be51c11a87b51adfda4ab69e9cbb1457e9d1a61f9aafa798b6c7fbeae35639681d7df653c1087472616e736665726733def739225d0f93dd2aed457d7b1fd074ec31ff00024140bd2923854d7b84b97a107bb3cddf18c8e3dddd2f36b41a1f5f5b23366484daa2d78e3046e66dc020e1634e1612e9455d0c8acac2305ae0563293d39bfa9d3bec232103d9fd62df332403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac41406d638653597774ce45812ea2653250806b657b32b7c6ad3e027ddeba91e9a9dab44a2531dc2504589734ce4534c74b58bdc0f3457cd53267331ec5211b0a4e842321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486ac", rawTxHex); | ||
| EXPECT_EQ("00d134120000c40900000000000050c3000000000000fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a4d02e9001496f688657b95be51c11a87b51adfda4ab69e9cbb1457e9d1a61f9aafa798b6c7fbeae35639681d7df653c1087472616e736665726733def739225d0f93dd2aed457d7b1fd074ec31ff00024140bd2923854d7b84b97a107bb3cddf18c8e3dddd2f36b41a1f5f5b23366484daa2d78e3046e66dc020e1634e1612e9455d0c8acac2305ae0563293d39bfa9d3bec232103d9fd62df332403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac41406d638653597774ce45812ea2653250806b657b32b7c6ad3e027ddeba91e9a9dab44a2531dc2504589734ce4534c74b58bdc0f3457cd53267331ec5211b0a4e842321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486ac", rawTxHex); |
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.
Expected 00d134120000c40900000000000050c3000000000000fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a4d02e9001496f688657b95be51c11a87b51adfda4ab69e9cbb1457e9d1a61f9aafa798b6c7fbeae35639681d7df653c1087472616e736665726733def739225d0f93dd2aed457d7b1fd074ec31ff00024140bd2923854d7b84b97a107bb3cddf18c8e3dddd2f36b41a1f5f5b23366484daa2d78e3046e66dc020e1634e1612e9455d0c8acac2305ae0563293d39bfa9d3bec232103d9fd62df332403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac41406d638653597774ce45812ea2653250806b657b32b7c6ad3e027ddeba91e9a9dab44a2531dc2504589734ce4534c74b58bdc0f3457cd53267331ec5211b0a4e842321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486ac instead. Can you please check if all correct?
I could be wrong with the comment by a mistake, but still better to validate
…verifying APIs for zillqa
satoshiotomakan
left a comment
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.
A couple of changes please
| } | ||
|
|
||
| PublicKey::PublicKey(std::shared_ptr<TW::Rust::TWPublicKey> _impl) | ||
| : _impl(_impl) { |
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.
std::move(_impl)
| } | ||
| } | ||
|
|
||
| pub fn recover_from_signature( |
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.
secp256k1SignatureSize is the same as nist256p1 signature length.
For the reference, we have nist256p1::PublicKey::recover function, so I think we can expose it to FFI too
| type SigningMessage = Vec<u8>; | ||
| type Signature = Signature; | ||
|
|
||
| fn sign(&self, message: Self::SigningMessage) -> KeyPairResult<Self::Signature> { |
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.
No I mean a link to zilliqa_rs crate
src/PrivateKey.cpp
Outdated
| bytes = data; | ||
| _curve = curve; | ||
| auto* privkey = Rust::tw_private_key_create_with_data(data.data(), data.size(), static_cast<uint32_t>(curve)); | ||
| _impl = privkey ? Rust::wrapTWPrivateKey(privkey) : nullptr; |
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.
Not fixed yet. Let's throw an exception if privkey is null
src/PrivateKey.cpp
Outdated
| append(bytes, chainCode2); | ||
| _curve = curve; | ||
| auto* privkey = Rust::tw_private_key_create_with_data(bytes.data(), bytes.size(), static_cast<uint32_t>(curve)); | ||
| _impl = privkey ? Rust::wrapTWPrivateKey(privkey) : nullptr; |
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, let's throw an exception
| PublicKey PrivateKey::getPublicKey(TWPublicKeyType type) const { | ||
| auto* pubkey = Rust::tw_private_key_get_public_key_by_type(_impl.get(), static_cast<uint32_t>(type)); | ||
| if (pubkey == nullptr) { | ||
| return PublicKey(Data(), 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.
I think it's legacy, and we should improve it. If either PrivateKey or PublicKey instances created, it means that they are valid, and can be used later in the code.
I really hope this line is never reached in prod
wallet-core/src/PrivateKey.cpp
Line 222 in 4b63b63
| return PublicKey(result, type); |
src/PublicKey.cpp
Outdated
| auto* pubkey = Rust::tw_public_key_create_with_data(data.data(), data.size(), static_cast<uint32_t>(type)); | ||
| if (pubkey != nullptr) { | ||
| _impl = Rust::wrapTWPublicKey(pubkey); | ||
| Rust::CByteArrayWrapper pubkeyData = Rust::tw_public_key_data(_impl.get()); | ||
| bytes = pubkeyData.data; | ||
| } else { | ||
| _impl = nullptr; | ||
| bytes = data; |
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.
Please throw an exception if couldn't create the PublicKey
satoshiotomakan
left a comment
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.
One more important change
rust/tw_keypair/src/tw/private.rs
Outdated
| let bytes = if bytes.len() == Self::SIZE { | ||
| // For a regular private key, replicate it 6 times to match the Cardano extended format | ||
| let mut extended_bytes = Vec::with_capacity(Self::CARDANO_SIZE); | ||
| for _ in 0..6 { | ||
| extended_bytes.extend_from_slice(&bytes[Self::KEY_RANGE]); | ||
| } | ||
| extended_bytes | ||
| } else { | ||
| 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.
Fix as discussed DM
satoshiotomakan
left a comment
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.
LGTM!
| dummyKey, dummyKey, dummyKey, TWCurveED25519); | ||
| auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); | ||
| EXPECT_ANY_THROW(new AddressV3(publicKey)); | ||
| dummyKey, dummyKey, dummyKey, TWCurveED25519)); |
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.
Would this test pass if we replace TWCurveED25519ExtendedCardano?
| dummyKey, dummyKey, dummyKey, TWCurveED25519); | ||
| auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); | ||
| EXPECT_ANY_THROW(new AddressV2(publicKey)); | ||
| dummyKey, dummyKey, dummyKey, TWCurveED25519)); |
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 question
Description
This PR migrates the implementation in
PrivateKey.cppandPublicKey.cppto use Rust instead of TrezorCrypto.How to test
Existing tests continue to work
Types of changes
Refactor
Checklist
If you're adding a new blockchain