Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(content): update filecoin piece content #1016

Merged
merged 10 commits into from
Sep 22, 2020
58 changes: 48 additions & 10 deletions content/systems/filecoin_files/piece/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,62 @@ title: Piece
weight: 2
bookCollapseSection: true
dashboardWeight: 1.5
dashboardState: wip
dashboardAudit: n/a
dashboardState: stable
dashboardAudit: 0
dashboardTests: 0
---

# Piece - Part of a file
# The Filecoin Piece
---

The _Filecoin Piece_ is the main _unit of negotiation_ for data that users store on the Filecoin network. The Filecoin Piece is _not a unit of storage_, it is not of a specific size, but is upper-bounded by the size of the _Sector_. A Filecoin Piece can be of any size, but if a Piece is larger than the size of a Sector that the miner supports it has to be split into more Pieces so that each Piece fits into a Sector.

A `Piece` is an object that represents a whole or part of a `File`,
and is used by `Clients` and `Miners` in `Deals`. `Clients` hire `Miners`
to store `Pieces`.
and is used by `Storage Clients` and `Storage Miners` in `Deals`. `Storage Clients` hire `Storage Miners` to store `Pieces`.

The piece data structure is designed for proving storage of arbitrary
The Piece data structure is designed for proving storage of arbitrary
IPLD graphs and client data. This diagram shows the detailed composition
of a piece and its proving tree, including both full and bandwidth-optimized
piece data structures.

of a Piece and its proving tree, including both full and bandwidth-optimized
Piece data structures.

![Pieces, Proving Trees, and Piece Data Structures](pieces.png)

## Data Representation

It is important to highlight that data submitted to the Filecoin network go through several transformations before they come to the format at which the `StorageProvider` stores it.

Below is the process followed from the point a user starts preparing a file to store in Filecoin to the point that the provider produces all the identifiers of Pieces stored in a Sector.

The first three steps take place on the client side.

1. When a client wants to store a file in the Filecoin network, they start by producing the IPLD DAG of the file. The hash that represents root node of the DAG is an IPFS-style CID, called _Payload CID_.
yiannisbot marked this conversation as resolved.
Show resolved Hide resolved

