|
| 1 | +# Durable Contract Details |
| 2 | + |
| 3 | +Zoe allows you to write smart contracts that manage interactions between cooperative, but possibly untrusting, |
| 4 | +parties. Some contracts perform a single simple task like trading one good for another, and then are |
| 5 | +done. Others stay around and manage assets and interactions over time. The first kind don't need persistence |
| 6 | +or the ability to upgrade, while the second kind need to make use of more sophisticated functionality. |
| 7 | + |
| 8 | +When a contract is intended to continue running and serving many customers over a long time, its objects and |
| 9 | +data need to be persistent, its owners or managers may need to be able to adjust parameters or perform other |
| 10 | +governance actions, and it needs to be upgradeable so that the code can adapt over time. |
| 11 | + |
| 12 | +## Durable Objects |
| 13 | + |
| 14 | +The first step toward contracts that can be upgraded is storing all the data that a later incarnation will |
| 15 | +need. This means putting relevant state in |
| 16 | +[Baggage](http://localhost:5173/guides/zoe/contract-upgrade.html#baggage), and ensuring that reachable objects |
| 17 | +that will be accessible to users have an identity that can be maintained as the behavior changes with contract |
| 18 | +upgrades. |
| 19 | + |
| 20 | +We use zone.exo(), zone.exoClass(), and zone.exoClassKit() to define durable objects. |
| 21 | + |
| 22 | +[Zone](http://localhost:5173/glossary/#zone) provides an interface for defining objects and classes that |
| 23 | +supports both ephemeral objects (allocated on the heap), and durable objects that can persist and that |
| 24 | +SwingSet will page in or out on demand. |
| 25 | + |
| 26 | +Our persistent objects are designed to encapsulate their state, and can present different facets to different |
| 27 | +clients to distinguish different aspects of authority. `zone.exoClass()` defines a kind with a single facet, |
| 28 | +while `zone.exoClassKit()` defines a kind with multiple facets. (Here are some relevant [naming |
| 29 | +conventions](https://docs.agoric.com/guides/ertp/#method-naming-structure).) |
| 30 | + |
| 31 | +``` |
| 32 | +zone.exoClassKit(tag, guard, init, methodKit, options) |
| 33 | +zone.exoClass(tag, guard, init, methods, options) |
| 34 | +zone.exo(tag, guard, methods, options) |
| 35 | +``` |
| 36 | + |
| 37 | +The next several sub-sections explain how the parameters to those functions are used. |
| 38 | + |
| 39 | +### Tag: naming kinds of objects |
| 40 | + |
| 41 | +The `tag` provides the identity of the kind of object, which is associated with the defined behavior. When a |
| 42 | +contract (or other vat) is restarted or upgraded, SwingSet requires that all kinds that were previously |
| 43 | +defined in a vat be defined again. With an upgrade, the behavior can be changed. We use the term "null |
| 44 | +upgrade" to refer to upgrades that don't change the behavior. |
| 45 | + |
| 46 | +### Init: specifying state |
| 47 | + |
| 48 | +The `init` parameter defines a function that is used to define the state associated with each instance of an |
| 49 | +object. Notice that `exo()` doesn't take `init` as a parameter; this limits the defined (singleton) object to |
| 50 | +referencing values defined in the enclosing scope. Classes can also refer to variables in their defining |
| 51 | +scope, but any values inherited from the scope will be accessible to all instances of that type. The `init` |
| 52 | +function's parameters become the parameters of the maker function returned by `exoClass()` and |
| 53 | +`exoClassKit()`. `init`'s return value is the state that will be preserved by SwingSet for each |
| 54 | +object. `exo()` doesn't have an init parameter, so it creates and returns the singleton immediately rather |
| 55 | +than returning a maker. |
| 56 | + |
| 57 | +The Exo objects (or just "Exos") defined by these functions are both persistent and virtualizable. SwingSet |
| 58 | +knows their complete state, so it can page them out when space is tight, and page them back in when they are |
| 59 | +referenced again. |
| 60 | + |
| 61 | +Fields of the object returned by the `init` function become fields of the persistent state. (These cannot |
| 62 | +currently be changed on upgrade, though we're considering relaxing that restriction.) Within methods they can |
| 63 | +each be accessed as fields of `this.state`. Our convention is to extract the fields that will be used in a |
| 64 | +method on its first line, like `const { a, b } = this.state;` Once that has been done, those variable can be |
| 65 | +read or written as normal javascript variables, and the values will persist. (For more details, see [the note |
| 66 | +here](https://docs.agoric.com/guides/zoe/contract-upgrade.html#kinds)). |
| 67 | + |
| 68 | +### Methods: defining behavior |
| 69 | + |
| 70 | +The methods argument of `exoClass()` is a bag of methods, written in [concise method |
| 71 | +syntax](https://github.com/Agoric/agoric-sdk/wiki/Arrow-Function-Style#far-classes-do-not-use-arrow-function-style). |
| 72 | +`exoClass()` defines a single facet. |
| 73 | + |
| 74 | +The methodKit parameter to `exoClassKit` is a record of labelled bags of methods. Each label defines a facet |
| 75 | +of the object. All facets of each object share access to the same state, but each facet is a separate |
| 76 | +capability. Within the methods, other facets can be reached by name within `this.facets`. The maker you get |
| 77 | +from `exoClassKit()` builds a new object each time it is called, and return all the facets. The caller can |
| 78 | +decide which of the facets to pass to each recipient. |
| 79 | + |
| 80 | +### Guards: defensive methods |
| 81 | + |
| 82 | +These objects and facets are designed to be durable and shareable across address space boundaries. Since code |
| 83 | +in different vats might not be mutually trusting, code needs to be defensive about parameters received from |
| 84 | +remote callers. Interface Guards let us express in code what parameters each method expects and can handle |
| 85 | +safely. If a caller provides a parameter that doesn't match the template specified in the guard, SwingSet |
| 86 | +returns an exception to the caller without notifying the receiver. If the return value doesn't match, the |
| 87 | +function returns an exception to the caller. |
| 88 | + |
| 89 | +`exoClass()` takes a guard for a single interface, defined by |
| 90 | + |
| 91 | +``` |
| 92 | +M.interface('name', { |
| 93 | + methodA: M.call(paramsAShape).returns(resultAShape), |
| 94 | + methodB: M.callWhen(M.await(paramsBShape)).returns(resultBShape), |
| 95 | +} |
| 96 | +``` |
| 97 | + |
| 98 | +`M.call()` verifies that all parameters match the guard before passing them through to the |
| 99 | +method. `M.callWhen(M.await(paramsBGuard))` awaits the resolution of the parameter, and then verifies that the |
| 100 | +result matches before invoking the method. When a guard is written this latter way, the method doesn't have to |
| 101 | +be `async`. In both cases, the method doesn't have to check for compliance with the guard. |
| 102 | + |
| 103 | +[Shapes can specify](https://endojs.github.io/endo/interfaces/_endo_patterns.PatternMatchers.html) simple |
| 104 | +types like `M.string()`, `M.remotable()`, and `M.number()`, as well as complex structures of records and |
| 105 | +arrays. The list of parameters can specify required and optional parameters, as well as allowing unspecified |
| 106 | +rest parameters. |
| 107 | + |
| 108 | +If you want to make use of the power of this type-checking within methods, you can call `mustMatch(specimen, |
| 109 | +pattern)` or `matches(specimen, pattern)` directly. The former throws if the pattern doesn't match, while the |
| 110 | +latter returns a boolean. |
| 111 | + |
| 112 | +### Options: finish and stateShape |
| 113 | + |
| 114 | +All the type definers can also take an [options |
| 115 | +argument](https://endojs.github.io/endo/types/_endo_exo.FarClassOptions.html) at the end, which is commonly |
| 116 | +used for a `finish()` function to complete initialization, or a stateShape, which can enforce invariants on |
| 117 | +the state values. |
| 118 | + |
| 119 | +`finish()`, if provided, is called after instances have been created but before they are returned to the |
| 120 | +caller. They can be used to send an initial state update, or to complete initialization which couldn't be done |
| 121 | +in `init`. `finish` has access to state and facets if needed. |
| 122 | + |
0 commit comments