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

[WIP] Draft of Registry EIP #1

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
154 changes: 154 additions & 0 deletions EIPS/eip-ethpm-registry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
---
eip: unknown
title: Contract Package Registry Interface
author: Piper Merriam <[email protected]>, Christopher Gewecke <[email protected]>
Copy link

Choose a reason for hiding this comment

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

Chris your name should be first

type: Standards Track
category: ERC
status: Draft
created: 2018-08-07
discussions-to:
---

## Abstract
This EIP specifies an interface for publishing to and retrieving assets from smart contract package registries. It is a companion EIP to [1123](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1123.md) which defines a standard for smart contract package manifests.
Copy link

Choose a reason for hiding this comment

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

(Minor thought)

I'm a bit ambivalent about how to word this abstract. Part of me prefers simply to write this EIP as saying it specifies "a package registry interface", vs. how you have it. I see the value in this canonical definition as "what the interface does", but I also think there's value in expressing the contents as the noun "registry". I don't know. This is just muttering.

Copy link
Author

Choose a reason for hiding this comment

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

Not thrilled by the wording here either although it does help to define the EIP's scope. Some of your comments below about further work allude the possibility later EIPs might address things like how registries are linked together etc.


## Motivation
The goal is to establish a framework that allows smart contract publishers to design and deploy code registries with arbitrary business logic while exposing a set of common endpoints that tooling can use to retrieve assets for contract consumers.

A clear standard would help the existing EthPM Package Registry evolve from a centralized, single-project community resource into a decentralized multi-registry system whose constituents are bound together by the proposed interface. In turn, these registries could be ENS name-spaced, enabling installation conventions familiar to users of `npm` and other package managers.

**Examples**
```shell
$ ethpm install packages.zeppelin.eth/Ownership
```

```javascript
const SimpleToken = await web3.packaging
.registry('packages.ethpm.eth')
.getPackage('simple-token')
.getVersion('^1.1.5');
```

