Skip to content

Commit e83f737

Browse files
committed
random beacon - WIP
1 parent f773677 commit e83f737

File tree

6 files changed

+325
-51
lines changed

6 files changed

+325
-51
lines changed

examples/chat/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ async function main() {
108108

109109
let id = await createIdentity(resources, name);
110110

111-
console.log()
111+
console.log();
112112
console.log('Press enter to create a chat room, or input the 3 code words to join an existing one.');
113113
console.log();
114114

examples/randomness-beacon/index.ts

Lines changed: 123 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,134 @@
1-
import { Beacon } from './model/Beacon';
1+
2+
3+
import { Identity } from 'data/identity';
4+
import { RSAKeyPair } from 'data/identity';
5+
26
import { RNGImpl } from 'crypto/random';
37

8+
9+
10+
import { Space } from 'spaces/Space';
11+
import { Resources } from 'data/model';
12+
13+
import { Store } from 'storage/store';
14+
import { MemoryBackend } from 'storage/backends';
15+
16+
import { Mesh } from 'mesh/service';
17+
import { IdentityPeer } from 'mesh/agents/peer';
18+
19+
import { Beacon } from './model/Beacon';
20+
import { BeaconValueOp } from './model/BeaconValueOp';
21+
22+
import * as readline from 'readline';
23+
24+
425
const STEPS = 66;
526

27+
function initResources(): Resources {
28+
return { store: new Store(new MemoryBackend(new RNGImpl().randomHexString(128))), mesh: new Mesh(), config: {}, aliasing: new Map()};
29+
}
30+
31+
async function createBeaconSpace(resources: Resources, steps=STEPS): Promise<Space> {
32+
console.log();
33+
console.log('Generating new randomness beacon...');
34+
let beacon = new Beacon(new RNGImpl().randomHexString(160), steps);
35+
36+
const keyPair = RSAKeyPair.generate(1024);
37+
const localIdentity = Identity.fromKeyPair({}, keyPair);
38+
console.log('Generated keys.')
39+
40+
resources.config.id = localIdentity;
41+
42+
let endpoint = (await IdentityPeer.fromIdentity(localIdentity).asPeer()).endpoint;
43+
44+
let space = Space.fromEntryPoint(beacon, resources, endpoint);
45+
space.startBroadcast();
46+
47+
await resources.store.save(beacon);
48+
49+
beacon.setResources(resources);
50+
beacon.startSync();
51+
52+
console.log();
53+
console.log('Beacon is ready, wordcode is:');
54+
console.log();
55+
console.log((await space.getWordCoding()).join(' '));
56+
console.log();
57+
58+
return space;
59+
}
60+
61+
async function joinBeaconSpace(resources: Resources, wordcode: string[]): Promise<Space> {
62+
const keyPair = RSAKeyPair.generate(1024);
63+
const localIdentity = Identity.fromKeyPair({}, keyPair);
64+
resources.config.id = localIdentity;
65+
console.log();
66+
console.log('Generated keys.')
67+
68+
69+
let endpoint = (await IdentityPeer.fromIdentity(localIdentity).asPeer()).endpoint;
70+
let space = Space.fromWordCode(wordcode, resources, endpoint);
71+
72+
console.log();
73+
console.log('Trying to join randomness beacon with word code "' + wordcode.join(' ') + '"...');
74+
await space.entryPoint;
75+
console.log('Done.');
76+
console.log();
77+
78+
space.startBroadcast();
79+
let beacon = await space.getEntryPoint();
80+
81+
await resources.store.save(beacon);
82+
83+
beacon.setResources(resources);
84+
beacon.startSync();
85+
86+
return space;
87+
}
88+
689
async function main() {
790

8-
console.log('creating random beacon...');
91+
await BeaconValueOp.vdfInit();
92+
93+
let resources = initResources();
94+
95+
let rl = readline.createInterface({
96+
input: process.stdin,
97+
output: process.stdout
98+
});
99+
100+
console.log();
101+
console.log('Press enter to create a new beacon, or input the 3 code words to join computing an existing one.');
102+
console.log();
103+
104+
let command = await new Promise((resolve: (text: string) => void/*, reject: (reason: any) => void*/) => {
105+
rl.question('>', (command: string) => {
106+
resolve(command);
107+
});
108+
});
109+
110+
let space: Space;
111+
if (command.trim() === '') {
112+
113+
space = await createBeaconSpace(resources);
114+
115+
} else {
116+
117+
let wordcode: string[] = command.split(' ');
118+
119+
if (wordcode.length !== 3) {
120+
console.log('expected 3 words, like: pineapple greatness flurry');
121+
console.log('cannot join beacon, exiting.');
122+
process.exit();
123+
}
124+
125+
space = await joinBeaconSpace(resources, wordcode);
126+
}
9127

10-
let beacon = new Beacon(new RNGImpl().randomHexString(128), STEPS);
128+
let beacon = await space.getEntryPoint() as Beacon;
11129

12-
//let res = await Beacon.computeVdfAsync(STEPS, new RNGImpl().randomHexString(128));
130+
console.log('Starting VDF computation...');
131+
console.log();
13132

14133
beacon.startCompute();
15134

examples/randomness-beacon/model/Beacon.ts

Lines changed: 118 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
11
import '@hyper-hyper-space/node-env';
22

3-
import { Hashing, HashedObject, MutableObject, MutationOp } from 'data/model';
3+
import { Hashing, HashReference, HashedObject, MutableObject, MutationOp } from 'data/model';
4+
5+
import { Identity } from 'data/identity';
6+
7+
8+
import { SpaceEntryPoint } from 'spaces/SpaceEntryPoint';
9+
10+
import { Mesh } from 'mesh/service';
11+
import { LinkupManager } from 'net/linkup';
12+
import { ObjectDiscoveryPeerSource } from 'mesh/agents/peer';
13+
import { PeerGroupInfo } from 'mesh/service';
14+
import { IdentityPeer } from 'mesh/agents/peer';
415

516
import { BeaconValueOp } from './BeaconValueOp';
617

7-
import { Worker, parentPort } from 'worker_threads';
18+
import { Worker } from 'worker_threads';
819
import { Logger, LogLevel } from 'util/logging';
920

10-
const createVdf = require('@subspace/vdf').default;
11-
(global as any).document = { }; // yikes!
12-
13-
class Beacon extends MutableObject {
21+
class Beacon extends MutableObject implements SpaceEntryPoint {
1422

1523
static log = new Logger(Beacon.name, LogLevel.DEBUG)
1624

@@ -24,26 +32,11 @@ class Beacon extends MutableObject {
2432
_values: string[];
2533

2634
_computation?: Worker;
35+
_computationTermination?: Promise<Number>;
2736
_autoCompute: boolean;
2837

29-
static computeVdf(): void {
30-
31-
parentPort?.on('message', async (q: {challenge: string, steps: number}) => {
32-
33-
const vdfInstance = await createVdf();
34-
const result = vdfInstance.generate(q.steps, Buffer.from(q.challenge, 'hex'), 2048, true);
35-
36-
parentPort?.postMessage(
37-
{
38-
challenge: q.challenge,
39-
steps: q.steps,
40-
result: Buffer.from(result).toString('hex')
41-
}
42-
);
43-
});
44-
45-
46-
}
38+
_mesh?: Mesh;
39+
_peerGroup?: PeerGroupInfo;
4740

4841
constructor(seed?: string, steps?: number) {
4942
super(Beacon.opClasses);
@@ -64,7 +57,7 @@ class Beacon extends MutableObject {
6457

6558
stopCompute() {
6659
this._autoCompute = false;
67-
this.stopCompute();
60+
this.stopRace();
6861
}
6962

7063
race() {
@@ -73,35 +66,58 @@ class Beacon extends MutableObject {
7366
Beacon.log.debug(() => 'Racing for challenge (' + this.steps + ' steps): "' + this.currentChallenge() + '".');
7467

7568
this._computation = new Worker('./dist-examples/examples/randomness-beacon/model/worker.js');
76-
77-
this._computation.postMessage({steps: this.steps, challenge: this.currentChallenge()});
78-
69+
console.log('is death immediate?')
70+
this._computation.on('online', () => {console.log('worker is online')});
71+
this._computation.on('error', (err: Error) => { console.log('ERR');console.log(err)});
72+
this._computation.on('exit', (exitCode: number) => {
73+
console.log('worker exited with ' + exitCode);
74+
})
75+
console.log('created worker')
7976
this._computation.on('message', async (msg: {challenge: string, result: string}) => {
8077

8178
Beacon.log.debug(() => 'Solved challenge "' + msg.challenge + '" with: "' + msg.result + '".');
8279

83-
this.stopRace();
80+
8481

8582
if (msg.challenge === this.currentChallenge()) {
8683
let op = new BeaconValueOp(this, this.currentSeq(), msg.result);
8784

8885
if (this._lastOp !== undefined) {
8986
op.setPrevOps(new Set([this._lastOp.createReference()]).values());
87+
} else {
88+
op.setPrevOps(new Set<HashReference<BeaconValueOp>>().values());
9089
}
9190

9291
await this.applyNewOp(op);
93-
if (this._autoCompute) {
94-
this.race();
95-
}
92+
await this.getStore().save(this);
93+
94+
} else {
95+
console.log('mismatched challenge');
9696
}
9797
});
98+
this._computation.postMessage({steps: this.steps, challenge: this.currentChallenge()});
99+
console.log('posted message to worker')
100+
101+
} else {
102+
console.log('race was called but a computation is running');
98103
}
99104
}
100105

101106
stopRace() {
107+
console.log('stopRace()');
102108
if (this._computation !== undefined) {
103-
this._computation.terminate();
104-
this._computation = undefined;
109+
if (this._computationTermination === undefined) {
110+
console.log('need to stop')
111+
this._computationTermination = this._computation.terminate().then(
112+
(ret: number) => {
113+
console.log('stopped');
114+
this._computation = undefined;
115+
this._computationTermination = undefined;
116+
return ret;
117+
}
118+
);
119+
120+
}
105121
}
106122
}
107123

@@ -131,7 +147,12 @@ class Beacon extends MutableObject {
131147
if (this._lastOp === undefined ||
132148
!this._lastOp.equals(op)) {
133149

134-
if (op.prevOps?.size() === 0) {
150+
if (op.prevOps === undefined) {
151+
throw new Error('BeaconValueOp must have a defined prevOps set (even if it is empty).');
152+
}
153+
154+
155+
if (op.prevOps.size() === 0) {
135156

136157
if (this._lastOp !== undefined) {
137158
throw new Error('Initial BeaconValueOp received, but there are already other ops in this beacon.');
@@ -142,7 +163,7 @@ class Beacon extends MutableObject {
142163
throw new Error('Non-initial BeaconValueOp received, but there are no values in this beacon.');
143164
}
144165

145-
if (!this._lastOp.equals(op.prevOps?.values().next().value)) {
166+
if (!this._lastOp.hash() === op.prevOps.values().next().value.hash) {
146167
throw new Error('Received BeaconValueOp does not point to last known beacon value.');
147168
}
148169
}
@@ -151,10 +172,16 @@ class Beacon extends MutableObject {
151172

152173
this._values.push(Hashing.toHex(op.hash()));
153174

154-
this.stopRace();
155-
156175
if (this._autoCompute) {
157-
this.race();
176+
if (this._computation === undefined) {
177+
console.log('computation was finished');
178+
this.race();
179+
} else {
180+
console.log('chaining');
181+
this.stopRace();
182+
this._computationTermination?.then(() => { console.log('finished now!');this.race(); });
183+
}
184+
158185
}
159186

160187
}
@@ -178,6 +205,58 @@ class Beacon extends MutableObject {
178205
return this.steps !== undefined && this.getId() !== undefined;
179206
}
180207

208+
async startSync(): Promise<void> {
209+
210+
let resources = this.getResources();
211+
212+
if (resources === undefined) {
213+
throw new Error('Cannot start sync: resources not configured.');
214+
}
215+
216+
this._mesh = resources.mesh;
217+
218+
if (this._mesh === undefined) {
219+
throw new Error('Cannot start sync: mesh is missing from configured resources.');
220+
}
221+
222+
let linkupServers = resources.config.linkupServers === undefined?
223+
[LinkupManager.defaultLinkupServer] : resources.config.linkupServer as string[];
224+
225+
226+
let localIdentity = resources.config.id as Identity;
227+
228+
const localPeer = await new IdentityPeer(linkupServers[0] as string, localIdentity.hash(), localIdentity).asPeer();
229+
230+
this._mesh.startObjectBroadcast(this, linkupServers, [localPeer.endpoint]);
231+
232+
let peerSource = new ObjectDiscoveryPeerSource(this._mesh, this, linkupServers, localPeer.endpoint, IdentityPeer.getEndpointParser(resources.store));
233+
234+
this._peerGroup = {
235+
id: 'sync-for-' + this.hash(),
236+
localPeer: localPeer,
237+
peerSource: peerSource
238+
}
239+
240+
this._mesh.joinPeerGroup(this._peerGroup);
241+
this._mesh.syncObjectWithPeerGroup(this._peerGroup.id, this);
242+
243+
this.loadAndWatchForChanges();
244+
}
245+
246+
async stopSync(): Promise<void> {
247+
248+
const peerGroupId = this._peerGroup?.id as string;
249+
250+
this._mesh?.stopSyncObjectWithPeerGroup(peerGroupId, this.hash());
251+
this._mesh?.stopObjectBroadcast(this.hash());
252+
this._mesh?.leavePeerGroup(peerGroupId);
253+
254+
this._mesh = undefined;
255+
this._peerGroup = undefined;
256+
}
257+
181258
}
182259

260+
HashedObject.registerClass(Beacon.className, Beacon);
261+
183262
export { Beacon };

0 commit comments

Comments
 (0)