Request for comments on the views proposal. The comments can be left below.
Why do we need
GET_STORAGE if we have
The existence of
GET_STORAGE means that the storage type of a contract becomes definitely part of its observable behavior.
So, for example, a compiler cannot modify the storage type (perhaps optimizing things away, or adding things) while preserving semantics. Indeed, two contracts will be semantically equivalent only if they have identical storage types (presumably including annotations, unless they are eliminated someday, or the
GET_STORAGE check ignores them as a special case.)
VIEW will be forbidden to involve
ticket or any other future non-DUPable/“forged” types (for
VIEW, in the return type)?
GET_STORAGE will be completely unusable on any contract whose storage involves
VIEW requires the targeted contract to prepare “function” and
GET_STORAGE allows us to receive storage directly from the targeted contract.
The type for
GET_STORAGE is necessary and observable since Michelson is typechecked and it is a part of definitions of contract.
VIEW aren’t forbidden that since these two instructions are read-only. They can’t modify the storage.
What’s the concern for forbidden that?
Yes. The new thing to me is that the
storage type of a contract becomes observable to other contracts, like the parameter type is now, or like the types of views would (and should) be. In other words, the storage type becomes part of the on-chain “public interface” of a contract. Personally, I like that the storage type is “private.”
The most obvious implementations of
ticket in the return type would violate the intended resource semantics of tickets. It would allow a contract to receive copies of tickets while leaving the original tickets in the other contract’s storage. For
GET_STORAGE the ownership semantics would be completely destroyed, as any contract could steal copies of any other contract’s tickets.
Maybe, instead of forbidding tickets, one could walk the storage value, replacing any tickets with some kind of dummy tickets. (Maybe replace the ticketer address with a dummy address…?) This would have to be done lazily for tickets contained in big_maps too, I guess.
Please correct me if I am wrong. I think it isn’t a issue because when inspected the value of the received ticket by
VIEW or others, it will include the amount, and the address of the ticketer, i.e. the contract that created the ticket. So, we can’t really “forge” and pretend a ticket which has been created by another ticketer.
Tickets are intended to have a resource interpretation, like linear logic, or affine really; they can be
VIEW should not provide a way for a contract (or others) to effectively
DUP its tickets, and
GET_STORAGE definitely should not provide a way for other contracts to
DUP a contract’s tickets without its consent.
Anyway, my personal recommendation is to drop
GET_STORAGE and to forbid
allow_forged:false) in the return type of views.
The only specific (not very convincing) motivation I have heard for
GET_STORAGE is that it would provide access to the storage data of “adversarial” contracts. But if
ticket is forbidden for
GET_STORAGE, and such adversarial contracts could simply include a
ticket in their storage type to block the use of
GET_STORAGE, then it wouldn’t serve that purpose.
There could be other solutions, though…
For conservation of tickets, it also seems necessary to forbid
operation in the return type of
view, since tickets can be smuggled under transfers or originations. (In any case,
operation in a view seems questionable…)
Other miscellaneous questions arise too: are all the various transaction-context instructions like
SELF_ADDRESS (but not
SELF), etc… allowed inside
view? Do they have the same values seen by the caller? Maybe some of them (
SELF?) take values pertaining to the callee?
It is not only about adversarial contracts.
It can be about retroactively adding new views to the contract: the information is already there, but the view was not written before, and we want to use that new information.
But yeah. We will drop
GET_STORAGE as a result. I do not believe we can find a good model for it (stubbing tickets? what about big maps??).
There is actually a good one. Replacing
ticket types with
GET_STORAGE 's return. The data itself doesn’t need to change.
For views, yup,
tickets and wrappers (big maps or operations) have to be forbidden.
Yuck. But it does sound workable to me…
At the moment, because tickets are represented as
Pair x y z, you could simply replace
ticket t with the
pair address t nat.)
But, we might like to introduce a
Ticket tag in the future, to reduce risk from type confusion bugs (e.g. like the original VIEW draft.) In that case,
ticket_stub must be a new type, and will have to parse
Ticket (so that “the data itself doesn’t need to change”.)
Actually, I’m not sure this strategy will avoid all risk from type confusion…
A possible hazard with
VIEW is calling it on yourself. It should be well-known that doing this intentionally is an evil (and currently inefficient) hack, and that the view will see a “stale” storage value.
But a more difficult hazard is that you must consider unknowingly calling
VIEW on yourself. Generally,
this might happen whenever you use a this might happen whenever any view you call (directly or indirectly) ends up calling one of your views.
view interface which you also implement.
I suppose you can call this a “reentrancy” hazard, which remains even though
VIEW is “read-only”.
VIEW on yourself be forbidden? I don’t know… This will turn the safety hazards – and innocent examples – into liveness hazards.