Skip to content
77 changes: 77 additions & 0 deletions merkledag/ipld-compat-protobuf.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# IPLD conversion with Protocol Buffer legacy IPFS node format

IPLD has a known conversion with the legacy Protocol Buffers format in order for new IPLD objects to interact with older protocol buffer objects.

## Detecting the format in use

The format is encapsulated after two multicodec headers. The first have the codec path `/mdagv1` and can be used to detect whether IPLD objects are transmitted or just legacy protocol buffer messages.

The second multicodec header is used to detect the actual format in which the IPLD object is encoded:

- `/protobuf/msgio`: is the encapsulation for protocol buffer message
- `/json`: is the encapsulation for JSON encoding
- `/cbor`: is the encapsulation for CBOR encoding

For example, a protocol buffer object encapsulated in a multicodec header would start with "`\x08/mdagv1\n\x10/protobuf/msgio\n`" corresponding to the bytes :

08 2f 6d 64 61 67 76 31 0a
10 2f 70 72 6f 74 6f 62 75 66 2f 6d 73 67 69 6f 0a

A JSON encoded object would start with "`\x08/mdagv1\n\x06/json\n`" and a CBOR encoded object would start with "`\x08/mdagv1\n\x06/cbor\n`".


## Description of the legacy protocol buffers format

This format is defined with the Protocol Buffers syntax as:

message PBLink {
optional bytes Hash = 1;
optional string Name = 2;
optional uint64 Tsize = 3;
}

message PBNode {
repeated PBLink Links = 2;
optional bytes Data = 1;
}

## Conversion to IPLD model

The conversion to the IPLD data model MUST be convertible back to protocol buffers, resulting in an identical byte stream (so the hash corresponds). This implies that ordering and duplicate links must be preserved in some way. As such, they are stored in an array and not in a map indexed by their name.

There is a canonical form which is described below:

{
"data": "<Data>",
"links": [
{
"@link": "/ipfs/<Links[0].Hash.(base58)>",
"name": "<Links[0].Name>",
"size": <Links[0].Tsize>
},

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

these could be offsets into the array, to decrease memory concerns. otherwise we blow up by ~2x

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

(im ok with either, can maybe state implementations can just dedup?)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We don't duplicate links here. A link is represented only once. The rule is that:

  • if the link can be represented in named-links, it is represented just there. The ordered-links section contain just the string key referencing the named link.
  • if the link cannot be represented in the named-links (name conflict, malformed protocol buffer document), it only appear, this time in full, in the ordered-links section.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ah I see. i think we should reverse to simplify, i.e.

  • always keep the full link object in ordered-links
  • always point (with an offset number) from the named-links section.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I don't know. It depends on which information you want to have most accessible. The named links section was initially the only section, but the ordered links was necessary to be able to convert objects with duplicate links back to protocol buffers.

The idea is that all named links are available in the named-links section by their name, and the key is the name of the link. It's easy to make a mapping between the link and its name.

If the named links contain the index to the ordered links, making the mapping between each link and its name is a little more difficult if you are not parsing both sections at the same time. Also, the benefit of having access to the link with a path containing the link name is gone.

If we want to simplify, I'd be in favor of removing the named links section entirely instead and adding a name attribute inside the link object. There is no benefit of having a named links section being just a mapping between name and link index.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The main argument here is that link order was never meant to be significant in the protocol buffer serialization format. We keep the order only to allow conversion back to the original format and be guaranteed we have the same hash. If not for that (and for the possibility of link conflicts), we would have removed the ordered links section entirely

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Im ok removing the named-links section, but it may help-- makes it easier to find stuff. In any case, we can remove it for now, and add it later if we need it, as the ordered-links section is the important part anyway.

Link conflicts require the ordering, so ordering is very significant. Most protobuf objects out there have lots of links with the same link name (""), this is current unixfs files' sub-files.

{
"@link": "/ipfs/<Links[1].Hash.(base58)>",
"name": "<Link[1].Name>",
"size": <Links[1].Tsize>
},
{
"@link": "/ipfs/<Links[2].Hash.(base58)>",
"name": "<Links[2].Name>",
"size": <Links[2].Tsize>
},
...
]
}

The main object contains:

- A `data` key containing the binary data string
- A `links` array containing links in the correct order

Each link consists of:

- A `@link` key containing the path to the destination document (Using the `/ipfs/` prefix)
- A `name` key containing the link name (a text string)
- A `size` unsigned integer containing the link size as stored in the Protocol Buffer object

Implementations are free to add any other top level key they need. In particular it may be interesting to access the links indexed by their name. This is a purely optional feature and additional keys cannot possibly be encoded back to the protonal Protocol Buffer format.