Merge branch 'master' of https://github.com/smartkek/lazy-snark into fusking-truffle

This commit is contained in:
Игорь Соболев 2019-06-23 16:46:47 +03:00
commit 01bdecd96b
17 changed files with 136 additions and 219 deletions

View File

@ -1,26 +1,47 @@
# LAZY SNARK: trustless off-chain zk-proof verification.
## Abstract
In Ethereum, it is expensive to check zk-proofs on-chain, so we propose to use Fluence to do heavy-lifting off-chain and only go on-chain to challenge incorrect proofs. Our project should help exisiting Ethereum projects that rely on zkp to achieve privacy, scalability and other nice qualities.
In Ethereum, it is expensive to check zero-knowledge proofs on-chain. So, we propose to use Fluence to do heavy-lifting off-chain and only go on-chain to challenge incorrect proofs. Our project should help exisiting Ethereum projects that rely on zk-proofs to achieve privacy, scalability, and trustlessness.
## Why
Let us say, there is a project that needs to verify zk-proofs in Ethereum smart contract. The problem is that zk-proof verification is a heavy computational task and thus costs a lot of gas. As a result, checking proofs on-chain is expensive, and is susceptible to network congestion.
## What
We suggest checking proofs on Fluence instead. This option does not has gas problem, is much cheaper, and is trustless.
We suggest checking proofs on Fluence instead. This option does not has gas problem. Thus, it is much cheaper. Also, it won't consume all the gas in the block. Besides, it is trustless and the results of the checks are public.
## How it works
The process includes the following entities:
- Ethereum smart contract that stores (data, proof) pairs and implements on-chain proof verification. In case the proof is not correct, the smart contract rewards the user who challenged this proof with ether.
- Ethereum project Operator who uploads (data, proof) pairs to the smart contract.
- Ethereum smart contract that stores (data, proof) pairs and implements on-chain proof verification. In case the proof is not correct, the smart contract rewards the user who challenges the invalid proof with ether.
- Proof supplier who uploads (data, proof) pairs to the smart contract. The proof supplier stakes ether to the smart contract. In case the proof supplier provides an invalid proof, the proof supplier is punished: a part of the stake is given to the one who chellenged the proof as a reward.
- Fluence back end that implements off-chain proof verification. It also stores proof verification results.
- Ethereum project user. The user checks if Operator provides valid proofs and challenges invalid ones using smart contract to get a reward.
- Arweave front-end. The user performs all the actions via the front end.
- Ethereum project user aka proof consumer. The user checks whether the proof supplier has provided valid proofs and challenges invalid ones using smart contract to get a reward.
- Arweave front end. The user performs all the actions via the front end. Also, proof results from the Fluence back end are displayed in the front end.
Here is the workflow:
1. The operator uploads (data, proof) to the smart contract.
2. The user takes (data, proof) from the smart contract and uploads it to the back end (Fluence).
1. The proof supplier uploads (data, proof) to the smart contract.
2. The user takes (data, proof) from the smart contract and sends it to the back end.
3. The back end checkes the proof.
4. a) If the proof is correct, it is stored by the back end with TRUE flag. Other users can see it an will not check this proof again.
b) If the proof is false, the user checks the same proof in the smart contract. In that case the user is sure that the proof is FALSE and thus the user will get the reward.
4. The following actions depend on the result of the check:
- a. If the proof is valid, it is stored by the back end with TRUE flag. Other users can see it in the front end and will not check this proof again.
- b. If the proof is invalid, it is stored by the back end with FALSE flag. The user challenges this proof in the smart contract. In that case the user is sure that the proof is FALSE and thus the user will get the reward.
To better understand the workflow, please review the scheme.
![Image](Scheme.png "Scheme")
## Benefits
LAZY SNARK (not to be confused with Lady Stark) provides the following benefits compared to checking zk-proofs on-chain:
- It doesn't require much gas. In case of e.g. mass exit the zk-proofs verifications will take all the gas in the block. LAZY SNARK won't.
- It is ~10 times cheaper than verifying zk-proofs in Ethereum smart contract. Checking zk-proof on-chain costs ~$1 (gasprice and ETH price on June 23, 2019). Checking them in Fluence costs ~$0.01. Since we still need to put data and proofs on-chain, the whole system operation will cost 10 times less than checking the proofs on-chain.
LAZY SNARK provides the following benefits compared to checking zk-proofs locally:
- It is trustless unlike checking zk-proofs locally. All the proof check results are available on Fluence via Arweave front end.
- The results are public, so everyone will be able to see which proofs are valid and which proof suppliers are honest.
- The results are public, so the users who seek for invalid proofs won't check the proofs that has already been checked.
## Use cases
LAZY SNARK can be used in the following cases (and not only in these):
- Plasma implementations that require zk-proofs.
- Mixers.
- Private money like ZkDai.
- Games.
- Many other systems that use zk-proofs. For now, they are mostly limited to money-like systems, hence the examples above. However, it is only the matter of imagination what other use cases need zk-proofs, scalability, and trustlessness.
## Build instructions
To be provided :)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 108 KiB

