leonardoalt / fusion

An experimental progressive and fast zkRollup written in Rust, focused on performance, modularity, and applying cutting-edge Verifiable Computation proof systems.
GNU General Public License v3.0
119 stars 5 forks source link

A possible circuit risk? #85

Open xcshuan opened 1 year ago

xcshuan commented 1 year ago

I don't know if my observation is correct.

In the current circuit implementation, the state before transaction execution is first verified through Merkle proof, and then postRoot is used to verify the account state after transaction execution. However, there is no constraint that only the state transition caused by the transaction itself exists from preRoot to postRoot. In theory, it can pass verification by secretly inserting more state changes into postState.

// Verify new state

// Verify post state `sender` account for all transactions.
acc = acc && verify_merkle_path(postRoot, hash_leaf(postAccountSender), directionSelector[0], postPath[0]);
// Only verify post state `to` account for Transfer transactions.
acc = acc && if tx.kind == 0 { verify_merkle_path(postRoot, hash_leaf(postAccountRecipient), directionSelector[1], postPath[1]) } else { true };

It's also possible that I'm missing some constraints that make this attack impossible.

leonardoalt commented 1 year ago

Hi, thanks for raising this issue.

I think you're right! An attack in this case would be generating a valid proof using a valid tx plus a bunch of hidden txs that no one else can ever generate.

The bug in the circuit implementation is that we're allowing 2 different siblings paths for the pre and post states. If we allow only one tx at a time, the array of sibling nodes should be the same, which actually simplifies the circuit.

Thanks again!