|
53 | 53 | ;; Map of `path vector` -> `segment name` -> `[value trust-monitor meta child]`
|
54 | 54 | ;; Where:
|
55 | 55 | ;; value = Target value for CNS resolution, usually an address or scoped address
|
56 |
| -;; trust-monitor = controller for this CNS entry |
| 56 | +;; trust-monitor = controller for this CNS record |
57 | 57 | ;; meta = metadata field
|
58 | 58 | ;; child = child CNS node, may be nil. Usually a scoped address defining an actor and a path key e.g. [#5675 "bob"]
|
59 |
| -;; |
| 59 | +;; |
| 60 | +;; Trust monitor may be called with following actions: |
| 61 | +;; :update - Update CNS record |
| 62 | +;; |
60 | 63 | ;; Node key is implementation defined in general, but for main registry uses:
|
61 | 64 | ;; Empty vector for CNS root
|
62 | 65 | ;; Vector of segment strings for paths
|
|
78 | 81 | (split (name sym) \.)
|
79 | 82 | (fail :ARGUMENT "CNS name must be a Symbol")))
|
80 | 83 |
|
| 84 | +(defn -check-values [values] |
| 85 | + (cond |
| 86 | + (vector? values) |
| 87 | + (cond |
| 88 | + (= 4 (count values)) :OK |
| 89 | + (fail :ARGUMENT "CNS record must have 4 elements")) |
| 90 | + (fail :ARGUMENT "CNS record values must be a Vymbol"))) |
| 91 | + |
81 | 92 | ;; ========================================================
|
82 | 93 | ;; CNS User API - See CAD014
|
83 | 94 |
|
|
117 | 128 | (get (read sym) 0))
|
118 | 129 |
|
119 | 130 | (defn create ^{
|
120 |
| - :doc {:description "Creates a CNS entry with given reference, controller and metadata" |
| 131 | + :doc {:description "Creates a CNS entry with given data [value, controller, metadata, child]" |
121 | 132 | :examples [{:code "(*registry*/create 'my.actor.name target *address* {:some :metadata})"}]
|
122 |
| - :signature [{:params [sym]}]}} |
123 |
| - ([sym] (recur sym nil *address* nil nil)) |
124 |
| - ([sym addr] (recur sym addr *address* nil nil)) |
125 |
| - ([sym addr cont] (recur sym addr cont nil nil)) |
126 |
| - ([sym addr cont meta] (recur sym addr cont meta nil)) |
127 |
| - ([sym addr cont meta child] |
128 |
| - (let [path (-check sym) |
| 133 | + :signature [{:params [sym]} |
| 134 | + {:params [sym value]} |
| 135 | + {:params [sym value cont]} |
| 136 | + {:params [sym value cont meta]} |
| 137 | + {:params [sym value cont meta child]}]}} |
| 138 | + ([sym & vals] |
| 139 | + (let [nv (count vals) |
| 140 | + _ (cond (> nv 4) (fail :ARITY "Too many CNS record values")) |
| 141 | + path (-check sym) |
129 | 142 | n (count path)]
|
130 | 143 | (cond (zero? n) (fail :ARGUMENT "CNS path must have at least one segment"))
|
131 | 144 | (loop [i 0
|
|
135 | 148 | (nil? ref)
|
136 | 149 | (fail :STATE (str "No CNS child path at: " (slice path 0 i)))
|
137 | 150 |
|
138 |
| - ;; are we at end of path? if so perform write at current position |
| 151 | + ;; are we at end of path? if so, perform write at current position |
139 | 152 | (>= (inc i) n)
|
140 |
| - (call ref (cns-write pname addr cont meta child) ) |
141 |
| - |
142 |
| - (if-let [rec (call ref (cns-read pname))] |
143 |
| - (recur (inc i) (first rec)) |
| 153 | + (let [rec (cond (< nv 4) |
| 154 | + (let [evs (call ref (cns-read pname)) |
| 155 | + evs (or evs [nil *address* nil nil])] |
| 156 | + (concat vals (slice evs nv 4))) |
| 157 | + vals) ] |
| 158 | + (call ref (cns-write pname rec))) |
| 159 | + |
| 160 | + (let [rec (call ref (cns-read pname))] |
| 161 | + (if-let [child (get rec 3)] |
| 162 | + (recur (inc i) child) |
144 | 163 |
|
145 |
| - ;; need to construct a new (empty) intermediate CNS child node here |
146 |
| - (let [nref (call ref (cns-create-node pname cont))] |
147 |
| - (call ref (cns-write pname nil cont nil nref) ) |
148 |
| - (recur (inc i) nref))) |
| 164 | + ;; need to construct a new (empty) intermediate CNS child node here |
| 165 | + (let [cont (or (get vals 1) *address*) ;; controller for intermediate node |
| 166 | + nref (call ref (cns-create-node pname cont)) |
| 167 | + rec (cond rec |
| 168 | + (assoc rec 3 nref) ;; update existing child node link |
| 169 | + [nil cont nil nref])] |
| 170 | + (call ref (cns-write pname rec)) |
| 171 | + (recur (inc i) nref)))) |
149 | 172 | ))
|
150 | 173 | )
|
151 | 174 | ;; TODO: Not clear what default return value should be? [~*address* path]?
|
152 | 175 | )))
|
153 | 176 |
|
| 177 | +(defn update |
| 178 | + ^{:doc {:description "Updates a CNS entry with given value. Record must already exist." |
| 179 | + :signature [{:params [sym value]}]}} |
| 180 | + [sym value] |
| 181 | + (if-let [rec (read sym)] |
| 182 | + (apply create sym (assoc rec 0 value)) |
| 183 | + (fail :STATE (str "CNS record to update does not exist: " sym)))) |
| 184 | + |
154 | 185 | (defn control
|
| 186 | + ^{:doc {:description "Change controller for a CNS node." |
| 187 | + :signature [{:params [name controller]}]}} |
155 | 188 | [sym cont]
|
156 | 189 | (if-let [rec (read sym)]
|
157 |
| - (let [[v c m child] rec] |
158 |
| - (update sym v cont m child)) |
| 190 | + (apply create sym (assoc rec 1 cont)) |
159 | 191 | (fail :STATE "CNS record does not exist")))
|
160 | 192 |
|
161 |
| - |
162 |
| -(defn change-control |
163 |
| - ^{:callable true |
164 |
| - :doc {:description "Changes controller for a CNS node." |
165 |
| - :examples [{:code "(call *registry* (cns-control 'my.actor trust-monitor-address)"}] |
166 |
| - :signature [{:params [name addr]}]}} |
167 |
| - [cont] |
168 |
| - (let [owners cns-owners |
169 |
| - own (get owners *scope*)] |
170 |
| - (cond |
171 |
| - (trust/trusted? own *caller* :control) |
172 |
| - (set! cns-owners (assoc owners *scope* cont)) |
173 |
| - (fail :TRUST "No control right for CNS node")))) |
174 |
| - |
| 193 | +;; private function to get controller for a node |
175 | 194 | (defn -controller [path]
|
176 | 195 | (cond
|
177 |
| - (empty? *scope*) root-controller |
178 |
| - (let [[& ps p] *scope*] |
| 196 | + (empty? path) root-controller |
| 197 | + (let [[& ps p] path] |
179 | 198 | (get-in cns-database [ps p 1]))))
|
180 | 199 |
|
181 | 200 | ;; ============================================================================
|
182 | 201 | ;; Standard CNS SPI - exprected to be callable by libraries / advanced users
|
183 | 202 |
|
184 |
| -(defn cns-control |
| 203 | +(defn change-control |
185 | 204 | ^{:callable true
|
186 |
| - :doc {:description "Updates a CNS name mapping to set a new controller. May only be performed by a current controller." |
| 205 | + :doc {:description "Changes controller for a CNS node. May only be performed by a current controller." |
187 | 206 | :examples [{:code "(call *registry* (cns-control 'my.actor trust-monitor-address)"}]
|
188 | 207 | :signature [{:params [name addr]}]}}
|
189 |
| - [sym controller] |
190 |
| - (let [path (-check sym) |
191 |
| - record (get cns-database path)] |
192 |
| - (when (nil? record) |
193 |
| - (fail :STATE "CNS record does not exist")) |
194 |
| - (when (not (trust/trusted? (second record) *caller* :control)) |
195 |
| - (fail :TRUST "Caller is not trusted with transferring control for that CNS record")) |
196 |
| - (set-in! cns-database [path 1] controller))) |
197 |
| - |
198 |
| -(defn cns-resolve |
199 |
| - ^{:callable true |
200 |
| - :doc {:description "Resolves a name in the Convex Name Service." |
201 |
| - :examples [{:code "(call *registry* (cns-resolve 'convex.registry)"}] |
202 |
| - :signature [{:params [addr]}]}} |
203 |
| - [sym] |
204 |
| - (let [path (-check sym) |
205 |
| - record (get cns-database path)] |
206 |
| - (if record (first record) nil))) |
207 |
| - |
208 |
| -(defn cns-update |
209 |
| - ^{:callable true |
210 |
| - :doc {:description "Updates or adds a name mapping in the Convex Name Service. Only the owner of a CNS record may update the mapping for an existing name" |
211 |
| - :examples [{:code "(call *registry* (cns-update 'my.actor addr)"}] |
212 |
| - :signature [{:params [name addr]}]}} |
213 |
| - ([sym addr] |
214 |
| - (recur sym addr nil)) |
215 |
| - ([sym addr meta] |
216 |
| - (when-not (account addr) |
217 |
| - (fail :NOBODY "Can only use an existing account")) |
218 |
| - (let [path (-check sym) |
219 |
| - record (get cns-database path) |
220 |
| - monitor (if record (second record) *caller*)] ;; TODO limit ability to crteate top level CNS |
221 |
| - (and record (not (trust/trusted? monitor *caller* :update)) |
222 |
| - (fail :TRUST "Unauthorised update to CNS record")) |
223 |
| - |
224 |
| - (set! cns-database |
225 |
| - (assoc cns-database |
226 |
| - path |
227 |
| - [addr monitor meta]))))) |
| 208 | + [controller] |
| 209 | + (let [path *scope*] |
| 210 | + (when (not (trust/trusted? (get cns-owners path) *caller* :control)) |
| 211 | + (fail :TRUST "Formbitten to change controller for CNS node")) |
| 212 | + (set-in! cns-owners [path] controller))) |
228 | 213 |
|
229 | 214 | (defn cns-create-node
|
230 | 215 | ^{:callable true
|
231 |
| - :doc {:description "Creates a child CNS node." |
| 216 | + :doc {:description "Creates a child CNS node, if it does not yet exist. Returns child node scoped reference." |
232 | 217 | :examples [{:code "(call parent-node (cns-create-node \\\"child-name\\\"))"}]
|
233 | 218 | :signature [{:params [sym]}]}}
|
234 | 219 | [pname owner]
|
235 |
| - (or (trust/trusted? (get cns-owners *scope*) *caller* :create pname) (fail :TRUST "No permission to create CNS node")) |
236 |
| - (let [path (conj *scope* pname)] |
237 |
| - (if (get cns-database path) (fail :STATE "CNS node already exists")) |
238 |
| - (set-in! cns-owners [path] owner) |
239 |
| - (set-in! cns-database [path] {}) |
240 |
| - [~*address* path])) |
| 220 | + (or (trust/trusted? (get cns-owners *scope*) *caller* :create pname) (fail :TRUST "Forbidden to create CNS node")) |
| 221 | + (let [rec (get-in cns-database [*scope* pname]) |
| 222 | + _ (if-let [existing (get rec 3)] (return existing)) |
| 223 | + path (conj *scope* pname) |
| 224 | + ref [~*address* path]] |
| 225 | + (set-in! cns-owners [path] owner) ;; new node owner |
| 226 | + (set-in! cns-database [path] {}) ;; new mapping to child records |
| 227 | + ref)) |
241 | 228 |
|
242 | 229 | (defn cns-read
|
243 | 230 | ^{:callable true
|
|
252 | 239 | :doc {:description "Writes a CNS record from this Actor. Assumes a path vector passed in *scope*."
|
253 | 240 | :examples [{:code "(call [cns-node cns-key] (cns-write \"my-name\" new-record))"}]
|
254 | 241 | :signature [{:params [sym]}]}}
|
255 |
| - [pname addr cont meta child] |
| 242 | + [pname values] |
| 243 | + (or (str? pname) (fail :ARGUMENT "CNS path element must be a String")) |
| 244 | + (-check-values values) |
256 | 245 | (let [sm (get cns-database *scope*)]
|
257 |
| - (or (str? pname) (fail :ARGUMENT "CNS path element must be a string")) |
258 | 246 | (or sm (error :STATE "CNS Node key not valid"))
|
259 | 247 | (if-let [rec (get sm pname)]
|
260 | 248 | ;; This is an existing record, so check record controller
|
261 |
| - (or (trust/trusted? (get rec 1) *caller* :update) (fail :TRUST "No permission to update CNS record")) |
262 |
| - ;; This is a new record, so check create permission TODO use per-node monitor? |
263 |
| - (or (trust/trusted? (-controller *scope*) *caller* :create pname) (fail :TRUST "No permission to create CNS record"))) |
| 249 | + (or (trust/trusted? (get rec 1) *caller* :update) (fail :TRUST "Forbidden to update CNS record")) |
| 250 | + |
| 251 | + ;; This is a new record, so check create permission |
| 252 | + (or (trust/trusted? (-controller *scope*) *caller* :create pname) (fail :TRUST "Forbidden to create CNS record"))) |
264 | 253 |
|
265 | 254 | ;; update record since at this point all required checks have passed
|
266 |
| - (set-in! cns-database [*scope* pname] [addr cont meta child]))) |
| 255 | + (set-in! cns-database [*scope* pname] values) |
| 256 | + values)) |
267 | 257 |
|
268 | 258 | ;; =========================================
|
269 | 259 | ;; Trust SPI
|
|
0 commit comments