Skip to main content

RedStuff Properties and Parameters

RedStuff has specific properties, Walrus-specific parameters, and authentication mechanisms described in the RedStuff encoding algorithm.

Properties and observations

The following sections describe key design decisions, fault-tolerance properties, and storage overhead of the RedStuff encoding.

Why the rectangular layout?

The rectangular layout of the message matrix is an optimization for the Byzantine setting. When storing the blob, a client can only await NfN - f responses, as the remaining ff shards might be Byzantine. Yet, ff of these NfN-f might be the Byzantine ones, and the ff that did not reply were only slow due to network asynchrony. Therefore, the blob needs to be encoded such that N2fN-2f symbols are sufficient to recover the original blob. The primary encoding achieves this.

After the initial sharing phase, the honest shards share and reconstruct the missing slivers from each other. At a steady state, you can always assume that NfN - f honest shards are in possession of their slivers. The secondary encoding can therefore have a higher rate, (Nf)/N(N-f)/N, decreasing the storage overhead while maintaining the same fault tolerance properties.

Worst case initial sharing

The following describes how the NfN-f honest shards can obtain their sliver pairs in the worst case outlined above, where the client shares the slivers with NfN-f shards, and ff Byzantine shards drop them.

  1. The N2fN-2f honest shards receive the sliver pairs.
  2. The remaining ff honest shards are notified of the stored blob, such as through the chain, and start the process to recover their sliver pairs.
  3. First, they recover their secondary slivers, as they can be decoded from N2fN-2f recovery symbols.
  4. Then, once all NfN-f honest shards have their secondary slivers, they can start recovering the primary slivers, which require NfN-f recovery symbols.
  5. All honest shards have their sliver pairs.

Storage overhead

Assume for simplicity that N=3f+1N=3f+1. Then, the original blob is divided into roughly f2f=2f2f \cdot 2f = 2f^2 symbols. The system stores N2fN \cdot 2f primary sliver symbols and NfN \cdot f secondary sliver symbols, for a total storage of about 9f29f^2 symbols.

Therefore, the storage overhead due to RedStuff encoding is about 9f2/2f2=4.59f^2 / 2f^2 = 4.5 times the original blob size.

Differences with the Twin-Code framework

The key modifications in RedStuff, compared to the original Twin-Code framework, are the following:

  • RedStuff uses the RaptorQ fountain code for both the Type 0 and Type 1 encoding, as they are called in the paper. The rates are about (N2f)/N(N-2f)/N and (Nf)/N(N-f)/N respectively. The Type 0 encoding is called the primary encoding, and the Type 1 encoding the secondary encoding.
  • The blob is not laid out in a square message matrix, but in a rectangular one. This is an optimization for the specific BFT setting described here.
  • The system stores both Type 0 and Type 1 encodings on each shard. These form slivers, and the 2 together form a sliver pair.

Walrus-specific parameters and considerations

The following sections describe the concrete parameter choices and mechanisms that Walrus uses when applying the RedStuff encoding.

Sliver-pair-to-shard mapping

The previous sections assumed that sliver pair ii is stored on shard ii. In practice, the system maps sliver pairs to shards in a pseudo-random fashion to ensure that the systematic slivers, which contain the original data, are not always stored on the same shard.

This is important because systematic slivers are the most frequently accessed, as they allow you to access the data without any decoding.

The mapping works as follows: the system assigns each encoded blob a 32-byte pseudo-random blob ID. This ID is interpreted as an unsigned big-endian integer, and its remainder modulo NN serves as a rotation offset, such that sliver pair ii is stored on shard (i+offset)modN(i + \text{offset}) \mod N.

Decoding probability and decoding safety limit

As mentioned above, the reconstruction failure probability of the RaptorQ code is O(256(H+1))O(256^{-(H+1)}), where HH is the number of extra symbols received. Therefore, in a system with ff Byzantine shards, the number of source symbols for the primary encoding should be slightly below N2fN-2f, and for the secondary encoding slightly below NfN-f. This ensures that whenever a node receives a validity or quorum threshold of messages, there is always a positive HH for a low failure probability.

The encoding configuration uses the following parameters:

  • ff, the maximum number of Byzantine shards, is (N1)/3\lfloor (N-1) / 3 \rfloor.
  • The safety limit for the encoding, σ\sigma, is set as a function of NN to ensure high reconstruction probability (see table below).
  • The number of primary source symbols (equivalent to the number of symbols in a secondary sliver) is N2fσN - 2f -\sigma.
  • The number of secondary source symbols (equivalent to the number of symbols in a primary sliver) is NfσN - f -\sigma.

The system selects σ\sigma depending on the number of shards as follows:

