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

Update PRG interface and implementation #6

Merged
merged 34 commits into from
Nov 8, 2023

Conversation

sisyphusSmiling
Copy link
Contributor

Related: #5

Copy link
Collaborator

@tarakby tarakby left a comment

Choose a reason for hiding this comment

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

I made a partial review and left the PRG main functions (init and next 64 bits) as I'm still thinking through the design. I'll continue the review shortly, but left some first comments for now.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at [email protected]. All
Copy link
Collaborator

Choose a reason for hiding this comment

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

not sure if there is a Flow team address available

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. This was taken from an existing open source template, so I'm not sure if we have a dedicated flow team email address for this purpose.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I asked around but didn't get an answer so far

README.md Outdated Show resolved Hide resolved

> :warning: This repo is still a work in progress - the underlying RandomBeaconHistory is also still a work in progress

## Overview
Copy link
Collaborator

Choose a reason for hiding this comment

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

Clear and easy-to-read doc 👌🏼

transactions/random-beacon-history/heartbeat.cdc Outdated Show resolved Hide resolved
transactions/pseudo-random-generator/next_uint64.cdc Outdated Show resolved Hide resolved
if let prg = signer.borrow<&PseudoRandomGenerator.PRG>(from: PseudoRandomGenerator.StoragePath) {
var i = 0
while i < generationLength {
prg.nextUInt64()
Copy link
Collaborator

Choose a reason for hiding this comment

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

This transaction seems to evolve the state of the PRG but does not return the randoms generated. What is the purpose of it?
Or maybe it could return maybe return an array of Uint64 of sizegenerationLength ?

Copy link
Contributor Author

@sisyphusSmiling sisyphusSmiling Nov 6, 2023

Choose a reason for hiding this comment

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

The purpose here is just for demonstration of how one would use the PRG to generate a number of random values. We don't need this for our use case here as the PRG is simply used in the contract, but it could be helpful for others depending on their use case.

We aren't able to return values from transactions, so a caller would need to advance the state of the PRG then query the generated values from a script. If the caller wanted to capture the generated values, they would need to be stored in some resource. I'm also realizing this would all be helpful context in this transaction's prefixed comment


let prg <- PseudoRandomGenerator.createPRG(sourceOfRandomness: seed, salt: salt)

let randUInt64 = prg.nextUInt64()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Similarly, I think this script could be generalized to take an integer input and return the first outputs of the RNG as a[UInt64]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This script is wrapped in the random function we pass to BasicDistributionTest so we can get the distribution of random numbers. I believe the test method requires a single UInt64 be returned from said random function, so we'll need at least one script with this or similar signature. What if we add a script that does what you're describing which, as I understand it, would generate an array of random UInt64 of length generationLength?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I believe the test method requires a single UInt64 be returned from said random function

You're right about the testing function constraint.
However I'm thinking if we could implement the test this way: have a generalized script that returns [UInt64]. The test calls the script and saves the huge array in a temp bufffer, then the testing function would just read a random at a time from the temp buffer.
This way we avoid the complicated/non-intuitive process of : calling a transaction to advance the PRG state each time but without reading the random, and then call a script that only reads one random.

Curious to know what you think.

Copy link
Contributor Author

@sisyphusSmiling sisyphusSmiling Nov 7, 2023

Choose a reason for hiding this comment

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

Ah of course, that make sense. I'll need to experiment with the largest supported generation length - I wonder if we'll run into limits depending on the size of the array we're trying to generate

Copy link
Collaborator

Choose a reason for hiding this comment

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

you're right, size might be an issue, I think it's about 80000 values of 8 bytes each.
Btw on what network does the test run, and how often?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The test runs in emulator AKA locally on push & PR, but I'll update the test to run on PR since we don't need a stat test every push

return getAccount(prgAddress).getCapability<&PseudoRandomGenerator.PRG>(
PseudoRandomGenerator.PublicPath
).borrow()
?.nextUInt64()
Copy link
Collaborator

Choose a reason for hiding this comment

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

The call to nextUInt64() updates the PRG state in the execution state. Maybe this is supposed to be a transaction and not a script?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We aren't able to return values from transactions, so we'll need to use a script to retrieve the value.

Given the inability for transaction to return values, retrieving a newly generated value will involve some arrangement of both a transaction and a script. This isn't very intuitive, but I don't see a way around this for those requesting random numbers offchain.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I agree the combination of a transaction and script for each random is non-intuitive.

I somehow think that saving a PRG object as a resource might not be needed. I believe most use cases would create a PRG and get all the randoms they need at the same operation (transaction or script). (btw this is what I'm also suggesting for the distribution test)

If the same randoms need to be retrieved, it's the seed (or block height of history contract) and resource ID that need to be stored on-chain.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree, I questioned myself whether the PRG needed to be a resource, and don't have a good reason why it shouldn't be. Maybe I might care about uniqueness and continuity of PRGs which a resource can give guarantees of - i.e. my Receipt has attached to it some PRG instance. I'll update the PRG object to a struct

contracts/PseudoRandomGenerator.cdc Outdated Show resolved Hide resolved
contracts/PseudoRandomGenerator.cdc Outdated Show resolved Hide resolved
Copy link
Collaborator

@tarakby tarakby left a comment

Choose a reason for hiding this comment

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

More comments about the xorshift128+ itself

contracts/PseudoRandomGenerator.cdc Outdated Show resolved Hide resolved
contracts/PseudoRandomGenerator.cdc Outdated Show resolved Hide resolved
contracts/PseudoRandomGenerator.cdc Outdated Show resolved Hide resolved
contracts/PseudoRandomGenerator.cdc Outdated Show resolved Hide resolved
contracts/PseudoRandomGenerator.cdc Outdated Show resolved Hide resolved
contracts/PseudoRandomGenerator.cdc Outdated Show resolved Hide resolved
@sisyphusSmiling sisyphusSmiling marked this pull request as ready for review November 6, 2023 22:37
@sisyphusSmiling sisyphusSmiling requested a review from a team November 6, 2023 22:37
Copy link
Collaborator

@tarakby tarakby left a comment

Choose a reason for hiding this comment

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

Thanks for addressing the initial comments, I made another round of comments.

Comment on lines 75 to 77
/// Creates a new XORSift128+ PRG with the given source of randomness and salt
///
/// @param sourceOfRandomness: The 32 byte source of randomness used to seed the PRG
Copy link
Collaborator

Choose a reason for hiding this comment

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

In theory, this contract can be used with the source of randomness from the history contract, or with any other source of entropy the developer wanna use.

Also adding a condition about the input length. 16 is enough although we have 32 in our case with the history contract.

Suggested change
/// Creates a new XORSift128+ PRG with the given source of randomness and salt
///
/// @param sourceOfRandomness: The 32 byte source of randomness used to seed the PRG
/// Creates a new xorshift128+ PRG with the given source of randomness and salt
///
/// @param sourceOfRandomness: The entropy bytes used to seed the PRG. It is recommended to use at least 16 bytes of entropy.

/// @param salt: The bytes used to salt the source of randomness
///
/// @return A new PRG resource
access(all) fun createPRG(sourceOfRandomness: [UInt8], salt: [UInt8]): @PRG {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Enforcing a minimum length on the input

Suggested change
access(all) fun createPRG(sourceOfRandomness: [UInt8], salt: [UInt8]): @PRG {
access(all) fun createPRG(sourceOfRandomness: [UInt8], salt: [UInt8]): @PRG {
pre {
sourceOfRandomness.length >= 16 "At least 16 bytes of entropy should be used"
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See comment on feedback below

contracts/XorShift128Plus.cdc Outdated Show resolved Hide resolved
contracts/XorShift128Plus.cdc Outdated Show resolved Hide resolved
contracts/XorShift128Plus.cdc Outdated Show resolved Hide resolved

/// Advances the PRG state and generates the next UInt64 value
/// See https://arxiv.org/pdf/1404.0390.pdf for implementation details and reasoning for triplet selection.
/// Note that state only advances when this function is called from a transaction. Calls from within a script
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nice doc 👌🏼

contracts/XorShift128Plus.cdc Outdated Show resolved Hide resolved
CONTRIBUTING.md Outdated
Comment on lines 1 to 3
# Contributing to the Non-Fungible Token Standard

The following is a set of guidelines for contributing to the Flow NFT standard. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
Copy link
Member

Choose a reason for hiding this comment

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

Should this be changed to say ramdom coin toss implementation instead of NFT standard?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ha you're right! Good catch

@sisyphusSmiling sisyphusSmiling changed the base branch from pre-prg-impl to main November 7, 2023 23:17
@sisyphusSmiling sisyphusSmiling changed the base branch from main to pre-prg-impl November 7, 2023 23:17
README.md Outdated
@@ -0,0 +1,32 @@
# [WIP] Random Coin Toss

> :warning: This repo is still a work in progress - the underlying RandomBeaconHistory is also still a work in progress
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is the RandomBeaconHistory contract in this repo the same core-protocol one? If yes I guess it is not WIP right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, the contract is finalized. I'll update this section

contracts/CoinToss.cdc Outdated Show resolved Hide resolved
Co-authored-by: Tarak Ben Youssef <[email protected]>
Copy link
Collaborator

@tarakby tarakby left a comment

Choose a reason for hiding this comment

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

Nice work! Thank you for your patience with all my comments 🙏🏼

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants