Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Noir flowcheck #2

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions noir/enc-flowcheck/Nargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "flowcheck"
type = "bin"
authors = [""]
compiler_version = ">=0.25.0"

[dependencies]

19 changes: 19 additions & 0 deletions noir/enc-flowcheck/Prover.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[[edges]]
amt = "42"
dst = "0"
src = "1"

[[edges]]
amt = "42"
dst = "1"
src = "2"

[[edges]]
amt = "42"
dst = "2"
src = "0"

[[edges]]
amt = "0"
dst = "2"
src = "0"
264 changes: 264 additions & 0 deletions noir/enc-flowcheck/src/main.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
use dep::std::hash::poseidon2::Poseidon2;

use dep::std::ec::tecurve::affine::{Point,Curve};
use dep::std::ec::consts::te::{baby_jubjub};


// Graph parameters are statically fixed
global V = 10; // how many nodes
global E = 4; // how many edges

global SIZE = 1;

// this will be encrypted into 3 field elements:
// 2 for Point
// 1 for src | dst | amt
struct Edge {
srcPubKey: Point,
src: u32,
dst: u32,
amt: i64 // Signed 128-bit integer amt
}

unconstrained fn find(obligations: [Edge; E], src: u32, dst: u32) -> i64 {
let mut found_index = -1;

for i in 0..obligations.len() {
let e = obligations[i];
if (e.src == src) & (e.dst == dst) {
found_index = i as i64;
}
}

if found_index < 0 {
assert(false);
}
found_index
}

unconstrained fn hashField(r: Field) -> Field {
Poseidon2::hash([r], 1)
}

fn packValue(src: u32, dst: u32, amt: i64) -> Field {
(src as Field +
dst as Field * 1.pow_32(32) +
amt as Field * 1.pow_32(64)
)
}

unconstrained fn unpackValue(valueField: Field) -> (u32, u32, i64){

let value = U128::from_integer(valueField);

let mask = (1 << 32) - 1;

let src = value.to_integer() & mask;
let dst = (value >> U128::from_integer(32)).to_integer() & mask;
let amt = (value >> U128::from_integer(64)).to_integer() & mask;


(src as u32, dst as u32, amt as i64)
}

// takes in
// - enclaves private key
// - encrypted obligations (from the chain)
// - clear text flow solution (from the solver)
//
// do:
// - decrypt
// - checks (flows dont exceed obs, flows balance)
// - encrypt
//
// we need to check:
// - flows do not exceed obligations. this implies that each flow corresponds to an obligation
// - flows balance
//
fn main(
enclavePrivKey: Field,
cipherObligations: pub [(Point, [Field;3]); E],
flows: [Edge; E], // TODO separate param
randomSeed: Field,
) -> pub [(Point, [Field;1]); 2*E]{


//------------------
// Decrypt and get pubkeys
// ----------------

// array of public keys fetched from decrypted Edges
let mut keys = [Point{x:0, y:0}; V];

// array of clear text edges from decrypting cipherObligations.
// this allows
let mut edges = [Edge{srcPubKey: Point{x:0,y:0},src:0,dst:0,amt:0}; E];

// Loop over encrypted edges to get clear text edges and src pubkeys
for i in 0..cipherObligations.len() {
let cipherEdge = cipherObligations[i];

let e = decrypt(enclavePrivKey, cipherEdge);
let (x, y, packedValue) = (e[0], e[1], e[2]);
let (src, dst, amt) = unpackValue(packedValue);

let srcPubKey = Point{x:x,y:y};

keys[src] = srcPubKey; // TODO deal with duplicate srcs

edges[i] = Edge{srcPubKey: srcPubKey, src: src, dst: dst, amt: amt};
}


//------------------
// Run Checks
// ----------------


// Local RAM array for accumulating net position
let mut net = [0; V];

// Loop over flows to accumulate net positions
// and check that the flow is within the obligation amount
for i in 0..flows.len() {
let f = flows[i];

// Input-dependent lookup
net[f.src] -= f.amt;
net[f.dst] += f.amt;

let edgeID = find(edges, f.src, f.dst);

// assert flow is less than obligation
let obligationAmt = edges[edgeID].amt;
let flowAmt = f.amt;
assert(flowAmt <= obligationAmt);
}

// heck the final net flow is zero
for n in net {
assert(n == 0);
}

//------------------
// Encrypt Setoffs
// ----------------

// encrypted set off notices,
// 2 for each edge, with 1 encrypted to each of src and dst
// Field is (src | dst | amt)
let mut setoffs = [(Point{x:0,y:0}, [0 as Field; 1]); 2*E];
// ) -> pub [(Point, [Field;1]); 2*E]{

let mut r = randomSeed;

for i in 0..flows.len(){
let flow = flows[i];

// encode the flow value as src | dst | amt)
let value = packValue(flow.src, flow.dst, flow.amt);
// let value = (flow.src as u128) + (flow.dst as u128) << 32 + (flow.amt as u128) << 64;

// encrypt to src
let srcKey = keys[flow.src];
r = hashField(r);
setoffs[2*i] = encrypt(srcKey, r, [value]);

// encrypt to dst
let dstKey = keys[flow.dst];
r = hashField(r);
setoffs[2*i+1] = encrypt(dstKey, r, [value]);
}

// return encrypted setoffs
setoffs
}

global bjj = baby_jubjub();
global G = bjj.base8;

