Skip to content
This repository has been archived by the owner on Nov 23, 2023. It is now read-only.

chore: deprecation notice #380

Merged
merged 1 commit into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 22 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# ZK Eigen Trust
# ZK EigenTrust - Deprecated

duguorong009 marked this conversation as resolved.
Show resolved Hide resolved
**Notice: This project is no longer being maintained as of November 2023.**

[![MIT licensed][mit-badge]][mit-url]
[![Build Status][actions-badge]][actions-url]
Expand All @@ -8,47 +10,39 @@
[actions-badge]: https://github.com/eigen-trust/protocol/actions/workflows/test.yml/badge.svg
[actions-url]: https://github.com/eigen-trust/protocol/actions?query=branch%3Amaster

A library for managing trust in a distributed network with zero-knowledge features.

## Main characteristics:

**Self-policing** - the shared ethics of the user population is defined and enforced by the peers themselves and not by some central authority.
A Rust and Halo2-based library designed to manage trust in distributed networks with zero-knowledge proofs, guided by the framework established in the original [EigenTrust paper](https://nlp.stanford.edu/pubs/eigentrust.pdf). Its primary characteristics are:

**Minimal** - computation, infrastructure, storage, and message complexity are reduced to a minimum.
- **Self-policing**: The shared ethics of the user population is defined and enforced by the peers themselves and not by some central authority.

**Incorruptible** - Reputation should be obtained by consistent good behavior through several transactions. This is enforced for all users, so no one can cheat the system and obtain a higher reputation. It is also resistant to malicious collectives.
- **Minimal**: computation, infrastructure, storage, and message complexity are reduced to a minimum.

## Development Guidelines
- **Incorruptible**: Reputation should be obtained by consistent good behavior through several transactions. This is enforced for all users, so no one can cheat the system and obtain a higher reputation. It is also resistant to malicious collectives.

The following document explains the development process and our values:
## Deprecation Notice

[Development Process](https://hackmd.io/MzCV5EGyTo-aNIRUV0PnUQ)
Effective November 2023, this project has been deprecated and is no longer under active development.

duguorong009 marked this conversation as resolved.
Show resolved Hide resolved
## Usage
We invite the community to fork and maintain their own versions of this codebase. Should you choose to do so, we remind you to comply with the terms outlined in the [license](LICENSE).

To build the project:
## Structure

```bash
./scripts/build.sh
```
The project is organized in three crates:

To build the documentation:
- [eigentrust](eigentrust): This is the core library crate. It provides the `Client` struct for interfacing with the EigenTrust algorithm's circuits and includes additional modules to extend its functionality and facilitate integration.

```bash
./scripts/build-docs.sh
- [eigentrust-cli](eigentrust-cli): This crate offers a command-line interface application that serves as a practical example of using the library. It supports operations such as deploying smart contracts, submitting attestations, calculating global trust scores, and generating and verifying zero-knowledge proofs.

# Open the documentation in the browser
cargo doc --no-deps --open
```
- [eigentrust-zk](eigentrust-zk): Dedicated to the zero-knowledge components of the protocol, this crate encompasses the necessary Chips, Chipsets, and Circuits that pertain to the EigenTrust protocol implementation.

## License
For a more in-depth understanding of the project's architecture and functionality, please refer to the documentation in the [docs](docs) directory.

This library is licensed under the following license:
There's also a [scripts](scripts) directory containing scripts for building documentation, running tests across the workspace, and compiling the entire project.

- MIT license ([LICENSE](LICENSE) or [opensource.org license link](http://opensource.org/licenses/MIT))
### License

## Acknowledgements
Licensed under the MIT License - see the [LICENSE](LICENSE) file for details or visit [opensource.org](http://opensource.org/licenses/MIT).

This project is developed under the Ethereum Foundation grant.
### Acknowledgements

The library is implemented according to the original [Eigen Trust paper](https://web.archive.org/web/20230219174826/http://ilpubs.stanford.edu:8090/562/1/2002-56.pdf).
- Ethereum Foundation and Privacy & Scaling Explorations team.
- All contributors to this repository.
28 changes: 28 additions & 0 deletions docs/0_attestation_station.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
This page describes the AttestationStation smart contract and how its used within the EigenTrust protocol context.
---

# AttestationStation

The AttestationStation smart contract is a key component of the EigenTrust protocol, which is designed to enable trust among peers in decentralized networks. Attestations, also known as opinions or ratings, are an important part of the protocol as they allow peers to express their trust or distrust of other peers. The AttestationStation contract serves as a repository for attestations submitted by peers in the network.

Examples (Let's assume we are doing ratings from 0-5):
- Alice attests Bob with a rating of 5
- Alice attests to Carol with a rating of 2
- Bob attests to Alice with a rating of 3
- Carol says Bob with a rating of 4
- Alice attests Bob with a rating of 1

The mapping attestations is a 3-dimensional mapping that stores attestations submitted by peers. The first key is the address of the peer submitting the attestation. The second key is the address of the peer being attested to. The third key is a bytes32 hash representing the transaction or interaction between the peers. The value stored in the mapping is a bytes array representing the attestation data.
```solidity
mapping(address => mapping(address => mapping(bytes32 => bytes))) public attestations;
```

The AttestationData struct represents an attestation submitted by a peer. The `about` field is the address of the peer being attested to. The `key` field is a bytes32 hash representing the transaction or interaction between the peers. The `val` field is a bytes array representing the attestation data. The structure of the `val` field is described in more detail in the [Attestations](../1_attestations.md) documentation.
```solidity
struct AttestationData {
address about;
bytes32 key;
bytes val;
}
```
68 changes: 68 additions & 0 deletions docs/1_attestations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
This page describes the smart contract used for attestations
---

# Attestations

Attestations are the ratings or opinions given by one peer about another peer in the EigenTrust protocol. Each attestation is given for a single transaction or interaction between peers.

The structure of an attestation is defined as follows:
```rust
struct Attestation<F: FieldExt> {
about: F,
key: F,
value: F,
message: F
}
```

Here's a breakdown of each field in the attestation:

- `about`: the Ethereum address of the peer being rated. This could be an EOA, a smart contract, a DAO, etc.
- `key`: a unique identifier for the transaction or interaction being rated. This could be a hash of the transaction data or a random number generated by the rater.
- `value`: the score given by the rater for the transaction or interaction. The score can range from 0 to a maximum score defined as a constant in the protocol.
- `message`: an optional field for attaching additional information to the attestation. This could be a message from the rater, a domain in which the transaction took place, or a content hash related to the transaction.

To ensure the integrity and authenticity of an attestation, it is hashed using the Poseidon hash function and then signed using the ECDSA signing algorithm:
```rust
let att_hash = Poseidon::hash(attestation);
let sig = ECDSA::sign(att_hash, keys);
```
The resulting signature, value and message bytes are stored in the AttestationStation smart contract. The bytes layout would be:
```rust
r = [u8; 32]
s = [u8; 32]
value = u8
message = [u8; 32]
```
This adds up to 97 bytes or 65 if we exclude message bytes.

In case of fetching the attestation from AS and verifying it - first, we read the event:
```solidity
event AttestationCreated(
address indexed creator,
address indexed about,
bytes32 indexed key,
bytes val
);
```

Using this data, we extract the `r` and `s`, we verify the signature:
```rust
let (r, s, value, message) = extract_r_s_value_message(val);
let att = Attestation::new(about, key, value, message);
let hash = Poseidon::hash(att);
let is_valid = ECDSA::verify(pub_key, r, s, hash);
assert!(is_valid);
```

Then check if the used `pub_key` is actually the pre-image of the `creator`:
```rust
let pk_hash = keccak256(pub_key);
let creator_address = to_address(pk_hash);
assert!(creator_address == creator);
```

See [AttestationStation](../0_attestation_station.md) for more details on how attestations are stored and managed in the EigenTrust protocol.

By signing the attestation, the rater can prove that they made the rating and that the rating has not been tampered with. This is important for verifying the validity of the attestation in an off-chain environment, such as when calculating the EigenTrust scores for each peer.
51 changes: 51 additions & 0 deletions docs/2_data_processing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
This page provides an overview of how data is processed after being pulled from AttestationStation. The data stored in AttestationStation is submitted from one user to another, but the EigenTrust algorithm processes opinions from one user to the whole group.
---

To start, a group needs to be defined. This is typically done by assigning a group ID and creating a list of peers, as shown below:
```
group_id = 1377
group = [peer1, peer2, peer3, peer4, peer5]
```


And lets assume we have some attestations in AS already:
```
peer1 => peer2 => 1377 => 5
peer2 => peer3 => 1377 => 7
peer4 => peer2 => 1377 => 3
```

Once the group is defined, the next step is to search AttestationStation to construct an opinion map. This is done by iterating through each peer in the group and searching for relevant attestations. The pseudo code snippet below demonstrates this process:
```rust
for i in 0..group.len() {
let peer_i = group[i];
if peer_i == null {
continue;
}
for j in 0..group.len() {
let peer_j = group[j];

let is_null = peer_j == null;
let is_self = peer_i == peer_j;
if is_null || is_self {
continue;
}

let att = AS.attestations(peer_i, peer_j, group_id);
op_map[peer_i][j] = (peer_j, att);
}
}
```

In this code, `peer_i` and `peer_j` represent two peers in the group, and `AS.attestations(peer_i, peer_j, group_id)` retrieves the attestation between the two peers for the given group ID. The resulting opinion map, stored in the op_map variable, is a two-dimensional array that maps each peer to a list of their attestations with other peers in the group.

Here's an example of what the opinion map might look like, based on the attestations shown earlier:
```
peer1_op => [(peer1, 0), (peer2, 5), (peer3, 0), (peer4, 0), (peer5, 0)]
peer2_op => [(peer1, 0), (peer2, 0), (peer3, 7), (peer4, 0), (peer5, 0)]
peer4_op => [(peer1, 0), (peer2, 3), (peer3, 0), (peer4, 0), (peer5, 0)]
```

This opinion map is then passed to a filtering algorithm before being passed to the EigenTrust algorithm.\
The details of the filtering algorithm are discussed in more detail in the [Dynamic Sets](../3_dynamic_sets.md) page.
110 changes: 110 additions & 0 deletions docs/3_dynamic_sets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
---
This page describes our dynamic sets filtering algorithm.
---

Suppose we create a fixed set of peers with a limit of 5.\
Instead of using Ethereum addresses we will use simplified identifiers
like peer1, peer2, peer3, etc. and we will use `null` for empty slots.

This set will change over time as we will be able to add and remove members.

For example, let's say we have the following set of peers:
```
set = [peer1, peer2, peer3, null, null]
```

Now, imagine we have a map that represent opinions from one peer to the whole group. Every opinion should match the set to be valid, in the following way:
```
peer1 => [(peer1, 0), (peer2, 4), (peer3, 6), (null, 0), (null, 0)]
```
The opinion array should equal the original set in length.\
The items in the array are touples of the id of the peer that we want to give the score to and the actual score. The id at each index should match the id in the set in order to be considered valid.

The whole map should look like this:
```
scores => {
peer1 => [(peer1, 0), (peer2, 4), (peer3, 6), (null, 0), (null, 0)]
peer2 => [(peer1, 4), (peer2, 0), (peer3, 6), (null, 0), (null, 0)]
peer3 => [(peer1, 4), (peer2, 6), (peer3, 0), (null, 0), (null, 0)]
}
```

Now, let's take a look at how we filter out invalid cases from the opinion array.

**Filtering of invalid cases:**

1) Id at the specific index does not match the one in the set:\
Suppose we want to give a score of 5 to peer13 at the index 3:
```
peer1 => [(peer1, 0), (peer2, 4), (peer3, 6), (peer13, 5), (null, 0)]
```
Since the id at index 3 is null, the id and the score will be nullified, and the new opinion will look like:
```
peer1 => [(peer1, 0), (peer2, 4), (peer3, 6), (null, 0), (null, 0)]
```

2) Non 0 score was given to itself:\
Giving score to itself is forbiden since peers would be able to give the score only to themselves, thus introducing reputation leaking during the convergence.
So, this type of opinion:
```
peer1 => [(peer1, 4), (peer2, 0), (peer3, 6), (null, 0), (null, 0)]
```
will turn into:
```
peer1 => [(peer1, 0), (peer2, 0), (peer3, 6), (null, 0), (null, 0)]
```

3) Total sum of scores is 0:\
If the initial opinion, or the filtered opinion has a sum of scores of 0,
the equal score (score of 1) is given to each peer, so this:
```
peer1 => [(peer1, 0), (peer2, 0), (peer3, 0), (null, 0), (null, 0)]
```
will turn into this:
```
peer1 => [(peer1, 0), (peer2, 1), (peer3, 1), (null, 0), (null, 0)]
```

4) Opinion array does not exist/not signed:\
Will be treated the same way as 3). The equal score will be distributed to all peers

The pseudo code algorithm:
```rust
for i in set.len() {
let pk_i = set[i];
if pk_i == null {
continue;
}

for j in set.len() {
let pk_j = set[j];
let op_pk_j = scores[pk_i][j].0;

let is_diff_pk_j = pk_j != op_pk_j;
let is_pk_j_zero = pk_j == null;
let is_pk_i = pk_j == pk_i;

if is_diff_pk_j || is_pk_j_zero || is_pk_i {
scores[pk_i][j].1 = 0;
}

if is_diff_pk_j {
scores[pk_i][j].0 = pk_j;
}
}

let op_score_sum = sum(scores[pk_i]);
if op_score_sum == 0 {
for j in 0..group.len() {
let pk_j = scores[pk_i][j].0;

let is_diff_pk = pk_j != pk_i;
let is_not_null = pk_j != null;

if is_diff_pk && is_not_null {
scores[pk_i][j] = (pk_j, Fr::from(1));
}
}
}
}
```
Loading
Loading