ethereum/solidity

Introduce a real constant keyword and rename the current behaviour

axic opened this issue ยท 47 comments

axic commented

I think the following makes more sense compared to what we have now.

Now:

  • constant function should not modify the state (not fully enforced yet)
  • constant state variable (ie. the one in the class and not in a method) is evaluated every time it is called

After the change:

    1. the keyword view is introduced for functions (it replaces constant). Calling a view cannot alter the behaviour of future interactions with any contract. This means such functions cannot use SSTORE, cannot send or receive ether and can only call other view or pure functions.
    1. the keyword pure is introduced for functions, they are view functions with the additional restriction that their value only depends on the function arguments. This means they cannot use SSTORE, SLOAD, cannot send or receive ether, cannot use msg or block and can only call other pure functions.
    1. the keyword constant is invalid on functions
    1. the keyword constant on any variable means it cannot be modified (and could be placed into memory or bytecode by the optimiser)
  • 5) the keyword volatile is introduced for variables, which read their referenced value every time (this is what constant does today to variables) [agreed to remove this functionality]

Any example of the volatile keyword:

contract A {
    uint volatile number = block.number;
    function a() returns (uint) {
        return number; // this returns the *current* block.number every time it is called
    }
}

I'm not sure the volatile option is useful and thus we might decide to remove it.

the keyword stateless is introduced for functions (it replaces constant)

This seems like the wrong word; constant functions are definitely not stateless; they often depend on state. If a new name must be used, perhaps something like immutable or pure (in the sense that a pure function is side-effect-free, which is what we're really describing)?

axic commented

I'm trying to find the word and stateless ia far from perfect. It should describe that the state is not modified.

Also additionally we could introduce real constant functions, which are indeed stateless and dont depend on the state. I.e. calculating something based on the inputs.

Well, from a CS-theoretic POV, 'pure' is definitely the term you want. More informally, 'side effect free', but that's harder to condense into a keyword.

I think 'immutable' will be the least ambiguous to most developers.

axic commented

I like pure, but are we likely to find another use for it? Does view as keyword make sense?

axic commented

@redsquirrel immutable wouldmean to me that child classes cannot ovveride it.

@axic I suggest following Java and using 'final' for functions that cannot be overridden.

I like pure, but are we likely to find another use for it?

I don't think so; certainly this is the only usage for the term I'm aware of.

I suggest following Java and using 'final' for functions that cannot be overridden.

I don't think that was under discussion here?

I agree that we should use pure...perhaps strict? But I really like the idea of pure as a side effect free input. Rather than volatile which conjures up bad imagery, why not static?

nonmutating?

too long...overly blunt descriptive. Less is more imo.

readonly?

what's wrong with pure or strict as is used in js?

axic commented

Many of the definitions of pure say it does not depend on state and no matter when it is called, based on the input parameters it should always return the same predictable result.

Therefore it does not suit our application.

@VoR0220 static does not reflect what it is. The volatile part is a different behaviour (= what constant variables do).

Updated the description with further clarification which should help coming up with a good name.

hmmm...so I like that idea in regards to pure it enables solidity to become "functional" (and pleases all the zealots), but in regards to view...I think it might be better to just bite the bullet and call it immutable (because that's what it is). As for volatile, there are uses for it, but not many...probably just call it mutable.

The problem with immutable is that you cannot apply that adjective to the function. The function itself never changes.

right but the output does. Perhaps you could create such a differentiation. You might do something like this

function f) returns immutable (my, output, signature) {

}
axic commented

@VoR0220 it is not the output or the function which is mutable/immutable.

Case 1: function can read state variables (sload). The output of this function will depend on the current state.

Case 2: function can modify state variables (sstore). The output of this function will depend on the current state, as well as it can change the state, which then affects any future invocations.

I've borrowed view from databases. The function provides a modifiedview of the current state (i.e. it uses sload and applies transformations).

axic commented

Can we agree that the behaviour called volatile above should not be present and we can remove it?

I don't see much need for volatile.

Many of the definitions of pure say it does not depend on state and no matter when it is called, based on the input parameters it should always return the same predictable result.
Therefore it does not suit our application.

Doesn't the current proposal fit pure? 2) the keyword pure is introduced for functions (they cannot call SSTORE or SLOAD)
Which part is pure breaking?

I think the 4 proposals are good. No one else has made comments about readonly, and I think it fits well with in the 1st proposal, which would read as:

  1. the keyword readonly is introduced for functions (it replaces constant, these functions cannot call SSTORE, but can call SLOAD)

readonly and pure sound accurate enough.

axic commented

Doesn't the current proposal fit pure?

Sorry for the confusion, the description was updated after those comments. pure (and every other keyword) was initially proposed for what 1) is. It fits 2) well.

