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
Publish UnixFS specifications at specs.ipfs.tech #331
base: main
Are you sure you want to change the base?
Conversation
53dbc07
to
64c86d3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @Jorropo! I know how time-consuming spelunking code and writing specs is, this is effort is very appreciated ❤️
Did a first pass read and dropped some comments – mostly questions about things I did not know and trying to confirm I understood them right + flagging sections we should research or reorder / rephrase.
UNIXFSv1.md
Outdated
|
||
- Node, Block | ||
A node is a word from graph theory, this is the smallest unit present in the graph. | ||
Due to how unixfs work, there is a 1 to 1 mapping between nodes and blocks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
HAMT node will be backed by more than one block, maybe rephrase?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't a HAMT a concatenation of directories ?
I don't see why you couldn't reuse lower parts of the HAMTs (*assuming you find colliding hashes).
UNIXFSv1.md
Outdated
####### Link ordering | ||
|
||
The cannonical sorting order is lexicographical over the names. | ||
|
||
In theory there is no reason an encoder couldn't use an other ordering, however this lose some of it's meaning when mapped into most file systems today (most file systems consider directories are unordered-key-value objects). | ||
|
||
A decoder SHOULD if it can, preserve the order of the original files in however it consume thoses names. | ||
|
||
However when some implementation decode, modify then reencode some, the orignal links order fully lose it's meaning. (given that there is no way to indicate which sorting was used originally) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: rephrase this as clear MUST (always creating sorted data) and SHOULD (try parsing data in original order, if possible) for implementers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand what you mean, @rvagg fixed the go implementations after the linux2ipfs incident.
They support reading "unsorted" data, however will resort them lexicographically if you modify it.
UNIXFSv1.md
Outdated
|
||
####### Path Resolution | ||
|
||
Pop the left most component of the path, and try to match it to one of the Name in Links. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pop the left most component of the path, and try to match it to one of the Name in Links. | |
Pop the left most component of the path after the current root, and try to match it to one of the Name in Links. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my internal representation the root have already been poped when downloading the root.
UNIXFSv1.md
Outdated
|
||
Pop the left most component of the path, and try to match it to one of the Name in Links. | ||
|
||
<!--TODO: check Kubo does this-->If you find a match you can then remember the CID. You MUST continue your search, however if you find a match again you MUST error. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is search strategy left to implementers? I feel we should suggest something other than iterating over Links list that is "kinda expected to be sorted lexicographically, but may not be".
Knowing what Kubo and JS do will be useful.
UNIXFSv1.md
Outdated
optional string Name = 2; | ||
|
||
// cumulative size of target object | ||
optional uint64 Tsize = 3; // also known as dagsize |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is dagsize
a thing, or just more friendly label created for these specs?
Perhaps replacing dagsize
and DagSize
with "Tsize
(DAG size)" will be more clear?
UNIXFSv1.md
Outdated
``` | ||
|
||
The two different schemas plays together and it is important to understand their different effect, | ||
- `dag-pb` also named `PBNode` is the "outside" protobuf message, it is the first one you decode. It contain the list of links and some "opaque user data". |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- `dag-pb` also named `PBNode` is the "outside" protobuf message, it is the first one you decode. It contain the list of links and some "opaque user data". | |
- `dag-pb` also named `PBNode` is the "outside" protobuf message, it is the first one you decode. It contains the list of links and some "opaque user data". |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Piggybacking off of this, first bullet point suggestion:
- The `dag-pb` protobuf is the "outside" protobuf message; in other words, it is the first message decoded. This protobuf contains the list of links and some "opaque user data".
Also, as a noob reader, I wouldn't be clear what you mean by "opaque user data". Might be good to clarify this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I'd suggest moving this callout dag-pb also named PBNode
up to line 79, since that's the first dag-pb is mentioned.
UNIXFSv1.md
Outdated
- Symlink | ||
This represent a POSIX Symlink.<!--TODO: Add link to POSIX spec.--> | ||
|
||
### Paths |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this paragraph explain how this maps onto the protobufs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't. (I attempted to convey this because of the level 2 ## Paths
)
This attempt to explain how you should read this/is/a/path
as []string{"this", "is", "a", "path"}
.
UNIXFSv1.md
Outdated
// binary CID (with no multibase prefix) of the target object | ||
optional bytes Hash = 1; | ||
|
||
// UTF-8 string name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
iiuc
// UTF-8 string name | |
// UTF-8 string name, used for pathing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thx, but is it really UTF-8 ? Some current mainstream implementations does not prevent users from using arbitrary bytes in their file names (as long as they don't contain 0x2f
)
(see utf8
war in dag-cbor
...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also this is copied straight from dag-pb spec is not an authoritative section, I don't think I should update this.
UNIXFSv1.md
Outdated
} | ||
|
||
message PBNode { | ||
// refs to other objects |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain what links are used for in UnixFS?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's kinda what the whole dag-pb
section bellow is dedicated to.
This is just some protobuf definition, not meant to self documenting code.
UNIXFSv1.md
Outdated
`node.Data.Data` is some bitfield, ones indicates weather or not the links are part of this HAMT or leaves of the HAMT. | ||
The usage of this field is unknown given you can deduce the same information from the links names. | ||
|
||
###### Path resolution on HAMTs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With my very limited knowledge of HAMTs, I'm having trouble understanding the problem and how it is solved.
UNIXFSv1.md
Outdated
- Implementations encoding or decoding wire-representations must observe the following: | ||
- An `mtime` structure with `FractionalNanoseconds` outside of the on-wire range `[1, 999999999]` is **not** valid. This includes a fractional value of `0`. Implementations encountering such values should consider the entire enclosing metadata block malformed and abort processing the corresponding DAG. | ||
- The `mtime` structure is optional - its absence implies `unspecified`, rather than `0` | ||
- For ergonomic reasons a surface API of an encoder must allow fractional 0 as input, while at the same time must ensure it is stripped from the final structure before encoding, satisfying the above constraints. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you specifying the API here? This sounds like it makes in Go, but this might not apply to other languages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i dont i copied the metadata spec over, someone else already spécified that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Glad this is moving along 🎉.
Spec documents where things unrelated to the spec such as implementation details, alternatives considered, etc. are intertwined with the spec such that they're hard to distinguish is painful. The UnixFS spec is confusing enough without these extra distractions, many of which came from the previous version of the spec, so let's drop them. If people want to keep them around then moving them somewhere separate (e.g. to an appendix) would be great.
UNIXFSv1.md
Outdated
|
||
A so called "block limit" is in place, we do not allow any single block to be bigger than 2MiB. | ||
|
||
Implementation SHOULD try to not emit 1MiB bigger blocks, but MUST decode blocks <= 2MiB. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is probably not the right place to discuss this
Agreed, as mentioned above I don't think the UnixFS spec is the place to discuss the block limit. We can add an IPLD/block limit spec though and discuss there.
but 1 MB seems really small.
I have no preference for what the magic number is. However, every number is too small or too big for someone.
I've seen a crew of people who believe having a 100MB (or a non-existent) block limit would make data transfer in IPFS magically fast and that it's a major problem in IPFS data transfer today. This opinion is only valid if you're willing to concede that BitTorrent-v2 is slow (16kib chunks) and standard BitTorrent-v1 settings (256kib) are also slow. I have yet to meet someone who holds both views, but maybe I haven't talked to enough people 🤷.
Can talk about this in another issue. Interested parties may also want to read this thread https://discuss.ipfs.tech/t/supporting-large-ipld-blocks/15093.
cc @aschmahmann do you know where this come from 🙃
@Stebalien would probably know more, but I think the TLDR is people like round numbers and are bad at math.
Longer version: kubo (formerly go-ipfs) had a 1MiB max size for UnixFS chunking.... but you could use 1MiB chunks with the extraneous original protobuf wrapping which bumps it over the limit so you need a new limit. People like round numbers so 2MiB.
Generally speaking people will be bad at math (off by one errors, forgetting about protobuf wrapping, miscounting link sizes/names for directories, ....) if you tell people to go for 1MiB and they mess up and go over a bit things will be fine. If you tell people 2MiB and they go over the limit things get tricky. People will say things like "Bitswap enforce slightly bellow 4MiB" (https://github.com/ipfs/specs/pull/331/files#r1038743285) which might convince someone their 2.1MiB block is fine... but that's only go-bitswap today, but some other services (e.g. web3.storage) hard limit you at 2MiB blocks and go-bitswap could reasonably make that change as well.
For those who insist that the block limit is a critical performance issue, see above.
That being said if people feel the SHOULD is unnecessary and just want the MUST, sure 🤷.
SHOULD in https://www.ietf.org/rfc/rfc2119.txt
This word, or the adjective "RECOMMENDED", mean that there
may exist valid reasons in particular circumstances to ignore a
particular item, but the full implications must be understood and
carefully weighed before choosing a different course.
That sounds close to accurate to me, given that if you choose to start using the full 2MiB limit without being careful to not exceed the limit you'll run into interoperability problems.
UNIXFSv1.md
Outdated
optional uint64 filesize = 3; | ||
repeated uint64 blocksizes = 4; | ||
optional uint64 hashType = 5; | ||
optional uint64 fanout = 6; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see the word fanout mentioned in the rest of this document
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not used in kubo (kubo does emit it but IMO that is chunker implementation details,
I think we should remove this field and add a comment // field 6 is reserved for backward compatibility and SHOULD NOT be emited by implementations
):
$ rgrep -I fanout | grep "\.go:" | grep -v libp2p
vendor/github.com/ipfs/go-unixfsnode/data/unmarshal.go: fanout, n := protowire.ConsumeVarint(remaining)
vendor/github.com/ipfs/go-unixfsnode/data/unmarshal.go: qp.MapEntry(ma, Field__Fanout, qp.Int(int64(fanout)))
vendor/github.com/ipfs/go-unixfsnode/hamt/errors.go: // ErrNoFanoutField indicates the HAMT node's UnixFS structure lacked a fanout field, which is required
vendor/github.com/ipfs/go-unixfs/unixfs.go:func HAMTShardData(data []byte, fanout uint64, hashType uint64) ([]byte, error) {
vendor/github.com/ipfs/go-unixfs/unixfs.go: pbdata.Fanout = proto.Uint64(fanout)
vendor/github.com/ipfs/go-unixfs/unixfs.go:// Fanout gets fanout of format
vendor/github.com/ipfs/go-unixfs/pb/unixfs.pb.go: Fanout *uint64 `protobuf:"varint,6,opt,name=fanout" json:"fanout,omitempty"`
I know TSize
is also never red however I still talk about it, but it's tricky and easy to get wrong and important to mention that it MUST NOT be used in offset computation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not used in kubo
You have to be more careful in the areas of the spec you assume are unused. This one is used in existing unixfs implementations and could be discovered with some cursory looking around in those repos. Your grepping even identified some lines to look at so I'm not sure how you reached the conclusion that the lines were unused and should not be emitted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aschmahmann ah mb, I should have done -iI
not -I
I'll check it out, I guess everything that I assume to be 256 wide in the HAMT are actually variable sized ... Great the hamt is more complex than I thought!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know TSize is also never red
I think you need to do more poking around at these areas of the spec where people are asking for clarification. This is incorrect as well. A bit of looking using the GitHub UI shows at the very least:
https://github.com/ipfs/kubo/blob/4d4841f41cdc2d797e87f7b62c230ee957513f94/core/commands/files.go
There may be more examples as well, but saying it's never read is incorrect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aschmahmann I don't see TSize nor DagSize nor Fanout in the last file you linked it's using Filesize
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aschmahmann I don't see TSize nor DagSize
@Jorropo https://github.com/ipfs/kubo/blob/4d4841f41cdc2d797e87f7b62c230ee957513f94/core/commands/files.go#L249
If you'd like to see it in action (commands using pwsh, but translate to whatever shell you want):
❯ ipfs name resolve /ipns/ipfs.io | %{ipfs files stat $_}
QmegA7HiEvLmyJgVcBxgZ2hjEp5YZ4aVxcjBdHcKvD2f73
Size: 0
CumulativeSize: 10776742
ChildBlocks: 16
Type: directory
❯ ipfs name resolve /ipns/ipfs.io | %{ipfs files stat $_/index.html}
Qmf5nTcgHNZ4jB29d4XN7JhPykZMpCGNQysEcXjtRYguwW
Size: 190713
CumulativeSize: 190727
ChildBlocks: 0
Type: file
You can take a look in go-merkledag (where dagpb nodes are defined) if you're trying to follow the code paths without using any tooling like an IDE or tracing execution paths.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thx, I can't use my IDE effectively because everything is an interface.
UNIXFSv1.md
Outdated
|
||
### SHOULD NOT names | ||
|
||
Thoses names SHOULD NOT<!--MUST NOT ? in future revisions--> be used: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps a bad idea/overkill, but it seems like a lot of the characters that are unfriendly could be marked as SHOULD NOT even if some implementations will let many of them through.
There's a whole slew of bad characters/path components mentioned in ipfs/kubo#1710, but basically we might be doing people some favors by gathering that information so that implementers don't have to go figure it out themselves until they get pressed to by their users. For example, most implementations should probably just error on path components with newline characters until their users start asking for support with some non-troll dataset.
UNIXFSv1.md
Outdated
|
||
A directory node is a named collection of file. | ||
|
||
The minimum valid `PBNode.Data` field for a directory is (pseudo-json): `{"Type":"Directory"}`, other values are covered in Metadata. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In practice, directories seem generally to have some sizing information in their PBNode.Data
- maybe worth a SHOULD
section of recommended Data for these
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have an example CID where it's the case ?
AFAIT Kubo always just set PBNode.Data
to unixfsEncode({"Type": "Directory"})
UNIXFSv1.md
Outdated
|
||
Currently symlinks are not followable, that mean implementations needs to return symlinks objects and fail if a consumer tries to follow it through. | ||
|
||
This is a SHOULD level, you probably wont break much things if you start following them. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's unclear how to me how the unixfs format rather than the consumer would be able to follow symlinks, since the path doesn't provide a CID destination and the destination context will not be reliably clear at the point of link decoding (e.g. across mount, etc.)
UNIXFSv1.md
Outdated
|
||
### SHOULD NOT names | ||
|
||
Thoses names SHOULD NOT<!--MUST NOT ? in future revisions--> be used: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
another consideration is to avoid using /
in node names, as some tooling (gateway, fs) considers this as a directory separator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I was about to say that it's actually fine because we could use \/
or %2F
but \/
actually don't work on tmpfs and ext4 on linux so let's not allow that one, nice catch!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually this is already covered with a MUST NOT
before:
Components MUST NOT contain
/
unicode codepoints because else it would break the path into two components.
UNIXFSv1.md
Outdated
|
||
They never have any childs, and thus are also known as single block files. | ||
|
||
Their size (both `dagsize` and `blocksize`) is the length of the block body. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is previously refered to as TSize
UNIXFSv1.md
Outdated
``` | ||
|
||
3. Profit | ||
Assuming we stored this block in some implementation of our choice which makes it accessible to our client, we can try to decode it: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some implementation of our choice of what?
you might want to refer to kubo
directly, as an IPFS implementation with a datastore that persists data locally, and implements UnixFS.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't wanted to include poking into flatfs or creating a car file as part of the example and used Assuming we stored this block in some implementation of our choice
as a magic exercise left to the reader sentence.
I can add echo -n "test" | ipfs block put
if you want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made some initial writing suggestions here, please lmk if it's helpful @Jorropo. I didn't get to everything. I can make another pass shortly. I know this is a work-in-progress.
UNIXFSv1.md
Outdated
|
||
### IPLD `dag-pb` | ||
|
||
A very important other spec for unixfs is the [`dag-pb`](https://ipld.io/specs/codecs/dag-pb/spec/) IPLD spec: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Phrasing suggestion:
The IPLD [`dag-pb`](https://ipld.io/specs/codecs/dag-pb/spec/) spec (also known as `PBNode`) is also used by unixfs, and is represented by the following protobuf:
I suggested moving the callout that dag-pb
is also called PBNode up here, since it's the first time the reader encounters the term dab-pb
in this doc
UNIXFSv1.md
Outdated
} | ||
``` | ||
|
||
The two different schemas plays together and it is important to understand their different effect, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Phrasing suggestion:
Each protobuf schema plays a different role in unixfs. These differences are described below:
UNIXFSv1.md
Outdated
``` | ||
|
||
The two different schemas plays together and it is important to understand their different effect, | ||
- `dag-pb` also named `PBNode` is the "outside" protobuf message, it is the first one you decode. It contain the list of links and some "opaque user data". |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Piggybacking off of this, first bullet point suggestion:
- The `dag-pb` protobuf is the "outside" protobuf message; in other words, it is the first message decoded. This protobuf contains the list of links and some "opaque user data".
Also, as a noob reader, I wouldn't be clear what you mean by "opaque user data". Might be good to clarify this
UNIXFSv1.md
Outdated
``` | ||
|
||
The two different schemas plays together and it is important to understand their different effect, | ||
- `dag-pb` also named `PBNode` is the "outside" protobuf message, it is the first one you decode. It contain the list of links and some "opaque user data". |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I'd suggest moving this callout dag-pb also named PBNode
up to line 79, since that's the first dag-pb is mentioned.
UNIXFSv1.md
Outdated
They are always of type file. | ||
|
||
They can be recognised because their CIDs have `Raw` codec. | ||
|
||
The file content is purely the block body. | ||
|
||
They never have any childs, and thus are also known as single block files. | ||
|
||
Their size (both `dagsize` and `blocksize`) is the length of the block body. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: use bullet points, phrasing and grammar
- They are always of type `file`.
- Their CIDs have a `Raw` codec.
- The file content is the block body.
- They never have any children nodes, and thus are also known as single block files.
- Both the `dagsize` and `blocksize` fields specify the length of the block body.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Their CIDs have a
Raw
codec.
I don't agree with this sentence, here I understand Raw
codecs in CIDs as a property of Raw
nodes while it's an implication.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both the
dagsize
andblocksize
fields specify the length of the block body.
I'm not native english I need a check on this, when I read this I understand this as the wrong way around, I understand that dagsize
and blocksize
→ length of the block body.
While in reality it is dagsize
and blocksize
← length of the block body.
UNIXFSv1.md
Outdated
|
||
####### The sister-lists `PBNode.Links` and `decodeMessage(PBNode.Data).blocksizes` | ||
|
||
The sister-lists are the key point of why `dag-pb` is important for files. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few thoughts here:
- format sister-list using italics since it's a term i.e.
_sister-list_
- Include the example from your comment https://github.com/ipfs/specs/pull/331/files#r1038744725 below this sentence so the reader knows what a sister-list is
- Suggestion:
_Sister-lists_ are a key reason why `dag-pb` is important for files. In the following example, the `PBNode.Links` and `PBNode.Data.blocksizes` slices are sisters. This means that they must have the same length and each map to the same entity at the same index.
In other words, instead of having two lists of properties, we have a single list of properties where some of the properties are stored in `PBNode.Links[n]`, and other properties of the same object are stored in PNode.Data.blocksizes[n]
type PBNode struct {
Links []struct{
tsize uint64
hash cid.Cid
}
Data struct{
blocksizes []uint64
}
}
UNIXFSv1.md
Outdated
This allows us to concatenate smaller files together. | ||
|
||
Linked files would be loaded recursively with the same process following a DFS (Depth-First-Search) order. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion:
_Siter-lists_ allow us to concatenate smaller files together.
Otherwise, linked files would be loaded recursively during concatenation following Depth-First-Search order.
I might not be understanding what you're trying to convey here, lmk
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The sister lists is relevant to ranging. the DFS is the default mode when you don't do ranging (such as while fetching the complete file or if your range cover a multiple blocks in the file)
UNIXFS.md
Outdated
- JavaScript | ||
- Data Formats - [unixfs](https://github.com/ipfs/js-ipfs-unixfs) | ||
- Importer - [unixfs-importer](https://github.com/ipfs/js-ipfs-unixfs-importer) | ||
- Exporter - [unixfs-exporter](https://github.com/ipfs/js-ipfs-unixfs-exporter) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also add
https://github.com/ipld/js-unixfs
What about https://github.com/web3-storage/fast-unixfs-exporter
I just learned of these in the last couple of days and from what I understand they are actively maintained and used by DAGHouse
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://github.com/ipld/js-unixfs ✅ - yes we use this for encoding UnixFS DAGs now
https://github.com/web3-storage/fast-unixfs-exporter ❌ - temporary fork now deprecated
@ElPaisano : are you able to review, particularly for a grammar/organization regard? |
UNIXFS.md
Outdated
It MUST be murmur3-x64-64 (multihash `0x22`). | ||
- `node.Data.Data` is some bitfield, ones indicates whether or not the links are part of this HAMT or leaves of the HAMT. | ||
The usage of this field is unknown given you can deduce the same information from the links names. | ||
- `node.Data.fanout` MUST be a power of two. This encode the number of hash permutations that will be used on each resolution step. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any reason someone would choose a power of 2 that's not a power of 4? Just thinking about encoding into hex, where there's 4 bits to a digit.
UNIXFS.md
Outdated
Thoses nodes are also sometimes called sharded directories, they allow to split directories into many blocks when they are so big that they don't fit into one single block anymore. | ||
|
||
- `node.Data.hashType` indicates a multihash function to use to digest path components used for sharding. | ||
It MUST be murmur3-x64-64 (multihash `0x22`). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to have backward compatibility? It would seem the most prominent implementations are only taking the first 64 bits of the digest into account.
Maybe replace "lowest" in step 2 down on line 259 with a detailed description of how to pull out the correct bits from the middle of the 128-bit digest, in which case you could be backward-compatible with existing data and still allow the longer digest going forward (for large fanout and/or many, many, many entries).
f3a406d
to
c9d0296
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor cosmetic and writing things from my read through.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hacdias this is a quick pass with some pending notes I had (some need to be verified)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODOs/asks from my old notes:
-
Add section about Inlining. Have a "rule of thumb" ("SHOULD") around inlining small blocks should be part of the spec, including a maximum block size that makes sense to inline (e.g. 32)
-
Add section with "Test Vectors".
- List CIDs of empty directories and zero-length files (with and without raw leaves, with and without inlining)
- include/mention vectors from https://github.com/ipld/codec-fixtures/
- include/mention vectors from http://ipld.io/specs/transport/trustless-pathing/fixtures/unixfs_20m_variety/
- include/mention vectors from https://ipld.io/specs/codecs/dag-pb/fixtures/cross-codec/
[CID]: https://docs.ipfs.io/guides/concepts/cid/ | ||
[Bitswap]: https://github.com/ipfs/specs/blob/master/BITSWAP.md | ||
[MFS]: https://docs.ipfs.io/guides/concepts/mfs/ | ||
Moved to https://specs.ipfs.tech/architecture/unixfs/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should move this to top level, to avoid breaking links in the future.
We can still adjust tags
in front matter to move it between categories, but the permalink is more future-proof this way:
Moved to https://specs.ipfs.tech/architecture/unixfs/ | |
Moved to https://specs.ipfs.tech/unixfs-data-format/ |
##### `decode(PBNode.Data).filesize` | ||
|
||
If present, this field MUST be equal to the `Blocksize` computation above. | ||
Otherwise, this file is invalid. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: incorporate comment by @ribasushi from https://www.notion.so/HTTP-Gateway-Requests-for-Graphs-as-CARs-001d2a9f5a35418bb0fb7d9d182d24ec
.Data.Filesize ( field 3 ) is mandatory for types 0 and 2. It is marked as “optional” in the PB is because of the other types ( dir, etc ). When this is unspecified, it defaults to 0, and file becomes a zero-length, thus no bytes.
<!--TODO: check that this is true--> | ||
There is no failure mode known for this field, so your implementation should be | ||
able to decode nodes where this field is wrong (not the value you expect), or | ||
partially or completely missing. This also allows smarter encoders to give a | ||
more accurate picture (Don't count duplicate blocks, etc.). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's make things even more clear: paid pinning services should not trust the Tsize
in client's DAGs is correct:
<!--TODO: check that this is true--> | |
There is no failure mode known for this field, so your implementation should be | |
able to decode nodes where this field is wrong (not the value you expect), or | |
partially or completely missing. This also allows smarter encoders to give a | |
more accurate picture (Don't count duplicate blocks, etc.). | |
:::warning | |
An implementation SHOULD NOT assume the `TSize` values are correct. The value is only a hint that provides performance optimization for better UX. | |
Following the [Robustness Principle](https://specs.ipfs.tech/architecture/principles/#robustness), implementation SHOULD be | |
able to decode nodes where the `Tsize` field is wrong (not matching the sizes of sub-DAGs), or | |
partially or completely missing. | |
When total data size is needed for important purposes such as accounting, billing, and cost estimation, the `Tsize` SHOULD NOT be used, and instead a full DAG walk SHOULD to be performed. | |
::: |
An example of where this could be useful is as a hint to smart download clients. | ||
For example, if you are downloading a file concurrently from two sources that have | ||
radically different speeds, it would probably be more efficient to download bigger | ||
links from the fastest source, and smaller ones from the slowest source. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An example of where this could be useful is as a hint to smart download clients. | |
For example, if you are downloading a file concurrently from two sources that have | |
radically different speeds, it would probably be more efficient to download bigger | |
links from the fastest source, and smaller ones from the slowest source. | |
:::note | |
Examples of where `Tsize` is useful: | |
- User interfaces, where total size of a DAG needs to be displayed immediately, without having to do the full DAG walk. | |
- Smart download clients, downloading a file concurrently from two sources that have radically different speeds. It is more efficient to download bigger | |
links from the fastest source, and smaller ones from the slowest source. | |
::: |
|
||
- The file content is purely the block body. | ||
- They never have any children nodes, and thus are also known as single block files. | ||
- Their size (both `dagsize` and `blocksize`) is the length of the block body. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to invent new thing and name it dagsize
here? iiuc it is Tsize
, right?
- Their size (both `dagsize` and `blocksize`) is the length of the block body. | |
- Their size is the length of the block body (`Tsize` in parent is equal to `blocksize`). |
### `TSize` / `DagSize` | ||
|
||
This is an optional field in `PBNode.Links[]`. It **does not** represent any | ||
meaningful information of the underlying structure, and there is no known | ||
usage of it to this day, although some implementations omit these. | ||
|
||
To compute the `DagSize` of a node, which is stored in the parents, sum the length of the `dag-pb` outside message binary length and the `blocksizes` of all child files. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: this needs cleanup, people get Tsize
wrong all the time, and the text here is not very clear:
-
the
DagSize
(name, field) does not exist and was invented only for this spec, right @Jorropo ? -
"no known usage" feels wrong:
Tsize
is used all over the place, in places like MFS and in every GUI that exists for showing sizes of UnixFS files without traversing entire dag each time, including directory listing at all HTTP Gateways.
Would below be more clear?
### `TSize` / `DagSize` | |
This is an optional field in `PBNode.Links[]`. It **does not** represent any | |
meaningful information of the underlying structure, and there is no known | |
usage of it to this day, although some implementations omit these. | |
To compute the `DagSize` of a node, which is stored in the parents, sum the length of the `dag-pb` outside message binary length and the `blocksizes` of all child files. | |
### `TSize` (child DAG size hint) | |
`Tsize` is an optional field in `PBNode.Links[]` which represents the precomputed size of the specific child DAG. It provides a performance optimization: a hint about the total size of child DAG can be read without having to fetch any child nodes. | |
To compute the `Tsize` of a child DAG, sum the length of the `dag-pb` outside message binary length and the `blocksizes` of all nodes in the child DAG. |
name: Protocol Labs | ||
url: https://protocol.ai/ | ||
|
||
tags: ['architecture'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: I think we may want to move this to formats
category before this PR is merged.
|
||
In UnixFS, a node can be encoded using two different multicodecs, listed below. More details are provided in the following sections: | ||
|
||
- `raw` (`0x55`), which are single block :ref[Files]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the partial ranges of data in multi-block files are also encoded as raw
nodes, right?
|
||
### Metadata | ||
|
||
UnixFS currently supports two optional metadata fields. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is 'supports' - what expectations are there around generation / parsing of these? - we have implementations that don't encode mode fully
2023-10-17 maintainer conversation: this needs someone to comb through the comments. This is a nice-to-have before Instanbul and a must-have before end of year nucleation. |
Invite me to the next maintainer meeting and I can maybe scrum it a tiny bit? feels like a PR that's blocking other also-important PRs |
The field `Name` of an element of `PBNode.Links` for a HAMT starts with an | ||
uppercase hex-encoded prefix, which is `log2(fanout)` bits wide. | ||
|
||
##### Path Resolution |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: cross check HAMT-specifics with GO and JS:
- https://github.com/ipfs/helia/blob/31cdfa8b8990acc9c99a55dd2c078d0c415055ea/packages/unixfs/src/commands/utils/dir-sharded.ts#L213C1-L225C1
- https://github.com/ipfs/helia/blob/2eff2c2c4d3eedf83d3b6cd6fce928f29aa60a5a/packages/unixfs/src/commands/utils/find-shard-cid.ts#L9-L17
- https://github.com/ipfs/js-ipfs-unixfs/blob/4749d9a7c1eddd86b8fc42c3fa47f88c7b1b75ae/packages/ipfs-unixfs-importer/src/dir-sharded.ts#L169-L177
This need some touch up, (Table of Content, fixtures, ...) but I would to gather feedback first.
cc #162 #316