2. In order to make a _Filecoin Piece_, the IPLD DAG is serialised into a ["Content-Addressable aRchive" (.car)](https://github.com/ipld/specs/blob/master/block-layer/content-addressable-archives.md#summary) file, which is in raw bytes format. A CAR file is an opaque blob of data that packs together and transfers IPLD nodes. The _Payload CID_ is common between the CAR'ed and un-CAR'ed constructions. This helps later during data retrieval, when data is transferred between the storage client and the storage provider as we discuss later.

3. The resulting .car file is _padded_ with extra zero bits in order for the file to make a binary Merkle tree. To achieve a clean binary Merkle Tree the the .car file size has to be in some power of two (^2) size. Therefore, the padding process takes the input file and finds the size above the input size that makes for a power of two size. The gap between the input size and the next power of two size is padded with zeros. Finally, an extra padding process is applied, called `fr32 padding` which adds two (2) zero bits to every 254 bits (to make 256 bits in total).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this para has gone through some iterations and lost the initial ordering in an attempt to make it easier to explain - which I understand, but it's now not quite accurate. The final size needs to be a ^2 but the fr32 padding gets in there before it goes to ^2. So to calculate how much zero padding to add, you have to account for the 254/256 inflation that's going to occur.

I think in an earlier iteration it was suggested to switch the order of the steps and say that fr32 happens first and then inflation to ^2 after that. I think this is the ideal framing because it makes it easier to understand (even though in code it happens the other way around, it doesn't actually matter so it's worth doing what's best for descriptive purposes). So that would mean pulling the last sentence to be first, "A process called fr32 padding which adds ... is applied. Extra padding is applied to the end of the data, adding zero bytes until the resulting blob is a power of two sie.". Something like that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch @rvagg! I've tried to explain it both ways, but indeed it becomes tricky, you're right. I've updated in commit 49a025b and included only the easier to grasp process.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot to mention that I've removed the definition of fr32 from here and included it in the Glossary (outstanding PR atm).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just noticing this now. I'm commenting here without an entirely clear point, but so it doesn't get lost in the context shift:

Fr32 is well-defined in the rust-fil-proofs implementation. As far as I can tell from the glossary, it's not given a substantive definition there. It's use here as an adjective was never particularly well-defined (from what I saw), although some true statements were made justifying the adjective.

Without making a specific suggestion for how to untangle this, let me offer the definition underlying the type called Fr32 in rust-fil-proofs. Fr32 is a 32-bit representation of a field element (which, in our case, is now, is the arithmetic field of BLS12-381). To be well-formed, a value of type Fr32 must actually fit within that field, but this is not enforced by the type system. It is an invariant which must be perserved by correct usage.

In the case of so-called Fr32 padding, two zero bits are inserted 'after' a number requiring at most 254 bits to represent. This guarantees that the result will be Fr32, regardless of the value of the initial 254 bits. This is a 'conservative' technique, since for some initial values, only one bit of zero-padding would actually be required.

It's possible that none of this needs to be spelled out explicitly here. It's not clear that an 'official' definition of the term makes much sense without at least some of this context, though. I've put this comment here rather than there because it's in the context of this conversation that the added detail is obviously relevant.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should note that Fr32 is not obviously a concept which belongs in the spec, even though it seems to have made its way into the preferred name for what I historically called 'bit padding'. I don't mind if it does stay in the spec as a glossary term. I mention this just to clarify that its primary definition is as an implementation type with relatively elaborate characteristics.

In particular, it's important to understand that Fr32-padded data represents a smaller set of values than inhabit the Fr32 type.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@porcuquine this is useful input. I have added your definition above as an extra "important note" in this section (see commit 972763d) and will also add this as a definition in the glossary too, if you don't mind.

I should note that Fr32 is not obviously a concept which belongs in the spec

I generally tend to agree. However, if we don't mention anything specific to Fr32, then we should just mention what is in one of the notes: "any implementation has to make sure that the initial IPLD DAG is serialised and padded so that it gives a clean binary tree, and therefore, calculating the Merkle root out of the resulting blob of data gives the same Piece CID", and leave it to that, without any mention of padding, .car etc.

I suggest leaving the text as is for now, given we mention that all this is Lotus-specific and the alternative would be slightly confusing to describe. Let me know if you're ok with this.


The term `fr32` is derived from the name of a struct that Filecoin uses to represent the elements of the arithmetic field of a pairing-friendly curve, specifically Bls12-381—which justifies use of 32 bytes. `F` stands for "Field", while `r` is simply a mathematic letter-as-variable substitution used to denote the modulus of this particular field.

Given that padding involves only adding zeroes, the padding process described in step 3 above can be described or be implemented differently. For instance, `fr32` padding can be applied first to add 2 zero bits per 254 bits throughout the input client file. Once this is done, the process should find the next ^2 size above the size resulting from the `fr32` padding and add zeroes to reach this size.

In order to justify the reasoning behind these steps, it is important to understand the overall negotiation process between the `StorageClient` and a `StorageProvider`. The piece CID or CommP is what is included in the deal that the client negotiates and agrees with the storage provider. When the deal is agreed, the client sends the file to the provider (using GraphSync). The provider has to construct the CAR file out of the file received and derive the Piece CID on their side. In order to avoid the client sending a different file to the one agreed, the Piece CID that the provider generates has to be the same as the one included in the deal negotiated earlier.

The following steps take place on the `StorageProvider` side (apart from step 4 that can also take place at the client side).

4. Once the `StorageProvider` receives the file from the client, they calculate the Merkle root out of the hashes of the Piece (padded .car file). The resulting root of the clean binary Merkle tree is the **Piece CID**. This is also referred to as _CommP_ or _Piece Commitment_ and as mentioned earlier, has to be the same with the one included in the deal.

5. The Piece is included in a Sector together with data from other deals. The `StorageProvider` then calculates Merkle root for all the Pieces inside the Sector. The root of this tree is _CommD_ (aka _Commitment of Data_ or `UnsealedSectorCID`).

6. The `StorageProvider` is then sealing the sector and the root of the resulting Merkle root is the _CommRLast_.

7. Proof of Replication (PoRep), SDR in particular, generates another Merkle root hash called _CommC_, as an attestation that replication of the data whose commitment is _CommD_ has been performed correctly.

8. Finally, _CommR_ (or _Commitment of Replication_) is the hash of CommC || CommRLast.

Finally, it is important to add a note related to the _Payload CID_ (discussed in the first two steps above) and the data retrieval process. The retrieval deal is negotiated on the basis of the _Payload CID_. When the retrieval deal is agreed, the retrieval miner starts sending the unsealed and "un-CAR'ed" file to the client. The transfer starts from the root node of the IPLD Merkle Tree and in this way the client can validate the _Payload CID_ from the beginning of the transfer and verify that the file they are receiving is the file they negotiated in the deal and not random bits.

## PieceStore

The `PieceStore` module allows for storage and retrieval of Pieces from some local storage. The piecestore's main goal is to help the [storage](https://github.com/filecoin-project/go-fil-markets/blob/master/storagemarket) and [retrieval market](https://github.com/filecoin-project/go-fil-markets/blob/master/retrievalmarket) modules to find where sealed data lives inside of sectors. The storage market writes the data, and retrieval market reads it in order to send out to retrieval clients.

{{< embed src="piece.id" lang="go" >}}
The implementation of the PieceStore module can be found [here](https://github.com/filecoin-project/go-fil-markets/tree/master/piecestore).
55 changes: 0 additions & 55 deletions content/systems/filecoin_files/piece/piece.id

This file was deleted.

14 changes: 0 additions & 14 deletions content/systems/filecoin_files/piece/piece_store.id

This file was deleted.

16 changes: 0 additions & 16 deletions content/systems/filecoin_files/piece/piece_store.md

This file was deleted.