// Compute the public key X = x*G
fn derive_pubkey(x: Field) -> (Point) {
let X = bjj.curve.mul(x, G);
X
}

// encrypt msg of size 32 bytes using pubkey and randomness r
fn encrypt(pubkey: Point, r: Field, msg: [Field; SIZE]) -> (Point, [Field; SIZE]) {
// gA = pubkey
let gR = derive_pubkey(r);
let gAR = bjj.curve.mul(r, pubkey);

let mut cipherText = [0;SIZE];

for i in 0..msg.len() {
let m = msg[i];

// hash(m || counter)
let hashKey = Poseidon2::hash([gAR.x, i as Field], 2);

cipherText[i] = m + hashKey;
}

(gR, cipherText)
}

// TODO dont hardcode 3!
fn decrypt(privkey: Field, cipher: (Point, [Field;3])) -> [Field; 3] {

let (gR, cipherText) = cipher;
let gAR = bjj.curve.mul(privkey, gR);

let mut clearText = [0;3];

for i in 0..cipherText.len() {
let m = cipherText[i];

// hash(m || counter)
let hashKey = Poseidon2::hash([gAR.x, i as Field], 2);

clearText[i] = m - hashKey;
}

clearText
}


#[test]
fn test_main() {
let a = 0x3fbbccb240537392421955b07a0d65eded9e7637995bf2f9cfe29e19b580e4;
let r = 0x106885ee5c8f2757c6bde259b31b3f00300f538c8901b28c5bcf982c85e17493;
let mut msg = [0; SIZE];
msg[0] = 42;
msg[1] = 137; // alpha baby
msg[2] = 118;

let gA = derive_pubkey(a);
println(gA);

//let (gR, cipherText) = encrypt(gA, r, msg);
//let clearText = decrypt(a, (gR, cipherText));

//assert(msg == clearText);
}


/*#[test]
fn test_main() {
let mut edges = [Edge{src:0,dst:1,amt:5},
Edge{src:1,dst:2,amt:5},
Edge{src:2,dst:0,amt:5},
Edge{src:2,dst:0,amt:0}];
let _ = main(edges);
}*/

/*#[test(should_fail)]
fn test_fail() {
let mut edges = [Edge{src:0,dst:1,amt:5},
Edge{src:1,dst:2,amt:5},
Edge{src:2,dst:0,amt:5},
Edge{src:2,dst:0,amt:0}];
edges[0].amt += 1;
let _ = main(edges);
}
*/
3 changes: 2 additions & 1 deletion noir/flowcheck/Nargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ type = "bin"
authors = [""]
compiler_version = ">=0.25.0"

[dependencies]
[dependencies]

5 changes: 5 additions & 0 deletions noir/flowcheck/Prover.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ src = "2"
amt = "42"
dst = "2"
src = "0"

[[edges]]
amt = "0"
dst = "2"
src = "0"
22 changes: 13 additions & 9 deletions noir/flowcheck/src/main.nr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use dep::std::hash::poseidon2::Poseidon2;

// Graph parameters are statically fixed
global V = 4000; // how many nodes
global E = 3; // how many edges
global V = 2500; // how many nodes
global E = 4; // how many edges

struct Edge {
src: u32,
Expand All @@ -26,27 +26,31 @@ fn main(edges: [Edge; E]) -> pub Field {
// Finally check the final net flow is zero
for n in net {
assert(n == 0);
}
}

// Accumulate residual graph into a Merkle tree
// TODO: subtract the residual graph...
// Show that the edges correspond to the encrypted edges
// TODO: This is just a place holder, to show that even hashing
// the elements results in too much overhead for one proof!
let vHash = Poseidon2::hash(net.map(|n:i64| n as Field), V);
//let vHash = 0 as Field;
vHash
}

#[test]
fn test_main() {
let mut edges = [Edge{src:0,dst:1,amt:5},
Edge{src:1,dst:2,amt:5},
Edge{src:2,dst:0,amt:5}];
main(edges);
Edge{src:2,dst:0,amt:5},
Edge{src:2,dst:0,amt:0}];
let _ = main(edges);
}

#[test(should_fail)]
fn test_fail() {
let mut edges = [Edge{src:0,dst:1,amt:5},
Edge{src:1,dst:2,amt:5},
Edge{src:2,dst:0,amt:5}];
Edge{src:2,dst:0,amt:5},
Edge{src:2,dst:0,amt:0}];
edges[0].amt += 1;
main(edges);
let _ = main(edges);
}
25 changes: 19 additions & 6 deletions noir/hashing/src/main.nr
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
use dep::std::hash::poseidon2::Poseidon2;
use dep::std::hash::poseidon;
use dep::std::hash::{pedersen_hash,mimc::mimc_bn254};


fn main(x: Field, y: pub Field) {
assert(x != y);
global N = 3000;

fn main(x: u64) -> pub (Field) {

let mut arr = [0; N];
// Use an input dependent access
arr[x] = 1;

// Compute the hash of an N array
//let xHash = poseidon::bn254::hash_4(arr);
let xHash = Poseidon2::hash(arr.map(|xi:u64| xi as Field), N);
//let xHash = pedersen_hash(arr.map(|xi:u64| xi as Field));
//let xHash = mimc_bn254(arr.map(|xi:u64| xi as Field));
xHash
}

#[test]
fn test_main() {
main(1, 2);

// Uncomment to make test fail
// main(1, 1);
let xHash = main(0);
assert(xHash != 0);
}
Loading