Skip to content

Add account delegation preparation #3076

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

Merged
merged 80 commits into from
May 20, 2025
Merged

Conversation

LXIF
Copy link
Contributor

@LXIF LXIF commented May 15, 2025

Motivation

In order to be able to authenticate with accounts, we need to first prepare the delegation.

Changes

Add necessary functions - calculate_account_delegation seed (which resolves to a regular anchor number based seed for default accounts and former default accounts), prepare_account_delegation which closely mirrors prepare_delegation but uses the different seed and checks whether the anchor has access to the account, as well as the prepare_account_delegation endpoint in main.rs which checks auth, records activity and returns the delegation, wrapped in a Result for frontend convenience. Changing legacy get_delegation method to also use new functions. Also, related structs/methods etc.

Tests

No additional tests at this time, created Jira tickets for the integration tests. However, since legacy endpoint uses new functions, there is also some amount of test coverage.

LXIF added 30 commits May 13, 2025 11:15
@LXIF LXIF requested review from sea-snake and lmuntaner May 19, 2025 13:39
Copy link
Collaborator

@lmuntaner lmuntaner left a comment

Choose a reason for hiding this comment

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

Looking good!

// this should only happen if a named account was created, and then both it and the
// default account references were moved/deleted.
if acc_ref_vec.is_empty() {
return Some(Account::new(
Copy link
Collaborator

Choose a reason for hiding this comment

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

If they were moved or deleted, shouldn't we return 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.

Normally yes, but we do have to guarantee that a user can always log in. If we don't return a synthetic default account here, hypothetically a user could have hit the maximum after deleting a renamed default account. Maybe we should talk about this more though, basically transferring a default account and also having a synthetic default account here means the original identity retains access to the transferred default account.

In any case, this should not happen for now because we cannot currently move or delete accounts.

Copy link
Collaborator

Choose a reason for hiding this comment

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

That makes sense.

Let's add it in the comment as well. It's not clear at first sight why we should return the synthetic account.

I agree we should think this more. We should have a WARNING in the comment, to review this at some point and the edge cases we have with the current code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Adding this.

@@ -735,21 +735,21 @@ impl<M: Memory + Clone> Storage<M> {
.map(|list| list.into())
}

pub fn has_account_reference(
fn has_account_reference(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we rename this method to find_account_reference? It returns an AccountReference while the name has_... indicates a boolean.

I probably wrote this method, but I think it's confusing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done ✅

Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we add two more tests for read_account for the two new use cases?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done ✅

@LXIF LXIF requested a review from lmuntaner May 20, 2025 12:23
Copy link
Collaborator

@lmuntaner lmuntaner left a comment

Choose a reason for hiding this comment

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

Looking good thanks!

Already approving, but please extend the comments. There are some edge cases that we should solve sooner rather than later.


if stored_accounts >= MAX_ANCHOR_ACCOUNTS as u64 {
return Err(UpdateAccountError::AccountLimitReached);
storage_borrow_mut(|storage| {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I realized now, but waas this necessary for the prepare delegation?

No need to do it in another PR, just trying to understand the changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You and @sea-snake had asked to put anchor_has_account functionality into read_account, so I removed it here. Not sure why storage_borrow_mut got moved, moving it back now (no need to borrow storage if we're not using it).

@@ -150,6 +119,21 @@ fn calculate_seed(anchor_number: AnchorNumber, frontend: &FrontendHostname) -> H
hash_bytes(blob)
}

pub fn calculate_account_seed(account_number: AccountNumber) -> Hash {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we add a comment on why we don't need the frontend nor the anchor number?

It's not obvious unless you know everything about the accounts.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done ✅

timestamp,
}| (user_key, timestamp),
)
.unwrap_or_else(|err| trap(&format!("{:?}", err)))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are we planning on use the AccountDelegationError returned by prepare_account_delegation here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In the spirit of backwards compatibility, I have not updated legacy endpoints. We can do that, but would also need to update the frontend.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I guess that's fine for now. I was wondering whether you discussed it already.

}
None => {
// If this is an added account, we derive from the account number.
delegation::calculate_account_seed(self.account_number.unwrap())
Copy link
Collaborator

Choose a reason for hiding this comment

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

To be safer, you could match both:

match (self.get_seed_anchor(), self.account_number)

This way we don't need to rely in unwrapping and we can trap with a nicer error message. Wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If this is about the error message, we can just .expect() with a nicer message. Matching both means we need four branches, two of which are basically identical, this would feel a bit superfluous.

Copy link
Collaborator

@lmuntaner lmuntaner May 20, 2025

Choose a reason for hiding this comment

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

You don't need four branches. Three should be enough and it's maybe clearer the error.

(Some(seed_from_anchor), _) => ...
(None, Some(account_number) => delegation::calculate_account_seed(account_number)
(None, None) => trap("Missing both seed anchor and account number in this account")

Just a nitpick though. No need to implement it. I like pattern matching personally 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done ✅

// this should only happen if a named account was created, and then both it and the
// default account references were moved/deleted.
if acc_ref_vec.is_empty() {
return Some(Account::new(
Copy link
Collaborator

Choose a reason for hiding this comment

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

That makes sense.

Let's add it in the comment as well. It's not clear at first sight why we should return the synthetic account.

I agree we should think this more. We should have a WARNING in the comment, to review this at some point and the edge cases we have with the current code.

@@ -498,3 +496,68 @@ fn should_count_accounts_different_anchors() {
"Total counters after anchor 2 additional account mismatch"
);
}

#[test]
fn should_read_default_account_with_empty_reference_list() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Replicate part of the comment on why we return a default account in this case here, please.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done ✅

/// Returns the requested account.
/// If the anchor doesn't own this account, returns None.
/// If the account is default but has been moved/deleted, returns None.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this comment accurate? Aren't we returning a default account if the list is empty?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is correct but incomplete - if the default has been moved or deleted but there are other accounts, we return None. If there are no accounts left (all accounts moved/deleted), we return a synthetic default account. Updating comments.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(I do agree that this is not a great solution - we currently cannot actually get to this state, but we do need to think about this at some point)

}

// if there is a default account in the list, we return it
// else we return None, account has been moved or deleted
Copy link
Collaborator

Choose a reason for hiding this comment

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

Shuold we mention here that the user can still log in with the other account?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Adding...

@LXIF LXIF added this pull request to the merge queue May 20, 2025
Merged via the queue into main with commit effb443 May 20, 2025
70 checks passed
@LXIF LXIF deleted the andri/implement-create-account-delegation branch May 20, 2025 14:12
LXIF added a commit that referenced this pull request May 20, 2025
* add get_accounts_for_origin to account_management and get_accounts to main, update existing code

* add authorization fn

* clippy

* add tests for get and create

* clippy

* add account updating endpoint and related account management functions as well as tests

* cwippy

* add seed_from_anchor to Account

* add seed_from_anchor to account struct

* add calculate_account_delegation_seed function

* make get_accounts return a result

* add principal to GetAccountsError::Unauthorized

* add const

* add global acc limit and unit test

* add max acc limit to update function

* add authorization, add checking if anchor has account (for later)

* clippy

* fix did

* add per-acc auth check in account management

* add tests, restructure per-acc auth check in update_account

* change variant name, update comment

* cleanup function inputs

* cwippy

* cwippy again

* minor changes

* add account delegation preparation function and update endpoint

* fix did

* fix did

* cwippy

* fix did again

* cwippy fix

* fix did?

* fix typing

* clippyyy

* cleanup

* clippy

* fix read_account method to account for seed_from_anchor

* rename

* move seed generation function into account impl

* clippy

* cleanup delegation generation, handle default account in has_account_reference, move anchor_has_account

* return account from anchor_has_account

* cleanupt

* restructure has_account_reference and anchor_has_account

* cwippy

* return default acc if no application number

* cwippy

* cleanup

* restructure storage borrowing

* make nicer

* update list_accounts to return accounts and use read_account

* remove anchor_has_account, pack everything into read_account

* clippy

* clippy

* add comment

* clippy

* change error type

* fix function inputs

* fix tests

* 🤖 cargo-fmt auto-update

* add tests, rename some things

* remove comment

* update comments

* add expect message, add comments

* clippy

* update match statement in calculate_seed

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
github-merge-queue bot pushed a commit that referenced this pull request May 21, 2025
* add get_accounts_for_origin to account_management and get_accounts to main, update existing code

* add authorization fn

* clippy

* add tests for get and create

* clippy

* add account updating endpoint and related account management functions as well as tests

* cwippy

* add seed_from_anchor to Account

* add seed_from_anchor to account struct

* add calculate_account_delegation_seed function

* make get_accounts return a result

* add principal to GetAccountsError::Unauthorized

* add const

* add global acc limit and unit test

* add max acc limit to update function

* add authorization, add checking if anchor has account (for later)

* clippy

* fix did

* add per-acc auth check in account management

* add tests, restructure per-acc auth check in update_account

* change variant name, update comment

* cleanup function inputs

* cwippy

* cwippy again

* minor changes

* add account delegation preparation function and update endpoint

* fix did

* fix did

* cwippy

* fix did again

* cwippy fix

* fix did?

* fix typing

* clippyyy

* cleanup

* clippy

* fix read_account method to account for seed_from_anchor

* start updating api

* rename

* move seed generation function into account impl

* start adding from

* clippy

* add get_account_delegation method in account_management

* cwippy

* cleanup delegation generation, handle default account in has_account_reference, move anchor_has_account

* return account from anchor_has_account

* merge updates

* cleanupt

* restructure has_account_reference and anchor_has_account

* cwippy

* return default acc if no application number

* cwippy

* cleanup

* restructure storage borrowing

* replaced existing get_delegation with new and more general get_account_delegation

* cwippy

* cwippy again

* update did

* update did

* update did

* make nicer

* update list_accounts to return accounts and use read_account

* remove anchor_has_account, pack everything into read_account

* clippy

* clippy

* add comment

* clippy

* change error type

* fix function inputs

* fix tests

* 🤖 cargo-fmt auto-update

* clippy

* clippy

* add tests, rename some things

* minor adjustments

* Add account delegation preparation (#3076)

* add get_accounts_for_origin to account_management and get_accounts to main, update existing code

* add authorization fn

* clippy

* add tests for get and create

* clippy

* add account updating endpoint and related account management functions as well as tests

* cwippy

* add seed_from_anchor to Account

* add seed_from_anchor to account struct

* add calculate_account_delegation_seed function

* make get_accounts return a result

* add principal to GetAccountsError::Unauthorized

* add const

* add global acc limit and unit test

* add max acc limit to update function

* add authorization, add checking if anchor has account (for later)

* clippy

* fix did

* add per-acc auth check in account management

* add tests, restructure per-acc auth check in update_account

* change variant name, update comment

* cleanup function inputs

* cwippy

* cwippy again

* minor changes

* add account delegation preparation function and update endpoint

* fix did

* fix did

* cwippy

* fix did again

* cwippy fix

* fix did?

* fix typing

* clippyyy

* cleanup

* clippy

* fix read_account method to account for seed_from_anchor

* rename

* move seed generation function into account impl

* clippy

* cleanup delegation generation, handle default account in has_account_reference, move anchor_has_account

* return account from anchor_has_account

* cleanupt

* restructure has_account_reference and anchor_has_account

* cwippy

* return default acc if no application number

* cwippy

* cleanup

* restructure storage borrowing

* make nicer

* update list_accounts to return accounts and use read_account

* remove anchor_has_account, pack everything into read_account

* clippy

* clippy

* add comment

* clippy

* change error type

* fix function inputs

* fix tests

* 🤖 cargo-fmt auto-update

* add tests, rename some things

* remove comment

* update comments

* add expect message, add comments

* clippy

* update match statement in calculate_seed

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>

* Improve copy in buttons (#3082)

* Improve copy in buttons

* Revert changes in use another account

* small fixes

* merge main and small fixes

* run generate

* cleanup

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Llorenç Muntaner <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants