Create and ingest a drop

In this tutorial we will use create_drop and ingest_drop to transport data between two Stores via a Sideloading drop.

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 Store API would be helpful. If you're not yet familiar, please see our dedicated tutorial for stores.

Setup

  1. Create a new directory on your filesystem and name it something like drop.
  2. Using your terminal, run cargo init within the newly created directory.
  3. After than, run cargo add willow_25 willow-store-simple-sled sled smol ufotofu willow-sideload.

Create a store and ingest an entry

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

use willow_25::{
    AccessMode, AuthorisationToken, AuthorisedEntry, Capability, Entry, NamespaceId25, Path,
    PayloadDigest25, SubspaceId25,
    data_model::{EntryIngestionSuccess, EntryOrigin, Store},
};
use willow_store_simple_sled::StoreSimpleSled;

fn main() {
    smol::block_on(async {
        // Instantiate a store
        let namespace_id = NamespaceId25::new_communal();
        let export_db = sled::open("import_db").unwrap();

        let export_store = StoreSimpleSled::<
            1024,
            1024,
            1024,
            NamespaceId25,
            SubspaceId25,
            PayloadDigest25,
            AuthorisationToken,
        >::new(&namespace_id, export_db)
        .unwrap();

        println!("Instantiated the export store!");

        // Create an entry
        let (alfie_id, alfie_secret) = SubspaceId25::new();

        let write_cap =
            Capability::new_communal(namespace_id.clone(), alfie_id.clone(), AccessMode::Write)
                .unwrap();

        let path = Path::from_slices(&["ideas", "clock"]).unwrap();
        let payload = b"An emoji clock";
        let digest = PayloadDigest25::new_from_slice(payload);

        let entry = Entry::new(
            namespace_id.clone(),
            alfie_id.clone(),
            path.clone(),
            100,
            payload.len() as u64,
            digest.clone(),
        );

        let token = write_cap.authorisation_token(&entry, alfie_secret).unwrap();
        let authed_entry = AuthorisedEntry::new(entry, token).unwrap();

        smol::block_on(async {
            // Ingest the entry...
            if let Ok(EntryIngestionSuccess::Success) = export_store
                .ingest_entry(authed_entry, false, EntryOrigin::Local)
                .await
            {
                println!("We ingested the entry!")
            }
        }
        
        std::fs::remove_dir_all("my_db").unwrap();
    }
}

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

Instantiated the export store!
We ingested the entry!

Create a drop

Next, we'll create a drop using create_drop and store it in a Vec.

Add the following tosrc/main.rs:

use ufotofu::consumer::IntoVec;
use willow_25::{
    AccessMode, Area, AuthorisationToken, AuthorisedEntry, Capability, Entry, NamespaceId25, Path,
    PayloadDigest25, SubspaceId25, create_drop,
    data_model::{EntryIngestionSuccess, EntryOrigin, Store},
};
use willow_store_simple_sled::StoreSimpleSled;

fn main() {
    smol::block_on(async {
        // Instantiate a store
        let namespace_id = NamespaceId25::new_communal();
        let export_db = sled::open("import_db").unwrap();

        let export_store = StoreSimpleSled::<
            1024,
            1024,
            1024,
            NamespaceId25,
            SubspaceId25,
            PayloadDigest25,
            AuthorisationToken,
        >::new(&namespace_id, export_db)
        .unwrap();

        println!("Instantiated the export store!");

        // Create an entry
        let (alfie_id, alfie_secret) = SubspaceId25::new();

        let write_cap =
            Capability::new_communal(namespace_id.clone(), alfie_id.clone(), AccessMode::Write)
                .unwrap();

        let path = Path::from_slices(&["ideas", "clock"]).unwrap();
        let payload = b"An emoji clock";
        let digest = PayloadDigest25::new_from_slice(payload);

        let entry = Entry::new(
            namespace_id.clone(),
            alfie_id.clone(),
            path.clone(),
            100,
            payload.len() as u64,
            digest.clone(),
        );

        let token = write_cap.authorisation_token(&entry, alfie_secret).unwrap();
        let authed_entry = AuthorisedEntry::new(entry, token).unwrap();

        smol::block_on(async {
            // Ingest the entry...
            if let Ok(EntryIngestionSuccess::Success) = export_store
                .ingest_entry(authed_entry, false, EntryOrigin::Local)
                .await
            {
                println!("We ingested the entry!")
            }

            // Encode a sideloading drop into a Vec<u8>
            let full_area = Area::new_full();
            let drop_destination = IntoVec::<u8>::new();
            let intovec_with_drop = create_drop(
                drop_destination,
                namespace_id.clone(),
                vec![full_area],
                &export_store,
            )
            .await
            .unwrap();
            let drop_vec = intovec_with_drop.into_vec();
            println!("We created the drop!");

            std::fs::remove_dir_all("import_db").unwrap();
        });
    });
}

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

Instantiated the export store!
We ingested the entry!
We created the drop!

Ingest the drop

Finally, we'll create a new Store and ingest the drop into it using ingest_drop.

use ufotofu::{Producer, consumer::IntoVec, producer::FromSlice};
use willow_25::{
    AccessMode, Area, AuthorisationToken, AuthorisedEntry, Capability, Entry, NamespaceId25, Path,
    PayloadDigest25, SubspaceId25, create_drop,
    data_model::{EntryIngestionSuccess, EntryOrigin, QueryIgnoreParams, Store},
    ingest_drop,
};
use willow_store_simple_sled::StoreSimpleSled;

fn main() {
    smol::block_on(async {
        // Instantiate a store
        let namespace_id = NamespaceId25::new_communal();
        let export_db = sled::open("import_db").unwrap();

        let export_store = StoreSimpleSled::<
            1024,
            1024,
            1024,
            NamespaceId25,
            SubspaceId25,
            PayloadDigest25,
            AuthorisationToken,
        >::new(&namespace_id, export_db)
        .unwrap();

        println!("Instantiated the export store!");

        // Create an entry
        let (alfie_id, alfie_secret) = SubspaceId25::new();

        let write_cap =
            Capability::new_communal(namespace_id.clone(), alfie_id.clone(), AccessMode::Write)
                .unwrap();

        let path = Path::from_slices(&["ideas", "clock"]).unwrap();
        let payload = b"An emoji clock";
        let digest = PayloadDigest25::new_from_slice(payload);

        let entry = Entry::new(
            namespace_id.clone(),
            alfie_id.clone(),
            path.clone(),
            100,
            payload.len() as u64,
            digest.clone(),
        );

        let token = write_cap.authorisation_token(&entry, alfie_secret).unwrap();
        let authed_entry = AuthorisedEntry::new(entry, token).unwrap();

        smol::block_on(async {
            // Ingest the entry...
            if let Ok(EntryIngestionSuccess::Success) = export_store
                .ingest_entry(authed_entry, false, EntryOrigin::Local)
                .await
            {
                println!("We ingested the entry!")
            }

            // Encode a sideloading drop into a Vec<u8>
            let full_area = Area::new_full();
            let drop_destination = IntoVec::<u8>::new();
            let intovec_with_drop = create_drop(
                drop_destination,
                namespace_id.clone(),
                vec![full_area],
                &export_store,
            )
            .await
            .unwrap();
            let drop_vec = intovec_with_drop.into_vec();
            println!("We created the drop!");

            // Create a new store and ingest the drop into it
            let import_db = sled::open("import_db").unwrap();
            let import_store = StoreSimpleSled::<
                1024,
                1024,
                1024,
                NamespaceId25,
                SubspaceId25,
                PayloadDigest25,
                AuthorisationToken,
            >::new(&namespace_id, import_db)
            .unwrap();

            let source_vec = FromSlice::new(&drop_vec);
            ingest_drop(source_vec, &import_store).await.unwrap();
            println!("We ingested the drop!");

            // Check if the entries are in the store we just created.
            let full_area = Area::new_full();
            let mut entries = import_store
                .query_area(&full_area, QueryIgnoreParams::default())
                .await
                .unwrap();
            while let Ok(lengthy_authed_entry) = entries.produce_item().await {
                println!("{lengthy_authed_entry:?}")
            }

            std::fs::remove_dir_all("import_db").unwrap();
        });
    });
}

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

Instantiated the export store!
We ingested the entry!
We created the drop!
We ingested the drop!
TODO: NICE ENTRY PRINTING!

Summary

In this tutorial we used sideloading to transport willow data between two Stores.

  • We created a StoreSimpleSled
  • We created an Entry, authorised it, and ingested in the store with Store::ingest_entry.
  • We used create_drop to create a drop of an Area of the StoreSimpleSled's contents.
  • We created another StoreSimpleSled, and used ingest_drop to ingest the drop we'd just created into that store.