Skip to content
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

[wip] Token haver plugin #92

Merged
merged 10 commits into from May 17, 2024

Conversation

asktree
Copy link
Contributor

@asktree asktree commented Apr 10, 2024

I also add a devcontainer w/ Dockerfile to be used for Codespaces and verifiable builds.

@asktree asktree marked this pull request as draft April 10, 2024 21:38
Copy link
Collaborator

@SebastianBor SebastianBor left a comment

Choose a reason for hiding this comment

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

Looks good
I added the comments about token_owner_record validation in update_voter_weight_record before I realised it was not needed for that instruction but I left the comments anyway for the thought process. I hope that would be useful

let registrar = &ctx.accounts.registrar;
let voter_weight_record = &mut ctx.accounts.voter_weight_record;

let governance_program_id = ctx.accounts.token_owner_record.owner;
Copy link
Collaborator

Choose a reason for hiding this comment

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

governance_program_id must be taken from Registrar and not from token_owner_record.owner to ensure the provided TOR belongs to the configured program.
Otherwise you could deploy your own program and manufacture any TOR which would pass this condition and exploit it.

get_token_owner_record_data() asserts that token_owner_record.owner == governance_program_id


let governance_program_id = ctx.accounts.token_owner_record.owner;

let token_owner_record = token_owner_record::get_token_owner_record_data(
Copy link
Collaborator

Choose a reason for hiding this comment

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

We have to ensure the provided token_owner_record matches the configured realm and governing_token_mint and belongs to the configured governance_program_id For that reason get_token_owner_record_data_for_realm_and_governing_mint(), which does the referential integrity checks, should be used to deserialise the account.

Hint: It's a good idea to draw a referential integrity graph between all instruction accounts and check if their relationship are enforced.
You can see the referential integrity for VoterWeightRecord is checked for realm and governing_token_mint in the Anchor declarative way, plus the owner_program is also checked implicitly by Anchor

TokenAccount.owner is checked against token_owner_record.governing_token_owner explicitly in the code.

let nonzero_token_accounts: Vec<Account<TokenAccount>> = ctx
.remaining_accounts
.iter()
.filter(|account| !account.data_is_empty()) // filter out empty accounts, so we don't need to check them in UI
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would expect Account::<TokenAccount>::try_from in the next statement to fail if the account data is empty and I would rather throw an error for invalid accounts then silently ignoring them

voter_weight_record.voter_weight_expiry = Some(Clock::get()?.slot);

// Set action and target to None to indicate the weight is valid for any action and target
voter_weight_record.weight_action = None;
Copy link
Collaborator

Choose a reason for hiding this comment

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

These are default values and never changed by the program and the assignment is not required here


// Set action and target to None to indicate the weight is valid for any action and target
voter_weight_record.weight_action = None;
voter_weight_record.weight_action_target = None;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same as above

// Throw an error if a token account's owner doesnt match token_owner_record.governing_token_owner
require_eq!(
account.owner,
token_owner_record.governing_token_owner,
Copy link
Collaborator

Choose a reason for hiding this comment

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

I realised we actually don't need token_owner_record account for this instruction at all because the token_account.owner check can be done against voter_weight_record.governing_token_owner

}

// Setup voter_weight
voter_weight_record.voter_weight = nonzero_token_accounts.len() as u64;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe we could parametrise this formula in Registrar configuration to make this plugin more generic for frozen accounts? The options I'm thinking about would be:

  1. weight = Number of frozen accounts - the default as implemented now
  2. weight = sum(amounts) - this would require additional mint amount adjustments in case they would have different precisions (see VSR for reference)

I'm not sure if the extra effort would be justified for this use case

@asktree asktree marked this pull request as ready for review May 16, 2024 17:57
@SebastianBor SebastianBor merged commit b117178 into solana-labs:master May 17, 2024
2 of 5 checks passed
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.

None yet

2 participants