Skip to content

Commit

Permalink
fix: code review
Browse files Browse the repository at this point in the history
  • Loading branch information
vasco-santos committed Dec 24, 2018
1 parent 52937f0 commit 4cbb006
Showing 1 changed file with 39 additions and 68 deletions.
107 changes: 39 additions & 68 deletions naming/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Authors:

Reviewers:

- ...
- Steven Allen ([@Stebalien](https://github.com/Stebalien))

-----

Expand All @@ -26,38 +26,39 @@ All things considered, the IPFS naming layer is responsible for the creation of:
# Organization of this document

- [Introduction](#introduction)
- [Building Blocks](#building-blocks)
- [Implementation Details](#implementation-details)
- [IPNS Record](#ipns-record)
- [Protocol](#protocol)
- [Overview](#overview)
- [API Spec](#api-spec)
- [Integration with IPFS](#integration-with-ipfs)

# Introduction

IPFS is mostly concerned with content-addressed data, which by nature is immutable. That is, when a file is added to the network, its reference in the system is an immutable content-addressed hash. This characteristic is a security mechanism, which guarantees that a peer has not modified the original content of a file. Accordingly, this characteristic is crucial in a network composed of anonymous peers.
Each time a file added to IPFS needs to be modified, its content addressable idendifier will be automatically modified. As a consequence, the address previously used for getting that file needs to be updated by who is using it. As this is not pratical, IPNS was conceptualized to solve the problem.

Nevertheless, there are some requirements that do not match with immutable data. Taking into account a use case, where a web application will have its static files stored in IPFS. Each time a static file needs to be modified, its directory hash will be automatically modified. As a consequence, the address previously used for getting that static file needs to be updated. As this is not pratical, IPNS was conceptualized to solve this.
IPNS is based on [SFS](http://en.wikipedia.org/wiki/Self-certifying_File_System). It consists of a PKI namespace, where a name is simply the hash of a public key. As a result, whoever controls the private key has full control over the name. Accordingly, records are signed by the private key and then distributed across the network (in IPFS, via the routing system). This is an egalitarian way to assign mutable names on the Internet at large, without any centralization whatsoever, or certificate authorities.

IPNS was idealized based on [SFS](http://en.wikipedia.org/wiki/Self-certifying_File_System). It consists of a PKI namespace, where a name is simply the hash of a public key. As a result, whoever controls the private key has full control over the name. Accordingly, records are signed by the private key and then distributed across the network (in IPFS, via the routing system). This is an egalitarian way to assign mutable names on the Internet at large, without any centralization whatsoever, or certificate authorities.

# Building Blocks

## IPNS Record
# IPNS Record

An IPNS record is a data structure composed by:

- 1. **Value** (bytes)
- It can be a path to another IPNS record, a `dnslink` path (`/ipns/example.com`) or an IPFS path (`/ipfs/Qm...`)
- 2. **Validity** (bytes)
- Expiration date of the record using [RFC3339](https://www.ietf.org/rfc/rfc3339.txt) with nanoseconds precision.
- Note: Currently, the expiration date is the only available type of validity.
- 3. **Validity Type** (uint64)
- Allows us to define if the record is valid under certain conditions
- Only supporting expiration date with `validityType = 0` for now
- 4. **Signature** (bytes)
- Concatenate value, validity field and validity type
- Sign the concatenation result with the provided private key
- Note: Once we add new validity types, the signature must be changed. More information on [ipfs/notes#249](https://github.com/ipfs/notes/issues/249)
- 5. **Sequence** (uint64)
- Represents the current version of the record (starts at 0)
- 6. **Public Key** (bytes)
- Public key used for the creation
- Note: This used to be stored separately, but it is stored in the IPNS record now, as a result of some issues storing the public key inline with the record. Eventually, when we switch for using ed25519 for public keys (instead of RSA), the public key will be inlined into the peer ID itself, so we will not need to store it in the IPNS record anymore.
- Public key used to sign this record
- Note: The public key **must** be included if it cannot be extracted from the peer ID (reference [libp2p/specs#100](https://github.com/libp2p/specs/pull/100/files)).
- 7. **ttl** (uint64)
- A hint for how long the record should be cached before going back to, for instance the DHT, in order to check if it has been updated.

Expand All @@ -83,69 +84,21 @@ message IpnsEntry {
}
```

The record is stored locally, in order to allow us to understand which is the most recent record in the network, since it will be stored locally by the peer using the keys used to generate it.
# Protocol

**Note**: validity must be with nanoseconds precision.
Taking into consideration a p2p network, each peer should be able to publish IPNS records to the network, as well as to resolve the IPNS records published by other peers.

#### Local record
When a node intends to publish a record to the network, an IPNS record needs to be created first. The node needs to have a previously generated assymetric key pair to create the record according to the datastructure previously specified. It is important pointing out that the record needs to be uniquely identified in the network. As a result, the record identifier should be a hash of the public key used to sign the record.

This record is stored in the peer's repo datastore and contains the **latest** version of the IPNS record published by the provided key. This record is useful for republishing, as well as tracking the sequence number.
As an IPNS record may be updated during its lifetime, a versioning related logic is needed during the publish process. As a consequence, the record must be stored locally, in order to enable the publisher to understand which is the most recent record published. Accordingly, before creating the record, the node must verify if a previous version of the record exists, and update the sequence value for the new record being created.

**Key format:** `/ipns/base32(<HASH>)`
Once the record is created, it is ready to be spread through the network. This way, a peer can use whatever routing system it supports to make the record accessible to the remaining peers of the network.

Note: Base32 according to the [RFC4648](https://tools.ietf.org/html/rfc4648).
On the other side, each peer must be able to get a record published by another node. It only needs to have the unique identifier used to publish the record to the network. Taking into account the routing system being used, we may obtain a set of occurences of the record from the network. In this case, records can be compared using the sequence number, in order to obtain the most recent one.

#### Routing record
As soon as the node has the most recent record, the signature and the validity must be verified, in order to conclude that the record is still valid and not compromised.

The routing record is also stored in the datastore of the IPFS nodes, once they receive the data. As we intend to remove IPNS entries from the routing system when the record expires, we must have a different record identifier from the local record.

**Key format:** `/ipns/BINARY_ID`

The two routing systems available for IPNS are the `DHT` and `pubsub`. As the `pubsub` topics must be `utf-8` for interoperability among different implementations

# Implementation Details

## IPNS Publisher

Flow for publishing an IPNS record:

1. Get the private key to use
- the default key is the node's private key
- a generated key may also be provided
2. Verify if the value provided exists before proceeding with publish (optional through parameters)
3. Get the peer id (using the private key)
4. Start publishing the record
1. Verify if a local record already exists
- If it exists:
- unmarshal the obtained record
- increment the sequence number (if the new value is different from the already stored)
- update the value
- If it does not exist:
- try to get the record from the routing system
- if it is found, unmarshal the obtained entry and do the steps above
2. Create a new record with the previous updates, or create a new one if the record was not found.
3. Marshal the record using the protocol Buffer
4. Put the local record into the repo's datastore using the local record key
5. Put the routing record to the routing, according to the systems available (DHT / Pubsub)
6. Put the public key used to the routing

## IPNS Resolver

Flow for resolving an IPNS record:

1. Get multihash from the name
2. Get the peer id from the hash
3. Get public key from the network / from the record if embedded
- Note: Name should be the hash of a public key retrievable from IPFS.
4. Get value from the routing (if offline only use the local datastore)
5. Unmarshal data
6. Validate record
- Use the public key to verify the record signature
- Check validity

## IPNS Republisher

IPNS republisher gets in action each 4 hours (by default). Accordingly, the republisher will get the old record from the local repo and publish it with an updated validity in each cycle.
Finally, the network nodes may also republish their records, so that the records in the network continue to be valid to the other nodes.

## Overview

Expand All @@ -159,3 +112,21 @@ IPNS republisher gets in action each 4 hours (by default). Accordingly, the repu

- <https://github.com/ipfs/js-ipfs/tree/master/src/core/ipns>
- <https://github.com/ipfs/go-ipfs/tree/master/namesys>

# Integration with IPFS

#### Local record

This record is stored in the peer's repo datastore and contains the **latest** version of the IPNS record published by the provided key. This record is useful for republishing, as well as tracking the sequence number.

**Key format:** `/ipns/base32(<HASH>)`

Note: Base32 according to the [RFC4648](https://tools.ietf.org/html/rfc4648).

#### Routing record

The routing record is spread across the network according to the available routing systems.

**Key format:** `/ipns/BINARY_ID`

The two routing systems currenty available in IPFS are the `DHT` and `pubsub`. As the `pubsub` topics must be `utf-8` for interoperability among different implementations

0 comments on commit 4cbb006

Please sign in to comment.