Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from christianlundkvist/ethereum-cmake
Add serialized keys/proofs. Add Ethereum verifier + tests.
- Loading branch information
Showing
10 changed files
with
570 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
pragma solidity ^0.4.23; | ||
|
||
contract Migrations { | ||
address public owner; | ||
uint public last_completed_migration; | ||
|
||
constructor() public { | ||
owner = msg.sender; | ||
} | ||
|
||
modifier restricted() { | ||
if (msg.sender == owner) _; | ||
} | ||
|
||
function setCompleted(uint completed) public restricted { | ||
last_completed_migration = completed; | ||
} | ||
|
||
function upgrade(address new_address) public restricted { | ||
Migrations upgraded = Migrations(new_address); | ||
upgraded.setCompleted(last_completed_migration); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
pragma solidity ^0.4.14; | ||
library Pairing { | ||
struct G1Point { | ||
uint X; | ||
uint Y; | ||
} | ||
// Encoding of field elements is: X[0] * z + X[1] | ||
struct G2Point { | ||
uint[2] X; | ||
uint[2] Y; | ||
} | ||
/// @return the generator of G1 | ||
function P1() internal returns (G1Point) { | ||
return G1Point(1, 2); | ||
} | ||
/// @return the generator of G2 | ||
function P2() internal returns (G2Point) { | ||
return G2Point( | ||
[11559732032986387107991004021392285783925812861821192530917403151452391805634, | ||
10857046999023057135944570762232829481370756359578518086990519993285655852781], | ||
[4082367875863433681332203403145435568316851327593401208105741076214120093531, | ||
8495653923123431417604973247489272438418190587263600148770280649306958101930] | ||
); | ||
} | ||
/// @return the negation of p, i.e. p.add(p.negate()) should be zero. | ||
function negate(G1Point p) internal returns (G1Point) { | ||
// The prime q in the base field F_q for G1 | ||
uint q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; | ||
if (p.X == 0 && p.Y == 0) | ||
return G1Point(0, 0); | ||
return G1Point(p.X, q - (p.Y % q)); | ||
} | ||
/// @return the sum of two points of G1 | ||
function add(G1Point p1, G1Point p2) internal returns (G1Point r) { | ||
uint[4] memory input; | ||
input[0] = p1.X; | ||
input[1] = p1.Y; | ||
input[2] = p2.X; | ||
input[3] = p2.Y; | ||
bool success; | ||
assembly { | ||
success := call(sub(gas, 2000), 6, 0, input, 0xc0, r, 0x60) | ||
// Use "invalid" to make gas estimation work | ||
switch success case 0 { invalid } | ||
} | ||
require(success); | ||
} | ||
/// @return the product of a point on G1 and a scalar, i.e. | ||
/// p == p.mul(1) and p.add(p) == p.mul(2) for all points p. | ||
function mul(G1Point p, uint s) internal returns (G1Point r) { | ||
uint[3] memory input; | ||
input[0] = p.X; | ||
input[1] = p.Y; | ||
input[2] = s; | ||
bool success; | ||
assembly { | ||
success := call(sub(gas, 2000), 7, 0, input, 0x80, r, 0x60) | ||
// Use "invalid" to make gas estimation work | ||
switch success case 0 { invalid } | ||
} | ||
require (success); | ||
} | ||
/// @return the result of computing the pairing check | ||
/// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1 | ||
/// For example pairing([P1(), P1().negate()], [P2(), P2()]) should | ||
/// return true. | ||
function pairing(G1Point[] p1, G2Point[] p2) internal returns (bool) { | ||
require(p1.length == p2.length); | ||
uint elements = p1.length; | ||
uint inputSize = elements * 6; | ||
uint[] memory input = new uint[](inputSize); | ||
for (uint i = 0; i < elements; i++) | ||
{ | ||
input[i * 6 + 0] = p1[i].X; | ||
input[i * 6 + 1] = p1[i].Y; | ||
input[i * 6 + 2] = p2[i].X[0]; | ||
input[i * 6 + 3] = p2[i].X[1]; | ||
input[i * 6 + 4] = p2[i].Y[0]; | ||
input[i * 6 + 5] = p2[i].Y[1]; | ||
} | ||
uint[1] memory out; | ||
bool success; | ||
assembly { | ||
success := call(sub(gas, 2000), 8, 0, add(input, 0x20), mul(inputSize, 0x20), out, 0x20) | ||
// Use "invalid" to make gas estimation work | ||
switch success case 0 { invalid } | ||
} | ||
require(success); | ||
return out[0] != 0; | ||
} | ||
/// Convenience method for a pairing check for two pairs. | ||
function pairingProd2(G1Point a1, G2Point a2, G1Point b1, G2Point b2) internal returns (bool) { | ||
G1Point[] memory p1 = new G1Point[](2); | ||
G2Point[] memory p2 = new G2Point[](2); | ||
p1[0] = a1; | ||
p1[1] = b1; | ||
p2[0] = a2; | ||
p2[1] = b2; | ||
return pairing(p1, p2); | ||
} | ||
/// Convenience method for a pairing check for three pairs. | ||
function pairingProd3( | ||
G1Point a1, G2Point a2, | ||
G1Point b1, G2Point b2, | ||
G1Point c1, G2Point c2 | ||
) internal returns (bool) { | ||
G1Point[] memory p1 = new G1Point[](3); | ||
G2Point[] memory p2 = new G2Point[](3); | ||
p1[0] = a1; | ||
p1[1] = b1; | ||
p1[2] = c1; | ||
p2[0] = a2; | ||
p2[1] = b2; | ||
p2[2] = c2; | ||
return pairing(p1, p2); | ||
} | ||
/// Convenience method for a pairing check for four pairs. | ||
function pairingProd4( | ||
G1Point a1, G2Point a2, | ||
G1Point b1, G2Point b2, | ||
G1Point c1, G2Point c2, | ||
G1Point d1, G2Point d2 | ||
) internal returns (bool) { | ||
G1Point[] memory p1 = new G1Point[](4); | ||
G2Point[] memory p2 = new G2Point[](4); | ||
p1[0] = a1; | ||
p1[1] = b1; | ||
p1[2] = c1; | ||
p1[3] = d1; | ||
p2[0] = a2; | ||
p2[1] = b2; | ||
p2[2] = c2; | ||
p2[3] = d2; | ||
return pairing(p1, p2); | ||
} | ||
} | ||
contract Verifier { | ||
using Pairing for *; | ||
bool public verifyingKeySet = false; | ||
struct VerifyingKey { | ||
Pairing.G2Point A; | ||
Pairing.G1Point B; | ||
Pairing.G2Point C; | ||
Pairing.G2Point gamma; | ||
Pairing.G1Point gammaBeta1; | ||
Pairing.G2Point gammaBeta2; | ||
Pairing.G2Point Z; | ||
Pairing.G1Point[5] IC; | ||
uint IC_length; | ||
} | ||
VerifyingKey vk; | ||
struct Proof { | ||
Pairing.G1Point A; | ||
Pairing.G1Point A_p; | ||
Pairing.G2Point B; | ||
Pairing.G1Point B_p; | ||
Pairing.G1Point C; | ||
Pairing.G1Point C_p; | ||
Pairing.G1Point K; | ||
Pairing.G1Point H; | ||
} | ||
function verify(uint[] input, Proof proof) internal returns (uint) { | ||
require(input.length + 1 == vk.IC_length); | ||
// Compute the linear combination vk_x | ||
Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0); | ||
for (uint i = 0; i < input.length; i++) | ||
vk_x = Pairing.add(vk_x, Pairing.mul(vk.IC[i + 1], input[i])); | ||
vk_x = Pairing.add(vk_x, vk.IC[0]); | ||
if (!Pairing.pairingProd2(proof.A, vk.A, Pairing.negate(proof.A_p), Pairing.P2())) return 1; | ||
if (!Pairing.pairingProd2(vk.B, proof.B, Pairing.negate(proof.B_p), Pairing.P2())) return 2; | ||
if (!Pairing.pairingProd2(proof.C, vk.C, Pairing.negate(proof.C_p), Pairing.P2())) return 3; | ||
if (!Pairing.pairingProd3( | ||
proof.K, vk.gamma, | ||
Pairing.negate(Pairing.add(vk_x, Pairing.add(proof.A, proof.C))), vk.gammaBeta2, | ||
Pairing.negate(vk.gammaBeta1), proof.B | ||
)) return 4; | ||
if (!Pairing.pairingProd3( | ||
Pairing.add(vk_x, proof.A), proof.B, | ||
Pairing.negate(proof.H), vk.Z, | ||
Pairing.negate(proof.C), Pairing.P2() | ||
)) return 5; | ||
return 0; | ||
} | ||
function setVerifyingKey( | ||
uint[2][2] A, | ||
uint[2] B, | ||
uint[2][2] C, | ||
uint[2][2] gamma, | ||
uint[2] gamma_beta_1, | ||
uint[2][2] gamma_beta_2, | ||
uint[2][2] Z, | ||
uint[2][] IC) public { | ||
require(IC.length <= 5); | ||
vk.A = Pairing.G2Point([A[0][0], A[0][1]], [A[1][0], A[1][1]]); | ||
vk.B = Pairing.G1Point(B[0], B[1]); | ||
vk.C = Pairing.G2Point([C[0][0], C[0][1]], [C[1][0], C[1][1]]); | ||
vk.gamma = Pairing.G2Point([gamma[0][0], gamma[0][1]], [gamma[1][0], gamma[1][1]]); | ||
vk.gammaBeta1 = Pairing.G1Point(gamma_beta_1[0], gamma_beta_1[1]); | ||
vk.gammaBeta2 = Pairing.G2Point([gamma_beta_2[0][0], gamma_beta_2[0][1]], [gamma_beta_2[1][0], gamma_beta_2[1][1]]); | ||
vk.Z = Pairing.G2Point([Z[0][0], Z[0][1]], [Z[1][0], Z[1][1]]); | ||
vk.IC_length = IC.length; | ||
for(uint i=0; i<vk.IC_length; i++) { | ||
vk.IC[i] = Pairing.G1Point(IC[i][0], IC[i][1]); | ||
} | ||
verifyingKeySet = true; | ||
} | ||
event Verified(string); | ||
function verifyTx( | ||
uint[2] a, | ||
uint[2] a_p, | ||
uint[2][2] b, | ||
uint[2] b_p, | ||
uint[2] c, | ||
uint[2] c_p, | ||
uint[2] h, | ||
uint[2] k, | ||
uint[] input) public returns (bool r) { | ||
require(verifyingKeySet); | ||
Proof memory proof; | ||
proof.A = Pairing.G1Point(a[0], a[1]); | ||
proof.A_p = Pairing.G1Point(a_p[0], a_p[1]); | ||
proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]); | ||
proof.B_p = Pairing.G1Point(b_p[0], b_p[1]); | ||
proof.C = Pairing.G1Point(c[0], c[1]); | ||
proof.C_p = Pairing.G1Point(c_p[0], c_p[1]); | ||
proof.H = Pairing.G1Point(h[0], h[1]); | ||
proof.K = Pairing.G1Point(k[0], k[1]); | ||
uint[] memory inputValues = new uint[](input.length); | ||
for(uint i = 0; i < input.length; i++){ | ||
inputValues[i] = input[i]; | ||
} | ||
if (verify(inputValues, proof) == 0) { | ||
Verified("Transaction successfully verified."); | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
var Migrations = artifacts.require("./Migrations.sol"); | ||
|
||
module.exports = function(deployer) { | ||
deployer.deploy(Migrations); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
var Verifier = artifacts.require("Verifier"); | ||
|
||
module.exports = function(deployer) { | ||
deployer.deploy(Verifier); | ||
}; |
Oops, something went wrong.