-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathprovable.js
More file actions
73 lines (68 loc) · 2.24 KB
/
Copy pathprovable.js
File metadata and controls
73 lines (68 loc) · 2.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
const assert = require("assert");
const {
md5,
sha256,
ByteGenerator,
floats,
ints,
defaults,
toInteger,
assertSeed,
} = require("./utils");
const MAX_NONCE = Number.MAX_SAFE_INTEGER;
function positiveInteger(value, name) {
const result = toInteger(value, name);
assert(result >= 1, `${name} must be 1 or more`);
return result;
}
// Provable(emit)(config) -> generator
//
// Every draw hashes with the nonce as it currently stands, then advances the
// nonce and emits the new state. A fresh byte stream is opened for each draw,
// so a long-lived instance yields exactly the same results as re-creating one
// from the emitted state before every call. Outcome for nonce N is therefore
// always reproducible from (serverSeed, clientSeed, N) alone.
module.exports =
(emit = (x) => x) =>
(config) => {
config = defaults(config);
function draw(fn) {
assert(config.nonce + 1 < MAX_NONCE, "max nonce, rotate seed.");
const result = fn(ByteGenerator(config));
config.nonce += 1;
emit({ ...config });
return result;
}
return {
// Derives the seeds for the next rotation. The new server seed depends
// on the current one and `salt`; keep `salt` private so a revealed seed
// does not disclose its successor. Nonce and cursor restart at 0.
next(salt, clientSeed) {
assertSeed(salt, "salt");
if (clientSeed) assertSeed(clientSeed, "clientSeed");
return defaults({
clientSeed: clientSeed || md5(`${config.clientSeed}:${salt}`),
serverSeed: sha256(`${config.serverSeed}:${salt}`),
});
},
state() {
return { ...config };
},
floats(count = 1) {
count = positiveInteger(count, "count");
return draw((rng) => floats(rng, count));
},
// `count` integers in [min, min + max - 1]. `max` is the range size.
ints(count, max, min = 0) {
count = positiveInteger(count, "count");
max = positiveInteger(max, "max");
assert(max <= 2 ** 32, "max must be 2^32 or less");
min = toInteger(min, "min");
return draw((rng) => ints(rng, count, max, min));
},
tick() {
draw(() => undefined);
return { ...config };
},
};
};