# ADR 012: State Accessors
# Changelog
- 2019 Sep 04: Initial draft
# Context
SDK modules currently use the KVStore
interface and Codec
to access their respective state. While
this provides a large degree of freedom to module developers, it is hard to modularize and the UX is
mediocre.
First, each time a module tries to access the state, it has to marshal the value and set or get the
value and finally unmarshal. Usually this is done by declaring Keeper.GetXXX
and Keeper.SetXXX
functions,
which are repetitive and hard to maintain.
Second, this makes it harder to align with the object capability theorem: the right to access the
state is defined as a StoreKey
, which gives full access on the entire Merkle tree, so a module cannot
send the access right to a specific key-value pair (or a set of key-value pairs) to another module safely.
Finally, because the getter/setter functions are defined as methods of a module's Keeper
, the reviewers
have to consider the whole Merkle tree space when they reviewing a function accessing any part of the state.
There is no static way to know which part of the state that the function is accessing (and which is not).
# Decision
We will define a type named Value
:
The Value
works as a reference for a key-value pair in the state, where Value.m
defines the key-value
space it will access and Value.key
defines the exact key for the reference.
We will define a type named Mapping
:
The Mapping
works as a reference for a key-value space in the state, where Mapping.storeKey
defines
the IAVL (sub-)tree and Mapping.prefix
defines the optional subspace prefix.
We will define the following core methods for the Value
type:
We will define the following core methods for the Mapping
type:
Each method of the Mapping
type that is passed the arugments ctx
, key
, and args...
will proxy
the call to Mapping.Value(key)
with arguments ctx
and args...
.
In addition, we will define and provide a common set of types derived from the Value
type:
Where the encoding schemes can be different, o
arguments in core methods are typed, and ptr
arguments
in core methods are replaced by explicit return types.
Finally, we will define a family of types derived from the Mapping
type:
Where the key
argument in core method is typed.
Some of the properties of the accessor types are:
- State access happens only when a function which takes a
Context
as an argument is invoked - Accessor type structs give rights to access the state only that the struct is referring, no other
- Marshalling/Unmarshalling happens implicitly within the core methods
# Status
Proposed
# Consequences
# Positive
- Serialization will be done automatically
- Shorter code size, less boilerplate, better UX
- References to the state can be transferred safely
- Explicit scope of accessing
# Negative
- Serialization format will be hidden
- Different architecture from the current, but the use of accessor types can be opt-in
- Type-specific types (e.g.
Boolean
andInteger
) have to be defined manually