Views TZIP

Request for comments on the views proposal. The comments can be left below.

1 Like

Why do we need GET_STORAGE if we have VIEW?

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.)

Presumably, GET_STORAGE and VIEW will be forbidden to involve ticket or any other future non-DUPable/“forged” types (for VIEW, in the return type)?

If so, GET_STORAGE will be completely unusable on any contract whose storage involves ticket?

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.

Currently, GET_STORAGE and 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 GET_STORAGE and VIEW with 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 GET_STORAGE, 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 DROP’d, but:

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 ticket (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 SENDER, AMOUNT, BALANCE, SELF_ADDRESS (but not SELF), etc… allowed inside view? Do they have the same values seen by the caller? Maybe some of them (BALANCE? SELF_ADDRESS and 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.

1 Like

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 ticket_stub in 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. :wink: 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 opened_ticket_type (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”.)

But `ticket_stub` should unparse using some other tag, either `Pair` again or a new `Ticket_stub`. If it unparses with `Ticket` then the type confusion risk will come back: maybe I can steal your ticket as a `ticket_stub` using `GET_STORAGE`, then abuse a type confusion bug to cast it back to `ticket`.

Actually, I’m not sure this strategy will avoid all risk from type confusion…


Unrelatedly:

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 view interface which you also implement. this might happen whenever any view you call (directly or indirectly) ends up calling one of your views.

I suppose you can call this a “reentrancy” hazard, which remains even though VIEW is “read-only”.

Should calling VIEW on yourself be forbidden? I don’t know… This will turn the safety hazards – and innocent examples – into liveness hazards.