Introduction
Introduction of the Unirep protocol
- poseidon hash function
hashLeftRight()
(represented ashash
hereafter) andhash5()
identityCommitment
identityCommitment
is the hash of user's EdDSA pubkey,identityNullifier
andidentityTrapdoor
- There are users who receive reputation and prove received reputation
- 1.User: users sign up by calling
userSignUp
in Unirep contract- user's
identityCommitment
is revealed at this time and it will be recorded in the contract to prevent double signup - the identity commitment will not reveal the actual identity of the user but at the same time allow user to prove identity in the cirucit
- 2.Attester: there are attesters who give attestations to users and the attestations become the users' reputation
- attester signs up by calling
attesterSignUp
in Unirep contract - attesters would be given
attesterId
by the order they sign up,attesterId
begins with1
- attester record and attestation record are public and so everyone can see which attester submits which attestation to the Unirep contract
- so the receiver of an attestation is not a user's
identityCommitment
but an random value calledepochKey
, i.e., attester attests to anepochKey
instead of anidentityCommitment
epochKey
is computed by
hash5(identityNullifier, epoch, nonce, 0, 0)
- only the user knows his
identityNullifier
so only he knows if he is receiving an attestation, others would see an attestation attesting to a random value - and in the circuit user can prove that he knows the
epochKey
and can rightfully receive and process the attestations attested to thisepochKey
Can a user choose not to process certain attestations that are bad for his reputation?
- No. The attestations to an
epochKey
would be chained together. A hashchain would be formed by the hashes of the attestations.- So user can not omit any attestation because the circuit requires each attestation in the hashchain to be processed.
- if user omits an attestation, then the computed hashchain would not match the one in the Unirep contract
hashChainResult = hash(attestation_3, hash(attestation_2, hash(attestation_1, 0)))
- a user state tree is a sparse merkle tree with it's leaves storing reputation received from each attester, e.g.,
- a user state tree leaf = hash of the reputation
user state tree root/ \hash(DEFAULT_REP_HASH, 0xabc...) hash(0xbcd..., 0xcde...)/ \ / \[No rep for leaf 0] [leaf 1: 0xabc...] [leaf 2: 0xbcd...] [leaf 3: 0xcde...] - NOTE:
[leaf 1: 0xabc...]
represents the reputation received from attester withattesterId = 1
and0xabc...
is the hash of the reputation
- a global state tree is an incremental sparse merkle tree with it's leaves storing users'
identityCommitment
s anduserStateRoot
s, e.g.,- a global state tree leaf =
hash(identityCommitment, userStateRoot)
global state tree root
/ \
hash(0xabc..., 0xcde...) hash(0xdef..., DEFAULT_EMPTY_HASH)
/ \ / \
[leaf_0: 0xabc...] [leaf_1: 0xcde...] [leaf_2: 0xdef...] [DEFAULT_EMPTY_HASH]
- NOTE: this is an incremental merkle tree so leaves are inserted from left (leaf index 0) to right, one by one, instead of inserted directly into the specified leaf index.
- NOTE: since global state tree leaf is the hash of
identityCommitment
anduserStateRoot
, others will be not be able to tell which user (hisidentityCommitment
) inserted his user state into global state tree.
- an epoch tree is a sparse merkle tree with it's leaves storing hashchain results of each epoch key, e.g.,epoch tree root/ \hash(DEFAULT_EMPTY_HASH, 0x123...) hash(DEFAULT_EMPTY_HASH, 0x456...)/ \ / \[DEFAULT_EMPTY_HASH] [epk_1: 0x123...] [DEFAULT_EMPTY_HASH] [epk_3: 0x456...]
- an nullifier tree is a sparse merkle tree with it's leaves storing nullifier of already processed attestations or epoch keys, e.g.,nullifier tree root/ \hash(1, 0) hash(0, 1)/ \ / \[leaf 0: 1] [leaf 1: 0] [leaf_2: 0] [leaf_3: 1]
- NOTE: leaf 0 of nullifier tree is reserved, it always has value
1
- NOTE: leaf value
1
means the nullifier represented by the leaf index is processed. In the example above, nullifier3
is stored in nullifier tree, this nullifier could be a nullifier of an attestation or an epoch key, and it means that the attestation or the epoch key is processed. - NOTE: nullifiers are used to prevent user from processing an aleady processed attestation or epoch key.
- an attestation includes the following data:struct Attestation {// The attester’s IDuint256 attesterId;// Positive reputationuint256 posRep;// Negative reputationuint256 negRep;// A hash of an arbitary stringuint256 graffiti;// Whether or not to overwrite the graffiti in the user’s statebool overwriteGraffiti;}
- nullifier of an attestation is computed by
hash5(ATTESTATION_NULLIFIER_DOMAIN, identityNullifier, attesterId, epoch, epochKey)
ATTESTATION_NULLIFIER_DOMAIN
is used to prevent mixed-up of attestation nullifier and epoch key nullifier
- nullifier of an epoch key is computed by
hash5(EPOCH_KEY_NULLIFIER_DOMAIN, identityNullifier, epoch, nonce, 0)
EPOCH_KEY_NULLIFIER_DOMAIN
is used to prevent mixed-up of attestation nullifier and epoch key nullifier
- a reputation includes the following data:
posRep, negRep, graffiti
- it does not include
attesterId
like an attestation does because reputation is already stored in user state tree withattesterId
as leaf index
- There is the notion of epoch in Unirep. Every
epochLength
seconds, one epoch ends and next epoch begins.- Epoch transition happens when someone calls
beginEpochTransition
- in
beginEpochTransition
, all epoch keys attested during this epoch will have their hash chain sealed- by 'sealed' it means that the hash chain is hashed again with
1
, e.g.,hash(1, originalHashChain)
- if an epoch key received no attestation, it's hash chain would be
hash(1, 0)
- After hash chain of the epoch keys are sealed, these epoch keys and their hash chain will be inserted into the epoch tree of this epoch
- there's one epoch tree for every epoch
- caller will be compensated for executing the epoch transition
- There will be a new global state tree for each epoch
- and user needs to perform user state transition to transition his user state into the latest epoch
- user performs user state transition by calling
updateUserStateRoot
- once the user performed user state transition, his user state will be inserted into the global state tree of the latest epoch
- so if a user does not perform user state transition during an epoch, his user state will not be in the global state tree of that epoch
- User should perform user state transition before he can prove the latest attestations he received.
- Also, user should perform user state transition before he can receive any attestations further. Attester can still attest to a user's epoch key in the past epoch but the user will not be able to process the attestation.
Last modified 1yr ago