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

functions that use .call() should be not allowed in view functions #1338

Merged
merged 1 commit into from
Jun 16, 2023

Conversation

seanyoung
Copy link
Contributor

@seanyoung seanyoung commented May 29, 2023

Functions that modify the state, like the SPL-token mint_to, are not view functions, but the compiler warns that they can be declared as view.

Example:

	function mint_to(address mint, address account, address authority, uint64 amount) internal {
		bytes instr = new bytes(9);

		instr[0] = uint8(TokenInstruction.MintTo);
		instr.writeUint64LE(amount, 1);

		AccountMeta[3] metas = [
			AccountMeta({pubkey: mint, is_writable: true, is_signer: false}),
			AccountMeta({pubkey: account, is_writable: true, is_signer: false}),
			AccountMeta({pubkey: authority, is_writable: true, is_signer: true})
		];

		tokenProgramId.call{accounts: metas}(instr);
	}

Warning:

warning: function can be declared 'view'
   ┌─ /solang/solana-library/spl_token.sol:46:2
   │
46 │     function mint_to(address mint, address account, address authority, uint64 amount) internal {
   │

Fixes #997

Another PR will implement staticcall for Solana, which is allowed in view functions.

@seanyoung seanyoung marked this pull request as ready for review May 29, 2023 14:43
@seanyoung
Copy link
Contributor Author

@xermicus I don't know why there was a special case for Substrate, it may have been wrong or maybe there is a good reason. Do you know if seal_call is permitted in pure or view functions, i.e. non-mutable messages?

@xermicus
Copy link
Contributor

xermicus commented May 30, 2023

No I don't know why there was a special case. I'm not aware on why it should not be permitted in pure or view functions, as long as it is guaranteed that the callee is also pure or view respectively. For raw calls where we don't know, assuming that the function might write to state (as your PR does) looks correct to me.

Copy link
Contributor

@LucasSte LucasSte left a comment

Choose a reason for hiding this comment

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

Does this PR solve #997 ?

Edit: I've just read the PR summary. Can you add the example from #997?

Comment on lines 36 to 38
/// Is the function declared view, payer or default mutability
can_read_state: bool,
/// Is the function declared with default or payer mutability
Copy link
Contributor

Choose a reason for hiding this comment

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

You used the question mark on the other questions, but not here. Please, adopt a single notation.

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've added question marks but I think it questionable whether this improves things.

}
// We don't know if the external call will write to state, so don't give diagnostic about
// function may be declared view
state.may_write_state = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

You've only used may_write_to_state in a single place.
Why didn't you just reuse the does_write_to_state? You could have renamed it to may_write_to_state, as we are doing our best effort in this analysis.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We need to distinguish between does_write_state and may_write_state. If we have does_write_state then the function cannot be view or pure. If we only have may_write_state and !does_write_state then view is allowed.

Copy link
Contributor

Choose a reason for hiding this comment

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

If we don't know, why are we not assume for "the worst" and flag it as a state writing function?

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 problem with that is that you cannot mark functions as view which means you need pay for a transaction in order to call it. You don't want that in many cases. In fact, non-view functions cannot have return values Solana/Anchor so it would not work at all.

Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't it the point that you can't mark them as view, if you don't know (e.g. you can't prove to the compiler) whether they are actually view?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hopefully the comment I've added makes sense

@@ -27,9 +27,15 @@ pub fn mutability(file_no: usize, ns: &mut Namespace) {
/// While we recurse through the AST, maintain some state
struct StateCheck<'a> {
diagnostics: Vec<Diagnostic>,
/// Does the function have an expression or statement that reads from state?
Copy link
Contributor

@xermicus xermicus May 31, 2023

Choose a reason for hiding this comment

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

Sorry for this nitpick, but I think using an enum Mutability { Write, Read, None } instead a bunch of bools would make for more idiomatic code overall. The struct could then just have two fields of this enum, like explicit_mutability (explicit is what the contract defines in the code) and implicit_mutability.

I mean its unrelated to this bugfix; I'll just leave it as suggestion if you feel like it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great idea, I've re-written it with an enum.

@seanyoung seanyoung force-pushed the view-call branch 6 times, most recently from 38bf79a to ceda39f Compare June 1, 2023 12:30
@seanyoung seanyoung marked this pull request as draft June 1, 2023 13:51
@seanyoung seanyoung force-pushed the view-call branch 2 times, most recently from d923e5e to cfc65d4 Compare June 2, 2023 15:24
@seanyoung seanyoung changed the title functions that use .call() should be allowed to declared view or default mutability functions that use .call() should be not allowed in view functions Jun 2, 2023
@seanyoung seanyoung marked this pull request as ready for review June 2, 2023 19:49
…ult mutability

Neither option should produce a diagnostic.

Fixes hyperledger#997

Signed-off-by: Sean Young <[email protected]>
Copy link
Contributor

@xermicus xermicus left a comment

Choose a reason for hiding this comment

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

Looks a lot cleanier now, thanks!

@seanyoung seanyoung merged commit 4026d1c into hyperledger:main Jun 16, 2023
16 checks passed
@seanyoung seanyoung deleted the view-call branch June 17, 2023 10:36
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.

Solang should not ask functions that contain address.call to be declared as view
3 participants