Create and use capabilities

In this tutorial we'll create Write capabilities for communal and owned namespaces, and use them to create AuthorisedEntries.

Prerequisites

A basic knowledge of the Rust programming language and executing commands in the terminal will be helpful for completing this tutorial. Some of the steps below also require cargo to be installed.

Additionally, knowledge of the Area API would be helpful. If you're not yet familiar, please see our dedicated tutorial for groupings.

Setup

  1. Create a new directory on your filesystem and name it something like caps.
  2. Using your terminal, run cargo init within the newly created directory.
  3. After than, run cargo add willow25 rand@0.8.0.

Communal capabilities

First we'll create a WriteCapability for a communal namespace.

Open src/main.rs, delete its contents, and enter the following:

use rand::rngs::OsRng;
use willow25::prelude::*;

fn main() {
  let mut csprng = OsRng;

  let (alfie_id, alfie_secret) =
    randomly_generate_subspace(&mut csprng);
  let communal_namespace_id =
    NamespaceId::from_bytes(&[17; 32]);

  // Create a new communal capability.
  // To use it, we'll need Alfie's secret.
  let communal_cap =
    WriteCapability::new_communal(
      communal_namespace_id.clone(),
      alfie_id.clone(),
    );

  println!("A communal capability: {:#?}", communal_cap);
}

In your terminal, run cargo run, and you should see the following output:

A communal capability: WriteCapability {
    genesis: Communal(
        CommunalGenesis {
            access_mode: Write,
            namespace_key: NamespaceId(
                -1111111111111111111111111111111111111111111111111111111111111111,
            ),
            user_key: SubspaceId(
                e28e9da138838ebed5eabdbaa7d4ed8bfc10213be3c3df1b2cb38a7585e38957,
            ),
        },
    ),
    delegations: [],
}

Authorising an entry

Next we'll use the WriteCapability we created and use it to turn an Entry into an AuthorisedEntry.

Make the following changes tosrc/main.rs:

use rand::rngs::OsRng;
use willow25::prelude::*;

fn main() {
  let mut csprng = OsRng;

  let (alfie_id, alfie_secret) =
    randomly_generate_subspace(&mut csprng);
  let communal_namespace_id =
    NamespaceId::from_bytes(&[17; 32]);

  // Create a new communal capability.
  // To use it, we'll need Alfie's secret.
  let communal_cap =
    WriteCapability::new_communal(
      communal_namespace_id.clone(),
      alfie_id.clone(),
    );

  println!("A communal capability: {:#?}", communal_cap);

  let entry_communal = Entry::builder()
    .namespace_id(communal_namespace_id.clone())
    .subspace_id(alfie_id.clone())
    .path(path!("/ideas"))
    .timestamp(12345)
    .payload(b"chocolate with mustard")
    .build().unwrap();

  // Authorise the entry using the communal
  // capability and Alfie's secret.
  let communal_authed = entry_communal
    .into_authorised_entry(
      &communal_cap,
      &alfie_secret,
    );

  assert!(communal_authed.is_ok());
  println!("Entry for communal namespace was authorised!");
}

In your terminal, run cargo run, and you should see the following output:

A communal capability: WriteCapability {
    genesis: Communal(
        CommunalGenesis {
            access_mode: Write,
            namespace_key: NamespaceId(
                -1111111111111111111111111111111111111111111111111111111111111111,
            ),
            user_key: SubspaceId(
                e28e9da138838ebed5eabdbaa7d4ed8bfc10213be3c3df1b2cb38a7585e38957,
            ),
        },
    ),
    delegations: [],
}
Entry for communal namespace was authorised!

Owned capabilities and delegation

Finally we'll create a WriteCapability for an owned namespace, delegate it, and use it to produce an AuthorisedEntry.

Make the following changes tosrc/main.rs:

use rand::rngs::OsRng;
use willow25::prelude::*;