N shards fromN shards to (incl)σ\sigmaProb. failure
01500.00391
163011.53e-05
314525.96e-08
466032.33e-10
617549.09e-13
76inf53.55e-15

For example, the following settings apply:

N shardsfσ\sigmaNum primaryNum secondary
72035
103047
31102919
1003352962
30099597196
10003335329662

Blob size limits

RaptorQ encodes the size of a symbol as a 16-bit integer. Therefore, the maximum size of a blob that can be encoded is 2161=655352^{16} - 1 = 65535 bytes. At a minimum, a symbol must be at least 1 byte.

The system encodes the blob in the rectangular message matrix, which upper-bounds the blob size by source_symbols_primary * source_symbols_secondary * u16::MAX and lower-bounds it by source_symbols_primary * source_symbols_secondary. A few examples for the same configurations as above:

N shardsMin blob sizeMax blob sizeMin encoded blob sizeMax encoded blob size
715.0 B983 KiB56.0 B3.67 MiB
1028.0 B1.83 MiB110 B7.21 MiB
31171 B11.2 MiB868 B56.9 MiB
1001.80 KiB118 MiB9.10 KiB596 MiB
30019.0 KiB1.25 GiB87.9 KiB5.76 GiB
1000218 KiB14.3 GiB991 KiB64.9 GiB

Sliver authentication, blob metadata, and the blob ID

Alongside the efficient encoding performed by RedStuff, shards need to be able to authenticate that the slivers and encoding symbols they receive belong to the blob they requested. This section briefly outlines how this works.

The system constructs a Merkle tree for each sliver, primary or secondary.

Merkle tree constructed over sliver symbols

The system constructs the tree over all NN symbols of the fully expanded sliver. The root node of the Merkle tree (the sliver hash) appears in the metadata for the blob. To prove that a symbol is part of a sliver, the prover supplies the symbol alongside the Merkle path to the root hash, which every node has as part of the metadata.

The system then computes a Merkle tree over the sliver hashes to obtain a blob hash. It concatenates primary and secondary sliver hashes for each sliver pair, and then constructs the Merkle tree over the concatenations (cic_i in the figure). This construction reduces the number of hashing operations compared to hashing each sliver Merkle root individually.

Merkle tree over sliver hashes producing a blob hash

To prove that a sliver is part of a blob, it is sufficient to provide the Merkle path to the root.

Finally, the encoding type tag (representing the RedStuff version or alternative encoding), the length of the blob before encoding, and the Merkle root of the tree over the slivers hash together to produce the blob ID.

Metadata overhead

Each storage node stores the full metadata for the blob. The metadata consists of:

  • A 32 B Merkle root hash for each primary and secondary sliver.
  • The 32 B blob ID, computed as above.
  • The erasure code type (1 B).
  • The length of the unencoded blob size (8 B).

The hashes for the primary and secondary slivers can be a considerable overhead when the number of shards is high. The following table shows the cumulative size of the hashes stored on the system, depending on the number of nodes and shards.

N shardsOne nodefloor(N/floor(log2(N))) nodesN nodes
7448 B1.34 KiB3.14 KiB
10640 B1.92 KiB6.40 KiB
311.98 KiB13.9 KiB61.5 KiB
1006.40 KiB102 KiB640 KiB
30019.2 KiB710 KiB5.76 MiB
100064.0 KiB7.10 MiB64.0 MiB

The cumulative size of the hashes in the case of 1000 nodes (1 node per shard) is 64 KiB per node, or 64 MiB for a system of 1000 nodes. The number of shards is fixed and constant, while the number of nodes might vary, with each node having 1 or more shards, potentially lowering the overhead on the system. The following table shows the ratio between the size of the hashes stored on the system to the minimum and maximum blob sizes, for N=1000 shards and different numbers of nodes (1 node, floor(N/floor(log2(N))) = 111, and 1000).

N = 1000Total metadata sizeFactor min blobFactor max blobFactor min encoded blobFactor max encoded blob
Single node64.0 KiB0.2944.48e-060.06469.85e-07
floor(N/floor(log2(N))) nodes7.10 MiB32.60.0004987.170.000109
N nodes64.0 MiB2940.0044864.60.000985

For realistic node counts and small blob sizes, the total metadata overhead can be a multiple of the size of the initial unencoded blob. For larger blob sizes, the overhead is negligible.


Reference

[1] K. V. Rashmi, N. B. Shah and P. V. Kumar, "Enabling node repair in any erasure code for distributed storage," 2011 IEEE International Symposium on Information Theory Proceedings, St. Petersburg, Russia, 2011, pp. 1235-1239, doi: 10.1109/ISIT.2011.6033732.