# Erasure Bay
ErasureBay is a dapp built upon the Erasure protocol. It’s a marketplace for buying/selling information of any kind. It’s Numerai’s demonstration of the power of Erasure to improve the Web itself.
## How it works
* Post data to storage that no one owns(ex: ipfs)
* Stake money on your claims. Encrypt them, then reveal them, to prove you knew something.
* Sell them under a smart contract that must be enforced.
## State Machine Architecture
Erasure uses shared registries to establish a single source of truth for the Erasure Protocol. So far, the registries developed are:
* Erasure_Agreements: To keep track of Griefing templates.
* Erasure_Posts: To keep track of Feed and Post templates
* Erasure_Users: To keep track of users and their data
* Erasure_Escrows
### Clone Factories
Using the Spawner library, every item on Erasure is created as a clone of a previously deployed template. We call these Clone Factories. Every clone is also registered in a registry which provides a single source of truth on the status of the protocol.

### Staking

### Agreements
When two parties decide to engage, they begin by staking NMR and agreeing on a set of conditions for punishment. We call this combination of skin in the game and rules of engagement an Erasure_Agreement.
> Griefing is an example of an Agreement which allows two parties to come to a resolution without a third party arbitrator through punishing one another at a cost.
Griefing has two main methods: `_grief()` and `setRatio`
- `_grief` returns the cost of the punishment and is taken form the account of the punisher. Therefore requires appropriate ERC-20 token approval
- `setRatio` Set the grief ratio and type for a given staker. The ratio represents the cost in NMR to burn 1 NMR of the counterparty.
**First example of Griefing is *SimpleGriefing***
> SimpleGriefing agreement allows a staker to grant permission to a counterparty to punish, reward, or release their stake

- ***increaseStake()***
Can be called by staker only to increase the stake
- ***reward()***
Called by the counterparty to increase the stake
- ***releaseStake()***
Called by the counterparty to release the stake to the staker
- ***punish()***
Called by the counterparty to punish the staker
```js
function punish(uint256 punishment, bytes memory message) public returns
(uint256 cost) {
// restrict access
require(isCounterparty(msg.sender) || Operated.isOperator(msg.sender),
"only counterparty or operator");
// execute griefing
cost = Griefing._grief(msg.sender, _data.staker, punishment, message);
}
```
`punishment`: amount of NMR (18 decimals) to be burned from the stake
`message`: data to emit as event giving reason for the punishment
**Second Example is *CountdownGriefing***
> CountdownGriefing agreement allows a staker to grant permission to a counterparty to punish, reward, or release their stake until the countdown is completed.
>

It behaves similar to SimpleGriefing with an extra touch of a countdown.
`startCountdown`
Called by the staker to begin countdown to finalize the agreement
```js
function startCountdown() public returns (uint256 deadline) {
require(isStaker(msg.sender) || Operated.isOperator(msg.sender), "only
staker or operator");
// require countdown is not started
require(isInitialized(), "deadline already set");
// start countdown
return Countdown._start();
}
```
**Countdown** makes use of block timestamps to determine start time and end time.
`_start()` Starts the countdown based on the current block timestamp and returns `deadline` which is timestamp of the end of the countdown(current timestamp + countdown length)
Once the countdown is over, the stake can call `retrieveStake()` to retrieve the remaining stake as the agreement has ended.
### CountdownGriefingEscrow
This contract acts as escrow and allows for a buyer and a seller to deposit their stake and payment before sending it to a CountdownGriefing agreement.
This contract is designed such that there is only two end states: deposits are returned to the buyer and the seller OR the agreement is successfully created.

