Skip to content

Draft: Proto: Add a Global Table of Constants

Daniel Hines requested to merge marigold/tezos:dh@table-of-values2 into master

This MR implements the table of constants feature described in the corresponding TZIP.

The TZIP discusses the motivations for the change.

Overview

There are three main contributions in the MR:

  • A new Global_constants module to Storage.ml that represents a table mapping keys (as strings) to Micheline values.
    • A subset of functionality is re-exported from Alpha_context.ml.
  • A new manager operation register_global_constant that sets values in this table.
  • A new Michelson instruction GET_GLOBAL retrieves values from this table at runtime.

The MR also includes unit tests for each of the above, building on top of !2340 (merged) (thus, I recommend merging in 2340 first). Lastly, the MR makes an attempt at implementing a new client command executing the new manager operation; however, I got tripped up on a few idiosyncrasies with logging and gas accounting and will need correction.

There are several points of discussion:

Naming Scheme

Currently, arbitrary strings are used as the keys for the table. I don't think is a good idea, and it's merely a placeholder for a more considered naming scheme. I will post a few considerations in the Agora topic regarding this.

Gas and Storage Accounting

I'm not yet familiar with the intricacies of gas and storage accounting, and very likely did it wrong - a pro in this area will need to correct me.

Types, Storage, and Casting

The design in this MR works the following way:

When the user puts a value in the table, they also provide a type for that value. We first check that the type indeed matches the value, and then we store both the type and the value as Micheline. Upon interpreting a contract that uses a value from the table, the user provides the type that is used to decode the value. This means it's possible to cast compatible types, e.g. nat to int and vs. versa. As best I can tell, this isn't an issue.

A few points on this:

  • Currently I check that the type matches the value in apply.ml. I think this is the wrong abstraction layer to do this in - I should push this down into alpha_context.ml.
  • If the the Micheline value is wholly incompatible with the supplied type, I just return None. Maybe it's better to fail entirely in this case?
  • If no value exists at the given address, then I return None.
  • Even though we don't currently use the stored type, it's important to keep it in case we ever want to migrate data in storage to some new format. For example, in the future we may want to come up with a scheme whereby values are only typed once upon writing to storage, and a typed IR is serialized/deserialized, saving significant gas costs. This will be much easier to achieve if we have the types on hand.

Missing UX

Users will want some kind of RPC and client commands to view values at a given address, but this is not yet implemented. Are there other things users are going to want from this feature?

Edited by Daniel Hines

Merge request reports