From c1b310f2c3f9d8a9b54745667446c4e9ca0d1cba Mon Sep 17 00:00:00 2001 From: Tim McMackin Date: Mon, 22 Jan 2024 09:43:59 -0500 Subject: [PATCH] Data availability layer tutorial (#264) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * WIP: Kernel developers tutorial for DAL * ~400 bakers Co-authored-by: Raphaël Cauderlier * This number is `attestation_lag`. Co-authored-by: Raphaël Cauderlier * with enough tez. Co-authored-by: Raphaël Cauderlier * getting tez for free on Co-authored-by: Raphaël Cauderlier * request new tez Co-authored-by: Raphaël Cauderlier * typically the ones at index 0, 30, and 31 Co-authored-by: Raphaël Cauderlier * for this experiment. Co-authored-by: Raphaël Cauderlier * deposit tez to the Smart Rollup Co-authored-by: Raphaël Cauderlier * file archive * octez-dal-node run * octez-dal-node run * need comma here * fixes * Intro and learning objectives * Why the DAL? section * typo * Rust prereq * tutorial application * wasm compilation target * Move to MDX file for diagrams * lucid diagram * Enable syntez highlighting for toml * Setting up weeklynet * Move part 1 to a new page * Move parts 2 and 3 to separate pages * jq and xxd required * Clarify objective * Add command example * error message for dual write * Screencap from explorus * Lower case DAL * Typo * Clarify conflicts * Move part 4 to separate page * teztnets.com * Use data instead of file * Correct location for this error * How to see unattested slots * drop beta from explorus * rename index to slot_index * rename index to slot_index * next steps * additional data bandwidth * links for more info * endpoint is /slot * correct slot size * why use observer mode * it is available on the DAL for only a short time. * Add note about experimental DAL * title * The Smart Rollup Installer does not support the DAL Co-authored-by: Thomas Letan * typo * How to see the hex dump as text --------- Co-authored-by: Thomas Letan Co-authored-by: Raphaël Cauderlier Co-authored-by: Thomas Letan --- docs/tutorials.mdx | 8 + .../build-files-archive-with-dal.mdx | 153 ++++++++++++++++ .../get-dal-params.mdx | 173 ++++++++++++++++++ .../get-slot-info.mdx | 138 ++++++++++++++ .../publishing-on-the-dal.mdx | 141 ++++++++++++++ .../using-full-slot.mdx | 146 +++++++++++++++ docusaurus.config.js | 2 +- sidebars.js | 14 ++ static/img/tutorials/dal-explorus-slots.png | Bin 0 -> 95324 bytes 9 files changed, 774 insertions(+), 1 deletion(-) create mode 100644 docs/tutorials/build-files-archive-with-dal.mdx create mode 100644 docs/tutorials/build-files-archive-with-dal/get-dal-params.mdx create mode 100644 docs/tutorials/build-files-archive-with-dal/get-slot-info.mdx create mode 100644 docs/tutorials/build-files-archive-with-dal/publishing-on-the-dal.mdx create mode 100644 docs/tutorials/build-files-archive-with-dal/using-full-slot.mdx create mode 100644 static/img/tutorials/dal-explorus-slots.png diff --git a/docs/tutorials.mdx b/docs/tutorials.mdx index 8149c111a..a87efc5a5 100644 --- a/docs/tutorials.mdx +++ b/docs/tutorials.mdx @@ -117,4 +117,12 @@ These tutorials are intended for developers who are familiar with Tezos and want link="Start tutorial" /> + + diff --git a/docs/tutorials/build-files-archive-with-dal.mdx b/docs/tutorials/build-files-archive-with-dal.mdx new file mode 100644 index 000000000..121a98a0f --- /dev/null +++ b/docs/tutorials/build-files-archive-with-dal.mdx @@ -0,0 +1,153 @@ +--- +title: Implementing a file archive with the DAL and a Smart Rollup +authors: 'Tezos Core Developers' +last_update: + date: 17 January 2024 +--- + +import LucidDiagram from '@site/src/components/LucidDiagram'; + +:::note Experimental +The data availability layer is an experimental feature that is not yet available on Tezos Mainnet. +The way the DAL works may change significantly before it is generally available. +::: + +The data availability layer (DAL) is a companion peer-to-peer network for the Tezos blockchain, designed to provide additional data bandwidth to Smart Rollups. +It allows users to share large amounts of data in a way that is decentralized and permissionless, because anyone can join the network and post and read data on it. + +In this tutorial, you will set up a file archive that stores and retrieves files with the DAL. +You will learn: + +- How data is organized and shared with the DAL and the reveal data channel +- How to read data from the DAL in a Smart Rollup +- How to host a DAL node +- How to publish data and files with the DAL + +Because the DAL is not yet available on Tezos Mainnet, this tutorial uses the [Weeklynet test network](https://teztnets.com/weeklynet-about), which runs on a newer version of the protocol that includes the DAL. + +See these links for more information about the DAL: + +- For technical information about how the DAL works, see [Data Availability Layer](https://tezos.gitlab.io/shell/dal.html) in the Octez documentation. +- For more information about the approach for the DAL, see [The Rollup Booster: A Data-Availability Layer for Tezos](https://research-development.nomadic-labs.com/data-availability-layer-tezos.html). + +## Prerequisites + +This article assumes some familiarity with Smart Rollups. +If you are new to Smart Rollups, see the tutorial [Deploy a Smart Rollup](./smart-rollup). + +### Set up a Weeklynet environment and account + +Because Weeklynet requires a specific version of the Octez suite, you can't use most wallet applications and installations of the Octez suite with it. +Instead, you must set up an environment with a specific version of the Octez suite and use it to create and fund an account. +Note that Weeklynet is reset every Wednesday, so you must recreate your environment and account after the network resets. + +The easiest way to do this is to use the Docker image that is generated each time Weeklynet is reset and recreated. +As another option, you can build the specific version of the Octez suite locally. +For instructions, see the Weeklynet page at https://teztnets.com/weeklynet-about. + +To set up an environment and account in a Docker container, follow these steps: + +1. From the [Weeklynet](https://teztnets.com/weeklynet-about) page, find the Docker command to create a container from the correct Docker image, as in this example: + + ```bash + docker run -it --entrypoint=/bin/sh tezos/tezos:master_7f3bfc90_20240116181914 + ``` + + The image tag in this command changes each time the network is reset. + +1. Copy the URL of the public RPC endpoint for Weeklynet, such as `https://rpc.weeklynet-2024-01-17.teztnets.com`. +This endpoint also changes each time the network is reset. + +1. For convenience, you may want to set this endpoint as the value of the `ENDPOINT` environment variable. + +1. In the container, initialize the Octez client with that endpoint, such as this example: + + ```bash + octez-client -E https://rpc.weeklynet-2024-01-17.teztnets.com config init + ``` + +1. Create an account with the command `octez-client gen keys $MY_ACCOUNT`, where `$MY_ACCOUNT` is an alias for your account. + +1. Get the public key hash of the new account by running the command `octez-client show address $MY_ACCOUNT`. + +1. From the [Weeklynet](https://teztnets.com/weeklynet-about) page, open the Weeklynet faucet and send some tez to the account. + +Now you can use this account to deploy Smart Rollups. + +### Install Rust + +To run this tutorial, install Rust by running the following command. +The application in this tutorial uses Rust because of its support for WebAssembly (WASM), the language that Smart Rollups use to communicate. +Rollups can use any language that has WASM compilation support. + +```bash +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +``` + +Then, add WASM as a compilation target for Rust by running this command: + +```bash +rustup target add wasm32-unknown-unknown +``` + +You can see other ways of installing Rust at https://www.rust-lang.org. + +## Why the DAL? + +The DAL has earned the nickname "Rollup Booster" from its ability to address +the last bottleneck Smart Rollups developers could not overcome without +sacrificing decentralization: block space. Smart Rollups offload +*computation* from layer 1, but the transactions that they process still need to +originate from somewhere. + +By default, that "somewhere" is the layer 1 blocks, yet the size of a Tezos +block is limited to around 500KBytes. In this model, while Smart Rollups do not +compete for layer 1 gas anymore, they still compete for block space. + +{/* Is this info about the reveal data channel needed here? */} +Additionally, a Smart Rollup can fetch data from an additional source called the +reveal data channel, which allows them to retrieve arbitrary data. +The reveal channel is a powerful way to share data, because it allows a Smart Rollup +operator to post hashes instead of full data files on layer 1. But it is a +double-edged sword, because nothing enforces the availability of the data in the +first place. [Solutions exist to address this +challenge](https://research-development.nomadic-labs.com/introducing-data-availability-committees.html), +but they are purely off-chain ones, coming with no guarantee from layer 1. + +The DAL allows third parties to publish data and have bakers attest that the data is available. +When enough bakers have attested that the data is available, Smart Rollups can retrieve the data without the need for additional trusted third-parties. + +## How the DAL works + +In this tutorial, you create a file archive application that allows clients to upload data to the DAL. +You also create a Smart Rollup that listens to the DAL and responds to that data. + +The DAL works like this: + +1. Users post data to a DAL node. +1. The DAL node returns a certificate. +This certificate includes a commitment that the data is available and a proof of the data. +1. Users post the certificate to layer 1 via the Octez client, which is much cheaper than posting the complete data. +1. When the certificate is confirmed in a block, layer 1 splits the data into shards and assigns those shards to bakers, who verify that the data is available. +1. Bakers verify that the data is available and attest that the data is available in their usual block attestations to layer 1. +They have a certain number of blocks to do so, known as the _attestation lag_, and if they don't by the end of this period, the certificate is considered bogus and the related data is dropped. +1. Other DAL nodes get the data from the initial DAL node through the peer-to-peer network. +1. The Smart Rollup node monitors the blocks and when it sees attested DAL data, it connects to a DAL node to request the data. +1. The Smart Rollup node stores the data in its durable storage, addressed by its hash. +Smart Rollups must store the data because it is available on the DAL for only a short time. +1. Users who know the hash of the data can download it from the Smart Rollup node. + +The overall workflow is summarized in the following figure: + + + +There are many steps in the DAL process, but the most complicated parts (storing and sharing data) are handled automatically by the various daemons in the Octez suite. + +:::note The Smart Rollup installer does not support the DAL +As of today, the Smart Rollup installer does not support the DAL as a +data availability solution. This means we will need to rely on the reveal +channel to initialize our Smart Rollup correctly (which is not ideal for a +decentralized file archive). +::: + +When your environment is ready, get started by going to [Part 1: Getting the DAL parameters](./build-files-archive-with-dal/get-dal-params). diff --git a/docs/tutorials/build-files-archive-with-dal/get-dal-params.mdx b/docs/tutorials/build-files-archive-with-dal/get-dal-params.mdx new file mode 100644 index 000000000..03ea7292f --- /dev/null +++ b/docs/tutorials/build-files-archive-with-dal/get-dal-params.mdx @@ -0,0 +1,173 @@ +--- +title: "Part 1: Getting the DAL parameters" +authors: 'Tezos Core Developers' +last_update: + date: 17 January 2024 +--- + +import LucidDiagram from '@site/src/components/LucidDiagram'; + +The data availability layer stores information about the available data in layer 1 blocks. +Each block has several byte-vectors called _slots_, each with a maximum size. +DAL users can add information about the available data as _pages_ in these slots, as shown in this figure: + + + +The data in a slot is broken into pages to ensure that each piece of data can fit in a single Tezos operation. +This data must fit in a single operation to allow the Smart Rollup refutation game to work, in which every execution step of the Smart Rollup must be provable to layer 1. +{/* TODO link to Smart Rollup topic for more info on the refutation game */} + +When clients add data, they must specify which slot to add it to. +Note that because the DAL is permissionless, clients may try to add data to the same slot in the same block. +In this case, the first operation in the block takes precedence, which leaves the baker that creates the block in control of which data makes it into the block. +Other operations that try to add data to the same slot fail. + +The number and size of these slots can change. +Different networks can have different DAL parameters. +Future changes to the protocol may allow the DAL to resize dynamically based on usage. + +Therefore, clients must get information about the DAL before sending data to it. +In these steps, you set up a simple Smart Rollup to get the current DAL parameters and print them to the log. + +## Prerequisites + +Before you begin, make sure that you have installed the prerequisites and set up an environment and an account as described in [Implementing a File Archive with the DAL and a Smart Rollup](../build-files-archive-with-dal). + +## Fetching the DAL parameters in a kernel + +To get the DAL parameters, you can use built-in functions in the Tezos [Rust SDK](https://crates.io/crates/tezos-smart-rollup). + +1. In a folder for your project, create a file named `Cargo.toml` with this code: + + ```toml + [package] + name = "files_archive" + version = "0.1.0" + edition = "2021" + + [lib] + crate-type = ["cdylib", "lib"] + + [dependencies] + tezos-smart-rollup = { version = "0.2.2", features = [ "proto-alpha" ] } + ``` + + As a reminder, the kernel of a Smart Rollup is a WASM program. + The `proto-alpha` feature is necessary to get access to the functions specific to the DAL. + +1. Create a file named `src/lib.rs` to be the kernel. + +1. In the `src/lib.rs` file, add this code: + + ```rust + use tezos_smart_rollup::{kernel_entry, prelude::*}; + + pub fn entry(host: &mut R) { + let param = host.reveal_dal_parameters(); + debug_msg!(host, "{:?}\n", param); + } + + kernel_entry!(entry); + ``` + + This function gets the DAL parameters of the currently connected network and prints them to the log. + +1. Build the kernel: + + ```bash + cargo build --release --target wasm32-unknown-unknown + cp target/wasm32-unknown-unknown/release/files_archive.wasm . + ``` + +1. Get the installer kernel: + + ```bash + cargo install tezos-smart-rollup-installer + export PATH="${HOME}/.local/bin:${PATH}" + smart-rollup-installer get-reveal-installer \ + -P _rollup_node/wasm_2_0_0 \ + -u files_archive.wasm \ + -o installer.hex + ``` + +Now the Smart Rollup is ready to deploy. + +## Deploying the Smart Rollup and starting a node + +Follow these steps to deploy the Smart Rollup to Weeklynet and start a node: + +1. Run this command to deploy the Smart Rollup, replacing `$MY_ACCOUNT` with your account alias and `$ENDPOINT` with the RPC endpoint: + + ```bash + octez-client --endpoint ${ENDPOINT} \ + originate smart rollup files_archive from ${MY_ACCOUNT} \ + of kind wasm_2_0_0 of type unit with kernel "$(cat installer.hex)" \ + --burn-cap 2.0 --force + ``` + +1. Start the node with this command: + + ```bash + octez-smart-rollup-node --endpoint ${ENDPOINT} \ + run observer for files_archive with operators \ + --data-dir ./_rollup_node --log-kernel-debug + ``` + + For simplicity, this command runs the Smart Rollup in observer mode, which does not require a stake of 10,000 tez to publish commitments. + +1. Open a new terminal window and run this command to watch the node's log: + + ```bash + tail -F _rollup_node/kernel.log + ``` + +The log prints the current DAL parameters, as in this example: + +``` +RollupDalParameters { number_of_slots: 32, attestation_lag: 4, slot_size: 65536, page_size: 4096 } +RollupDalParameters { number_of_slots: 32, attestation_lag: 4, slot_size: 65536, page_size: 4096 } +RollupDalParameters { number_of_slots: 32, attestation_lag: 4, slot_size: 65536, page_size: 4096 } +RollupDalParameters { number_of_slots: 32, attestation_lag: 4, slot_size: 65536, page_size: 4096 } +RollupDalParameters { number_of_slots: 32, attestation_lag: 4, slot_size: 65536, page_size: 4096 } +``` + +These parameters are: + +- `number_of_slots`: The number of slots in each block +- `slot_size`: The size of each slot in bytes +- `page_size`: The size of each page in bytes +- `attestation_lag`: The number of subsequent blocks in which bakers can attest that the data is available; if enough attestations are available by the time this number of blocks have been created, the data becomes available to Smart Rollups + +## Setting up a deployment script + +In later parts of this tutorial, you will update and redeploy the Smart Rollup multiple times. +To simplify the process, you can use this script. +To use it, pass the alias of your account in the Octez client: + +```bash +#!/usr/bin/bash + +alias="${1}" + +set -e + +cargo build --release --target wasm32-unknown-unknown + +rm -rf _rollup_node + +cp target/wasm32-unknown-unknown/release/files_archive.wasm . + +smart-rollup-installer get-reveal-installer -P _rollup_node/wasm_2_0_0 \ + -u files_archive.wasm -o installer.hex + +octez-client --endpoint ${ENDPOINT} \ + originate smart rollup files_archive from "${alias}" of kind wasm_2_0_0 \ + of type unit with kernel "$(cat installer.hex)" --burn-cap 2.0 --force + +octez-smart-rollup-node --endpoint ${ENDPOINT} \ + run observer for files_archive with operators --data-dir _rollup_node \ + --dal-node http://localhost:10732 --log-kernel-debug +``` + +In the next section, you will get information about the state of slots in the DAL. +See [Part 2: Getting slot information](./get-slot-info). diff --git a/docs/tutorials/build-files-archive-with-dal/get-slot-info.mdx b/docs/tutorials/build-files-archive-with-dal/get-slot-info.mdx new file mode 100644 index 000000000..f8c893c68 --- /dev/null +++ b/docs/tutorials/build-files-archive-with-dal/get-slot-info.mdx @@ -0,0 +1,138 @@ +--- +title: "Part 2: Getting slot information" +authors: 'Tezos Core Developers' +last_update: + date: 17 January 2024 +--- + +When clients send data to the DAL, they must choose which slot to put it in. +This can cause conflicts, because only one client can write data to a given slot in a single block. +If more than one client tries to write to the same slot and a baker includes those operations in the same block, only the first operation in the block succeeds in writing data to the slot. +The other operations fail and the clients must re-submit the data to be included in a future block. + +For this reason, clients should check the status of slots to avoid conflicts. +For example, slots 0, 30, and 31 are often used for regression tests. + +To see which slots are in use, you can use the Explorus indexer at https://explorus.io/dal. +For example, this screenshot shows that slots 10 and 25 are in use: + +![The Explorus indexer, showing the slots that are in use in each block](/img/tutorials/dal-explorus-slots.png) + +You can also see the state of the DAL slots by running a DAL node. +To reduce the amount of data that they have to manage, DAL nodes can subscribe to certain slots and ignore the data in others. +Similarly, the protocol assigns bakers to monitor certain slots. + +## Starting a DAL node + +To run a DAL node, use the Octez `octez-dal-node` command and pass the slots to monitor in the `--producer-profiles` argument. + +Run this command to start a DAL node and monitor slot 0: + +```bash +octez-dal-node run --endpoint ${ENDPOINT} \ + --producer-profiles=0 --data-dir _dal_node +``` + +## Accessing the slot data from a Smart Rollup + +Follow these steps to update the Smart Rollup to access information about slot 0: + +1. Update the `src/lib.rs` file to have this code: + + ```rust + use tezos_smart_rollup::{host::RuntimeError, kernel_entry, prelude::*}; + use tezos_smart_rollup_host::dal_parameters::RollupDalParameters; + + pub fn run( + host: &mut R, + param: &RollupDalParameters, + slot_index: u8, + ) -> Result<(), RuntimeError> { + let sol = host.read_input()?.unwrap(); + + let target_level = sol.level as usize - param.attestation_lag as usize; + + let mut buffer = vec![0u8; param.page_size as usize]; + + let bytes_read = host.reveal_dal_page(target_level as i32, slot_index, 0, &mut buffer)?; + + if 0 < bytes_read { + debug_msg!( + host, + "Attested slot at index {} for level {}: {:?}\n", + slot_index, + target_level, + &buffer.as_slice()[0..10] + ); + } else { + debug_msg!( + host, + "No attested slot at index {} for level {}\n", + slot_index, + target_level + ); + } + + Ok(()) + } + + pub fn entry(host: &mut R) { + let param = host.reveal_dal_parameters(); + debug_msg!(host, "{:?}\n", param); + + match run(host, ¶m, 0) { + Ok(()) => debug_msg!(host, "See you in the next level\n"), + Err(_) => debug_msg!(host, "Something went wrong for some reasons"), + } + } + + kernel_entry!(entry); + ``` + + The key change is the addition of the function `run`. + Using this function allows the code to use the `?` operator of Rust by using a function that returns a `Result` type. + + The `run` function proceeds as follows: + + 1. First, it uses the DAL parameters to know the first level where a slot might be used. + It subtracts the attestation lag from the current level, which it gets from the Smart Rollup inbox; the result is the most recent block that may have attested data in it. + 1. It allocates `Vec` buffer of the current page size. + 1. It attempts to fill the buffer with the `read_dal_page` function provided + by the SDK. + 1. It checks the value returned by the function, which is the number of bytes + read. + Zero bytes mean that the slot has no attested data in it. + Otherwise, it is necessarily the size of the page, because that's the size of the buffer. + +1. Update the `Cargo.toml` file to add this dependency at the end: + + ```toml + tezos-smart-rollup-host = { version = "0.2.2", features = [ "proto-alpha" ] } + ``` + +1. Run the commands to build and deploy the Smart Rollup and start the node. +You can use the script in [Part 1: Getting the DAL parameters](./get-dal-params) to simplify the process. + +1. In another terminal window, view the log with the command `tail -F _rollup_node/kernel.log`. + +The log shows information about slot 0, as in this example: + +``` +RollupDalParameters { number_of_slots: 32, attestation_lag: 4, slot_size: 65536, page_size: 4096 } +No attested slot at index 0 for level 56875 +See you in the next level +RollupDalParameters { number_of_slots: 32, attestation_lag: 4, slot_size: 65536, page_size: 4096 } +Attested slot at index 0 for level 56876: [16, 0, 0, 2, 89, 87, 0, 0, 0, 0] +See you in the next level +RollupDalParameters { number_of_slots: 32, attestation_lag: 4, slot_size: 65536, page_size: 4096 } +No attested slot at index 0 for level 56877 +See you in the next level +``` + +For the first 4 Tezos blocks produced after the origination of the Smart Rollup, the kernel will report that no slot has been attested for the targeted level, _even if Explorus states the opposite_. +This is because, as of January, 2024, a Smart Rollup cannot fetch the content of a slot published before it is originated. +This is why you must wait for 4 blocks before seeing slot page contents being +logged. + +Now that you can see the state of the slots, you can find an unused slot and publish data to it. +When you are ready, continue to [Part 3: Publishing on the DAL](./publishing-on-the-dal). diff --git a/docs/tutorials/build-files-archive-with-dal/publishing-on-the-dal.mdx b/docs/tutorials/build-files-archive-with-dal/publishing-on-the-dal.mdx new file mode 100644 index 000000000..0eca70dd1 --- /dev/null +++ b/docs/tutorials/build-files-archive-with-dal/publishing-on-the-dal.mdx @@ -0,0 +1,141 @@ +--- +title: "Part 3: Publishing on the DAL" +authors: 'Tezos Core Developers' +last_update: + date: 17 January 2024 +--- + +Now that you can get information about the DAL, the next step is to publish data to it and verify that the kernel can access it. + +:::note Planning ahead +Before trying to run the code yourself, look at [Explorus](https://explorus.io/dal) and choose a slot that is not currently being used. +::: + +The examples in this tutorial use slot 10. + +## Switching slots + +When you have selected a slot that does not appear to be in use, follow these steps to restart the Smart Rollup and DAL node: + +1. Stop the DAL node and restart it with a new `--producer-profiles` argument. +For example, this command uses slot 10: + + ```bash + octez-dal-node run --endpoint ${ENDPOINT} \ + --producer-profiles=10 --data-dir _dal_node + ``` + +1. Update the kernel to monitor that slot by updating this line: + + ```rust + match run(host, ¶m, 0) { + ``` + + For example, to monitor slot 10, change the 0 to a 10, as in this code: + + ```rust + match run(host, ¶m, 10) { + ``` + +1. Run the commands to build and deploy the Smart Rollup and start the node. +You can use the script in [Part 1: Getting the DAL parameters](./get-dal-params) to simplify the process. + +## Publishing messages + +The DAL node provides an RPC endpoint for clients to send data to be added to a slot: `POST /slot`, whose body is the contents of the slot. + +1. Run this command to publish a message to the DAL: + + ```bash + curl localhost:10732/slot --data '"Hello, world!"' -H 'Content-Type: application/json' + ``` + + This command assumes that you have not changed the default RPC server address. + + The command returns the certificate from the DAL node, which looks like this example: + + ```json + { + "commitment": "sh1u3tr3YKPDYUp2wWKCfmV5KZb82FREhv8GtDeR3EJccsBerWGwJYKufsDNH8rk4XqGrXdooZ", + "commitment_proof":"8229c63b8e858d9a96321c80a204756020dd13243621c11bec61f182a23714cf6e0985675fff45f1164657ad0c7b9418" + } + ``` + + Note that the value of the message is in double quotes because it must be a valid JSON string, as hinted by the `Content-Type` header. + +1. Using the values of the commitment and proof from the previous command, post the certificate to layer 1 with this command: + + ```bash + commitment="sh1u3tr3YKPDYUp2wWKCfmV5KZb82FREhv8GtDeR3EJccsBerWGwJYKufsDNH8rk4XqGrXdooZ" + proof="8229c63b8e858d9a96321c80a204756020dd13243621c11bec61f182a23714cf6e0985675fff45f1164657ad0c7b9418" + octez-client --endpoint ${ENDPOINT} \ + publish dal commitment "${commitment}" from ${MY_ACCOUNT} for slot 10 \ + with proof "${proof}" + ``` + + After 4 blocks, you should see a message in the kernel log that looks like this: + + ``` + RollupDalParameters { number_of_slots: 32, attestation_lag: 4, slot_size: 65536, page_size: 4096 } + Attested slot at index 10 for level 57293: [72, 101, 108, 108, 111, 44, 32, 119, 111, 114] + See you in the next level + ``` + + You can verify your message by converting the bytes in the message back to the first 10 characters of the string "Hello, World!" + + If you see a message that says "A slot header for this slot was already proposed," another transaction tried to write to that slot in the same block, so you must try again. + + If you don't see information about the attested slot, check the page at https://explorus.io/dal. + If that page shows red (unattested) slots, it's possible that the attesters for the network are offline. + +## Publishing files + +You can also send raw bytes to the DAL node with the header `Content-Type: application/octet-stream`. +In this case, you must prefix the data with its size due to limitations of the DAL. + +1. Install the `jq` and `xxd` programs. + +1. Create a file named `upload_file.sh` and add this code: + + ```bash + #!/usr/bin/bash + + path="${1}" + alias="${2}" + index="${3}" + + target="$(mktemp)" + echo "storing temporary file at ${target}" + file_size="$(cat "${path}" | wc -c)" + slot_size_bin="$(printf "%08x" "${file_size}")" + slot_contents="$(cat ${path} | xxd -p)" + + echo -n "${slot_size_bin}${slot_contents}" | xxd -p -r > "${target}" + + certificate="$(curl localhost:10732/slot --data-binary "@${target}" -H 'Content-Type: application/octet-stream')" + + echo "${certificate}" + + commitment="$(echo -n ${certificate} | jq '.commitment' -r)" + proof="$(echo -n ${certificate} | jq '.commitment_proof' -r)" + + octez-client --endpoint ${ENDPOINT} \ + publish dal commitment "${commitment}" from "${alias}" \ + for slot "${index}" with proof "${proof}" + + rm "${target}" + ``` + + The script accepts three arguments: the file to send, the account alias to use and the slot index to use. + This script also assumes that the `PATH` and `ENDPOINT` environment variables are correctly set. + For example: + + ```bash + ./upload_file.sh myFile.txt $MY_ACCOUNT 10 + ``` + + Again, by inspecting the kernel logs, you should be able to see that the file that you wanted to publish is indeed the one fetched by the Smart Rollup. + +Now you can publish data to the DAL and use it in a Smart Rollup. +In the next section, you write to and retrieve the entire slot. +When you are ready, go to [Part 4: Using the entire slot](./using-full-slot). diff --git a/docs/tutorials/build-files-archive-with-dal/using-full-slot.mdx b/docs/tutorials/build-files-archive-with-dal/using-full-slot.mdx new file mode 100644 index 000000000..6ee53c95b --- /dev/null +++ b/docs/tutorials/build-files-archive-with-dal/using-full-slot.mdx @@ -0,0 +1,146 @@ +--- +title: "Part 4: Using the entire slot" +authors: 'Tezos Core Developers' +last_update: + date: 18 January 2024 +--- + +In some cases, you may want to retrieve the entire contents of a slot. +For example, it can be convenient to get the entire slot because it has a fixed size, while the data in the slot may be smaller and padded to fit the slot. + +## Fetching and storing the full slot + +Retrieving the full slot is similar to retrieving any data from the slot. +In this case, you change the kernel to retrieve data of the exact size of the slot. + +1. Update the `run` function in the `lib/rs` file to this code: + + ```rust + pub fn run( + host: &mut R, + param: &RollupDalParameters, + slot_index: u8, + ) -> Result<(), RuntimeError> { + // Reading one message from the shared inbox is always safe, + // because the shared inbox contains at least 3 messages per + // Tezos block. + let sol = host.read_input()?.unwrap(); + + let target_level = sol.level as usize - param.attestation_lag as usize; + + let mut buffer = vec![0u8; param.slot_size as usize]; + + let bytes_read = host.reveal_dal_page(target_level as i32, slot_index, 0, &mut buffer)?; + + if bytes_read == 0 { + debug_msg!( + host, + "No attested slot at index {} for level {}\n", + slot_index, + target_level + ); + + return Ok(()); + } + + debug_msg!( + host, + "Attested slot at index {} for level {}\n", + slot_index, + target_level + ); + + let num_pages = param.slot_size / param.page_size; + + for page_index in 1..num_pages { + let _result = host.reveal_dal_page( + target_level as i32, + slot_index, + page_index.try_into().unwrap(), + &mut buffer[page_index as usize * (param.page_size as usize) + ..(page_index as usize + 1) * (param.page_size as usize)], + ); + } + + let hash = blake2b::digest(&buffer, 32).unwrap(); + let key = hex::encode(hash); + let path = OwnedPath::try_from(format!("/{}", key)).unwrap(); + + debug_msg!(host, "Saving slot under `{}'\n", path); + + let () = host.store_write_all(&path, &buffer)?; + + Ok(()) + } + ``` + + Now the `run` function works like this: + + 1. It allocates a buffer of the size of a slot, not a size of a page. + 1. It tries to fetch the contents of the first page. + If 0 bytes are written by `reveal_dal_page`, the targeted slot has not been + attested for this block. + 1. If the targeted slot has been attested, the function reads as many pages as necessary to get the full slot data. + 1. It stores the data in the durable storage, using the Blake2B hash (encoded in hexadecimal) as its key. + +1. Add these `use` statements to the beginning of the file: + + ```rust + use tezos_crypto_rs::blake2b; + use tezos_smart_rollup::storage::path::OwnedPath; + ``` + + These dependencies use `tezos_crypto_rs` for hashing, and `hex` for encoding. + +1. Add the matching dependencies to the `Cargo.toml` file: + + ```toml + tezos_crypto_rs = { version = "0.5.2", default-features = false } + hex = "0.4.3" + ``` + + Adding `default-features = false` for `tezos_crypto_rs` is necessary for the crate to be compatible with Smart Rollups. + +1. Deploy the Smart Rollup again, publish a file as you did in the previous section, and wait for enough levels to pass. +The Smart Rollup log shows the hash of the data, as in this example: + + ``` + RollupDalParameters { number_of_slots: 32, attestation_lag: 4, slot_size: 65536, page_size: 4096 } + Attested slot at index 10 for level 15482 + Saving slot under `/6a578d1e6746d29243ff81923bcea6375e9344d719ca118e14cd9f3d3b00cd96' + See you in the next level + ``` + +1. Get the data from the slot by passing the hash, as in this example: + + ```bash + hash=6a578d1e6746d29243ff81923bcea6375e9344d719ca118e14cd9f3d3b00cd96 + curl "http://localhost:8932/global/block/head/durable/wasm_2_0_0/value?key=/${hash}" \ + -H 'Content-Type: application/octet-stream' \ + -o slot.bin + ``` + +1. Convert the contents of the slot to text by running this command: + + ```bash + xxd -r -p slot.bin + ``` + + The console shows your message in text, such as "Hi! This is a message to go on the DAL." + +:::note Why `diff` won't work +You cannot use `diff` to ensure that the file you originally published and the one that you downloaded from the rollup node are equal. +Indeed, they are not: because the size of a slot is fixed, the DAL node pads the value it receives from `POST /slot` in order to ensure that it has the correct slot size. +::: + +## Next steps + +Now you know how to send files to the DAL and use a Smart Rollup to store the data. + +From there, the sky's the limit. +You can implement many other features, such as: + +- Handling more than one file per level +- Having file publishers pay for the storage that they are using in layer 2 by allowing them to deposit tez to the Smart Rollup and sign the files they publish +- Building a frontend to visualize the files in the archive +- Providing the original size of the file by modifying the script to prefix the file with its size diff --git a/docusaurus.config.js b/docusaurus.config.js index 26b5ba1fc..f2ba4c536 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -121,7 +121,7 @@ const config = { }, prism: { theme: require('prism-react-renderer/themes/github'), - additionalLanguages: ['csharp'], + additionalLanguages: ['csharp', 'toml'], }, // https://github.com/flexanalytics/plugin-image-zoom // Enable click to zoom in to large images diff --git a/sidebars.js b/sidebars.js index dae502ddc..d0ac6504c 100644 --- a/sidebars.js +++ b/sidebars.js @@ -341,6 +341,20 @@ const sidebars = { 'tutorials/smart-rollup/run', ], }, + { + type: 'category', + label: 'Implement a file archive with the DAL', + link: { + type: 'doc', + id: 'tutorials/build-files-archive-with-dal', + }, + items: [ + 'tutorials/build-files-archive-with-dal/get-dal-params', + 'tutorials/build-files-archive-with-dal/get-slot-info', + 'tutorials/build-files-archive-with-dal/publishing-on-the-dal', + 'tutorials/build-files-archive-with-dal/using-full-slot', + ], + }, { type: 'category', label: 'Build an NFT marketplace', diff --git a/static/img/tutorials/dal-explorus-slots.png b/static/img/tutorials/dal-explorus-slots.png new file mode 100644 index 0000000000000000000000000000000000000000..01978f18749378bbdea4e84919968c4c631ead08 GIT binary patch literal 95324 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

A9h*0vi>JPSotIv&IDtB)`@z0?zMz2w8dpt_gb& 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 a{