code the circuit for stf

This commit is contained in:
thomaslavaur 2026-05-13 10:54:21 +02:00
parent c8b0d6747e
commit cad7cef90b
3 changed files with 127 additions and 8 deletions

View File

@ -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;
}

View File

@ -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);

View File

@ -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