Skip to content

fix(rust-client): replace RandomCoin with ChaCha20Rng - #2414

Open
ricomateo wants to merge 17 commits into
nextfrom
replace-random-coin
Open

fix(rust-client): replace RandomCoin with ChaCha20Rng#2414
ricomateo wants to merge 17 commits into
nextfrom
replace-random-coin

Conversation

@ricomateo

@ricomateo ricomateo commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Closes #2204

  • Replaces the usage of RandomCoin with ChaCha20Rng.
  • ClientBuilder::rng now requires the RNG to implement CryptoRng (i.e. RandomCoin is no longer accepted, since it is not a CryptoRng).
  • Added Client::secure_rng, a second RNG intended to be used to generate randomness for secret values.
  • Added a trybuild test asserting that ClientBuilder::rng rejects a FeltRng which is not a CryptoRng.

@ricomateo
ricomateo marked this pull request as ready for review August 19, 2026 15:51
@ricomateo ricomateo changed the title fix(rust-client): replace RandomCoin fix(rust-client): replace RandomCoin with ChaCha20Rng Aug 19, 2026

@juan518munoz juan518munoz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall. What I still don't understand is why we are keeping rng and secure_rng separately, for what I see we could just get away with the new secure implementation in all places.

If I'm not mistaken, keeping a single rng would also make the trybuild test redundant.

Comment thread crates/rust-client/src/lib.rs Outdated
/// the field modulus, which keeps the result uniform over the field. The rejection
/// probability is about 2^-32.
pub fn draw_felt(rng: &mut impl rand::Rng) -> crate::Felt {
use rand::RngExt;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should move the import outside this function

Comment thread crates/rust-client/src/lib.rs Outdated
/// Uses rejection sampling: [`Felt::new`](crate::Felt::new) rejects any `u64` at or beyond
/// the field modulus, which keeps the result uniform over the field. The rejection
/// probability is about 2^-32.
pub fn draw_felt(rng: &mut impl rand::Rng) -> crate::Felt {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this functions are better suited in a separate file, similar to how we do with utils.rs, we could add an rng.rs file.

Comment thread crates/rust-client/src/lib.rs Outdated
///
/// Use this for note serial numbers when building notes from a plain [`rand`] generator, which
/// does not implement [`FeltRng`].
pub fn draw_word(rng: &mut impl rand::Rng) -> crate::Word {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as other comment

@ricomateo

Copy link
Copy Markdown
Contributor Author

@juan518munoz thanks! all comments were addressed in this commit e3b4748.

What I still don't understand is why we are keeping rng and secure_rng separately, for what I see we could just get away with the new secure implementation in all places.

We keep both secure_rng and rng because rng may be a predictable RNG (since the caller can override it with ClientBuilder::rng, passing an RNG initialized with a small seed), which makes it unsafe, so a separate RNG (secure_rng) is used for account keys and transaction input encryption, as pointed out in this comment.


#[cfg(test)]
mod tests {
/// Checks that [`ClientBuilder::rng`] rejects a generator that is not a `CryptoRng`.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is already enforced by the compiler in the type system, we can remove this test IMO

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we remove the test, then we should remove this file

@juan518munoz juan518munoz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Before merging this, it may be a good idea to create a branch on web-sdk that points to this branch, so we can assure there won't be any setbacks.

@ricomateo

Copy link
Copy Markdown
Contributor Author

@juan518munoz I created a draft PR in the web-sdk pinning the rust-sdk dependency to this branch, to check this PR doesn't break the web client. It required some changes but the CI succeeds.

@huitseeker huitseeker left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking this on. Replacing RandomCoin with a standard CryptoRng is the right direction.

I checked the web SDK compatibility PR to understand why the client accepts a seed. Account recovery already has a separate path: newWallet(initSeed) derives the account key and initialization seed, while importPublicAccountFromSeed repeats that derivation. This path does not need ClientBuilder::rng.

The seed passed to createClient has a wider effect. It controls the client's operational RNG and also derives the web keystore's signing RNG. That behavior is useful for repeatable tests, but it should not be available to a client that may submit live transactions.

I suggest keeping one Client::rng for every client operation and seeding it from the operating system in normal builds. Whole-client deterministic seeding can move to the testing feature or the mock client. Account recovery can keep its explicit account seed.

With that setup, I think this PR can remove the second RNG field and the public secure_rng() accessor. ClientRng::inner_mut() should also be limited to testing if production code has no need to replace the generator after construction.

That smaller implementation would keep the CryptoRng requirement and the ChaCha20Rng default. It would route the current secure_rng call sites through the one client RNG. The web SDK can then stop passing createClient(seed) into the production client builder while leaving newWallet(initSeed) unchanged.

Does this match the intended role of the client seed? If another production caller needs to inject its own CryptoRng, I may of course have missed something!

Comment thread crates/rust-client/src/builder.rs Outdated
}

/// Optionally provide a custom RNG.
/// Optionally provide a custom RNG for note serial numbers, script arguments and account

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requiring CryptoRng is a good step. Web account recovery already uses newWallet(initSeed), so could we keep recovery there and make this whole-client override test-only, unless there's another production use?

Comment thread crates/rust-client/src/lib.rs Outdated
/// Returns a reference to the client's secure random number generator. This can be used to
/// generate randomness for secret values such as account keys, and the nonces that seal
/// transaction inputs.
pub fn secure_rng(&mut self) -> &mut ClientRng {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If whole-client overrides are limited to tests, every production operation can use the same OS-seeded RNG (and we would not need to return a mutable variant of it in those production paths.

nit: the name implies security, but that's contextual. Could we remove secure_rng() and keep a single Client::rng() instead?

Comment thread CHANGELOG.md

### Breaking Changes

* [BREAKING][removal][rust] Removed the `miden_client::crypto::RandomCoin` re-export. Use a `rand` CSPRNG such as `ChaCha20Rng`, plus the new `miden_client::crypto::draw_felt` / `draw_word` helpers where a `Felt` or `Word` is needed from a generator that does not implement `FeltRng` ([#2414](https://github.com/0xMiden/rust-sdk/pull/2414)).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The migration path here does not compile because both helpers live under miden_client::rng. Please change this to miden_client::rng::{draw_felt, draw_word} if the helpers remain public.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Investigate replacing RandomCoin

4 participants