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

CIP-110 Ceramic Anchor Contract #110

Closed
ukstv opened this issue Oct 14, 2021 · 5 comments
Closed

CIP-110 Ceramic Anchor Contract #110

ukstv opened this issue Oct 14, 2021 · 5 comments

Comments

@ukstv
Copy link
Contributor

ukstv commented Oct 14, 2021

cip: 110
title: Ceramic Anchor Contract
author: Sergey Ukustov <sergey@ukstv.me>
discussions-to: https://github.com/ceramicnetwork/CIP/issues/110
status: Draft
category: Standards
type: Core
created: 2021-10-14
edited: 2021-10-14

Simple Summary

We can open other anchor service operators to participate in Ceramic anchoring using an allowlist as a smart contract on Ethereum blockchain.

Abstract

This improvement proposal presents a Ceramic Anchor Contract. It is a novel way to make anchors on Ethereum network.

Motivation

As of today, anchoring on Ceramic is done independently by each Ceramic Anchor Service (CAS). An anchor is a transaction from a wallet to itself with data, that represent a CID of a Merkle root.

While being low cost, such a construction presents a security risk, especially for Ceramic mainnet. A malicious CAS might withhold IPFS data necessary for proving an anchor (i.e. Merkle tree elements put on IPFS), thus making a Ceramic stream effectively invalid. We would like to make anchor services available for running by anybody, but this risk we would like to mitigate. Thus, until a validator network is in place, we would like to have a known set of trusted anchor services. We would like though the list to be open for anybody to join.

Specification

We can achieve that using a smart contract on Ethereum network (as our main network used for anchoring) that contains an allowlist of trusted anchor services. Only an allowed CAS can make an anchor transaction. The allowlist can be modified, preferrable by a DAO.

Contract Features

  • contract should be "ownable"
    • an owner can transfer ownership to other address
    • this allows us to start with just a single externally-owned address as an owner, and later transfer ownership to a DAO
  • contract should maintain an allowlist of allowed CAS accounts
    • only the contract owner can modify the allowlist
    • "modify" means "add" or "remove"
    • the contract should emit appropriate events for both adding and removal
    • an allowed address can remove itself
  • contract has "anchor" function
    • only an allowed address can call the function
    • the function should accept a CID of the merkle root
    • the function should emit an appropriate event

Implementation details

The contract should be written in Solidity language, using Truffle or Hardhat toolboxes. It might use OpenZeppelin library.

For "ownable" feature we are going to extend our contract Ownable from OpenZeppelin. The functionality provided there is good enough, so we do not elaborate on that topic further.

The allowlist is maintained as mapping (address => bool). We use two functions to modify the allowlist: addCas and removeCas, both emit a respective event:

mapping (address => bool) allowlist;

event DidAddCas(address indexed _service);
event DidRemoveCas(address indexed _service);

// Only owner can add to the allowlist
function addCas(address _service) public onlyOwner {
    allowlist[_service] = true;
    emit DidAddCas(_service);
}
    
// Removal can be performed by the owner or the service itself
function removeCas(address _service) public {
    require((owner() == _msgSender()) || (allowlist[_msgSender()] && _msgSender() === _service), "Caller is not allowed or the owner");
    delete allowlist[_service];
    emit DidRemoveCas(_service);
}

The "anchor" function can be called by an allowed service only. Its responsibility is limited to just emit an event:

// Only an address in the allowlist is allowed.
modifier onlyAllowed() {
    require(allowlist[_msgSender()], "Allowlist: caller is not allowed");
    _;
}

event DidAnchor(address indexed _service, bytes _root);

// Here _root is a byte representation of Merkle root CID.
function anchor(bytes calldata _root) public onlyAllowed {
    emit DidAnchor(_msgSender(), _root);
}

Anchoring Process

We maintain an authoritative list of anchoring contracts across Ethereum networks: Ropsten, Rinkeby, mainnet. This list is shared by CAS and Ceramic nodes.

CAS should use a contract on a network of choice for anchoring. When validating an anchor record, Ceramic node should check if a transaction happens on the authoritative contract, and the emitted event contains a merkle root.

Backwards Compatibility

Contract-based anchoring is not backwards compatible. We have to delineate between version of the anchors. We add a version property to AnchorCommit payload. Contract-based anchors use version: 1. If version is omitted, we expect the proof to be a raw transaction, and we check if it originated from one of the old well-known CAS addresses. After some time, to be defined later when CIP is accepted, Ceramic network only allows version: 1 proofs.

Rationale

An allowlist is for CAS instances, it allows them to do blockchain transactions. Thus, it is natural, that it is implemented as a blockchain smart contract.

Mainly we use Ethereum network now for anchoring, so Ethereum-based allowlist is a natural choice. It does not prevent to use other blockchains in future.

Single Ethereum transaction could be considered expensive. With the outlined design we effectively equalize the costs through multiple co-existing CAS instances.

Implementation

No implementation yet.

Security Considerations

Ideally, the contract should be independently audited to mitigate smart contract risks.

We consider allowed CAS to behave well, which is only enforced on social consensus layer until fully decentralized validators network is in place.

Copyright

Copyright and related rights waived via CC0.

@ukstv ukstv changed the title CIP-TBD Ceramic Anchor Contract CIP-110 Ceramic Anchor Contract Oct 14, 2021
@ukstv ukstv self-assigned this Oct 14, 2021
@stbrody
Copy link
Contributor

stbrody commented Oct 14, 2021

After some time, to be defined later when CIP is accepted, Ceramic network only allows version: 1 proofs.

We will probably always need to accept the old format anchors - otherwise we'll invalidate any streams created before the switch. As long as we only accept old-style AnchorCommits when the transaction is performed by one of the known trusted cas eth addresses, we should be able to continue to accept them forever without any loss of security.

@stbrody
Copy link
Contributor

stbrody commented Oct 14, 2021

We could also only allow old-style anchors before a certain well-known blockNumber/blockTimestamp on ethereum based on when we made the switch to version:1 anchors.

@oed
Copy link
Member

oed commented Oct 15, 2021

We could also only allow old-style anchors before a certain well-known blockNumber/blockTimestamp on ethereum based on when we made the switch to version:1 anchors.

Yes, this is what we should do!

@ukstv
Copy link
Contributor Author

ukstv commented Oct 15, 2021

We could also only allow old-style anchors before a certain well-known blockNumber/blockTimestamp on ethereum based on when we made the switch to version:1 anchors.

Yes, this is what we should do!

This is what I implied when talking about time. We are not going to discard current anchors in any way.

@ukstv
Copy link
Contributor Author

ukstv commented Jul 3, 2023

CIP-110 is now Final. Closing this issue.

@ukstv ukstv closed this as completed Jul 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants
@ukstv @stbrody @oed and others