-
Notifications
You must be signed in to change notification settings - Fork 4
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
Conversation
There was a problem hiding this 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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
|
||
> :warning: This repo is still a work in progress - the underlying RandomBeaconHistory is also still a work in progress | ||
|
||
## Overview |
There was a problem hiding this comment.
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 👌🏼
if let prg = signer.borrow<&PseudoRandomGenerator.PRG>(from: PseudoRandomGenerator.StoragePath) { | ||
var i = 0 | ||
while i < generationLength { | ||
prg.nextUInt64() |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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]
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this 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
Co-authored-by: Tarak Ben Youssef <[email protected]>
There was a problem hiding this 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.
contracts/XorShift128Plus.cdc
Outdated
/// 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 |
There was a problem hiding this comment.
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.
/// 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. |
contracts/XorShift128Plus.cdc
Outdated
/// @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 { |
There was a problem hiding this comment.
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
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" | |
} |
There was a problem hiding this comment.
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
|
||
/// 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice doc 👌🏼
CONTRIBUTING.md
Outdated
# 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. |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
Co-authored-by: Tarak Ben Youssef <[email protected]>
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
Co-authored-by: Tarak Ben Youssef <[email protected]>
There was a problem hiding this 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 🙏🏼
Related: #5