Fix: veridise 801/825 refactor rng precompile#189
Fix: veridise 801/825 refactor rng precompile#189daltoncoder wants to merge 8 commits intoveridise-audit-feb-2026from
Conversation
| chunk_idx += 1; | ||
| } | ||
| output.truncate(len); | ||
| output |
538552b to
a0be18f
Compare
samlaf
left a comment
There was a problem hiding this comment.
Looks like nice simplification! Didn't fully look at the RNG and gas calculation logic as its a bit over my head right now, so will rely on others for that. Mostly looked at interface. Few comments but overall LGTM!
| live_rng_key: Option<schnorrkel::Keypair>, | ||
| /// Total remaining gas across all active call frames, set before precompile dispatch. | ||
| gas_remaining_all_frames: u64, |
There was a problem hiding this comment.
these 2 options are only for the rng precompiles right? Can we add this as a comment?
There was a problem hiding this comment.
for example maybe good to also add something like "gas_remaining_all_frames is used to make different calls to the RNG precompile inside a tx return different results"?
| /// - **Simulation mode** (`live_key = None`): generates a random key via `OsRng`. | ||
| /// Non-deterministic by design (each call gets a fresh random key). |
There was a problem hiding this comment.
do we really need this simulation mode at all? or could we just inject a random test execution key in tests and always use the "execution mode"?
There was a problem hiding this comment.
we discussed this offline and think you found a nice solution here already
| pub struct SeismicChain { | ||
| rng_container: RngContainer, | ||
| live_rng_key: Option<schnorrkel::Keypair>, | ||
| /// Total remaining gas across all active call frames, set before precompile dispatch. |
There was a problem hiding this comment.
actually its set in frame_init, which will run on any call/staticcall/create//delegatecall, not only to precompiles, right?
There was a problem hiding this comment.
Yes if we wanted to access this in another precompile it would be there. Or expose an opcode for any transaction to read it we could
This PR is to address Veradise Audit 801(Block proposers can bias precompile randomness) and Veridise Audit 825(System-call trait fallthrough bypasses Seismic RNG reset).
Both of these issues are based on the idea of the state of the Rng precompile being tampered with to get favored results on the Rng. After diving in i realized that despite the many leftover comments we left in the code, the Execution mode that is used is prod is actually stateless and not really susceptible to these state attacks.
What we had before this PR:
Two modes to start the precompile in;
SimulationMode => Uses a stateful Merlin Transcript that gets appended to everytime its used to generate range. The Merlin Transcript is then forked every call to provide a new RootRng key. The appended to Merlin Transcript is used for the next call which gives it state
ExecutionMode => What we use in production. This is stateless and derives random bytes via HKDF-SHA256 using a schnorrkel key that is received from the Enclave. HKDF parameters are the txn_hash and user provided personality_bytes. Same txn_hash and pers_bytes will yield the same random number.
What this PR changes:
One side effect that this introduces is that now it is impossible to get the same output in a single transaction more than once. Before in Execution mode you could pass the same personality bytes in multiple times in a row within a single transaction and get the same output. Now that is not the case. I personally think that this more desirable behavior but let me know if anyone thinks otherwise.