github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/docs/architecture/adr-012-state-accessors.md (about) 1 # ADR 012: State Accessors 2 3 ## Changelog 4 5 - 2019 Sep 04: Initial draft 6 7 ## Context 8 9 SDK modules currently use the `KVStore` interface and `Codec` to access their respective state. While 10 this provides a large degree of freedom to module developers, it is hard to modularize and the UX is 11 mediocre. 12 13 First, each time a module tries to access the state, it has to marshal the value and set or get the 14 value and finally unmarshal. Usually this is done by declaring `Keeper.GetXXX` and `Keeper.SetXXX` functions, 15 which are repetitive and hard to maintain. 16 17 Second, this makes it harder to align with the object capability theorem: the right to access the 18 state is defined as a `StoreKey`, which gives full access on the entire Merkle tree, so a module cannot 19 send the access right to a specific key-value pair (or a set of key-value pairs) to another module safely. 20 21 Finally, because the getter/setter functions are defined as methods of a module's `Keeper`, the reviewers 22 have to consider the whole Merkle tree space when they reviewing a function accessing any part of the state. 23 There is no static way to know which part of the state that the function is accessing (and which is not). 24 25 ## Decision 26 27 We will define a type named `Value`: 28 29 ```go 30 type Value struct { 31 m Mapping 32 key []byte 33 } 34 ``` 35 36 The `Value` works as a reference for a key-value pair in the state, where `Value.m` defines the key-value 37 space it will access and `Value.key` defines the exact key for the reference. 38 39 We will define a type named `Mapping`: 40 41 ```go 42 type Mapping struct { 43 storeKey sdk.StoreKey 44 cdc *codec.Codec 45 prefix []byte 46 } 47 ``` 48 49 The `Mapping` works as a reference for a key-value space in the state, where `Mapping.storeKey` defines 50 the IAVL (sub-)tree and `Mapping.prefix` defines the optional subspace prefix. 51 52 We will define the following core methods for the `Value` type: 53 54 ```go 55 // Get and unmarshal stored data, noop if not exists, panic if cannot unmarshal 56 func (Value) Get(ctx Context, ptr interface{}) {} 57 58 // Get and unmarshal stored data, return error if not exists or cannot unmarshal 59 func (Value) GetSafe(ctx Context, ptr interface{}) {} 60 61 // Get stored data as raw byte slice 62 func (Value) GetRaw(ctx Context) []byte {} 63 64 // Marshal and set a raw value 65 func (Value) Set(ctx Context, o interface{}) {} 66 67 // Check if a raw value exists 68 func (Value) Exists(ctx Context) bool {} 69 70 // Delete a raw value value 71 func (Value) Delete(ctx Context) {} 72 ``` 73 74 We will define the following core methods for the `Mapping` type: 75 76 ```go 77 // Constructs key-value pair reference corresponding to the key argument in the Mapping space 78 func (Mapping) Value(key []byte) Value {} 79 80 // Get and unmarshal stored data, noop if not exists, panic if cannot unmarshal 81 func (Mapping) Get(ctx Context, key []byte, ptr interface{}) {} 82 83 // Get and unmarshal stored data, return error if not exists or cannot unmarshal 84 func (Mapping) GetSafe(ctx Context, key []byte, ptr interface{}) 85 86 // Get stored data as raw byte slice 87 func (Mapping) GetRaw(ctx Context, key []byte) []byte {} 88 89 // Marshal and set a raw value 90 func (Mapping) Set(ctx Context, key []byte, o interface{}) {} 91 92 // Check if a raw value exists 93 func (Mapping) Has(ctx Context, key []byte) bool {} 94 95 // Delete a raw value value 96 func (Mapping) Delete(ctx Context, key []byte) {} 97 ``` 98 99 Each method of the `Mapping` type that is passed the arugments `ctx`, `key`, and `args...` will proxy 100 the call to `Mapping.Value(key)` with arguments `ctx` and `args...`. 101 102 In addition, we will define and provide a common set of types derived from the `Value` type: 103 104 ```go 105 type Boolean struct { Value } 106 type Enum struct { Value } 107 type Integer struct { Value; enc IntEncoding } 108 type String struct { Value } 109 // ... 110 ``` 111 112 Where the encoding schemes can be different, `o` arguments in core methods are typed, and `ptr` arguments 113 in core methods are replaced by explicit return types. 114 115 Finally, we will define a family of types derived from the `Mapping` type: 116 117 ```go 118 type Indexer struct { 119 m Mapping 120 enc IntEncoding 121 } 122 ``` 123 124 Where the `key` argument in core method is typed. 125 126 Some of the properties of the accessor types are: 127 128 - State access happens only when a function which takes a `Context` as an argument is invoked 129 - Accessor type structs give rights to access the state only that the struct is referring, no other 130 - Marshalling/Unmarshalling happens implicitly within the core methods 131 132 ## Status 133 134 Proposed 135 136 ## Consequences 137 138 ### Positive 139 140 - Serialization will be done automatically 141 - Shorter code size, less boilerplate, better UX 142 - References to the state can be transferred safely 143 - Explicit scope of accessing 144 145 ### Negative 146 147 - Serialization format will be hidden 148 - Different architecture from the current, but the use of accessor types can be opt-in 149 - Type-specific types (e.g. `Boolean` and `Integer`) have to be defined manually 150 151 ### Neutral 152 153 ## References 154 155 - [#4554](https://github.com/cosmos/cosmos-sdk/issues/4554)