This repository contains the code for the back-end of Fact Fortress, implemented in Ethereum.
The Fact Fortress framework incorporates smart contracts into its architecture to ensure transparency and accountability in data access. Certified data providers can securely store their sensitive data and set data access policies on how the data must be handled. Data analysts can request access to the data based on these policies to perform an analysis and compute the zero-knowledge proof (ZKP) locally or delegate the data analysis to the smart contract, which returns the ZKP and result directly to them. The framework defines a library of functions that can be computed on data of any form, and each function has an on-chain verifier that can validate a proof submitted by anyone. The verifier ensures that the proof was generated by the function claimed by the data analyst, the data used to generate the proof has not been tampered with, and the claimed result is the correct result of the function applied to the data. Analysts can confidently publish the results together with the proof, which anyone can publicly verify on-chain.
Fact Fortress is a blockchain-based framework that uses zero-knowledge proofs for trustworthy and private fact-checking. It ensures trustworthy data handling and computation by using proofs of data provenance and auditable data access policies. The solution democratizes circuit construction and deployment with a circuit compiler that supports various data formats and source authentication, and facilitates on-chain verification. This preserves sensitive data privacy while ensuring accountability and transparency in data handling and computation. It achieves this by enabling on-chain verification of computation and data provenance without revealing any information about the data itself.
Our framework provides a comprehensive solution that covers the entire process from circuit generation to proof generation, while facilitating collaboration among data analysts, data providers, external verifiers, and policy auditors.
For more information, check out our website at: https://pierg.github.io/fact-fortress-web/.
Install the backend and the frontend:
git clone git@github.com:pierg/fact-fortress-dapp.git
cd fact-fortress-dapp && pnpm install
git clone git@github.com:pierg/fact-fortress-frontend.git
cd fact-fortress-frontend && pnpm install
To deploy the backend and the frontend together, run the following command from the root directory:
make run
This command launches the backend, then opens the frontend in the browser (http://localhost:8080
).
From the root directory, run:
pnpm backend
(By default, the backend runs on port 3000
).
A Postman collection is provided to interact with the backend: tools/Postman_collection/Fact_Fortress.postman_collection.json
Once the backend is running (► server started on port 3000 ✓
), from the root directory run:
pnpm frontend
Then, open http://localhost:8080
on your browser.
(By default, the backend runs on port 8080
).
From the root directory, run:
pnpm run test
These tests notably contain an end-to-end flow, from the authorization of data providers to the on-chain verification of the proof of Schnorr signature.
Data providers generate a private/public key pair based on the Grumpkin elliptic curve used by Noir.
WARNING: This action should be performed offline. This endpoint is just a helper. Data providers are expected to generate the keys themselves. |
---|
GET http://localhost:3000/key_pair
public_key
Public key based on the Grumpkin curve, used by Noirprivate_key
Random private keyExample
curl --location 'http://localhost:3000/key_pair'
{
"public_key": "0x0dd7811f6af9d473c41376affb8660aba00e255c49844b31182f54bc0ab3e2ae1b23bd0e9afdb8275f880934b115057ed86f075048d4d8bd9fa8d92670dc6892",
"private_key": "ca1a2b52a7405f06f71c03cbaada78559aa86a0e2d01321540012f3762a12818"
}
Data providers have to be authorized to upload their public keys on the blockchain (otherwise, anyone could do it). To do so, an NFT-based mechanism is used. The owner of the NFT smart contract has to authorize data providers once by sending them NFTs for this purpose.
GET http://localhost:3000/authorize_provider
from: owner
Only the owner of the contract can mintaddress
Address of the data provider about to receive the NFTaddress
Address of the data provider having received the NFTtoken_id
NFT token IDExample
curl --location 'http://localhost:3000/authorize_provider?address=0x98526c571e324028250B0f5f247Ca4F1b575fadB' \
--header 'from: owner'
{
"address": "0x98526c571e324028250B0f5f247Ca4F1b575fadB",
"token_id": "1"
}
Data providers upload their public key (for the first time or when they generate a new one). This process enables the verification of the public inputs in the context of the proof of provenance.
PUT http://localhost:3000/publickey
from: owner
Only the data provider owner of an NFT can upload its public keyname
Name associated with the public keypublic_key
Grumpkin-based public keyname
Name associated with the public keypublic_key
Grumpkin-based public keypublic_key_version
Version of the public keyExample
curl --location --request PUT 'http://localhost:3000/publickey?name=ABC&public_key=0x0dd7811f6af9d473c41376affb8660aba00e255c49844b31182f54bc0ab3e2ae1b23bd0e9afdb8275f880934b115057ed86f075048d4d8bd9fa8d92670dc6892' \
--header 'from: providerA'
{
"name": "ABC",
"public_key": "0x0dd7811f6af9d473c41376affb8660aba00e255c49844b31182f54bc0ab3e2ae1b23bd0e9afdb8275f880934b115057ed86f075048d4d8bd9fa8d92670dc6892",
"public_key_version": "0"
}
Using this endpoint, anyone (including the verifiers) can get the public keys of data providers.
GET http://localhost:3000/publickey
name
Name associated with the public keyversion
Version of the public keypublic_key
Grumpkin-based public keyExample
curl --location 'http://localhost:3000/publickey?name=ABC&version=0'
{
"public_key": "0x0dd7811f6af9d473c41376affb8660aba00e255c49844b31182f54bc0ab3e2ae1b23bd0e9afdb8275f880934b115057ed86f075048d4d8bd9fa8d92670dc6892"
}
Data providers have to (SHA-256) hash and sign (using the Grumpkin elliptic curve) the Data.
WARNING: This action should be performed offline. This endpoint is just a helper. Data providers are expected to hash and sign the Data themselves. |
---|
POST http://localhost:3000/sign_message
private_key
Private key associated with the public key that will be used for the proofmessage
Data to hash and signhash
SHA-256 hash of the data (hex)signature
Signature of the hash (bytes)Example
curl --location 'http://localhost:3000/sign_message' \
--header 'Content-Type: application/json' \
--data '{
"private_key": "98f73670b22c67c1c2b092c5167d1317b661d82db9777751dd6b310efa7c4e17",
"message": {
"d1": [ 2, 1, 2, 0, 0, 0, 2, 1, 2, 0, 0, 2, 0, 2, 2, 1, 1, 0, 0, 0, 2, 0, 0, 0, 1, 1, 2, 2, 0, 2, 0, 0],
"d2": [ 23, 5, 15, 29]
}
}'
{
"hash": "e51b88c9ef2ee7a084f676a4d07313895e2850f6789e1bb1aa9845c3d2dd6dea",
"signature": [
30,
149,
144,
. . .
10,
179,
103
]
}
Data providers store the signature on the blockchain. That enables the verification of the proof of provenance.
GET http://localhost:3000/upload_signature
public_key
Grumpkin-based public keysignature
Signature of the hash (bytes)stored
Status: true
if the signature has been stored, false
otherwiseExample
curl --location 'http://localhost:3000/upload_signature?public_key=0x077418dea85cb9695990062d64d4d4add4a4d8cbbed3a5f9e5d5f299766bcdf22a10a3540173df59a3e03533011d867c7a8d879dc3819c8c4857ef3a04a6b103' \
--header 'from: ProviderA' \
--header 'Content-Type: application/json' \
--data '{
"hash": "e51b88c9ef2ee7a084f676a4d07313895e2850f6789e1bb1aa9845c3d2dd6dea",
"signature": [
30,
149,
144,
. . .
10,
179,
103
]
}'
{
"stored": true
}
Data analysts generate the proof (should be done online).
WARNING: This action should be performed offline. This endpoint is just a helper. Data analysts are expected to generate the proofs themselves. |
---|
POST http://localhost:3000/generate_proof
public_key
Grumpkin-based public keyhash
Data hash (hex; copied from step 4)signature
Signature of the hash (bytes; copied from step 4)Example
curl --location 'http://localhost:3000/generate_proof?public_key=0x0dd7811f6af9d473c41376affb8660aba00e255c49844b31182f54bc0ab3e2ae1b23bd0e9afdb8275f880934b115057ed86f075048d4d8bd9fa8d92670dc6892' \
--header 'Content-Type: application/json' \
--data '{
"hash": "e51b88c9ef2ee7a084f676a4d07313895e2850f6789e1bb1aa9845c3d2dd6dea",
"signature": [
30,
149,
144,
. . .
10,
179,
103
]
}'
[
13,
215,
129,
. . .
71,
13,
23
]
Verifiers verify the public inputs of the proof of provenance. This is a preliminary step to the verification of the proof of provenance itself (step 8). This step ensures that the data analyst has used the expected public key and signature as public inputs. It can also be performed off-chain.
POST http://localhost:3000/verify_public_inputs
public_key
Grumpkin-based public keypublic_input_match
: true
(public inputs match) or false
(public inputs do not match)Example
curl --location 'http://localhost:3000/verify_public_inputs?public_key=0x0dd7811f6af9d473c41376affb8660aba00e255c49844b31182f54bc0ab3e2ae1b23bd0e9afdb8275f880934b115057ed86f075048d4d8bd9fa8d92670dc6892' \
--header 'Content-Type: application/json' \
--data '[
13,
215,
129,
. . .
71,
13,
23
]'
{
"public_input_match": true
}
Verifiers verify the proof of provenance that ensures that the Data comes from a data provider.
POST http://localhost:3000/verify_public_inputs
valid_proof_of_provenance
: true
(valid proof) or false
(invalid proof)Example
curl --location 'http://localhost:3000/verify_proof' \
--header 'Content-Type: application/json' \
--data '[
13,
215,
129,
. . .
71,
13,
23
]'
{
"valid_proof_of_provenance": true
}
WARNING: Before running this flow, ensure to reset the accounts and authorizations using the frontend helper (implemented for demonstration purposes only): GET http://localhost:3000/reset_accounts |
---|
Get registered access policies when no data analyst has been authorized yet: only the default access policy is returned.
GET http://localhost:3000/all_access_policies
access_policies
List of all registered data policiesExample
curl --location 'http://localhost:3000/all_access_policies'
{
"access_policies": [
"default_policy"
]
}
An unauthorized data provider has no token ID
GET http://localhost:3000/provider_token_id
address
Address of the data providererror
When the data provider is unauthorized: Address does not have a token
Example
curl --location 'http://localhost:3000/provider_token_id?address=0x98526c571e324028250B0f5f247Ca4F1b575fadB'
{
"error": "Address does not have a token"
}
An unauthorized data analyst has no token ID
GET http://localhost:3000/analyst_token_id
address
Address of the data providererror
When the data provider is unauthorized: Address does not have a token
Example
curl --location 'http://localhost:3000/analyst_token_id?address=0x5455280E6c20A01de3e846d683562AdeA6891026'
{
"error": "Address does not have a token"
}
Authorize a data provider
GET http://localhost:3000/authorize_provider
from: owner
Only the owner of the smart contract can call the underlying functionaddress
Address of the data provideraddress
Address of the data providertoken_id
ID of the NFT sent to the data providerExample
curl --location 'http://localhost:3000/analyst_token_id?address=0x5455280E6c20A01de3e846d683562AdeA6891026'
{
"address": "0x98526c571e324028250B0f5f247Ca4F1b575fadB",
"token_id": "1"
}
Define all data access policies
POST http://localhost:3000/all_access_policies
from: owner
Only the owner of the smart contract can call the underlying functionaccess_policies
Set of all access policiesaccess_policies
Set of all access policiesExample
curl --location 'http://localhost:3000/all_access_policies' \
--header 'from: owner' \
--header 'Content-Type: application/json' \
--data '{
"access_policies": [
"TYPE_A",
"TYPE_B",
"TYPE_C",
]
}'
{
"access_policies": [
"TYPE_A",
"TYPE_B",
"TYPE_C",
]
}
Authorize a data analyst with a set of access policies
POST http://localhost:3000/authorize_analyst
from: owner
Only the owner of the smart contract can call the underlying functionaddress
Address of the data analystaccess_policies
Set of access policies for this data analyst (should be a subset of all access policies)address
Address of the data providertoken_id
ID of the NFT sent to the data providerExample
curl --location 'http://localhost:3000/authorize_analyst?address=0x5455280E6c20A01de3e846d683562AdeA6891026' \
--header 'from: owner' \
--header 'Content-Type: application/json' \
--data '{
"access_policies": [
"TYPE_A",
"TYPE_B",
"TYPE_C",
]
}'
{
"address": "0x98526c571e324028250B0f5f247Ca4F1b575fadB",
"token_id": "1"
}
Now that the data analyst has been approved, her token ID can be retrieved
GET http://localhost:3000/analyst_token_id
address
Address of the data analystaddress
Address of the data analysttoken_id
ID of the NFT sent to the data provideraccess_policies
Set of access policies for this data analyst (should be a subset of all access policies)Example
curl --location 'http://localhost:3000/analyst_token_id?address=0x5455280E6c20A01de3e846d683562AdeA6891026'
{
"address": "0x5455280E6c20A01de3e846d683562AdeA6891026",
"token_id": "1",
"access_policies": [
"TYPE_A",
"TYPE_B",
"TYPE_C",
]
}
Now that at least on data analyst has been authorized, the set of all access policies has been updated by the smart contract
GET http://localhost:3000/all_access_policies
access_policies
List of all registered data policiesExample
curl --location 'http://localhost:3000/all_access_policies'
{
"access_policies": [
"default_policy",
"TYPE_A",
"TYPE_B",
"TYPE_C",
]
}