fn main() {
  let mut csprng = OsRng;

  let (alfie_id, alfie_secret) =
    randomly_generate_subspace(&mut csprng);
  let communal_namespace_id =
    NamespaceId::from_bytes(&[17; 32]);

  // Create a new communal capability.
  // To use it, we'll need Alfie's secret.
  let communal_cap =
    WriteCapability::new_communal(
      communal_namespace_id.clone(),
      alfie_id.clone(),
    );

  println!("A communal capability: {:#?}", communal_cap);

  let entry_communal = Entry::builder()
    .namespace_id(communal_namespace_id.clone())
    .subspace_id(alfie_id.clone())
    .path(path!("/ideas"))
    .timestamp(12345)
    .payload(b"chocolate with mustard")
    .build().unwrap();

  // Authorise the entry using the communal
  // capability and Alfie's secret.
  let communal_authed = entry_communal
    .into_authorised_entry(
      &communal_cap,
      &alfie_secret,
    );

  assert!(communal_authed.is_ok());
  println!("Entry for communal namespace was authorised!");

  // Create the keypair of an owned namespace.
  let (owned_namespace_id, namespace_secret) =
    randomly_generate_owned_namespace(&mut csprng);

  // Create a new owned capability by using
  // the namespace secret.
  // To delegate it, we'll need Alfie's secret.
  let mut owned_cap = WriteCapability::new_owned(&namespace_secret, alfie_id);

  // Create a keypair for Betty.
  let (betty_id, betty_secret) =
    randomly_generate_subspace(&mut csprng);

  // Delegate our owned cap to Betty,
  // restricting her to her own subspace.
  // To use it, we'll need Betty's secret.
  owned_cap.delegate(
    &alfie_secret,
    Area::new_subspace_area(betty_id.clone()),
    betty_id.clone(),
  );

  println!(
    "A delegated owned capability: {:#?}",
    owned_cap,
  );

  let entry_owned = Entry::builder()
    .namespace_id(owned_namespace_id.clone())
    .subspace_id(betty_id)
    .path(path!("/blog"))
    .timestamp(45689)
    .payload(b"worried about alfie...")
    .build().unwrap();

  // Authorise the entry using the owned
  // capability and Betty's secret.
  let owned_authed = entry_owned
    .into_authorised_entry(&owned_cap, &betty_secret);

  assert!(owned_authed.is_ok());
  println!("Entry for owned namespace was authorised!")
}

In your terminal, run cargo run, and you should see the following output:

A communal capability: WriteCapability {
    genesis: Communal(
        CommunalGenesis {
            access_mode: Write,
            namespace_key: NamespaceId(
                -1111111111111111111111111111111111111111111111111111111111111111,
            ),
            user_key: SubspaceId(
                e28e9da138838ebed5eabdbaa7d4ed8bfc10213be3c3df1b2cb38a7585e38957,
            ),
        },
    ),
    delegations: [],
}
Entry for communal namespace was authorised!
A delegated owned capability: WriteCapability {
    genesis: Owned(
        OwnedGenesis {
            access_mode: Write,
            namespace_key: NamespaceId(
                -7e9f5c443cd2b20e3cda33f1e9deb614f8a5215526cd84a69b34b8254f458011,
            ),
            user_key: SubspaceId(
                e28e9da138838ebed5eabdbaa7d4ed8bfc10213be3c3df1b2cb38a7585e38957,
            ),
            initial_authorisation: ed25519::Signature {
                R: 0x9f88df74d7b9218ad350c311df15b18a5d5918e67a5d0cf449240ab2ba46ee19,
                s: 0x611df642f680487ae3b86dfbe6ca7521f7d0a863a73cae6f489bcb03c4823205,
            },
        },
    ),
    delegations: [
        Delegation {
            area: Area {
                subspace: Some(
                    SubspaceId(
                        7f010d66a791de15d84c3d8f6973ba4fcf8581874b497b036d1b20d46fd7d60f,
                    ),
                ),
                path: Path(
                    <empty>,
                ),
                times: Timestamp(
                    0,
                )..,
            },
            user: SubspaceId(
                7f010d66a791de15d84c3d8f6973ba4fcf8581874b497b036d1b20d46fd7d60f,
            ),
            signature: ed25519::Signature {
                R: 0x4fca226c8b31334a260225e51f7c932ebfbe4404b8c9d8d889914059648a0f6f,
                s: 0x179e00882bddea4d658cbc33079cbec2e5254829c00b46baa3043ecbba78740f,
            },
        },
    ],
}
Entry for owned namespace was authorised!

Summary

In this tutorial, we explored Willow’25's authorisation APIs: