Skip to content

Commit

Permalink
contracts (#59)
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonmorton authored Jul 28, 2023
1 parent cf63568 commit e443cb6
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 7 deletions.
5 changes: 2 additions & 3 deletions Tutorials/CryptoIdol/backend.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ order: 2

# Idol Backend Tutorial


This is part 3 of our tutorial on building the [Cryptoidol](https://cryptoidol.tech) demo app.
This is part 3 of our tutorial on building the [Cryptoidol](https://cryptoidol.tech) demo app; check out the [backend](https://github.com/zkonduit/cryptoidol) and [frontend](https://github.com/zkonduit/cryptoidol-frontend).

# Overview

Expand Down Expand Up @@ -598,4 +597,4 @@ If you have gone through all these steps you should now have a live server that
Setting up the proving server is very involved. Perhaps you have better things to do and would prefer a hosted service that computes proofs for your application. We now offer a managed service, ezkl hub, which takes care of all this work.
[Click here](https://mmycoj5vy74.typeform.com/to/Z2aikKUt) to join the waiting list and try out ezkl hub.
[Click here](https://mmycoj5vy74.typeform.com/to/Z2aikKUt) to join the waiting list and try out ezkl hub.
187 changes: 187 additions & 0 deletions Tutorials/CryptoIdol/contracts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
---
order: 1
---


# Idol Contracts Tutorial


This is part 4 of our tutorial on building the [Cryptoidol](https://cryptoidol.tech) demo app; check out the [backend](https://github.com/zkonduit/cryptoidol) and [frontend](https://github.com/zkonduit/cryptoidol-frontend).

The crypto idol contract stores the score of contestants and makes calls to the on-chain evm verifier of the corresponding ai model used to judge contestants to validate the submitted scores.

## Step 1. Write CryptoIdol.sol

- **Verifier Contract Interface:** The contract leverages an external Verifier contract to verify the proof submitted by contestants. The Verifier contract interface has one function - **`verify`** - which takes public inputs and a proof as parameters and returns a boolean indicating whether the proof is valid.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
interface Verifier {
function verify(
uint256[2] calldata pubInputs,
bytes calldata proof
) external view returns(bool);
}
```

- **Contestant Struct:** The Contestant struct keeps track of a participant's score and the cycle in which they participated. A mapping associates an address (the contestant's Ethereum address) with another mapping of uint256 (the contestants entry number) to Contestant. The number of entries a given contestant has done is store in the contestantsCount mapping.

```solidity
contract CryptoIdol {
struct Contestant {
uint256 score;
uint256 cycle;
}
mapping(address => uint256) public contestantsCount;
mapping(address => mapping(uint256 => Contestant)) public contestants;
```

- **Admin:** The contract has an admin address, responsible for updating the Verifier contract when a new cycle occurs. Only the admin can perform this operation. We set the admin account to the EZKL team’s multisig wallet.
- **Cycle:** This represents the current cycle of the competition. The cycle number is incremented whenever the admin updates the Verifier contract.

```solidity
// The admin address in charge of updating the to new verifier
// each new cycle.
address public immutable admin;
// The cycle number. This will be incremented by the admin each
// time a new cycle occurs.
uint16 public cycle = 1;
```

**3. Events**

- **NewEntry:** This event is emitted when a contestant submits their score. It logs the contestant's address, the count of their submissions, their score, and the cycle number. These events are indexed on the client-side to construct the leaderboard.
- **NewCycle:** This event is emitted when the admin updates the Verifier contract, signalling the start of a new cycle. It logs the new Verifier's address and the updated cycle number.

```solidity
event NewEntry (
address indexed contestant,
uint256 count,
uint256 score,
uint256 cycle
);
event NewCycle (
address indexed verifier,
uint256 cycle
);
```

**4. Functions**

- **updateVerifier:** This function is used by the admin to update the Verifier contract. It increments the cycle number and emits a NewCycle event.

```solidity
function updateVerifier(address _verifier) public {
// Called when a new cycle occurs. The admin will update the verifier to the new one.
require(msg.sender == admin);
require(_verifier != address(0));
verifier = Verifier(_verifier);
cycle += 1;
emit NewCycle(address(verifier), cycle);
}
```

- **submitScore:** Contestants use this function to submit their score and a proof. The function verifies the proof using the current Verifier contract, updates the contestant's score, and emits a NewEntry event.

```solidity
function submitScore(uint256 score, bytes memory proof) public {
// Verify EZKL proof.
require(verifier.verify([uint256(uint160(msg.sender)), score], proof));
// Update the score struct
uint256 count = ++contestantsCount[msg.sender];
contestants[msg.sender][count] = Contestant(score, cycle);
// Emit the New Entry event. All of these events will be indexed on the client side in order
// to construct the leaderboard as opposed to storing the entire leader board on the blockchain.
emit NewEntry(msg.sender, count, score, cycle);
}
```

**5. Protection Against Miner Extractable Value (MEV)**

- In order to guard against MEV, the contract design includes a critical feature: the address of the account submitting their score is both a private and a public input to the proof.
- Let's consider a scenario where a high score from the judge could result in a reward. There would then be an incentive for MEV bots to duplicate any issued valid proof and submit the transaction to the verifier contract, attempting to claim the reward before the original issuer.
- However, with the transaction creator's public key/address being a private input AND a public input to the proof, the on-chain verification will only succeed if the key passed in during proof creation is also passed in as a public input to the contract. This design ensures that the reward issued by the contract is irrevocably tied to the original contestant's key. So even if the proof is submitted by another actor, the reward would STILL go to the original contestant, thus providing a safeguard against MEV.

## Step 2. Deploy the contracts

**1. Adjust compiler settings.**

- As you prepare to deploy the verifier and crypto idol contracts, it is critical to set the Ethereum Virtual Machine (EVM) version to a configuration that's compatible with layer 2 blockchains. In our experience, the 'London' version has shown to be the most compatible. For the purpose of this tutorial, we'll use Remix as our deployment platform. To modify the EVM version to 'London', navigate to the 'Advanced Configurations' tab and select 'London' from the 'EVM Version' dropdown list. Neglecting to make this adjustment might result in unsuccessful deployment of your verifier Solidity code, often manifesting as a 'push0 not a valid opcode' error.
- Also in cases when you are deploying an especially large verifiers and want to save on deployment costs (or just get the verifier below the max contract size limit of 24.5 kb), you will need to enable optimizations by setting the runs param to 1 to maximize deployment costs savings.

![](../../assets/idolcontracts0.png)

**2. Deployment**

- You should deploy the verifier contract first, as we will need to pass the address of the verifier to the crypto idol contract’s constructor. Click the page icon next to ‘x’ on the deployed verifier instance to copy its address, then paste it into the _verifier deploy param of CryptoIdol.sol. For the _admin field, paste in whatever account address you want to have the ability to update the verifier contract that the crypto idol contract connects to.

![](../../assets/idolcontracts1.png)


## **Step 3. Create a Subgraph for the Leaderboard**

- Creating a subgraph enables the development of a GraphQL endpoint to query the "NewEntry" events emitted by the contract. This data forms the basis of the CryptoIdol leaderboard, displaying the contestants with the highest submitted scores for a given cycle. By using this method, we can avoid storing the complete leaderboard on the blockchain, significantly reducing storage requirements and ensuring efficient data access.

### **Set up a Subgraph**

Follow these steps to create a subgraph:

1. **Initialize a New Subgraph:** Initialize a new subgraph on The Graph's hosted service. Ensure you have the Graph CLI installed and use the command **`graph init`** to start a new subgraph.
2. **Define the Schema:** Define a GraphQL schema for your subgraph. Your schema should at least include the "NewEntry" events, with fields for **`score`**, **`contestant`**, and **`cycle`**.
3. **Create a Mapping:** The mapping script processes the event data from the blockchain and converts it into the format defined by your schema. It's written in AssemblyScript, a variant of TypeScript. For the "NewEntry" event, you will need to map the **`score`**, **`contestant`**, and **`cycle`** fields.
4. **Deploy the Subgraph:** Deploy the subgraph to The Graph's hosted service using the **`graph deploy`** command.

### **Query the Subgraph**

Here is a sample TypeScript script that uses the created subgraph to render the leaderboard data:

```tsx
import axios from 'axios';

interface Entry {
score: string;
contestant: string;
}

const cycle = "1"; // Set the cycle value programmatically

const query = `
query {
newEntries(where: { cycle: "${cycle}" }, orderBy: blockTimestamp, orderDirection: asc) {
score
contestant
}
}
`;

axios
.post('https://api.thegraph.com/subgraphs/name/ethan-crypto/crypto_idol', {
query: query,
})
.then((response) => {
let newEntries = response.data.data.newEntries;
console.log(newEntries);
let uniqueEntriesMap: { [key: string]: Entry } = {};
newEntries.forEach((entry: Entry) => {
const contestant = entry.contestant;
uniqueEntriesMap[contestant] = entry;
});

const leaderboard: Entry[] = Object.values(uniqueEntriesMap);

leaderboard.sort((a, b) => Number(b.score) - Number(a.score));

console.log(leaderboard);
})
.catch((error) => {
console.error(error);
});
```

This script sends a GraphQL request to the subgraph and retrieves the "NewEntry" events for a specific cycle. It then processes this data to generate a leaderboard, which it sorts in descending order by score.
5 changes: 3 additions & 2 deletions Tutorials/CryptoIdol/model.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ order: 3

# Idol Model Tutorial

This is part 2 of our tutorial on building the [Cryptoidol](https://cryptoidol.tech) demo app.
This is part 2 of our tutorial on building the [Cryptoidol](https://cryptoidol.tech) demo app; check out the [backend](https://github.com/zkonduit/cryptoidol) and [frontend](https://github.com/zkonduit/cryptoidol-frontend).


### Background Knowledge

Expand Down Expand Up @@ -69,4 +70,4 @@ Next, we set up the proving and verifying keys for our model and generate a full

Finally, we create an Ethereum Smart Contract that acts as a verifier for our model. We deploy the contract (using `deploy-verifier`) to a local Ethereum node using the [Anvil Ethereum node simulator](https://github.com/foundry-rs/foundry/blob/master/anvil/README.md) running on port `3030`. In a separate terminal window (or using the notebook bash tooling `!`) run `anvil -p 3030` to start the node.

After deploying the contract, we obtain its address which we will use to interact with and we can then verify the proof using the deployed contract by calling the **`verify_evm`** function from the EZKL library and passing the proof and the contract's address. If everything is set up correctly, the proof should be verified successfully!
After deploying the contract, we obtain its address which we will use to interact with and we can then verify the proof using the deployed contract by calling the **`verify_evm`** function from the EZKL library and passing the proof and the contract's address. If everything is set up correctly, the proof should be verified successfully!
4 changes: 2 additions & 2 deletions Tutorials/CryptoIdol/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ order: 5
# **Building a Voice Emotion Classifier and Verifier with PyTorch, EZKL, and Ethereum Smart Contracts**


This is part 1 of our tutorial on building the [Cryptoidol](https://cryptoidol.tech) demo app.
This is part 1 of our tutorial on building the [Cryptoidol](https://cryptoidol.tech) demo app. The finished app is on Github; check out the [backend](https://github.com/zkonduit/cryptoidol) and [frontend](https://github.com/zkonduit/cryptoidol-frontend).

## Background Knowledge

Expand Down Expand Up @@ -53,4 +53,4 @@ We then verify the proof using the deployed contract by calling the **`verify_ev

## **Conclusion**

We hope this tutorial provides a foundation for building more complex production-ready application on EZKL that require secure, verifiable judgments.
We hope this tutorial provides a foundation for building more complex production-ready application on EZKL that require secure, verifiable judgments.
Binary file added assets/idolcontracts0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/idolcontracts1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit e443cb6

Please sign in to comment.