For 1) I like view, readonly, and nonmutating (but this is way too long).

Many of the definitions of pure say it does not depend on state and no matter when it is called, based on the input parameters it should always return the same predictable result.

Yes, you're right - I stand corrected. readonly is probably the sanest I've seen so far, though view also works.

RE constant/volatile/etc, besides dropping the functionality (which seems good to me), why not call it 'alias' or 'define'? That seems to be effectively what it is.

I think that volatile is just a buggy behaviour of constant. We can introduce something like alias in the future, it is certainly planned for types (using x = y) and could also be introduced for general expressions.

I updated the description to specify the behaviour in more detail.

Question: Can a pure function return address(this)? I.e. is it required to be pure just from its source code or is it fine to be pure "after deployment"? Can a pure function return a storage value that it set in the constructor (i.e. from the block hash) and never modified in the contract?

From the keywords: The problem with readonly is similar to the one with immutable - it applies to the function while it should apply to the state. On the other hand, view is not an adjective.

axic commented

@chriseth moved alias into its own issue and removed volatile from the description.

I.e. is it required to be pure just from its source code or is it fine to be pure "after deployment"?

That is a good question. I would argue that it is more useful to allow this and any constants set during the constructor (if that becomes possible).

Storage should not be accessed by pure functions unless we introduce writeonce storage slots set during the constructor, but what is the reason to use them? They are more expensive than storing it in bytecode.

Further discussion: Are events allowed in view functions? I would rather say no, as they have an effect on the blockchain even if that effect is not visible from within the EVM.

View functions should not be able to spend any gas.

@ethernomad if they are executed on chain, they will always spend gas, but they can be used together with the new static_call opcode that is under discussion and they will be used together with eth_call if invoked from web3.js.

axic commented

Are events allowed in view functions?

I think this question also applies to pure. It is a though question, logs do change the state, but in a way which doesn't influence the future execution of either view or pure functions.

Let us write a specification about what is and what is not allowed (into the description of this issue). Things to be sorted: be sorted: selfdestruct, log (events), this.balance, address.balance, tx.*, new

Why do we need to sort those ones out? Can I haz links?

For volatile, would you be able to do this:

uint volatile foo = block.number + block.timestamp;

Or even:

uint volatile foo = this.bar()

If so, speaking from my experience as a python developer who has dealt many times with debugging code involving @Property declarations, this sounds extremely dangerous. Things should do what they seem to do and nothing else; variable lookups that are actually function calls that might lead to re-entrant weirdness sound like a nightmare for auditing and could potentially mislead contract readers.

axic commented

volatile describes what constant does currently. We have agreed to remove this functionality and will perhaps introduce typedef / aliases later: #1013.

Note that this "volatile" thing is abusing one application of the constant keyword. This application was intended to provide actual compile-time (or at least deploy-time) constants. As everywhere else, this is not yet correctly enforced.

axic commented

Things to be sorted: be sorted: selfdestruct, log (events), this.balance, address.balance, tx.*, new

Perhaps we need to define for view whether it can take the state of the contract only or the entire state as an input.

pure:

  • cannot use selfdestruct, new, log, this.balance, address.balance, tx.*, msg.*, block.*

view:

  • cannot use selfdestruct, new
  • can use this.balance
  • can use tx.*, msg.*, block.*

Canonically speaking, view should not use log as it changes the state, however, currently it changes the state in a way it won't affect future invocations of view. Being future proof, I would say it shouldn't be allowed to use it.

You are right, in serenity, log will be changed to actually make modifications to storage of a certain contract. This means, log should be viewed as a call to a non-view, non-pure function of another contract. Because of that, view should not be able to use log, I would say.

