Work with a Store
In this tutorial we will instantiate a MemoryStore and use it to store and retrieve an Entry and its Payload.
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 WriteCapability API would be helpful. If you're not yet familiar, please see our dedicated tutorial for capabilities.
Setup
- Create a new directory on your filesystem and name it something like
store. - Using your terminal, run
cargo initwithin the newly created directory. - After that, run
cargo add willow25 rand@0.8.0 ufotofu smol bab_rs.
Instantiate a store
Firstly we'll instantiate a MemoryStore.
Open src/main.rs, delete its contents, and enter the following:
use willow25::prelude::*;
use willow25::storage::MemoryStore;
fn main() {
// Store operations are async
smol::block_on(async {
// Instantiate an in-memory store.
let mut store = MemoryStore::new();
})
}
Now we have a Store, ready to work with.
Ingest an entry
Next, we'll create a new Entry, use that to create an AuthorisedEntry, and insert it into theMemoryStore we instantiated.
Make the following changes tosrc/main.rs:
use willow25::prelude::*;
use willow25::storage::MemoryStore;
use rand::rngs::OsRng;
fn main() {
// Store operations are async
smol::block_on(async {
// Instantiate an in-memory store.
let mut store = MemoryStore::new();
// Create an entry and authorise it.
let mut csprng = OsRng;
let (alfie_id, alfie_secret) = randomly_generate_subspace(&mut csprng);
let communal_namespace_id = NamespaceId::from_bytes(&[17; 32]);
let communal_cap =
WriteCapability::new_communal(communal_namespace_id.clone(), alfie_id.clone());
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();
// Authorise the entry using the communal
// capability and Alfie's secret.
let communal_authed = entry_communal
.into_authorised_entry(&communal_cap, &alfie_secret)
.unwrap();
// Insert an entry
store.insert_entry(communal_authed).await.unwrap();
println!("Successully inserted entry");
})
}
In your terminal, run cargo run, and you should see the following output:
Successully inserted entryRetrieve the entry
Next, we'll try and retrieve the AuthorisedEntry we just inserted.
Make the following changes tosrc/main.rs:
use willow25::prelude::*;
use willow25::storage::MemoryStore;
use rand::rngs::OsRng;
fn main() {
// Store operations are async
smol::block_on(async {
// Instantiate an in-memory store.
let mut store = MemoryStore::new();
// Create an entry and authorise it.
let mut csprng = OsRng;
let (alfie_id, alfie_secret) = randomly_generate_subspace(&mut csprng);
let communal_namespace_id = NamespaceId::from_bytes(&[17; 32]);
let communal_cap =
WriteCapability::new_communal(communal_namespace_id.clone(), alfie_id.clone());
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();
// Authorise the entry using the communal
// capability and Alfie's secret.
let communal_authed = entry_communal
.into_authorised_entry(&communal_cap, &alfie_secret)
.unwrap();
// Insert an entry
store.insert_entry(communal_authed).await.unwrap();
println!("Successully inserted entry");
// ... and retrieve it.
if let Some(_entry) = store
.get_entry(
&communal_namespace_id,
&(alfie_id.clone(), path!("/ideas")),
None,
)
.await
.unwrap()
{
println!("We got our entry back out!")
}
})
}
In your terminal, run cargo run, and you should see the following output:
Successully inserted entry
We got our entry back out!Try to retrieve the payload
Next, we'll try and retrieve the Payload of the AuthorisedEntry we've successfully inserted.
Make the following changes tosrc/main.rs:
use willow25::prelude::*;
use willow25::storage::MemoryStore;
use rand::rngs::OsRng;
use ufotofu::prelude::*;
fn main() {
// Store operations are async
smol::block_on(async {
// Instantiate an in-memory store.
let mut store = MemoryStore::new();
// Create an entry and authorise it.
let mut csprng = OsRng;
let (alfie_id, alfie_secret) = randomly_generate_subspace(&mut csprng);
let communal_namespace_id = NamespaceId::from_bytes(&[17; 32]);
let communal_cap =
WriteCapability::new_communal(communal_namespace_id.clone(), alfie_id.clone());
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();
// Authorise the entry using the communal
// capability and Alfie's secret.
let communal_authed = entry_communal
.into_authorised_entry(&communal_cap, &alfie_secret)
.unwrap();
// Insert an entry
store.insert_entry(communal_authed).await.unwrap();
println!("Successully inserted entry");
// ... and retrieve it.
if let Some(_entry) = store
.get_entry(
&communal_namespace_id,
&(alfie_id.clone(), path!("/ideas")),
None,
)
.await
.unwrap()
{
println!("We got our entry back out!")
}
// Retrieve the payload
let mut vec: Vec<u8> = vec![];
let mut vec_consumer = (&mut vec).into_consumer();
store
.get_payload_slice(
&communal_namespace_id,
&(alfie_id.clone(), path!("/ideas")),
None,
0,
u64::MAX,
&mut vec_consumer,
)
.await
.unwrap();
println!("{:?}", vec_consumer);
println!("Oops, we didn't append the payload yet!");
})
}
In your terminal, run cargo run, and you should see the following output:
Successully inserted entry
We got our entry back out!
IntoConsumerMut([])
Oops, we didn't append the payload yet!Our vec is empty! We never appended the corresponding Payload for this AuthorisedEntry. So let's do that next.
Append a payload (and retrieve it)
We're going to try and append some data to the Payload of our AuthorisedEntry, and then try to retrieve it again.
Make the following changes tosrc/main.rs:
use bab_rs::generic::storage::verifiable_streaming::SliceStreamingOptions;
use ufotofu::producer::clone_from_slice;
use willow25::prelude::*;
use willow25::storage::MemoryStore;
use rand::rngs::OsRng;
use ufotofu::prelude::*;
fn main() {
// Store operations are async
smol::block_on(async {
// Instantiate an in-memory store.
let mut store = MemoryStore::new();
// Create an entry and authorise it.
let mut csprng = OsRng;
let (alfie_id, alfie_secret) = randomly_generate_subspace(&mut csprng);
let communal_namespace_id = NamespaceId::from_bytes(&[17; 32]);
let communal_cap =
WriteCapability::new_communal(communal_namespace_id.clone(), alfie_id.clone());
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();
// Authorise the entry using the communal
// capability and Alfie's secret.
let communal_authed = entry_communal
.into_authorised_entry(&communal_cap, &alfie_secret)
.unwrap();
// Insert an entry
store.insert_entry(communal_authed).await.unwrap();
println!("Successully inserted entry");
// ... and retrieve it.
if let Some(_entry) = store
.get_entry(
&communal_namespace_id,
&(alfie_id.clone(), path!("/ideas")),
None,
)
.await
.unwrap()
{
println!("We got our entry back out!")
}
// Retrieve the payload
let mut vec: Vec<u8> = vec![];
let mut vec_consumer = (&mut vec).into_consumer();
store
.get_payload_slice(
&communal_namespace_id,
&(alfie_id.clone(), path!("/ideas")),
None,
0,
u64::MAX,
&mut vec_consumer,
)
.await
.unwrap();
println!("{:?}", vec_consumer);
println!("Oops, we didn't append the payload yet!");
// Append the payload
let mut payload_producer = clone_from_slice(b"chocolate with mustard");
store
.append_to_payload_prefix(
&communal_namespace_id,
&(alfie_id.clone(), path!("/ideas")),
&mut payload_producer,
SliceStreamingOptions::default(),
)
.await
.unwrap();
println!("We appended the payload");
// Retrieve the payload... again.
store
.get_payload_slice(
&communal_namespace_id,
&(alfie_id.clone(), path!("/ideas")),
None,
0,
u64::MAX,
&mut vec_consumer,
)
.await
.unwrap();
println!("{:?}", vec_consumer);
println!("That's more like it.");
})
}
In your terminal, run cargo run, and you should see the following output:
Successully inserted entry
We got our entry back out!
IntoConsumerMut([])
Oops, we didn't append the payload yet!
We appended the payload
IntoConsumerMut([99, 104, 111, 99, 111, 108, 97, 116, 101, 32, 119, 105, 116, 104, 32, 109, 117, 115, 116, 97, 114, 100])
That's more like it.Query an area
Next we'll query a Area to see which stored AuthorisedEntry are included by it.
Make the following changes tosrc/main.rs:
use bab_rs::generic::storage::verifiable_streaming::SliceStreamingOptions;
use ufotofu::producer::clone_from_slice;
use willow25::prelude::*;
use willow25::storage::MemoryStore;
use rand::rngs::OsRng;
use ufotofu::prelude::*;
fn main() {
// Store operations are async
smol::block_on(async {
// Instantiate an in-memory store.
let mut store = MemoryStore::new();
// Create an entry and authorise it.
let mut csprng = OsRng;
let (alfie_id, alfie_secret) = randomly_generate_subspace(&mut csprng);
let communal_namespace_id = NamespaceId::from_bytes(&[17; 32]);
let communal_cap =
WriteCapability::new_communal(communal_namespace_id.clone(), alfie_id.clone());
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();
// Authorise the entry using the communal
// capability and Alfie's secret.
let communal_authed = entry_communal
.into_authorised_entry(&communal_cap, &alfie_secret)
.unwrap();
// Insert an entry
store.insert_entry(communal_authed).await.unwrap();
println!("Successully inserted entry");
// ... and retrieve it.
if let Some(_entry) = store
.get_entry(
&communal_namespace_id,
&(alfie_id.clone(), path!("/ideas")),
None,
)
.await
.unwrap()
{
println!("We got our entry back out!")
}
// Retrieve the payload
let mut vec: Vec<u8> = vec![];
let mut vec_consumer = (&mut vec).into_consumer();
store
.get_payload_slice(
&communal_namespace_id,
&(alfie_id.clone(), path!("/ideas")),
None,
0,
u64::MAX,
&mut vec_consumer,
)
.await
.unwrap();
println!("{:?}", vec_consumer);
println!("Oops, we didn't append the payload yet!");
// Append the payload
let mut payload_producer = clone_from_slice(b"chocolate with mustard");
store
.append_to_payload_prefix(
&communal_namespace_id,
&(alfie_id.clone(), path!("/ideas")),
&mut payload_producer,
SliceStreamingOptions::default(),
)
.await
.unwrap();
println!("We appended the payload");
// Retrieve the payload... again.
store
.get_payload_slice(
&communal_namespace_id,
&(alfie_id.clone(), path!("/ideas")),
None,
0,
u64::MAX,
&mut vec_consumer,
)
.await
.unwrap();
println!("{:?}", vec_consumer);
println!("That's more like it.");
// Query by area
let entry_vec = Vec::new();
let mut entry_consumer = entry_vec.into_consumer();
let alfie_area = Area::new_subspace_area(alfie_id.clone());
store
.get_area(&communal_namespace_id, &alfie_area, &mut entry_consumer)
.await
.unwrap();
println!(
"Fetched our entry from the area! Our vec has this many entries inside: {:?}",
entry_consumer.as_slice().len()
);
})
}
In your terminal, run cargo run, and you should see the following output:
Successully inserted entry
We got our entry back out!
IntoConsumerMut([])
Oops, we didn't append the payload yet!
We appended the payload
IntoConsumerMut([99, 104, 111, 99, 111, 108, 97, 116, 101, 32, 119, 105, 116, 104, 32, 109, 117, 115, 116, 97, 114, 100])
That's more like it.
Fetched our entry from the area! Our vec has this many entries inside: 1Forget an entry
Finally, we're going to forget the AuthorisedEntry we inserted, and then try to retrieve it again.
Make the following changes tosrc/main.rs:
use bab_rs::generic::storage::verifiable_streaming::SliceStreamingOptions;
use ufotofu::producer::clone_from_slice;
use willow25::prelude::*;
use willow25::storage::MemoryStore;
use rand::rngs::OsRng;
use ufotofu::prelude::*;
fn main() {
// Store operations are async
smol::block_on(async {
// Instantiate an in-memory store.
let mut store = MemoryStore::new();
// Create an entry and authorise it.
let mut csprng = OsRng;
let (alfie_id, alfie_secret) = randomly_generate_subspace(&mut csprng);
let communal_namespace_id = NamespaceId::from_bytes(&[17; 32]);
let communal_cap =
WriteCapability::new_communal(communal_namespace_id.clone(), alfie_id.clone());
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();
// Authorise the entry using the communal
// capability and Alfie's secret.
let communal_authed = entry_communal
.into_authorised_entry(&communal_cap, &alfie_secret)
.unwrap();
// Insert an entry
store.insert_entry(communal_authed).await.unwrap();
println!("Successully inserted entry");
// ... and retrieve it.
if let Some(_entry) = store
.get_entry(
&communal_namespace_id,
&(alfie_id.clone(), path!("/ideas")),
None,
)
.await
.unwrap()
{
println!("We got our entry back out!")
}
// Retrieve the payload
let mut vec: Vec<u8> = vec![];
let mut vec_consumer = (&mut vec).into_consumer();
store
.get_payload_slice(
&communal_namespace_id,
&(alfie_id.clone(), path!("/ideas")),
None,
0,
u64::MAX,
&mut vec_consumer,
)
.await
.unwrap();
println!("{:?}", vec_consumer);
println!("Oops, we didn't append the payload yet!");
// Append the payload
let mut payload_producer = clone_from_slice(b"chocolate with mustard");
store
.append_to_payload_prefix(
&communal_namespace_id,
&(alfie_id.clone(), path!("/ideas")),
&mut payload_producer,
SliceStreamingOptions::default(),
)
.await
.unwrap();
println!("We appended the payload");
// Retrieve the payload... again.
store
.get_payload_slice(
&communal_namespace_id,
&(alfie_id.clone(), path!("/ideas")),
None,
0,
u64::MAX,
&mut vec_consumer,
)
.await
.unwrap();
println!("{:?}", vec_consumer);
println!("That's more like it.");
// Query by area
let entry_vec = Vec::new();
let mut entry_consumer = entry_vec.into_consumer();
let alfie_area = Area::new_subspace_area(alfie_id.clone());
store
.get_area(&communal_namespace_id, &alfie_area, &mut entry_consumer)
.await
.unwrap();
println!(
"Fetched our entry from the area! Our vec has this many entries inside: {:?}",
entry_consumer.as_slice().len()
);
// Forget our entry
store
.forget_entry(
&communal_namespace_id,
&(alfie_id.clone(), path!("/ideas")),
None,
)
.await
.unwrap();
if store
.get_entry(
&communal_namespace_id,
&(alfie_id.clone(), path!("/ideas")),
None,
)
.await
.unwrap()
.is_none()
{
println!("Our entry was forgotten!")
}
})
}
In your terminal, run cargo run, and you should see the following output:
Successully inserted entry
We got our entry back out!
IntoConsumerMut([])
Oops, we didn't append the payload yet!
We appended the payload
IntoConsumerMut([99, 104, 111, 99, 111, 108, 97, 116, 101, 32, 119, 105, 116, 104, 32, 109, 117, 115, 116, 97, 114, 100])
That's more like it.
Fetched our entry from the area! Our vec has this many entries inside: 1
Our entry was forgotten!Summary
In this tutorial, we explored the Store API:
- We instantiated a MemoryStore.
- We created an Entry, authorised it with a WriteCapability, and inserted in the store with Store::insert_entry.
- We saw what happened when we try to fetch a Payload with PayloadPrefixStore::append_to_payload_prefix we hadn't appended any data to.
- We queried an Area using Store::get_area and counted the results.
- We used Store::forget_entry to forget the AuthorisedEntry we originally inserted.