Skip to content

Commit

Permalink
Protects calling create and acquires by needing to pass a "witnes…
Browse files Browse the repository at this point in the history
…s" of actually owning the `Feature` type parameter. This goes back to a suggestion from Emma.

Not 100% sure whether `acquires` needs this restriction, but we can start from there. It greatly simplifies our verification requirements, resulting also in that I could simplify documentation.

Closes: aptos-labs#9305
  • Loading branch information
wrwg authored and bors-libra committed Oct 4, 2021
1 parent ccd0136 commit 5a36a85
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 76 deletions.
66 changes: 21 additions & 45 deletions language/move-stdlib/nursery/docs/Capability.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ called within a transaction which has a capability as a parameter, it is guarant
has been obtained via a proper signer-based authorization step previously in the transaction's execution.


<a name="@Basic_Usage_1"></a>
<a name="@Usage_1"></a>

### Basic Usage
### Usage


Capabilities are used typically as follows:
Expand All @@ -31,12 +31,14 @@ struct ProtectedFeature { ... } // this can be just a type tag, or actually some
public fun initialize(s: &signer) {
// Create capability. This happens once at module initialization time.
Capability::create<ProtectedFeature>(s);
// One needs to provide a witness for being the owner of <code>ProtectedFeature</code>
// in the 2nd parameter.
Capability::create<ProtectedFeature>(s, &ProtectedFeature{});
}
public fun do_something(s: &signer) {
// Acquire the capability. This is the authorization step. Must have a signer to do so.
let cap = Capability::acquire<ProtectedFeature>(s);
let cap = Capability::acquire<ProtectedFeature>(s, &ProtectedFeature{});
// Pass the capability on to functions which require authorization.
critical(cap);
}
Expand All @@ -51,34 +53,7 @@ Notice that a key feature of capabilities is that they do not require extra veri
to ensure authorization is valid.


<a name="@Advanced_Authorization_Scenarios_2"></a>

### Advanced Authorization Scenarios


In the basic usage above, in order to acquire <code><a href="Capability.md#0x1_Capability_Cap">Cap</a>&lt;ProtectedFeature&gt;</code>, we needed a signer
that owns this capability. Because <code>Capability::acquires</code> is a public function, everybody can
acquire the capability provided the right signer is presented. But what if there authorization
scenarios which go beyond having a signer?

The current way how to achieve this in Move is to build a wrapper around <code><a href="Capability.md#0x1_Capability_Cap">Capability::Cap</a>&lt;T&gt;</code>.
The wrapper type will be owned by a specific module, restricting how values of it can be created.
Below, we extend the example from above to illustrate this pattern:

```
struct ProtectedFeatureCap has copy, drop {
cap: Capability::Cap<ProtectedFeature>
}
public fun acquire_protected_feature_access(s: &signer): ProtectedFeatureCap {
let cap = Capability::acquire<ProtectedFeature>(s);
validate_authorization(s, cap); // Do any additional authorization validation
ProtectedFeatureCap{cap}
}
```


<a name="@Delegation_3"></a>
<a name="@Delegation_2"></a>

### Delegation

Expand Down Expand Up @@ -109,20 +84,19 @@ is_valid_delegate_for_protected_feature(d);


