This repository contains the proof-of-concept implementation of UPPR, a universal privacy-preserving revocation framework for Verifiable Credentials (VCs), presented at the IEEE International Conference on Blockchain 2025 (https://ieeexplore.ieee.org/document/11264637). UPPR supports both linkable one-show credentials (oVCs) and unlinkable Anonymous Credentials (ACs) by combining Verifiable Random Functions (VRFs) with a Bloom filter cascade. Revocation artifacts leak no metadata, holders never need to contact the issuer or any third party to prove non-revocation, and verifiers learn nothing beyond the credential's revocation status at the time of presentation. While the construction is agnostic to the underlying infrastructure, this implementation deploys the revocation artifact as an Ethereum smart contract.
Each credential is bound to a VRF key pair at issuance, with the public key embedded as a credential attribute and the secret key shared between issuer and holder. To revoke a credential, the issuer evaluates the VRF over the current epoch and publishes the resulting token in a Bloom filter cascade (i.e. a layered construction that eliminates false positives). The cascade is padded with random dummy tokens to hide the true number of revocations.
During presentation, the holder computes a fresh revocation token for the current epoch. For oVCs, the token and VRF proof are disclosed directly to the verifier. For ACs, a single zero-knowledge proof attests to the token's correctness without revealing the underlying key or any linkable information. In both cases, the verifier validates the token and checks its non-inclusion in the cascade. If the token is absent, the credential is not revoked.
Because proof generation depends only on the holder's secret key and the current epoch, holders never need to fetch the revocation artifact or contact any external party. The implementation uses go-ecvrf and vrf-solidity for the VRF, gnark with Groth16 for the AC zero-knowledge proofs, and Solidity for the on-chain revocation artifact. Since the construction only requires the revocation artifact to be publicly readable and exclusively updatable by the issuer, it can equally be deployed on traditional infrastructure, e.g., a signed file hosted on a CDN.
Implements the Bloom filter cascade used for encoding revocation artifacts.
sol/: Solidity implementation for on-chain verification.cascade.go: Go implementation for off-chain artifact construction.filter.go: Bloom filter logic adapted from bits-and-blooms/bloom.
Contains external dependencies and adapted libraries.
go-ecvrf/: Fork of vechain/go-ecvrf with improved EC operations using go-ethereum.vrf/: Solidity contracts from witnet/vrf-solidity for on-chain VRF functionality.
Implements holder-side logic for generating non-revocation proofs using credentials and revocation artifacts.
Implements issuer-side logic for credential issuance and revocation artifact generation.
Contains two Solidity smart contracts:
- A verifier for one-show credentials (oVC).
- A verifier for multi-show credentials (AC) using Zero-Knowledge Proofs.
Implements the Zero-Knowledge circuit for multi-show credential revocation using gnark and provides the corresponding Solidity verifier for on-chain validation.
All packages in this repository include comprehensive tests. You can run the full test suite from the root directory with:
go test ./...
To execute all available benchmarks, use:
go test -bench . -run=^$ -benchmem ./...
This will run all benchmarks across the codebase and display performance and memory statistics.
We provide comprehensive benchmarks to assess runtime and on-chain performance of key operations in UPPR. Below is a summary of selected results.
Artifact generation scales linearly with the number of issued credentials. Multi-show credentials are significantly cheaper to process than one-show credentials due to the use of hash functions instead of VRFs.
| Credential Type | Domain | Revocation Rate | N | Time per Operation (ns/op) |
|---|---|---|---|---|
| OneShow | 50000 | 0.10 | 10 | 209533808 |
| OneShow | 100000 | 0.10 | 10 | 417011904 |
| OneShow | 200000 | 0.10 | 10 | 836083775 |
| OneShow | 300000 | 0.10 | 10 | 1271718658 |
| OneShow | 400000 | 0.10 | 10 | 1721598354 |
| OneShow | 500000 | 0.10 | 10 | 2095792862 |
| OneShow | 600000 | 0.10 | 10 | 2553666121 |
| OneShow | 700000 | 0.10 | 10 | 2965309096 |
| OneShow | 800000 | 0.10 | 10 | 3417696242 |
| OneShow | 900000 | 0.10 | 10 | 3842405254 |
| OneShow | 1000000 | 0.10 | 10 | 4231854058 |
| MultiShow | 50000 | 0.10 | 10 | 104810962 |
| MultiShow | 100000 | 0.10 | 10 | 182449854 |
| MultiShow | 200000 | 0.10 | 10 | 366856717 |
| MultiShow | 300000 | 0.10 | 10 | 547776792 |
| MultiShow | 400000 | 0.10 | 10 | 729738012 |
| MultiShow | 500000 | 0.10 | 10 | 920114858 |
| MultiShow | 600000 | 0.10 | 10 | 1098911667 |
| MultiShow | 700000 | 0.10 | 10 | 1279644150 |
| MultiShow | 800000 | 0.10 | 10 | 1517099583 |
| MultiShow | 900000 | 0.10 | 10 | 1712877912 |
| MultiShow | 1000000 | 0.10 | 10 | 1944766500 |
| Type | Main Primitive | Time [ms] | Memory [MB] | On-Chain Verification [gas] |
|---|---|---|---|---|
| OneShow | VRF.Eval | 0.072 | 0.004 | 422209 |
| MultiShow | ZKP.Prove | 33.74 | 5.34 | 281741 |
The table below shows the gas and ETH cost for updating the Bloom filter cascade on-chain. The first update writes the filter to storage, while the second only modifies existing entries, making it significantly cheaper.
| Domain | Capacity | 1st Avg Gas | 1st ETH | 2nd Avg Gas | 2nd ETH |
|---|---|---|---|---|---|
| 50000 | 5% | 3,035,721 | 0.003035721 ETH | 853,150 | 0.000853150 ETH |
| 50000 | 10% | 4,742,556 | 0.004742556 ETH | 1,376,237 | 0.001376237 ETH |
| 200000 | 5% | 10,154,585 | 0.010154585 ETH | 2,837,370 | 0.002837370 ETH |
| 200000 | 10% | 16,677,645 | 0.016677645 ETH | 4,636,050 | 0.004636050 ETH |
| 400000 | 5% | 19,446,435 | 0.019446435 ETH | 5,367,919 | 0.005367919 ETH |
| 400000 | 10% | 32,448,724 | 0.032448724 ETH | 8,881,321 | 0.008881321 ETH |
| 600000 | 5% | 28,708,334 | 0.028708334 ETH | 7,878,829 | 0.007878829 ETH |
| 600000 | 10% | 48,067,620 | 0.048067620 ETH | 13,172,435 | 0.013172435 ETH |
| 800000 | 5% | 38,012,533 | 0.038012533 ETH | 10,376,464 | 0.010376464 ETH |
| 800000 | 10% | 63,809,878 | 0.063809878 ETH | 17,375,514 | 0.017375514 ETH |
| 1000000 | 5% | 47,056,090 | 0.047056090 ETH | 12,876,066 | 0.012876066 ETH |
| 1000000 | 10% | 79,404,093 | 0.079404093 ETH | 21,666,129 | 0.021666129 ETH |
Benchmark gas consumption for verifying a one-show credential presentation using CheckCredential (N = 500 credentials):
| Domain | Capacity | Avg Gas Used | ETH (1 Gwei) | Local Check [ms] |
|---|---|---|---|---|
| 1000 | 5% | 422209 | 0.000422209 | 0.745 |
| 1000 | 10% | 417678 | 0.000417678 | 0.749 |
| 10000 | 5% | 422580 | 0.000422580 | 0.774 |
| 10000 | 10% | 428846 | 0.000428846 | 0.814 |
| 100000 | 5% | 419191 | 0.000419191 | 0.794 |
| 100000 | 10% | 430095 | 0.000430095 | 0.766 |
| 1000000 | 5% | 423070 | 0.000423070 | 0.756 |
| 1000000 | 10% | 426334 | 0.000426334 | 0.801 |
Benchmark gas consumption for verifying a multi-show credential presentation using CheckCredential (N = 500 credentials):
| Domain | Capacity | Avg Gas Used | ETH (1 Gwei) | Local Check [ms] |
|---|---|---|---|---|
| 1000 | 5% | 279844 | 0.000279844 | 2.097 |
| 1000 | 10% | 280636 | 0.000280636 | 2.153 |
| 10000 | 5% | 281258 | 0.000281258 | 2.147 |
| 10000 | 10% | 282025 | 0.000282025 | 2.179 |
| 100000 | 5% | 282119 | 0.000282119 | 2.172 |
| 100000 | 10% | 283309 | 0.000283309 | 2.171 |
| 1000000 | 5% | 281741 | 0.000281741 | 2.162 |
| 1000000 | 10% | 281661 | 0.000281661 | 2.173 |
If you use this repository or build upon UPPR, please cite the following paper:
@INPROCEEDINGS{11264637,
author = {Rometsch, Leandro and Lehwalder, Philipp-Florens and Hoang, Anh-Tu and Kaaser, Dominik and Schulte, Stefan},
title = {{UPPR: Universal Privacy-Preserving Revocation}},
booktitle = {{2025 IEEE International Conference on Blockchain (Blockchain)}},
year = {2025},
pages = {161--170},
doi = {10.1109/Blockchain67634.2025.00030}
}The financial support by the Austrian Federal Ministry of Economy, Energy and Tourism, the National Foundation for Research, Technology and Development and the Christian Doppler Research Association is gratefully acknowledged. Further, this result is part of a project that received funding from the European Research Council (ERC) under the European Union’s Horizon 2020 and Horizon Europe research and innovation programs (grant CRYPTOLAYER-101044770). We also thank Mirko Mollik of the German Federal Agency for Breakthrough Innovation (SPRIND) for providing valuable industry insights.
Use of the source code is governed by the Apache 2.0 license that can be found in the LICENSE file.