rfcs icon indicating copy to clipboard operation
rfcs copied to clipboard

RFCS-0006: Manifest Metadata

Open dmihal opened this issue 3 years ago • 3 comments

RFC-0005: Manifest Metadata

Summary

Add optional metadata to the subgraph manifest, containing the source-code for mapping files and/or a cryptographic signature from the developer.

Goals & Motivation

CryptoStats is currently building tooling to improve the developer experience for building subgraphs. As part of this initiative, we are aiming to make the development of subgraphs more “collaborative”, allowing subgraph authors to be more easily identified, and providing an easy method for subgraphs to be “forked”. Adding these features necessitates adding additional metadata to the traditional YAML manifest file. This document aims to standardize these attributes, allowing other tooling in the Graph ecosystem to take advantage of them.

Urgency

While this feature is low-priority, CryptoStats will be launching a tool that uses these features soon.

Terminology

  • Manifest - the YAML file describing all attributes of a subgraph.
  • Entry point - the root file to be used when compiling source code.

Detailed Design

We propose adding two optional sections to the Manifest file: sourceCode and signature.

Source Code

The manifest provides most files needed for a developer to re-build their own subgraph, such as the schema document, all smart contracts and ABIs. However, the mapping file is only provided as compiled WASM bytecode, which can not be modified by other developers.

We would like to propose the addition of an optional sourceCode section to the manifest, containing a list of entities with the following attributes:

Field Type Description
file Path The path of the compiled output file (typically an IPLD link matching the link used elsewhere in the file)
source Path The path of the source code file or directory
name optional String Optional "file name" for the source file (if single file)
entryPoint optional String If the source path is a directory, entryPoint should be provided to define the entry point file for compilation

Example

specVersion: 0.0.2
repository: 'https://github.com/graphprotocol/uniswap-v2-subgraph'
dataSources:
  - kind: ethereum/contract
    mapping:
      abis:
        - file:
            /: /ipfs/QmZ55G1yYFzde8Vcq4cpLfNgPSEibpLi9aYCqS1jEvCKQ9
          name: Factory
        - file:
            /: /ipfs/QmXuTbDkNrN27VydxbS2huvKRk62PMgUTdPDWkxcr2w7j2
          name: ERC20
      apiVersion: 0.0.3
      entities:
        - Pair
        - Token
      eventHandlers:
        - event: 'PairCreated(indexed address,indexed address,address,uint256)'
          handler: handleNewPair
      file:
        /: /ipfs/QmZLfiUaV9CR62UQRccEoJM6fJa5p219rvXiwi3FQVpnny
      kind: ethereum/events
      language: wasm/assemblyscript
    name: Factory
    network: mainnet
    source:
      abi: Factory
      address: '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f'
      startBlock: 10000834
schema:
  file:
    /: /ipfs/QmRkCTHhWGGSb1JnAscsWNtCeEkymLrrF2ufX9yEVTXW6H
sourceCode:
  # If a mapping is compiled from a single file, the name attribute may be included
  - fileName: mapping.ts
    file: /ipfs/QmZLfiUaV9CR62UQRccEoJM6fJa5p219rvXiwi3FQVpnny
    source: /ipfs/QmM6fJa5p219rvXiwi3FQVpnnyZLfiUaV9CR62UQRccEoJ
  # If a mapping is compiled from multiple files, source can be a directory
  - file: /ipfs/QmZLfiUaV9CR62UQRccEoJM6fJa5p219rvXiwi3FQVpnny
    source: /ipfs/QmM6fJa5p219rvXiwi3FQVpnnyZLfiUaV9CR62UQRccEoJ
    # If a directory is provided, an entryPoint attribute must be specified
    entryPoint: pool.ts

Signature

As subgraphs become a more important piece of the blockchain stack, there may be value in having a cryptographic signature attached to a subgraph. This allows any user to verify that a subgraph was created by a specific account.

