diff --git a/circom_circuits/ledger/notes.circom b/circom_circuits/ledger/notes.circom index d3b4c59..a4013ed 100644 --- a/circom_circuits/ledger/notes.circom +++ b/circom_circuits/ledger/notes.circom @@ -14,4 +14,22 @@ template derive_public_key(){ hash.inp[0] <== dst.out; hash.inp[1] <== secret_key; out <== hash.out; +} + +template derive_note_id(){ + signal input op_id; + signal input output_number; + signal input value; + signal input pk; + + component note_id = Poseidon2_hash(5); + component note_id_v1 = NOTE_ID_V1(); + note_id.inp[0] <== note_id_v1.out; + note_id.inp[1] <== op_id; + note_id.inp[2] <== output_number; + note_id.inp[3] <== value; + note_id.inp[4] <== pk; + + signal output out; + out <== note_id.out; } \ No newline at end of file diff --git a/circom_circuits/ledger/update_state.circom b/circom_circuits/ledger/update_state.circom new file mode 100644 index 0000000..0c31719 --- /dev/null +++ b/circom_circuits/ledger/update_state.circom @@ -0,0 +1,104 @@ +//test +pragma circom 2.1.9; + +include "../hash_bn/poseidon2_hash.circom"; +include "../hash_bn/poseidon2_perm.circom"; +include "../misc/constants.circom"; +include "notes.circom"; +include "../hash_bn/merkle.circom"; + + +template update_state_with_one_note(merkle_depth){ + signal input merkle_root; + signal input old_value; + signal input old_pk; + signal input new_value; + signal input new_pk; + signal input selectors[merkle_depth]; + signal input path[merkle_depth]; + + //Get the old note ID + component old_note_id = derive_note_id(); + old_note_id.op_id <== 0; + old_note_id.output_number <== 0; + old_note_id.value <== old_value; + old_note_id.pk <== old_pk; + + // Check that the note is in the tree + //First check selectors are indeed bits + for(var i = 0; i < merkle_depth; i++){ + selectors[i] * (1 - selectors[i]) === 0; + } + //Then check the note id is in the tree + component old_membership = proof_of_membership(merkle_depth); + for(var i = 0; i < merkle_depth; i++){ + old_membership.nodes[i] <== path[i]; + old_membership.selector[i] <== selectors[i]; + } + old_membership.root <== merkle_root; + old_membership.leaf <== old_note_id.out; + old_membership.out === 1; + + // Then update the root with the new note replacing the old one + // First derive the new note ID + component new_note_id = derive_note_id(); + new_note_id.op_id <== 0; + new_note_id.output_number <== 0; + new_note_id.value <== new_value; + new_note_id.pk <== new_pk; + // Then update the Merkle root + component new_root = compute_merkle_root(merkle_depth); + for(var i = 0; i< merkle_depth; i++) { + new_root.nodes[i] <== path[i]; + new_root.selector[i] <== selectors[i]; + } + new_root.leaf <== new_note_id.out; + + // Output + signal output updated_root; + updated_root <== new_root.root; +} + +template update_state_with_several_notes(merkle_depth, batch_size){ + signal input previous_root; + signal input previous_note_values[batch_size]; + signal input previous_note_pk[batch_size]; + signal input new_note_values[batch_size]; + signal input new_note_pk[batch_size]; + signal input merkle_paths[batch_size][merkle_depth]; + signal input merkle_selectors[batch_size][merkle_depth]; + + signal output new_root; + + component update[batch_size]; + + // Update with the first note + update[0] = update_state_with_one_note(merkle_depth); + update[0].merkle_root <== previous_root; + update[0].old_value <== previous_note_values[0]; + update[0].old_pk <== previous_note_pk[0]; + update[0].new_value <== new_note_values[0]; + update[0].new_pk <== new_note_pk[0]; + for(var i = 0; i < merkle_depth; i++){ + update[0].selectors[i] <== merkle_selectors[0][i]; + update[0].path[i] <== merkle_paths[0][i]; + } + + // Update all the other notes + for(var i = 1; i < batch_size; i++){ + update[i] = update_state_with_one_note(merkle_depth); + update[i].merkle_root <== update[i-1].updated_root; + update[i].old_value <== previous_note_values[i]; + update[i].old_pk <== previous_note_pk[i]; + update[i].new_value <== new_note_values[i]; + update[i].new_pk <== new_note_pk[i]; + for(var j = 0; j < merkle_depth; j++){ + update[i].selectors[j] <== merkle_selectors[i][j]; + update[i].path[j] <== merkle_paths[i][j]; + } + } + + new_root <== update[batch_size -1].updated_root; +} + +component main {public [previous_root,previous_note_values,new_note_values]}= update_state_with_several_notes(32,3); \ No newline at end of file diff --git a/circom_circuits/mantle/pol_lib.circom b/circom_circuits/mantle/pol_lib.circom index 38dd6c6..0cc7fba 100644 --- a/circom_circuits/mantle/pol_lib.circom +++ b/circom_circuits/mantle/pol_lib.circom @@ -72,14 +72,11 @@ template would_win_leadership(merkle_depth){ // Derive the note id - component note_id = Poseidon2_hash(5); - component dst_note_id = NOTE_ID_V1(); - note_id.inp[0] <== dst_note_id.out; - note_id.inp[1] <== transaction_hash; - note_id.inp[2] <== output_number; - note_id.inp[3] <== value; - note_id.inp[4] <== pk.out; - + component note_id = derive_note_id(); + note_id.op_id <== transaction_hash; + note_id.output_number <== output_number; + note_id.value <== value; + note_id.pk <== pk.out; // Check the note ID is aged enough //First check selectors are indeed bits