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

tests: Use mollusk on process instruction #18

Merged
merged 4 commits into from
Jan 16, 2025
Merged

Conversation

febo
Copy link
Contributor

@febo febo commented Jan 11, 2025

Problem

PR #17 moved processors tests to the tests folder but instructions are executed directly against the program Processor.

Solution

Reimplement both do_process_instruction and do_process_instruction_dups to use mollusk to execute instructions.

While the intention was not to introduce any changes to existing tests, there were a couple of changes required:

  • Few tests that were using the Processor directly were updated to use do_process_instruction_dups
  • assert after the account is closed tests if the data is empty ontest_close_account

@febo febo marked this pull request as ready for review January 11, 2025 22:55
@febo febo requested review from joncinque and buffalojoec January 11, 2025 22:55
Base automatically changed from febo/move-processor-tests to main January 13, 2025 20:59
Copy link
Contributor

@joncinque joncinque left a comment

Choose a reason for hiding this comment

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

Just tiny things, this looks really good!

I had to look at only the second commit because for some reason the first commit still has everything from #17

*EXPECTED_DATA.write().unwrap() = expected_data;
}

struct SyscallStubs {}
Copy link
Contributor

Choose a reason for hiding this comment

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

Totally separate, but I just realized, with this change, we can remove the solana-program dependency from spl-token and make it much easier for downstream users

program/tests/processor.rs Outdated Show resolved Hide resolved
}
solana_program::entrypoint::SUCCESS
// Update accounts after the instruction is processed.
for (original, (_, updated)) in accounts.iter_mut().zip(result.resulting_accounts.iter()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: can the second be an into_iter() to avoid the clone in the next line?

Copy link
Contributor

Choose a reason for hiding this comment

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

Mollusk will vend you back the exact same accounts in the same order, but with any post-processing changes. So you can just do something like this:

accounts = result.resulting_accounts.into_iter().unzip().1;

Copy link
Contributor Author

@febo febo Jan 15, 2025

Choose a reason for hiding this comment

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

But they will be a "copy" of the input, right? In this case we have Vec<&mut SolanaAccount>, so we need to update those references, not the whole Vec.

We are giving mollusk a copy of the input here:

let instruction_accounts: Vec<(Pubkey, AccountSharedData)> = instruction
        .accounts
        .iter()
        .zip(&accounts)
        .map(|(account_meta, account)| {
            (
                account_meta.pubkey,
                AccountSharedData::from((*account).clone()),
            )
        })
        .collect();

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, they're a copy of the input but with any changes from the instruction(s) applied. So basically the same as what you are doing already.

Copy link
Contributor Author

@febo febo Jan 15, 2025

Choose a reason for hiding this comment

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

This loop is updating the original references:

for (original, (_, updated)) in accounts
        .iter_mut()
        .zip(result.resulting_accounts.into_iter())
{
    original.data = updated.data().to_vec();
    original.lamports = updated.lamports();
    original.owner = *updated.owner();
}

I think I need to keep this loop, since those references are used in the caller test code.

program/tests/processor.rs Outdated Show resolved Hide resolved
program/tests/processor.rs Show resolved Hide resolved
Copy link
Contributor

@buffalojoec buffalojoec 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 is a completely separate note, but going through this and thinking about p-token, I just realized you can write a pretty nifty benchmark that:

  1. Clones down SPL-Token from mainnet-beta
  2. Runs all fixtures on both programs using process_fixture (no validation)
  3. Collects each program's compute unit usage from the results.
  4. Compares them.

program/tests/processor.rs Outdated Show resolved Hide resolved
program/tests/processor.rs Outdated Show resolved Hide resolved
program/tests/processor.rs Outdated Show resolved Hide resolved
Comment on lines +89 to +99
// Dedup accounts for mollusk.
account_infos.iter().for_each(|account_info| {
if !cached_accounts.contains_key(account_info.key) {
let account = SolanaAccount {
lamports: account_info.lamports(),
data: account_info.try_borrow_data().unwrap().to_vec(),
owner: *account_info.owner,
executable: account_info.executable,
rent_epoch: account_info.rent_epoch,
};
dedup_accounts.push((*account_info.key, AccountSharedData::from(account)));
cached_accounts.insert(account_info.key, account_info);
}
});
Copy link
Contributor

Choose a reason for hiding this comment

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

Hm, interesting. This is required since if the user provides duplicate transaction accounts, the TransactionContext doesn't pilfer the duplicates out? I wonder if there's an elegant way to handle this within the Mollusk harness somehow.

Works for me if not, though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The main reason for it is that the function takes an AccountInfo and Mollusk expects an AccountSharedData for each account. Then during this conversion, we only convert one "copy" of each account.

@febo
Copy link
Contributor Author

febo commented Jan 15, 2025

Looking good!

This is a completely separate note, but going through this and thinking about p-token, I just realized you can write a pretty nifty benchmark that:

  1. Clones down SPL-Token from mainnet-beta
  2. Runs all fixtures on both programs using process_fixture (no validation)
  3. Collects each program's compute unit usage from the results.
  4. Compares them.

Oooh, that is nice! We can do that once we get everything merged.

@febo febo force-pushed the febo/mollusk-processor-tests branch from 9b284cb to b8d47b7 Compare January 15, 2025 10:33
@febo
Copy link
Contributor Author

febo commented Jan 15, 2025

I had to look at only the second commit because for some reason the first commit still has everything from #17

I think I was missing a rebase - now it looks fine.

@febo febo requested review from buffalojoec and joncinque January 15, 2025 10:43
Copy link
Contributor

@joncinque joncinque 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 to me!

@@ -4,7 +4,6 @@ on:
push:
branches: [main]
pull_request:
branches: [main]
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we want this in here still, right?

Copy link
Contributor Author

@febo febo Jan 15, 2025

Choose a reason for hiding this comment

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

Not sure, if we don't restrict the branches, we can have CI run in stacked PRs.

@febo febo merged commit eab98d4 into main Jan 16, 2025
12 checks passed
@febo febo deleted the febo/mollusk-processor-tests branch January 16, 2025 12:46
joncinque pushed a commit to joncinque/token that referenced this pull request Feb 3, 2025
* Use mollusk on process instruction

* Enable workflow

* Use non-chain process instruction

* Address review comments
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