Programming Hotmoka
A tutorial on Hotmoka and smart contracts in Takamaka
3.5 Storage types and constraints on storage classes
We have seen how to invoke a constructor of a class to build an object in the store of a node or to invoke a method on an object in the store of a node. Both constructors and methods can receive arguments. Constructors yield a reference to a new object, freshly allocated; methods might yield a returned value, if they are not declared as void. This means that there is a bidirectional exchange of data from outside the node to inside it, and back. But not any kind of data can be exchanged:
-
1. The values that can be exchanged from inside the node to outside the node are called storage values.
-
2. The values that can be exchanged from outside the node to inside the node are the same storage values as above, with the extra constraint that objects must belong to an @Exported class.
The set of storage value is the union of
-
1. primitive values of Java (characters, bytes, shorts, integers, longs, floats, doubles and booleans);
-
2. reference values whose class extends io.takamaka.code.lang.Storage (that is, storage objects);
-
3. null;
-
4. a few special reference values: java.math.BigIntegers and java.lang.Strings.
Storage values cross the node’s boundary inside wrapper objects. For instance the integer 2019 is first wrapped into StorageValues.intOf(2019) and then passed as a parameter to a method or constructor. In our previous example, when we called Person.toString(), the result s was actually a wrapper of a java.lang.String object. Boxing and unboxing into/from wrapper objects is automatic in Takamaka: our class Person does not show that machinery.
What should be retained of the above discussion is that constructors and methods of Takamaka classes, if we want them to be called from outside the node, must receive storage values as parameters and must return storage values (if they are not void methods). A method that expects a parameter of type java.util.HashSet, for instance, can be defined and called from Takamaka code, inside the node, but cannot be called from outside the node, such as, for instance, from the moka tool or from our Family class. The same occurs if the method returns a java.util.HashSet.
We conclude this section with a formal definition of storage objects. We have already said that storage objects can be kept in the store of a node and that their class must extend the library class io.takamaka.code.lang.Storage. But there are extra constraints. Namely, fields of a storage objects are part of the representation of such objects and must, themselves, be kept in store. Hence, a storage object:
-
1. has a class that extends (directly or indirectly) io.takamaka.code.lang.Storage, and
-
2. all its fields hold storage values (primitives, storage objects, null, a java.math.BigInteger or a java.lang.String).
Note that the above conditions hold for the class Person defined above. Instead, the following are examples of what is not allowed in a field of a storage object:
-
1. arrays
-
2. collections from java.util.*
We will see later how to overcome these limitations.

Again, we stress that such limitations only apply to storage objects. Other objects, that needn’t be kept in the store of a node but are useful for the implementation of Takamaka code, can be defined in a completely free way and used in code that runs in the node.