- [Overview](#@Overview_0)
- [Basic Usage](#@Basic_Usage_1)
- [Advanced Authorization Scenarios](#@Advanced_Authorization_Scenarios_2)
- [Delegation](#@Delegation_3)
- [Usage](#@Usage_1)
- [Delegation](#@Delegation_2)
- [Struct `Cap`](#0x1_Capability_Cap)
- [Resource `CapState`](#0x1_Capability_CapState)
- [Resource `CapDelegateState`](#0x1_Capability_CapDelegateState)
- [Constants](#@Constants_4)
- [Constants](#@Constants_3)
- [Function `create`](#0x1_Capability_create)
- [Function `acquire`](#0x1_Capability_acquire)
- [Function `delegate`](#0x1_Capability_delegate)
- [Function `revoke`](#0x1_Capability_revoke)
- [Function `remove_element`](#0x1_Capability_remove_element)
- [Function `add_element`](#0x1_Capability_add_element)
- [Module Specification](#@Module_Specification_5)
- [Module Specification](#@Module_Specification_4)


<pre><code><b>use</b> <a href="">0x1::Errors</a>;
Expand Down Expand Up @@ -216,7 +190,7 @@ An internal data structure for representing a configured delegated capability.

</details>

<a name="@Constants_4"></a>
<a name="@Constants_3"></a>

## Constants

Expand All @@ -243,10 +217,11 @@ An internal data structure for representing a configured delegated capability.

## Function `create`

Creates a new capability class, owned by the passed signer.
Creates a new capability class, owned by the passed signer. A caller must pass a witness that
they own the <code>Feature</code> type parameter.


<pre><code><b>public</b> <b>fun</b> <a href="Capability.md#0x1_Capability_create">create</a>&lt;Feature&gt;(owner: &signer)
<pre><code><b>public</b> <b>fun</b> <a href="Capability.md#0x1_Capability_create">create</a>&lt;Feature&gt;(owner: &signer, _feature_witness: &Feature)
</code></pre>


Expand All @@ -255,7 +230,7 @@ Creates a new capability class, owned by the passed signer.
<summary>Implementation</summary>


<pre><code><b>public</b> <b>fun</b> <a href="Capability.md#0x1_Capability_create">create</a>&lt;Feature&gt;(owner: &signer) {
<pre><code><b>public</b> <b>fun</b> <a href="Capability.md#0x1_Capability_create">create</a>&lt;Feature&gt;(owner: &signer, _feature_witness: &Feature) {
<b>let</b> addr = <a href="_address_of">Signer::address_of</a>(owner);
<b>assert</b>(!<b>exists</b>&lt;<a href="Capability.md#0x1_Capability_CapState">CapState</a>&lt;Feature&gt;&gt;(addr), <a href="_already_published">Errors::already_published</a>(<a href="Capability.md#0x1_Capability_ECAP">ECAP</a>));
move_to&lt;<a href="Capability.md#0x1_Capability_CapState">CapState</a>&lt;Feature&gt;&gt;(owner, <a href="Capability.md#0x1_Capability_CapState">CapState</a>{ delegates: <a href="_empty">Vector::empty</a>() });
Expand All @@ -271,10 +246,11 @@ Creates a new capability class, owned by the passed signer.
## Function `acquire`

Acquires a capability token. Only the owner of the capability class, or an authorized delegate,
can succeed with this operation.
can succeed with this operation. A caller must pass a witness that they own the <code>Feature</code> type
parameter.


<pre><code><b>public</b> <b>fun</b> <a href="Capability.md#0x1_Capability_acquire">acquire</a>&lt;Feature&gt;(requester: &signer): <a href="Capability.md#0x1_Capability_Cap">Capability::Cap</a>&lt;Feature&gt;
<pre><code><b>public</b> <b>fun</b> <a href="Capability.md#0x1_Capability_acquire">acquire</a>&lt;Feature&gt;(requester: &signer, _feature_witness: &Feature): <a href="Capability.md#0x1_Capability_Cap">Capability::Cap</a>&lt;Feature&gt;
</code></pre>


Expand All @@ -283,7 +259,7 @@ can succeed with this operation.
<summary>Implementation</summary>


<pre><code><b>public</b> <b>fun</b> <a href="Capability.md#0x1_Capability_acquire">acquire</a>&lt;Feature&gt;(requester: &signer): <a href="Capability.md#0x1_Capability_Cap">Cap</a>&lt;Feature&gt;
<pre><code><b>public</b> <b>fun</b> <a href="Capability.md#0x1_Capability_acquire">acquire</a>&lt;Feature&gt;(requester: &signer, _feature_witness: &Feature): <a href="Capability.md#0x1_Capability_Cap">Cap</a>&lt;Feature&gt;
<b>acquires</b> <a href="Capability.md#0x1_Capability_CapState">CapState</a>, <a href="Capability.md#0x1_Capability_CapDelegateState">CapDelegateState</a> {
<b>let</b> addr = <a href="_address_of">Signer::address_of</a>(requester);
<b>if</b> (<b>exists</b>&lt;<a href="Capability.md#0x1_Capability_CapDelegateState">CapDelegateState</a>&lt;Feature&gt;&gt;(addr)) {
Expand Down Expand Up @@ -419,7 +395,7 @@ Helper to add an element to a vector.

</details>

<a name="@Module_Specification_5"></a>
<a name="@Module_Specification_4"></a>

## Module Specification

Expand Down
43 changes: 12 additions & 31 deletions language/move-stdlib/nursery/sources/Capability.move
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
/// called within a transaction which has a capability as a parameter, it is guaranteed that the capability
/// has been obtained via a proper signer-based authorization step previously in the transaction's execution.
///
/// ## Basic Usage
/// ## Usage
///
/// Capabilities are used typically as follows:
///
Expand All @@ -18,12 +18,14 @@
///
/// public fun initialize(s: &signer) {
/// // Create capability. This happens once at module initialization time.
/// Capability::create<ProtectedFeature>(s);
/// // One needs to provide a witness for being the owner of `ProtectedFeature`
/// // in the 2nd parameter.
/// Capability::create<ProtectedFeature>(s, &ProtectedFeature{});
/// }
///
/// public fun do_something(s: &signer) {
/// // Acquire the capability. This is the authorization step. Must have a signer to do so.
/// let cap = Capability::acquire<ProtectedFeature>(s);
/// let cap = Capability::acquire<ProtectedFeature>(s, &ProtectedFeature{});
/// // Pass the capability on to functions which require authorization.
/// critical(cap);
/// }
Expand All @@ -37,29 +39,6 @@
/// Notice that a key feature of capabilities is that they do not require extra verification steps
/// to ensure authorization is valid.
///
/// ## Advanced Authorization Scenarios
///
/// In the basic usage above, in order to acquire `Cap<ProtectedFeature>`, we needed a signer
/// that owns this capability. Because `Capability::acquires` is a public function, everybody can
/// acquire the capability provided the right signer is presented. But what if there authorization
/// scenarios which go beyond having a signer?
///
/// The current way how to achieve this in Move is to build a wrapper around `Capability::Cap<T>`.
/// The wrapper type will be owned by a specific module, restricting how values of it can be created.
/// Below, we extend the example from above to illustrate this pattern:
///
/// ```
/// struct ProtectedFeatureCap has copy, drop {
/// cap: Capability::Cap<ProtectedFeature>
/// }
///
/// public fun acquire_protected_feature_access(s: &signer): ProtectedFeatureCap {
/// let cap = Capability::acquire<ProtectedFeature>(s);
/// validate_authorization(s, cap); // Do any additional authorization validation
/// ProtectedFeatureCap{cap}
/// }
/// ```
///
/// ## Delegation
///
/// Capabilities come with the optional feature of *delegation*. Via delegation, an owner of a capability
Expand Down Expand Up @@ -109,16 +88,18 @@ module Std::Capability {
root: address
}

/// Creates a new capability class, owned by the passed signer.
public fun create<Feature>(owner: &signer) {
/// Creates a new capability class, owned by the passed signer. A caller must pass a witness that
/// they own the `Feature` type parameter.
public fun create<Feature>(owner: &signer, _feature_witness: &Feature) {
let addr = Signer::address_of(owner);
assert(!exists<CapState<Feature>>(addr), Errors::already_published(ECAP));
move_to<CapState<Feature>>(owner, CapState{ delegates: Vector::empty() });
}

/// Acquires a capability token. Only the owner of the capability class, or an authorized delegate,
/// can succeed with this operation.
public fun acquire<Feature>(requester: &signer): Cap<Feature>
/// can succeed with this operation. A caller must pass a witness that they own the `Feature` type
/// parameter.
public fun acquire<Feature>(requester: &signer, _feature_witness: &Feature): Cap<Feature>
acquires CapState, CapDelegateState {
let addr = Signer::address_of(requester);
if (exists<CapDelegateState<Feature>>(addr)) {
Expand Down Expand Up @@ -173,4 +154,4 @@ module Std::Capability {
spec fun spec_delegates<Feature>(addr: address): vector<address> {
global<CapState<Feature>>(addr).delegates
}
}
}

0 comments on commit 5a36a85

Please sign in to comment.