We'll go into it's details below
## Packages
- [Erasure Crypto + IPFS Helpers](packages/crypto-ipfs)
This package contains the crypto system used on erasurequant.com plus some IPFS helper functions.
- [GraphQL API](packages/the-graph)
This is the code for our subgraph running on The Graph (a query protocol which listens for events on smart contracts and cache data which can be accessed by a graphql query). This subgraph provides a GraphQL API for querying all the data of the Erasure protocol.
Here's the deployed subgraph for erasure protocol https://thegraph.com/explorer/subgraph/jgeary/erasure
- [Local Dev Environment](packages/testenv)
Instantiate a ganache instance with NMR and erasure protocol.
## Step by Step Walkthrough
Let's see how erasure bay interacts with erasure protocol
### New User Registration
- ***New User connects to Erasure Client. ErasureClient generates asymmetric encryption keys `PubKey`, `PrivKey`***
```js
const keypair = ErasureHelper.crypto.asymmetric.generateKeyPair(sig, salt);
```
- ***ErasureClient registers user to `Erasure_Users` and uploading it's data***
```js
const ethers = require("ethers");
const ErasureUsersArtifact = require("Erasure_Users.json");
const user = keypair.publicKey; // Public Key
const data = 16; //any data
// UserData in bytes
const userData = ethers.utils.keccak256(
ethers.utils.toUtf8Bytes(data.toString())
);
// Registering a User
const txn = await ErasureUsersArtifact.from(user).registerUser(userData);
```
***Erasure_Users.sol***
```js
function registerUser(bytes memory data) public {
require(!_users.exists(msg.sender), "user already exists");
// add user
_users.insert(msg.sender);
// set metadata
_metadata[msg.sender] = data;
// emit event
emit UserRegistered(msg.sender, data);
}
```
Adds user to set of `Users`, sets it metadata and then emits an event saying `UserRegistered`
To remove a user just call method `removeUser`, it removes the user/sender from `_users` set then deletes it's metadata from mapping `_metadata` and finally emits an event `UserRemoved`
```js
function removeUser() public {
// require user is registered
require(_users.exists(msg.sender), "user does not exist");
// remove user
_users.remove(msg.sender);
// delete metadata
delete _metadata[msg.sender];
// emit event
emit UserRemoved(msg.sender);
}
```
To get any user's metadata
```js
function getUserData(address user) public view returns (bytes memory data) {
data = _metadata[user];
}
```
### Creating a Post
A Feed_Factory is a factory contract for managing feeds.
- Seller first creates a `Feed` template contract from Feed_Factory by calling method `create`
```js
function create(bytes memory callData) public returns (address instance) {
// deploy new contract: initialize it & write minimal proxy to runtime.
instance = Spawner._spawn(msg.sender, getTemplate(), callData);
_createHelper(instance, callData);
return instance;
}
```
- ***It creates a clone of `Feed` template using a nonce, the nonce is same for clones with same calldata. The nonce can also be used to determine the address of the new contract before creation.***
_Blob of abi-encoded calldata is used to initialize the template. It returns the instance address of the clone that was created_
- ***Generates a symmetric encryption key and it's hash***
```js
const hexlify = utf8str =>
ethers.utils.hexlify(ethers.utils.toUtf8Bytes(utf8str));
const nonce = ErasureHelper.crypto.asymmetric.generateNonce()
const symmetricKey = ErasureHelper.crypto.symmetric.generateKey()
// Uses nonce, symmetricKey and above generated asymmetric key's secret
const keyHash =
ErasureHelper.crypto.asymmetric.secretBox.encryptMessage(symmetricKey,
nonce, keypair.secretKey)
```
- ***Encrypt rawData with the symmetric key and gets it ipfs path***
```js
let buf = Buffer.from(rawData);
let dataEncoded = buf.toString('base64');
const encryptedFile = ErasureHelper.crypto.symmetric.encryptMessage(
symmetricKey, dataEncoded)
const rawHash = await ErasureHelper.ipfs.onlyHash(buf)
const rawDataHash = ErasureHelper.ipfs.hashToHex(rawHash)
```
- ***Get encryptedData's ipfs path***
```js
const encryptedFileIpfsPath = await ipfs.pinata.pinTextToIPFS(encryptedFile)
```
- ***Preparing metadata***
```js
const metaData = {
nonce,
rawDataHash
keyHash,
encryptedFileIpfsPath,
}
```
- ***Submitting proofHash to our `Feed`***
```js
const proofHash = await
ErasureHelper.multihash({input:JSON.stringify(metaData), inputType:'raw',
outputType:'digest'})
let confirmedTx = await this.submitProof(proofHash)
```
Feed contract's submitHash method is called to add this proof as hash to feed.
```js
function submitHash(bytes32 proofHash) public {
// only operator or creator of this template can call submit proofHash
require(Template.isCreator(msg.sender) ||
Operated.isOperator(msg.sender), "only operator or creator");
// submit proofHash
ProofHashes._submitHash(proofHash);
}
```
ProofHashes.sol
```js
function _submitHash(bytes32 hash) internal {
emit HashSubmitted(hash); // event emitted whenever submits proofHash
}
```
- ***Finally submitting metadata to ipfs***
```js
const metadataJsonIpfsPath = await ipfs.pinata.pinJSONToIPFS(metadata)
const metadataHex = ErasureHelper.ipfs.hashToHex(metadataJsonIpfsPath)
```
### Selling a Post
Once the post is created, it's time to sell.
- Seller creates `Escrow` using CountdownGriefingEscrow_Factory.create() with `calldata` as parameter. This is exactly same as creating a Feed Template as Feed_Factory and CountdownGriefingEscrow_Factory are both factory contracts and method `create` is a factory method.
CountdownGriefingEscrow_Factory.sol
```js
constructor(address instanceRegistry, address templateContract) public {
CountdownGriefingEscrow template;
// here instance type is Escrow
bytes4 instanceType = bytes4(keccak256(bytes('Escrow')));
// here initSelector is a Escrow template
bytes4 initSelector = template.initialize.selector;
// initializing factory contract
Factory._initialize(instanceRegistry, templateContract, instanceType, initSelector);
}
```
`CountdownGriefingEscrow_Factory.create(calldata)` will return a Factory Contract with template Escrow.
`_data` is a struct Data of this form
```js
struct Data {
address buyer;
address seller;
uint128 paymentAmount; // amount to be deposited by buyer as payment
uint128 stakeAmount; // amount to be staked by seller
EscrowStatus status;
AgreementParams agreementParams;
}
```
When this escrow template is created, we can provide some params which are then set in _data struct.
`buyer` and `seller` are optional params, if not provide then the first one to stake amount will become seller, the first one to deposit amount will become buyer.
EscrowStatus is an enum, `enum EscrowStatus { isOpen, onlyStakeDeposited, onlyPaymentDeposited, isDeposited, isFinalized, isCancelled }`
- ***Seller deposits the required stake using `Escrow.depositStake()`***
```js
function depositStake() public {
// Only seller(the one who created this template) or operator can call
require(isSeller(msg.sender) || Operated.isOperator(msg.sender), "only
seller or operator");
// There should be a seller
require(_data.seller != address(0), "seller not yet set");
// deposit stake
_depositStake();
}
function _depositStake() private {
require(isOpen() || onlyPaymentDeposited(), "can only deposit stake
once");
// declare storage variables in memory
address seller = _data.seller;
uint256 stakeAmount = uint256(_data.stakeAmount);
// Increase the current stake amount
if (stakeAmount != uint256(0)) {
Staking._addStake(seller, msg.sender, stakeAmount);
}
// emit event
emit StakeDeposited(seller, stakeAmount);
// If payment is deposited, finalize the escrow
if (onlyPaymentDeposited()) {
_data.status = EscrowStatus.isDeposited;
finalize();
} else {
_data.status = EscrowStatus.onlyStakeDeposited;
}
}
```
- ***Buyer deposits the required payment using `Escrow.depositPayment()`***
```js
function depositPayment() public {
require(isBuyer(msg.sender) || Operated.isOperator(msg.sender),
"only buyer or operator");
// restrict state machine
require(_data.buyer != address(0), "buyer not yet set");
// deposit payment
_depositPayment();
}
function _depositPayment() private {
require(isOpen() || onlyStakeDeposited(), "can only deposit payment
once");
// declare storage variables in memory
address buyer = _data.buyer;
uint256 paymentAmount = uint256(_data.paymentAmount);
// Add the payment as a stake
if (paymentAmount != uint256(0)) {
Staking._addStake(buyer, msg.sender, paymentAmount);
}
// emit event
emit PaymentDeposited(buyer, paymentAmount);
// If stake is deposited, start countdown for seller to finalize
if (onlyStakeDeposited()) {
_data.status = EscrowStatus.isDeposited;
Countdown._start();
} else {
_data.status = EscrowStatus.onlyPaymentDeposited;
}
}
```
`Countdown._start()` will start the countdown based on currentblock timestamp, and will return the timestamp at the end of countdown.
- ***Retrieve buyer's `PubKey` from Erasure_Users***
- ***Computes encryptedSymKey_Buyer = PubKey_Buyer.encrypt(SymKey)***
- ***Computes metadata***
- ***Finalize escrow with creating a griefing agreement `Agreement`***
Calling `Escrow.finalize()`
(1) Will create the agreement
(2) Transfer the stake and deposit to agreement
(3) Start the countdown for agreement `CountdownGriefing(agreement).startCountdown();`
- ***Uploads metadata to ipfs***
```js
const metadataJsonIpfsPath = await ipfs.pinata.pinJSONToIPFS(metadata)
const metadataHex = ErasureHelper.ipfs.hashToHex(metadataJsonIpfsPath)
```
- ***Submitting proofHash to buyer***
```js
const proofHash = await
ErasureHelper.multihash({input:JSON.stringify(metaData), inputType:'raw',
outputType:'digest'})
let tx = Escrow.submitData(proofHash)
```
- ***Submitting data to the buyer***
```js
function submitData(bytes memory data) public {
require(isSeller(msg.sender) || Operated.isOperator(msg.sender), "only
seller or operator");
// Can only submit after finalized
require(isFinalized(), "only after finalized");
// emit event
emit DataSubmitted(data);
}
```
- ***Retriving Data from Buyer***
From Erasure_Users (users registry), get buyer's public key by calling method `getUser(buyerAddr)`
```js
// Encrypt symkey with buyer's pubkey
const encryptedSymkey = crypto.symmetric.encryptMessage(buyerPubkey,
Buffer.from(symKey))
```
Send sellData ipfs path to CountdownEscrowGriefing
```js
const selldata = {
encryptedSymkey,
proofIpfsPath
}
const selldataIpfsPath = await
ErasureHelper.multihash({input:JSON.stringify(sellData), inputType:'raw',
outputType:'digest'})
const confirmedTx = await this.submitData(selldataIpfsPath)
```
- ***Retriving Data from Seller***
Gets submitted data's ipfs path (`path`) from erasure's subgraph (graphQL api) using escrow address. Get data from that ipfs path
```js
const dataSubmittedIPFS = await
this.ipfsMini.catJSON(hexToHash(path))
```
Get symmetricKey and metadata from ipfs
```js
// get encrypted symkey
const encryptedSymkey = dataSubmittedIPFS.encryptedSymkey
// decrypt it
const decryptedSymkey = crypto.symmetric.decryptMessage(keypair.privateKey,
Buffer.from(encryptedSymkey))
const proofIpfsPath = dataSubmittedIPFS.proofIpfsPath
const proofData = await this.ipfsMini.catJSON(proofIpfsPath)
```
Get encrypted data path from escrow and then encrypted data itself from it's ipfs path
```js
const encryptedDataIpfsPath = proofData.encryptedFileIpfsPath
const encryptedData = await this.ipfsMini.cat(encryptedDataIpfsPath)
```
Decrypt data
```js
const rawData =
ErasureHelper.crypto.symmetric.decryptMessage(decryptedSymkey, encryptedData)
```
### Revealing a Post
- ***Seller uploads SymKey to ipfs***
```js
const symkeyIpfsPath = await
ErasureHelper.multihash({input:JSON.stringify(SymKey), inputType:'raw',
outputType:'digest'})
```
- ***Seller uploads rawdata to ipfs***
```js
const rawdataIpfsPath = await
ErasureHelper.multihash({input:JSON.stringify(rawData), inputType:'raw',
outputType:'digest'})
```