Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initialize math/rand with entropy #2745

Open
moul opened this issue Aug 31, 2024 · 6 comments
Open

Initialize math/rand with entropy #2745

moul opened this issue Aug 31, 2024 · 6 comments
Labels
good first issue Good for newcomers security Security-sensitive issue

Comments

@moul
Copy link
Member

moul commented Aug 31, 2024

I propose initializing math/rand with the previous block hash, consensus metadata timestamp, and original caller. This method leverages three sources that are guessable but hard to manipulate:

  1. Previous Block Hash: Requires chain connection and quick computation.
  2. Timestamp: Requires validator set influence.
  3. Original Caller: Easy to guess but increases variance.

This aligns with Go 1.20 standards, improving our randomness initialization.

Alternative Proposal

Alternatively, we could stay with Go 1.18 and use the unused timestamp nanoseconds field to derive the previous block hash, adding entropy with the limitation of not having two blocks in under one second.

cc @wyhaines @kristovatlas

@moul moul added good first issue Good for newcomers security Security-sensitive issue labels Aug 31, 2024
@moul
Copy link
Member Author

moul commented Aug 31, 2024

Added the good first issue label for people who would like to try contributing to the gno.land app. I suggest you look at the VMKeeper and how it initializes the gno.Machine, its execution context, and how the time.Time() function is currently injected so it's callable from a Gno contract, while being able to return a value that depends on being injected in the VM itself. Good luck!

@jaekwon
Copy link
Contributor

jaekwon commented Sep 4, 2024

previous block hash

if we're talking about initializing the global rand instance, then why would the previous block hash help? it would always be zero, because initialization only happens at block height 0.

Timestamp

i guess this would be the genesis timestamp then.

This aligns with Go 1.20 standards

You mentioned prev block hash i think because you mean to use this source of randomness for every block, right?
But then I'm confused because Go 1.20 doesn't do anything like that. the internal global rand instance is only seeded once, even in Go 1.20.

Needs clarity


Using the block hash every block for anything adds computational complexity for every empty block, so should be avoided unless necessary.

If a programmer needs randomness that is secure by including entropy from the last block hash, that should be something outside of math/rand like a /p/ package.

@moul
Copy link
Member Author

moul commented Sep 4, 2024

if we're talking about initializing the global rand instance, then why would the previous block hash help? it would always be zero, because initialization only happens at block height 0.

If unclear, I was speaking about gno, not go.

It can help with any contract added after block 0 using addpkg (99.999% of them over time). It can also assist if people are initializing seeds not in their init(), but more dynamically, at each transaction.

For block 0 and init(), we still have the real timestamp.

But then I'm confused because Go 1.20 doesn't do anything like that. the internal global rand instance is only seeded once, even in Go 1.20.

My proposal suggests that we should follow the approach of Go 1.20, which initializes the random number generator "for you," eliminating the need to handle it manually. This is my preferred idea.

The second part of my comment offers an alternative proposal to continue using Go 1.18 and improve the efficiency of the well-known rand.Seed(time.Now().UTC().UnixNano()) pattern.

Using the block hash every block for anything adds computational complexity for every empty block, so should be avoided unless necessary.

This is not a block-level feature, but rather a VM feature. If you have no transactions, then your block will not compute it.

If a programmer needs randomness that is secure by including entropy from the last block hash, that should be something outside of math/rand like a /p/ package.

It was my initial idea as well, which is why I created the p/entropy package: https://gno.land/p/demo/entropy/entropy.gno

Recently, someone suggested introducing a new std.LastBlock.Hash() function to provide a source of entropy. However, I am strongly against adding this new std call if the only intended use is to generate entropy.

Instead, I would prefer to have math/rand be seeded with entropy by default, as in Go 1.20. Alternatively, we could use the nanosecond hack that makes the well-known Go patterns work without introducing a new std entry and by making it easier to switch from Go to Gno.

While my p/entropy package is usable, it is not entirely idiomatic Go.

@jefft0
Copy link
Contributor

jefft0 commented Sep 4, 2024

An obvious question: The random values are reproducible by anyone, right? If that's right, then do you think it will be clear to devs that it shouldn't be used to generate cryptographic secrets?

@moul
Copy link
Member Author

moul commented Sep 4, 2024

NOTE: This issue is outdated. I need to update it to make it current and more clear.

@wyhaines
Copy link

wyhaines commented Sep 4, 2024

FWIW, specifically on the issue of seeding, I think that using the last blockhash as a source of entropy to seed the nanoseconds field in the timestamp (I am assuming historical context here, but I am guessing that this is because we can't assume that nanosecond level timestamps will be available on nodes), while opaque, is fine so long as we make sure to document that this is what we are doing.

I think that I mentioned this when I inadvertently waded into this discussion on Signal, but just in case, I ran into this issue first hand when I was porting my ISAAC PRNG implementation to Gno, and I was trying to figure out what tools I had available for providing default seeding.

As you note, @jefft0, this approach is wholly inadequate for cryptographic purposes, but I would generally expect anyone who isn't a complete newcomer to writing code on a blockchain platform to understand the deterministic nature of the platform. I think that if it's clearly documented, it is the approach of least-friction.

I won't weigh in on the issue of non-entropy-related uses for the block hash here, as that just pollutes this discussion. If there is another issue (I haven't had the chance yet to search to see) for that, I'll weigh in there. Otherwise, @moul I am happy to create one where that issue could be discussed separately from this one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Good for newcomers security Security-sensitive issue
Projects
Status: 📥 Inbox
Status: Triage
Development

No branches or pull requests

4 participants