A Provably fair random hash generator which can be plugged into random-js.
#Install
npm install --save provable
#About This library attempts to implement the algorithm first outlined in the bitcoin talk forums: https://bitcointalk.org/index.php?topic=922898.0. The idea behind provably random is that a deteministic series of related but random hashes are generated before any random outcomes are calculated. Once generated, each hash in the series is used to create a random outcome.
The provable part comes in when a client wants to verify the integrity of the outcome. After every outcome is produced the hash which produced it can also be made public. If one has access to the current hash and the previous hash (from the previous outcome), then a SHA256 can be used on the current hash to generate the previous. This should hold true for all hashes in the series as they are exposed. You cannot predict the next hash, but you can verify the previous hash by hashing its predecessor. Any deviation from this verification result would mean that the hashes were tampered with and not part of the pregenerated series. Hence you can "prove" the random outcome was fair.
Bear in mind since the series is pre-generated, this means eventually you will run out of hashes to generate random outcomes and a new series of provably fair hashes must be created.
#Usage This library can be used standalone to generate a series of random hashes generated by nodes crypto sha256 hasher. The engine can then be plugged into random-js in order to give you more control over your random values. By default the engine will only supply a 32-bit random integer based on the next hash in the series. The state of the engine can be saved and resumed as needed.
When the engine runs out of hashes ( reaches end of series) an error is thrown. Its up to the user to catch the error and determine how to proceed from that point. Typically you would switch to or generate a new series and continue.
var Provable = require('provable')
var engine = Provable({
count:10000, //default:1, number of hashes in the series to produce, takes longer depending on how big the number is
seed:'optional seed to start your series with' //defaults to a random uuid4
})
//return a random int32
var int32 = engine()
//raw sha256 hash. Increments your hash index. Throws an error if no hashes left.
//if you want to do your own random calculations then use this.
var hash = engine.nextHash()
//internal state of the engine. Use this to save and resume your engine.
var state = engine.state()
//resuming will re-generate the entire hash chain
//from your seed and pick up where you left off with the index
var resumedEngine = Provable(state)
#Random-JS Random-js can give you more options over the types of random values you can generate. Keep in mind that random-js only uses 32 bits of the hash, the rest is discarded. This provable engine defaults to using the 32 least significant bits of the hash when generating the random integer.
var Random = require('random-js')
var Engine = require('provable')
//use random like a random-js object
var random = Random(Engine({ count:10000 })
//some examples
random.bool()
random.integer(min,max)
random.real(min,max,inclusive)
//etc...
//keep in mind, the provable engine will throw when out of values,
//so you should wrap each call in try catch. When the engine throws,
//you should start a new series.
#Proving Fairness When proving fairness to clients, you must expose each hash only after they are used to generate the random outcome. Never expose your seed parameter. Generally you can provide some third party site, like JSbin which takes a hash and generates the hash series using SHA256. This series should match all your previously exposed hashes.
#Other Things to know On construction, the engine will generate the entire hash series, the length of which is determined by the "count" parameter. By default count is 1. It is suggested to change this to something large, like 10000 or more. Hashes are generated syncronously so there may be blocking for long periods if the count is very large. Hashes are also stored in memory as 256-bit string, so memory usage can grow quickly.
Hashes are created in a deterministic way, so if you seed it with the same value, the hashes will come out the same. Never expose your seed or all your outcomes can be predicted. If you want random series every time, do not provide a seed on construction, a random UUIDv4 will be generated for you.
All parameters passed into the engine at construction time represent the internal state of the engine. As hashes are used, this state changes. State changes can be observed through the onChange callback or polled through the engine.state() function. At any point you can resume where you left off by constructing a new engine and passing in the last state.
When hashes run out, the call to get the next hash will fail and throw an error. Its up to you to handle how to proceed when that happens. You can also peek at the next value to see if its undefined, which means the next call will throw.
#API ##Construction Creating a hash series.
var Provable = require('provable')
//node the actual list of hashes is not passed in as option. This will be generated on engine construction
//based on the config you supply. You can get the list with engine.hashes()
var config = {
id: // a string representing the unique id of this hash series
index: // integer representing which hash in the series will be used next
count: //the number of hashes in this series
seed: //a string to seed nodes crypto engine to start generating the first hash (last hash of series). Withold to seed with random hash.
clientSeed: //an additional seed value supplied, will update the hash before being returned from nextHash()
onChange: //a function callback which returns the state of the engine after every change. Optionally poll state instead with engine.state()
}
var engine = Provable(config)
//do stuff..
##engine() The default function of the engine is to provide a random 32 bit integer. This allows random-js to wrap the engine and produce random primitives. The integer is based on sampling the least significant 32 bits of the current hash.
//integer is random between [0, 2^32] inclusive
//this increments your hash index and will call the change callback
var integer = engine()
##next() This gets the next raw sha256 hash in the series and increments your hash index. Will throw an error if no more hashes are found. If hashes run out, then generate a new engine with a new seed. Do not reuse old seeds as you will generate predictable hashes.
//the next raw sha256 hash in the series. Update hash index and throws if no more hashes found.
var hash = engine.next()
##peek(index) Peek will return a hash without changing engine state. The index parameter is optional and will default to the current engine state index, which is essentially the next hash. If index is provided it will return to you the hash at that index. Returns undefined if you have reached the end of the series or there is no hash at that index.
//the next raw sha256 hash in the series, does not change state of engine.
//will return undefined if no hash is found, or end of series will be reached
//with next call
var nextHash = engine.peek()
//return hash at index 100
var specifiedHash = engine.peek(100)
##generate(count,seed) Generate a raw hash series. This is used internally by the engine but is exposed in case its useful to use directly. Consider it like a static function, does not reference the internal state of the engine. The result is an array of hashes which should be used starting at series[0]. Its important to use the hashes in the correct order, or they will be predictable. If you do not provide a seed, a random one will be generated for you, but you will not have access to the seed. Its best to provide one or generate one using Provable.createSeed().
//generate 10000 hashes and returns an array of them with the seed value of "seed"
var series = require('Provable').generate(10000,'seed')
##createSeed() Returns a random UUIDV4 string created by random-js. Use this to seed your call to Provable.generate.
var Provable = require('Provable')
//results of this will be random every time
var seed = Provable.createSeed()
var series = Provable.generate(1000,seed)