We propose adding an optional signature section to the manifest with the following attributes:

Field Type Description
signer String The Ethereum address that signed the manifest
signature String The hex-encoded ECDSA signature string of the prefixed hash of the manifest (see below)

Example

specVersion: 0.0.2
description: Uniswap is a decentralized protocol for automated token exchange on Ethereum.
repository: 'https://github.com/graphprotocol/uniswap-v2-subgraph'
dataSources:
  ...
signature:
  signer: 0x8f73be66ca8c79382f72139be03746343bf5faa0
  signature: 0xb694cf5b76776702b8430298665e02eef435b5616a57f4ac309fa48a4a9943ed26bad253b520cc5de9c298ec90da87a59bed1a63f545353d83e88491c8ed78cf1c

Signature generation

The signature is generated by taking the keccak256 hash of the manifest string, prefixing it with the string Subgraph hash: , then taking a ECDSA signature of this string.

The resulting signature is then included in the new “signature” section and appended to the manifest.

The following code is an example of how a signature can be generated using ethers.js:

const manifest = `specVersion: 0.0.2
dataSources:
...`;

const hash = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(manifestCode));
const message = `Subgraph hash: ${hash}`;
const signature = await signer.signMessage(message);

const newManifestCode = `${manifestCode}
signature:
  signer: ${account}
  signature: ${signature}
`

Signature validation

To validate the signature attached to a subgraph, the signing process must be reversed, removing the signature section before validating.

The following code is an example of how to validate a manifest signature using ethers.js:

const SIGNATURE_REGEX = /\nsignature:\n  signer: ['"]?(0x[0-9a-fA-F]{40})['"]?\nsignature:['"]?(0x[0-9a-fA-F]{130})['"]?\n/

const signatureResults = SIGNATURE_REGEX.exec(code);
if (!signatureResults) {
  throw new Error('Signature not found');
}

const [signatureSection, signer, signature] = signatureResults;

const signedManifest = code.replace(signatureSection, ''); // Remove signature from manifest
const hash = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(signedCode));
const message = `Subgraph hash: ${hash}`;
const verifiedSigner = ethers.utils.verifyMessage(message, signature);

if (verifiedSigner.toLowerCase() !== signer.toLowerCase()) {
  throw new Error('Invalid signature');
}
console.log(`Subgraph verified signed by ${signer}`)

Compatibility

The proposed design is fully backwards compatable.

Drawbacks and Risks

This proposal only provides additional metadata for subgraphs, therefore there are no risks nown at this time.

Alternatives

Currently, subgraphs may include a link to a GitHub repo containing source code. However, this does not allow for programatic managment & compilation of source code. Furthermore, many GitHub repos may fall out of date compared to the published subgraphs.

Open Questions

  • None.

dmihal avatar Jun 29 '22 13:06 dmihal

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated
rfcs ✅ Ready (Inspect) Visit Preview Jun 29, 2022 at 2:16PM (UTC)

vercel[bot] avatar Jun 29 '22 13:06 vercel[bot]

This is interesting. Generally the protocol would not care about this metadata, I'd suggest that it is all grouped under a metadata: top-level key to avoid any possibility of conflict with 'official' fields. It would then just be a matter of patching graph-cli to add this automatically. Thanks for the well written proposal, but this repo isn't in use anymore, the current governance process is outlined here.

leoyvens avatar Jun 29 '22 22:06 leoyvens

I haven't yet seen this transferred to the Graph GIPS, so I will leave my comment here.

I agree with Leo that a metadata field could be adopted where you could include this information.

If these fields then get traction by the broader community, and many people are implementing signature and source code for mapping files, then it could get upgraded to an official manifest field.

Also, I think the signature could be immediately helpful. It can prevent problems in the explorer, where someone deploys a Uniswap subgraph, and no one can tell if it's from the official team.

davekay100 avatar Jul 15 '22 13:07 davekay100