so events are no longer going to be free/near free in terms of gas?

I think we more or less found that our framework is not yet fully specified because pure functions might modify memory. My current proposal would be the following:

view and pure are attributes applied to the function, and they only concern storage and other permanent state.
The pure keyword should be renamed, because a "pure" function is still able to modify memory references that are passed in (but not storage references). In order to prevent this, the type of such an input variable has to be <memory type> constant.

So in essence, "view" and "pure" apply to "this" (and other state including other contracts) and all other input variables have their own specifiers.

I suggest the new name as expression or volatile that will evaluate in each call.
See this code:

contract LockableCoin is AbstractCoin {
    //creation time, defined when contract is created
    uint public creationTime = now;
    //time constants, defines epoch size and periods
    uint public constant UNLOCKED_TIME = 25 days;
    uint public constant LOCKED_TIME = 5 days;
    uint public constant EPOCH_LENGTH = UNLOCKED_TIME + LOCKED_TIME;
    //current epoch constant formula, recalculated in any contract call
    uint public constant CURRENT_EPOCH = (now - creationTime) / EPOCH_LENGTH + 1;    
    //next lock constant formula, recalculated in any contract call
    uint public constant NEXT_LOCK = (creationTime + CURRENT_EPOCH * UNLOCKED_TIME) + (CURRENT_EPOCH - 1) * LOCKED_TIME;
//(...)
}

From https://github.com/ethereans/github-token/blob/master/contracts/LockableCoin.sol

Why word volatile being not considered? I see use cases for this, and its really useful and easy to understand. Volatile are expressions that result a value change due contract storage, chain or call variables change.
Such as:

 //caller balance
    mapping (address => uint) balances;
    uint volatile callerBalance = balances[msg.sender];

//lock epoch
    uint256 public creationTime = now;
    uint256 public unlockedTime = 25 days 
    uint256 public lockedTime = 5 days; 
    uint256 public volatile EPOCH_LENGTH = unlockedTime + lockedTime;
    uint256 public volatile CURRENT_EPOCH = (now - creationTime) / EPOCH_LENGTH + 1;    
    uint256 public volatile NEXT_LOCK = (creationTime + CURRENT_EPOCH * unlockedTime) + (CURRENT_EPOCH - 1) * lockedTime;

I was first fooled by a constant owner that changed in any call, but using a special keyword for this case makes sense.
Just removing this hability makes solidity more restrictive.
Volatile is useful when you need a constant that is synced to various variables and is used a lot of times in almost all functions, if not this case, internal function and a memory var is better.

I like to restrict the number of keywords as much as possible, especially if there is another solution that does almost the same thing.

Related: #2181

axic commented

Here is a summary of the current design:

There's enum for statemutability, from most restricted to least restricted: pure, view, nonpayable, payable. A function can only be one of these.

view:

  • cannot use selfdestruct, new
  • can use this.balance
  • can use tx.*, msg.*, block.*
  • can only call view or pure functions
  • cannot send ether
  • can use sload
  • cannot use sstore

pure:

  • cannot use selfdestruct, new, log, this.balance, address.balance, tx.*, msg.*, block.*
  • can only call pure functions
  • cannot use sload
  • cannot use sstore

Remarks:

  • JSON ABI has a new field statemutability introduced with a string value as above
  • JSON ABI keeps constant/payable for backwards compatibility for a while
  • constructor / fallback cannot be view or pure
  • constant as a specifier on functions is phased out (removed at the next breaking release)
axic commented

This is finally implemented and documented. To only thing left is enforcing these restrictions and that is tracked in #2606.

What version is this implemented in?

@fulldecent I believe view and constant are both working right now, I would like to see readonly, but for compatibility I prefer to stick with view, it's still less ambiguous than constant anyway
https://ethereum.stackexchange.com/questions/25200/solidity-what-is-the-difference-between-view-and-constant

mojl commented

Is it normal behavior for a pure function to access a constant "state variable"?

It make sense architecture-wise, but is this intended and will be doubled down on?

axic commented

A constant "state variable" is just a constant value, it is not a storage variable.