Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Make into_sub_account check the seed length and add hash_into_sub_account function #8672

Closed
wants to merge 2 commits into from

Conversation

hirschenberger
Copy link
Contributor

@hirschenberger hirschenberger commented Apr 27, 2021

fixes #8564

TODO:

  • Shouldn't into_sub_account be renamed to try_into_sub_account to be consistent with the rest of the trait?
  • Shouldn't we also return an Option from hash_into_sub_account to return None if no part of the seed was included in the returned AccountId?
  • I find interface strange because it relies on the user knowing how much space is left in the underlying datastructure to create a subaccount and in the worst case nothing additional is added and the same as the original key is returned without the user noticing it.
  • Check and use new functions in Substrate codebase
  • Add Polkadot companion

@@ -1118,7 +1120,7 @@ impl<'a> codec::Input for TrailingZeroInput<'a> {
/// This type can be converted into and possibly from an AccountId (which itself is generic).
pub trait AccountIdConversion<AccountId>: Sized {
/// Convert into an account ID. This is infallible.
fn into_account(&self) -> AccountId { self.into_sub_account(&()) }
fn into_account(&self) -> AccountId { self.into_sub_account(&()).unwrap() }
Copy link
Contributor

Choose a reason for hiding this comment

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

unwrap here seems wrong.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, the PR is just a WIP, with the intention to discuss the implementation with @shawntabrizi . I added some of my discussion points in the description.

The unwrap is safe here, bc. &() adds no additional size to the encoding and will never trigger a None.

Copy link
Member

Choose a reason for hiding this comment

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

If unwrap is safe, then you should put .expect("proof why it is safe. q.e.d.")

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think it is safe, in case size of T is less than size of Self then it always returns none.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, you're right. I assumed that in the previous implementation it always fits, but that was also not true. I think there need to be more tests especially for the corner cases. Or a new interface as you suggested in the ticket.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe the new MaxEncodedLen can be used here.

@@ -1134,7 +1136,11 @@ pub trait AccountIdConversion<AccountId>: Sized {
/// - `self.into_sub_account(0u32)`
/// - `self.into_sub_account(vec![0u8; 0])`
/// - `self.into_account()`
Copy link
Contributor

Choose a reason for hiding this comment

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

doc is outdated

fn into_sub_account<S: Encode>(&self, sub: S) -> T {
(Id::TYPE_ID, self, sub).using_encoded(|b|
fn into_sub_account<S: Encode>(&self, sub: S) -> Option<T> {
if size_of::<T>() < size_of_val(&Id::TYPE_ID) + size_of::<Self>() + sub.size_hint() {
Copy link
Contributor

Choose a reason for hiding this comment

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

it is not sure that encoded size of self is size_of::<Self>(). some type have different encoded size that their type.

also size_hint is a hint, it is not exact at all.

Copy link
Contributor Author

@hirschenberger hirschenberger Apr 28, 2021

Choose a reason for hiding this comment

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

Good point, in the tests Idid it worked as expected but I'll check again with other types.

None
} else {
(Id::TYPE_ID, self, sub).using_encoded(|b|
T::decode(&mut TrailingZeroInput(b))
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe instead of using TrailingZeroInput you can use a specific struct which checks that all the bytes of b were read.

where
H::Output: Encode
{
(Id::TYPE_ID, self, <H as Hash>::hash(sub.as_ref())).using_encoded(|b|
T::decode(&mut TrailingZeroInput(b))
).unwrap_or_default()
Copy link
Contributor

Choose a reason for hiding this comment

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

I know it is not from this PR but the unwrap_or_default here is also suspicious to me.
if T doesn't decode any from kind of byte slice then this will fail and always return default without anybody noticing.

I feel we can bring a better trait in order to generate an account id safely.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I also noticed that as a sloppy implementation.

@stale
Copy link

stale bot commented Jul 7, 2021

Hey, is anyone still working on this? Due to the inactivity this issue has been automatically marked as stale. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the A5-stale Pull request did not receive any updates in a long time. No review needed at this stage. Close it. label Jul 7, 2021
@hirschenberger
Copy link
Contributor Author

I'm unsure how to continue here. There seems to be no consensus on a good design. Maybe we can discuss again?

@stale stale bot removed the A5-stale Pull request did not receive any updates in a long time. No review needed at this stage. Close it. label Jul 7, 2021
@gui1117
Copy link
Contributor

gui1117 commented Jul 14, 2021

I still think we should come up with a design which doesn't give a Result and fail on invalid configuration. Instead we should bound what is necessary on frame_system and have methods which always succeed

I gave some thought there #8564 (comment)

@stale
Copy link

stale bot commented Aug 13, 2021

Hey, is anyone still working on this? Due to the inactivity this issue has been automatically marked as stale. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the A5-stale Pull request did not receive any updates in a long time. No review needed at this stage. Close it. label Aug 13, 2021
@stale stale bot closed this Aug 27, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A5-stale Pull request did not receive any updates in a long time. No review needed at this stage. Close it.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

into_sub_account should check seed length
3 participants