## Specification
The specification describes a small read/write API whose components are mandatory. It allows registries to manage versioned releases using the conventions of [semver](https://semver.org/) without imposing this as a requirement. It assumes registries will share the following structure and conventions:

+ a **registry** is a deployed contract which manages a collection of **packages**.
+ a **package** is a collection of **releases**
+ a **package** is identified by a unique string name and unique bytes32 **packageId** within a given **registry**
+ a **release** is identified by a `bytes32` **releaseId** which must be unique for a given package name and release version string pair.
+ a **releaseId** maps to a set of data that includes a **manifestURI** string which describes the location of an [EIP 1123 package manifest](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1123.md). This manifest contains data about the release including the location of its component code assets.
+ a **manifestURI** is a URI as defined by [RFC3986](https://tools.ietf.org/html/rfc3986) which can be used to retrieve the contents of the package manifest. In addition to validation against RFC3986, each **manifestURI** must also contain a hash of the content as specified in the [EIP 1123](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1123.md).

### Examples

**Package Names / Release Versions**

```shell
"simple-token" # package name
"1.0.1" # version string
```

**Release IDs**

Implementations are free to choose any scheme for generating a **releaseId**. A common approach would be to hash the strings together as below:

```solidity
// Hashes package name and a release version string
function generateReleaseId(string packageName, string version)
public
pure
returns (bytes32)
{
return keccak256(abi.encodePacked(packageName, version));
}
```
Implementations **must** expose this id generation logic as part of their public `read` API so
tooling can easily map a string based release query to the registry's unique identifier for that release.

**Manifest URIs**

Any IPFS or Swarm URI meets the definition of **manifestURI**.

Another example is content on GitHub addressed by its SHA-1 hash. The Base64 encoded content at this hash can be obtained by running:
```shell
$ curl https://api.github.com/repos/:owner/:repo/git/blobs/:file_sha

# Example
$ curl https://api.github.com/repos/rstallman/hello/git/blobs/ce013625030ba8dba906f756967f9e9ca394464a
```

The string "hello" can have its GitHub SHA-1 hash independently verified by comparing it to the output of:
```shell
$ printf "blob 6\000hello\n" | sha1sum
> ce013625030ba8dba906f756967f9e9ca394464a
```

### Write API Specification
The write API consists of a single method, `release`. It passes the registry the package name, a
version identifier for the release, and a URI specifying the location of a manifest which
details the contents of the release.
```solidity
function release(string packageName, string version, string manifestURI) public
returns (bytes32 releaseId);
```
### Read API Specification

The read API consists of a set of methods that allows tooling to extract all consumable data from a registry.

```solidity
// Retrieves a slice of the list of all unique package identifiers in a registry.
// `offset` and `limit` enable paginated responses / retrieval of the complete set. (See note below)
function getAllPackageIds(uint offset, uint limit) public view
returns (
bytes32 packageIds,
uint offset
);

// Retrieves the unique string `name` associated with a package's id.
function getPackageName(bytes32 packageId) public view returns (string name);

// Retrieves the registry's unique identifier for an existing release of a package.
function getReleaseId(string packageName, string version) public view returns (bytes32);

// Retrieves a slice of the list of all release ids for a package.
// `offset` and `limit` enable paginated responses / retrieval of the complete set. (See note below)
function getAllReleaseIds(string packageName, uint offset, uint limit) public view
returns (
bytes32[] ids,
uint offset
);

// Retrieves package name, release version and URI location data for a release id.
function getReleaseData(bytes32 releaseId) public view
returns (
string packageName,
string version,
string manifestURI
);

// Retrieves the release id a registry *would* generate for a package name and version pair
// when executing a release.
function generateReleaseId(string packageName, string version) pure returns (bytes32);
```
**Pagination**

`getAllPackageIds` and `getAllReleaseIds` support paginated requests because it's possible that the return values for these methods could become quite large. The methods should return an `offset` that is a pointer to the next available item in a list of all items such that a caller can use it to pick up from where the previous request left off. (See [here](https://mixmax.com/blog/api-paging-built-the-right-way) for a discussion of the merits and demerits of various pagination strategies.) The `limit` parameter defines the maximum number of items a registry should return per request.

Copy link

Choose a reason for hiding this comment

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

I believe there is value in adding a new interface section Supported Interfaces API (ie. heading at the same level as Write API and Read API), to require the use of EIP-165.

My reasoning for this is mainly for forward-compatibility reasons. Due to this effort's nature of aiming to be functionally unopinionated, it seems plausible to expect that future EIPs will build on (and possibly supersede) this standard. EIP-165 provides a lightweight mechanism that allows a contract the capability of stating which standard interfaces it supports.

By requiring the use of EIP-165 in this standard, it simultaneously sets a clear/consistent precedent for future registry standards, as well as ensuring that all standard registries adhere to this practice for capability expressiveness from the very beginning. Should future work to improve this standard, then this adherence will make implementation easier for all registry consumers.

I understand that it's prescriptive to require this behavior, and adds coupling to 165, but 165 is already finalized, and it does not add a significant gas or implementation time overhead. If we merely "recommend" 165 instead of requiring it (should instead of must), we lose our easiest opportunity to ensure conformance across the board.

## Rationale
The proposal hopes to accomplish the following:

+ Define the smallest set of inputs necessary to allow registries to map package names to a set of
release versions while allowing them to use any versioning schema they choose.
+ Provide the minimum set of getter methods needed to retrieve package data from a registry so that registry aggregators can read all of their data.
+ Define a standard query that synthesizes a release identifier from a package name and version pair so that tooling can resolve specific package version requests without needing to query a registry about all of a package's releases.

Registries may offer more complex `read` APIs that manage requests for packages within a semver range or at `latest` etc. This EIP is agnostic about how tooling or registries might implement these. It recommends that registries implement [EIP 165](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md) and avail themselves of resources to publish more complex interfaces such as [EIP 926](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-926.md).

## Backwards Compatibility
No existing standard exists for package registries. The package registry currently deployed by EthPM would not comply with the standard since it implements only one of the method signatures described in the specification.

Copy link

Choose a reason for hiding this comment

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

As we discussed, it might be worth adding a "future work" section, to "bait" the community into pursuing advances in this area.

Some ideas that come to mind that I think would be cool:

  • If there's a SemVer standard ERC at some point, there could be a SemVerRegistry standard
  • Standard(s) for authorized registries
  • Standard(s) for registry federation

## Implementation
A reference implementation of this proposal can be found at the EthPM organization on Github [here](https://github.com/ethpm/escape-truffle).

## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).