View File

@ -0,0 +1,7 @@
# Usage
0. [install](https://github.com/fluencelabs/tutorials/tree/master/dice-game#developing-the-backend-app) needed dependencies for Fluence
1. compiling Rust to WebAssembly, in directory backend_fluence/src
```cargo +nightly build --target wasm32-unknown-unknown --release```
2. [publishing](https://fluence.network/docs/book/quickstart/publish.html) to Fluence network

View File

@ -29,34 +29,58 @@ impl ProofManager {
}
/*
let tt = "99999";
//////////////// vk
let vk_byte = "
vk.alpha = 0x2c2cb1812fb05d4f31791c66ff995d756b73162f3bb016a5c114befe5cd7903e, 0x0abc1f8a5d49cb2dbda15b5a8b7cd81bec0a581e7c2e16f79446af2d2f5340c0
vk.beta = [0x071644533641f7e3acb8606328c591853b2bc27253f29bc11d008a67996fc07f, 0x26ca2720c073a085d8452aef541aac280879971c09b199a6e0f21bf36745e1d8], [0x0b17104896ed701b6d52279992c1f20d558bc0de8284087645633bf3ca1a0c98, 0x2c10eb5b6c0ca42ede8cdcf60642c6dca040abe9abb8294948f4aa0be59a0d42]
vk.gamma = [0x0afbadec2ecafdd62278c7021095660f5786f445c040e628e4ed1a410454b582, 0x038aa6f04ee254a97e2b75ea1f30e36785b6cde4dfd3a2371e058ce089b9ad51], [0x077720bb216fb0051c5e153c1bd9aa36a678173b9c13e8d3a83cb5a75ca36948, 0x1f9b58e9abde296abc3c3bab8fb0be2a4f497d8e5d9d463997d316e9cc558a7d]
vk.delta = [0x16526b9b519fa544d3f9ce35a5f4afa7aac0aa4dd54421c4864b3fe8d2415f41, 0x24e24f35699cca59416a7f43c0e93e148b2353440978994df8f81603a46f8839], [0x299f9f09280310aedf63055c5ce76feb16557ed7ff11ba35adad718102b5651a, 0x0c4a2fc4db77ef6c19511b2ffb369981cebbffcb5337a671e1ad678b460ac5e9]
vk.gammaABC.len() = 6
vk.gammaABC[0] = 0x2f910078bf5092a7ea9d3ce750b7b5399b101509adb8017a6e12fa1a4c638d5b, 0x0b76454d4300571c8d86714b4e5ef095688b51080e674425e8e5edb201f64128
vk.gammaABC[1] = 0x2922a307d415f70c8df6f14b664c46df12a89cd3a89cf7960663907bf9483b68, 0x1bc30a719ddc0099f557cca61a0687766e6275fc98b185baa77735b93bf2a0ef
vk.gammaABC[2] = 0x0201dc8c8faa3dc5b8eec85f029d2482bf11a6b46d5f8e4d9f17d41ac3e4c9c1, 0x0a1d62c1142c92dff75b53d5a572fd7a013708118acf10f718c61fb6226160f5
vk.gammaABC[3] = 0x02276f5896610ec573cd6cdc6e47c69e756362d2b1b1c51c5ab90ac838d1a898, 0x13fa6cc7987f4f3118f6ee3ab85dcd708df17050636d487914077348e0af05b1
vk.gammaABC[4] = 0x0778ae3718fd7f48564bc33b60ec4f39a238e97cb4cc0bbd4ff37119942ff7d4, 0x0282e96481744ee21524d802b3e524bf0596bb37bb63e5ed37c77fc1a5c8e89d
vk.gammaABC[5] = 0x1ce40e230695bdab7d2ff7ebcf6e6fedb68d1a320238fc98845b151ae4ee3b54, 0x0feac76664d37b57a4ea5a774252bb82355294e55635a8aeb7a1327405d27128";
let mut c = Cursor::new(Vec::new());
// Write into the "file" and seek to the beginning
c.write_all(tt.as_bytes()).unwrap();
c.write_all(vk_byte.as_bytes()).unwrap();
c.seek(SeekFrom::Start(0)).unwrap();
let vk = VerifyingKey::<E>::read(&mut c)?;
let pvk = {
let pvk = prepare_verifying_key(&vk);
}
//////////////// proof
let proof = {
let proof_byte = "
a: [0x12d0dbcfc1da3ea29bc017288fceea3929401f4f12dbd0bba73781420d31aa2d, 0x2811c1eaa63f4a804951bd7f994cbb6bea9df64591793b8392400e8756d1bca7],
b: [[0x04c33f68e1bd55be0928b086c647debcdf7aa0e3c3efc6a8efbc2596a77a0e67, 0x17e7392e0e3ec2b5701e675e6e0569330d03ffffe476fc8d63cfeaa0ba1c8a97], [0x2fc402693a54cd1b176abeed209674f2f12ced1496c6ce27ba8cf16903daa4cc, 0x2c47efba3f4f260da643bb6427d08b551bb3446537d6ac4857d611be2355a446]],
c: [0x04d40f14694092d0f70890a20492b2b68e7eaabdcee744e519678d687c9c3ed0, 0x28de140e393154b0e70b3ef12806af963a4a33b45c24e7864391093b6028fa2b]";
}
let mut c2 = Cursor::new(Vec::new());
let pub_input = {
c2.write_all(proof_byte.as_bytes()).unwrap();
c2.seek(SeekFrom::Start(0)).unwrap();
}
let proof = Proof::<E>::read(&mut c2);
/////////////// pub_input
let pub_input = "inputs: [0x00000000000000000000000000000000c6481e22c5ff4164af680b8cfaa5e8ed, \
0x000000000000000000000000000000003120eeff89c4f307c4a6faaae059ce10, \
0x000000000000000000000000000000005b6d7d198c48c17c9540d29275a04662, \
0x00000000000000000000000000000000f7a9aa434629a33c84eec3e16e196f27, \
0x0000000000000000000000000000000000000000000000000000000000000001]";
result_bool = verify_proof(
&pvk,
&proof,
&[Fr::one()]
).unwrap();*/
pub_input
).unwrap();
*/
// update proof status
self.proofs.insert(proof_id, result);
@ -68,61 +92,6 @@ impl ProofManager {
serde_json::to_value(response).map_err(Into::into)
}
/*pub fn read<R: Read>(
mut reader: R
) -> io::Result<Self>
{
let mut g1_repr = <E::G1Affine as CurveAffine>::Uncompressed::empty();
let mut g2_repr = <E::G2Affine as CurveAffine>::Uncompressed::empty();
reader.read_exact(g1_repr.as_mut())?;
let alpha_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g1_repr.as_mut())?;
let beta_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g2_repr.as_mut())?;
let beta_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g2_repr.as_mut())?;
let gamma_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g1_repr.as_mut())?;
let delta_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g2_repr.as_mut())?;
let delta_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let ic_len = reader.read_u32::<BigEndian>()? as usize;
let mut ic = vec![];
for _ in 0..ic_len {
reader.read_exact(g1_repr.as_mut())?;
let g1 = g1_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
} else {
Ok(e)
})?;
ic.push(g1);
}
Ok(VerifyingKey {
alpha_g1: alpha_g1,
beta_g1: beta_g1,
beta_g2: beta_g2,
gamma_g2: gamma_g2,
delta_g1: delta_g1,
delta_g2: delta_g2,
ic: ic
})
}*/
pub fn check(&self, proof_id: u64) -> AppResult<Value> {
let status = self.proof_status(proof_id)?;
let response = Response::Check { verified: status };

View File

@ -1,130 +0,0 @@
/*
* Copyright 2018 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::request_response::{Request, Response};
use crate::main;
// TODO: add more tests
#[test]
fn correct_bets() {
let response = Response::Join { player_id: 0 };
assert_eq!(
main(create_join_request()),
serde_json::to_string(&response).unwrap()
);
let response = Response::Join { player_id: 1 };
assert_eq!(
main(create_join_request()),
serde_json::to_string(&response).unwrap()
);
let response = Response::Bet {
outcome: 6,
player_balance: 85,
};
assert_eq!(
main(create_bet_json(0, 1, 15)),
serde_json::to_string(&response).unwrap()
);
let response = Response::Bet {
outcome: 4,
player_balance: 85,
};
assert_eq!(
main(create_bet_json(1, 1, 15)),
serde_json::to_string(&response).unwrap()
);
let response = Response::Bet {
outcome: 6,
player_balance: 510,
};
assert_eq!(
main(create_bet_json(0, 6, 85)),
serde_json::to_string(&response).unwrap()
);
let response = Response::Bet {
outcome: 2,
player_balance: 0,
};
assert_eq!(
main(create_bet_json(1, 1, 85)),
serde_json::to_string(&response).unwrap()
);
let response = Response::GetBalance {
player_balance: 510,
};
assert_eq!(
main(create_get_balance_json(0)),
serde_json::to_string(&response).unwrap()
);
}
#[test]
fn incorrect_bets() {
let response = Response::Join { player_id: 0 };
assert_eq!(
main(create_join_request()),
serde_json::to_string(&response).unwrap()
);
let response = Response::Error {
message: "Incorrect placement, please choose number from 1 to 6".to_string(),
};
assert_eq!(
main(create_bet_json(0, 7, 15)),
serde_json::to_string(&response).unwrap()
);
let response = Response::Error {
message: "Player with id 1 wasn\'t found".to_string(),
};
assert_eq!(
main(create_bet_json(1, 1, 0)),
serde_json::to_string(&response).unwrap()
);
let response = Response::Error {
message: "Player hasn\'t enough money: player\'s current balance is 100 while the bet is 4294967295".to_string()
};
assert_eq!(
main(create_bet_json(0, 6, std::u32::MAX)),
serde_json::to_string(&response).unwrap()
);
}
fn create_join_request() -> String {
let request = Request::Join;
serde_json::to_value(request).unwrap().to_string()
}
fn create_bet_json(player_id: u64, placement: u8, bet_amount: u32) -> String {
let request = Request::Bet {
player_id,
placement,
bet_amount,
};
serde_json::to_value(request).unwrap().to_string()
}
fn create_get_balance_json(player_id: u64) -> String {
let request = Request::GetBalance { player_id };
serde_json::to_value(request).unwrap().to_string()
}

14
backend_zk/README.md Normal file
View File

@ -0,0 +1,14 @@
# Usage
0. [install](https://zokrates.github.io/gettingstarted.html) ZoKrates
1. compile ```zokrates compile -i root.code```
2. perform the setup phase ```zokrates setup```
3. execute the program ```zokrates compute-witness -a 0 0 0 5 5 0 0 0 263561599766550617289250058199814760685 65303172752238645975888084098459749904 121528245299328017710050549170605934178 329200266467600403224363203181133000487```
4. generate a proof of computation ```zokrates generate-proof```
5. export a solidity verifier ```zokrates export-verifier```
* Generated folder contains already generated files.

View File

@ -1,8 +1,6 @@
# Usage
0. set the `appId` (Fluence) and `lazyAddress` (Eth contract) in `index.js`
1. run ```npm i```
2. run ```npm run build```
3. run ```npm run start```
1. run `npm i`
2. run `npm run build`
3. run `npm run start`

View File

@ -36,7 +36,7 @@ contract Lazy is Structs {
function submit(Data calldata data, Proof calldata proof) external payable {
require(msg.value == stake);
Task memory task = Task(data, proof, msg.sender, uint96(now), Status.UNCHECKED);
Task memory task = Task(data, proof, msg.sender, now, Status.UNCHECKED);
uint index = tasks.push(task);
emit Submitted(msg.sender, index, task);

View File

@ -15,9 +15,9 @@ contract VerifierProxy is IVerifier {
// Truffle gives `UnimplementedFeatureError: Encoding struct from calldata is not yet supported.`
// that's why function is public and uses memory location modifier
function isValid(Data memory data, Proof memory proof) public returns (bool) {
bytes memory payload = abi.encodeWithSelector(verifier.verifyTx.selector, proof, data);
bytes memory payload = abi.encodeWithSelector(verifier.verifyTx.selector, proof.a, proof.b, proof.c, data.input);
(bool success, bytes memory r) = address(verifier).call(payload);
require(success);
return abi.decode(r, (bool));
return success && abi.decode(r, (bool));
// return verifier.verifyTx(proof.a, proof.b, proof.c, data.input);
}
}

38
truffle/test/test.js Normal file
View File

@ -0,0 +1,38 @@
const Lazy = artifacts.require('./Lazy.sol');
const Verifier = artifacts.require('./Verifier.sol');
contract("Testing Lazy", accounts => {
it("should deploy with 2 tasks", async () => {
let instance = await Lazy.deployed();
let tasksNum = await instance.tasksNum.call();
assert.equal(tasksNum.valueOf(), 2);
});
it("should detect incorrect proof", async () => {
let instance = await Lazy.deployed();
let task = await instance.tasks(0);
assert.equal(task.status, 0);
await instance.challenge(0);
task = await instance.tasks(0);
assert.equal(task.status, 2);
});
});
contract("Testing Verifier", accounts => {
const a = ["0x12d0dbcfc1da3ea29bc017288fceea3929401f4f12dbd0bba73781420d31aa2d","0x2811c1eaa63f4a804951bd7f994cbb6bea9df64591793b8392400e8756d1bca7"];
const b = [["0x04c33f68e1bd55be0928b086c647debcdf7aa0e3c3efc6a8efbc2596a77a0e67","0x17e7392e0e3ec2b5701e675e6e0569330d03ffffe476fc8d63cfeaa0ba1c8a97"],["0x2fc402693a54cd1b176abeed209674f2f12ced1496c6ce27ba8cf16903daa4cc","0x2c47efba3f4f260da643bb6427d08b551bb3446537d6ac4857d611be2355a446"]];
const c = ["0x04d40f14694092d0f70890a20492b2b68e7eaabdcee744e519678d687c9c3ed0","0x28de140e393154b0e70b3ef12806af963a4a33b45c24e7864391093b6028fa2b"];
const input = ["0x00000000000000000000000000000000c6481e22c5ff4164af680b8cfaa5e8ed","0x000000000000000000000000000000003120eeff89c4f307c4a6faaae059ce10","0x000000000000000000000000000000005b6d7d198c48c17c9540d29275a04662","0x00000000000000000000000000000000f7a9aa434629a33c84eec3e16e196f27","0x0000000000000000000000000000000000000000000000000000000000000001"]
it("should process proofs", async () => {
let instance = await Verifier.deployed();
let result = await instance.verifyTx.call(a,b,c,input);
console.log("verfifier thinks that result is " + result)
// assert.equal(result.valueOf(), false);
});
});