---
# System prepended metadata

title: Ceramic Anchor Contract

---

---
cip: TBD
title: Ceramic Anchor Contract
author: Sergey Ukustov <sergey@ukstv.me>
discussions-to: TBD
status: Draft
category: Standards
type: Core
created: 2021-10-13
edited: 2021-10-13
---

## 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](https://github.com/OpenZeppelin/openzeppelin-contracts) library.

For "ownable" feature we are going to extend our contract [`Ownable`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol) 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:


```solidity=
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:

```solidity=
// 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](https://creativecommons.org/publicdomain/zero/1.0/).