ZK Rollup Tutorial Part 4

Light nodes are the final building block of our Sovereign Rollup. They can be run by third parties - including users of the Sovereign Rollup - to independently check two properties:

  1. The correctness of the Sovereign Rollup state. The light node does this by verifying the ZK block proofs produced by the prover.
  2. The availability of the Sovereign Rollup data. The light node does this by engaging in Data Availability Sampling of relevant blob data from NomosDA.

As a result, light nodes give their operators confidence that the Sovereign Rollup’s sequencer is acting honestly and correctly, detecting any problems whenever they appear. This page will lead you through the steps required to implement a light node that verifies proofs and sampling data from Nomos.

<aside> đź’ˇ

Sovereign Rollup light nodes, like the one built in this tutorial, should not be confused with Nomos light nodes, which are planned for future releases of Nomos. While Sovereign Rollup light nodes verify the correctness and availability of Sovereign Rollup updates, Nomos light nodes verify the Nomos consensus process, engage in DA Sampling, and verify published proofs from Nomos Zones.

For more information on Nomos light nodes, take a look at the relevant section in the Nomos Whitepaper.

</aside>

Building the Light Node

1. Define Consensus Structs

<aside> đź’ˇ

This section of the tutorial assumes some familiarity with Nomos’ Cryptarchia consensus protocol. Please read the relevant section in the Nomos Whitepaper for an accessible introduction.

</aside>

To begin building a light node that will interact with our Sovereign Rollup, we need to define struct s to hold information about the most recent block in Cryptarchia, Nomos’ consensus protocol. In the lightnode/src/nomos.rs file, add the following struct to hold the most recent block’s HeaderId, the current slot (consensus time unit), and the latest block’s height.

lightnode/src/nomos.rs

// Struct for consensus information
// Implements traits to allow reading and writing into JSON format, as well as debugging
#[derive(Serialize, Deserialize, Debug)]
pub struct CryptarchiaInfo {
    pub tip: HeaderId,
    pub slot: u64,
    pub height: u64,
}

We also need to define the HeaderId struct and some of its traits to facilitate reading from and writing to the JSON format, and also to print out the block ID in a readable way. To do so, add the following code to the lightnode/src/nomos.rs file.

lightnode/src/nomos.rs

// Struct for block ID, which is a hash of its header data
#[derive(Clone, Debug, Eq, PartialEq, Copy, Hash, PartialOrd, Ord, Default)]
pub struct HeaderId([u8; 32]);

// Define the deserialize trait for HeaderId, decoding from hexadecimal
impl<'de> Deserialize<'de> for HeaderId {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let hex_str = String::deserialize(deserializer)?;

				// Decode from hexadecimal
        let bytes = <[u8; 32]>::from_hex(hex_str)
            .map_err(|e| serde::de::Error::custom(format!("Invalid hex string: {}", e)))?;

        Ok(HeaderId(bytes))
    }
}

// Define the Serialize trait for HeaderId, encoding into hexadecimal
impl Serialize for HeaderId {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let hex_str = hex::encode(self.0);
        serializer.serialize_str(&hex_str)
    }
}

use std::fmt;

// Define the Display trait to write the block ID
impl fmt::Display for HeaderId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for byte in &self.0 {
            write!(f, "{:02x}", byte)?;
        }
        Ok(())
    }
}

2. Interacting with the Nomos Client

<aside> đź’ˇ

This section relies on the following files in the Nomos repository:

Now that the consensus structs are defined, we can write the code that will allow our Sovereign Rollup to interact with Nomos’ Cryptarchia. Begin by adding a struct to the lightnode/src/lib.rs file to hold the credentials required to connect to your Nomos node.

lightnode/src/lib.rs

// Credentials for the Nomos node
// Implement traits to copy the struct and to print its fields for debugging
#[derive(Clone, Debug)]
pub struct Credentials {
    pub username: String,
    pub password: Option<String>,
}