From 274369e8cffd7031035fc19e3091341ea955fd50 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 2 Jan 2020 20:30:06 +0100 Subject: [PATCH 1/3] Add outstanding questions --- docs/architecture/adr-003-dynamic-capability-store.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/architecture/adr-003-dynamic-capability-store.md b/docs/architecture/adr-003-dynamic-capability-store.md index 7272c138f816..8d7e61842416 100644 --- a/docs/architecture/adr-003-dynamic-capability-store.md +++ b/docs/architecture/adr-003-dynamic-capability-store.md @@ -247,6 +247,13 @@ func (k Mod1Keeper) UseResource(ctx Context, capability Capability, resource str If module 2 passed the capability key to module 3, module 3 could then claim it and call module 1 just like module 2 did (in which case module 2 could no longer call `GetCapability`, since it would then be owned uniquely by module 3). +### Outstanding questions + +The correct answers to these questions depend on the intended use-cases, which we do not yet understand in sufficiently precise detail. This document should be updated once they are better understood. + +- Should capabilities be single-owner or multi-owner? +- How should revokability & claimability work? + ## Status Proposed. From d09a65d33973f3e30f7c111b4fa2535327c120e7 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Sat, 4 Jan 2020 21:04:16 +0100 Subject: [PATCH 2/3] Update all methods --- .../adr-003-dynamic-capability-store.md | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/docs/architecture/adr-003-dynamic-capability-store.md b/docs/architecture/adr-003-dynamic-capability-store.md index 8d7e61842416..bfda81f40aa3 100644 --- a/docs/architecture/adr-003-dynamic-capability-store.md +++ b/docs/architecture/adr-003-dynamic-capability-store.md @@ -28,11 +28,11 @@ to the existing `TransientStore` but without erasure on `Commit()`, which this ` The `CapabilityKeeper` will use two stores: a regular, persistent `KVStore`, which will track what capabilities have been created by each module, and an in-memory `MemoryStore` (described below), which will store the actual capabilities. The `CapabilityKeeper` will define the following types & functions: -The `Capability` interface, similar to `StoreKey`, provides `Name()` and `String()` methods. +The `Capability` interface is similar to `StoreKey`, but has a globally unique `Index()` instead of a name. A `String()` method is provided for debugging. ```golang type Capability interface { - Name() string + Index() uint64 String() string } ``` @@ -99,18 +99,21 @@ func (ck CapabilityKeeper) InitialiseAndSeal(ctx Context) { persistentStore := ctx.KVStore(ck.persistentKey) memoryStore := ctx.KVStore(ck.memoryKey) // initialise memory store for all names in persistent store - for _, key := range persistentStore.Iter() { - capability = &CapabilityKey{name: key} - memoryStore.Set("fwd/" + capability, name) - memoryStore.Set("rev/" + name, capability) + for index, value := range persistentStore.Iter() { + capability = &CapabilityKey{index: index} + for moduleAndCapability := range value { + moduleName, capabilityName := moduleAndCapability.Split("/") + memoryStore.Set(moduleName + "/fwd/" + capability, capabilityName) + memoryStore.Set(moduleName + "/rev/" + capabilityName, capability) + } } ck.sealed = true } ``` `NewCapability` can be called by any module to create a new unique, unforgeable object-capability -reference. The newly created capability is *not* automatically persisted, `ClaimCapability` must be -called on the `ScopedCapabilityKeeper` in order to persist it. +reference. The newly created capability is automatically persisted; the calling module need not +call `ClaimCapability`. ```golang func (sck ScopedCapabilityKeeper) NewCapability(ctx Context, name string) (Capability, error) { @@ -119,25 +122,33 @@ func (sck ScopedCapabilityKeeper) NewCapability(ctx Context, name string) (Capab if memoryStore.Get("rev/" + name) != nil { return nil, errors.New("name already taken") } + // fetch the current index + index := persistentStore.Get("index") // create a new capability - capability := &CapabilityKey{name: name} + capability := &CapabilityKey{index: index} + // set persistent store + persistentStore.Set(index, Set.singleton(sck.moduleName + "/" + name)) + // update the index + index++ + persistentStore.Set("index", index) // set forward mapping in memory store from capability to name - memoryStore.Set("fwd/" + capability, name) + memoryStore.Set(sck.moduleName + "/fwd/" + capability, name) // set reverse mapping in memory store from name to capability - memoryStore.Set("rev/" + name, capability) + memoryStore.Set(sck.moduleName + "/rev/" + name, capability) // return the newly created capability return capability } ``` `AuthenticateCapability` can be called by any module to check that a capability -does in fact correspond to a particular name (the name can be untrusted user input). +does in fact correspond to a particular name (the name can be untrusted user input) +with which the calling module previously associated it. ```golang func (sck ScopedCapabilityKeeper) AuthenticateCapability(name string, capability Capability) bool { memoryStore := ctx.KVStore(sck.memoryKey) // return whether forward mapping in memory store matches name - return memoryStore.Get("fwd/" + capability) === name + return memoryStore.Get(sck.moduleName + "/fwd/" + capability) === name } ``` @@ -147,16 +158,17 @@ func (sck ScopedCapabilityKeeper) AuthenticateCapability(name string, capability to call `ClaimCapability` will own it. To avoid confusion, a module which calls `NewCapability` SHOULD either call `ClaimCapability` or pass the capability to another module which will then claim it. ```golang -func (sck ScopedCapabilityKeeper) ClaimCapability(ctx Context, capability Capability) error { +func (sck ScopedCapabilityKeeper) ClaimCapability(ctx Context, capability Capability, name string) error { persistentStore := ctx.KVStore(sck.persistentKey) memoryStore := ctx.KVStore(sck.memoryKey) - // fetch name from memory store - name := memoryStore.Get("fwd/" + capability) - if name === nil { - return errors.New("capability not found") - } - // set name to module in persistent store - persistentStore.Set(name, sck.moduleName) + // set forward mapping in memory store from capability to name + memoryStore.Set(sck.moduleName + "/fwd/" + capability, name) + // set reverse mapping in memory store from name to capability + memoryStore.Set(sck.moduleName + "/rev/" + name, capability) + // update owner set in persistent store + owners := persistentStore.Get(capability.Index()) + owners.add(sck.moduleName + "/" + name) + persistentStore.Set(capability.Index(), owners) } ``` @@ -165,15 +177,10 @@ claims a capability, the previously owning module will no longer be able to clai ```golang func (sck ScopedCapabilityKeeper) GetCapability(ctx Context, name string) (Capability, error) { - persistentStore := ctx.KVStore(sck.persistentKey) memoryStore := ctx.KVStore(sck.memoryKey) - // check that this module owns the capability with this name - res := persistentStore.Get(name) - if res != sck.moduleName { - return errors.New("capability of this name not owned by module") - } - // fetch capability from memory store, return it - capability := memoryStore.Get("rev/" + name) + // fetch capability from memory store + capability := memoryStore.Get(sck.moduleName + "/rev/" + name) + // return the capability return capability } ``` @@ -219,7 +226,7 @@ mod2Keeper.SomeFunction(ctx, capability, args...) ```golang func (k Mod2Keeper) SomeFunction(ctx Context, capability Capability) { - k.sck.ClaimCapability(ctx, capability) + k.sck.ClaimCapability(ctx, capability, "resourceABC") // other logic... } ``` @@ -245,14 +252,7 @@ func (k Mod1Keeper) UseResource(ctx Context, capability Capability, resource str ``` If module 2 passed the capability key to module 3, module 3 could then claim it and call module 1 just like module 2 did -(in which case module 2 could no longer call `GetCapability`, since it would then be owned uniquely by module 3). - -### Outstanding questions - -The correct answers to these questions depend on the intended use-cases, which we do not yet understand in sufficiently precise detail. This document should be updated once they are better understood. - -- Should capabilities be single-owner or multi-owner? -- How should revokability & claimability work? +(in which case module 1, module 2, and module 3 would all be able to use this capability). ## Status From 9724a3a846e5aba7796c6d5c4cbbb705684c399f Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Sat, 4 Jan 2020 21:07:42 +0100 Subject: [PATCH 3/3] Update documentation for `ClaimCapability` --- docs/architecture/adr-003-dynamic-capability-store.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/architecture/adr-003-dynamic-capability-store.md b/docs/architecture/adr-003-dynamic-capability-store.md index bfda81f40aa3..7fd438fd931c 100644 --- a/docs/architecture/adr-003-dynamic-capability-store.md +++ b/docs/architecture/adr-003-dynamic-capability-store.md @@ -152,10 +152,9 @@ func (sck ScopedCapabilityKeeper) AuthenticateCapability(name string, capability } ``` -`ClaimCapability` allows a module to claim a capability key which it has received (perhaps by calling `NewCapability`, or from another module), so that future `GetCapability` calls will succeed. +`ClaimCapability` allows a module to claim a capability key which it has received from another module so that future `GetCapability` calls will succeed. -`ClaimCapability` MUST be called, even if `NewCapability` was called by the same module. Capabilities are single-owner, so if multiple modules have a single `Capability` reference, the last module -to call `ClaimCapability` will own it. To avoid confusion, a module which calls `NewCapability` SHOULD either call `ClaimCapability` or pass the capability to another module which will then claim it. +`ClaimCapability` MUST be called if a module which receives a capability wishes to access it by name in the future. Capabilities are multi-owner, so if multiple modules have a single `Capability` reference, they will all own it. ```golang func (sck ScopedCapabilityKeeper) ClaimCapability(ctx Context, capability Capability, name string) error {