trac-peer 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "trac-peer",
3
+ "main": "src/index.js",
4
+ "version": "0.0.1",
5
+ "type": "module",
6
+ "dependencies": {
7
+ "autobase": "7.0.45",
8
+ "b4a": "1.6.7",
9
+ "bare-fs": "4.0.1",
10
+ "bip39": "^3.1.0",
11
+ "brittle": "3.0.0",
12
+ "corestore": "7.0.22",
13
+ "debounceify": "1.1.0",
14
+ "ed25519-key-generator": "file:../ed25519-key-generator",
15
+ "hyperbee": "2.23.0",
16
+ "hypercore": "11.0.48",
17
+ "hypercore-crypto": "3.4.0",
18
+ "hyperdht": "6.19.0",
19
+ "hyperswarm": "4.8.4",
20
+ "is-options": "1.0.2",
21
+ "pear-interface": "1.0.0",
22
+ "protomux-wakeup": "^2.2.1",
23
+ "ready-resource": "^1.0.0",
24
+ "safety-catch": "1.0.2",
25
+ "sodium-native": "^4.3.3",
26
+ "xache": "1.2.0"
27
+ }
28
+ }
@@ -0,0 +1,24 @@
1
+ class Contract {
2
+
3
+ constructor(protocol, options = {}) {
4
+ this.protocol = protocol;
5
+ this.storage = null;
6
+ this.options = options;
7
+ }
8
+
9
+ async dispatch(op, node, storage) {
10
+ throw Error('Not implemented: Contract.dispatch(op, node, signed_storage, storage)');
11
+ }
12
+
13
+ async get(key){
14
+ if(typeof this.storage === "undefined" || this.storage === null) throw new Error('get(key): storage undefined');
15
+ return await this.storage.get(key);
16
+ }
17
+
18
+ async put(key, value){
19
+ if(typeof this.storage === "undefined" || this.storage === null) throw new Error('put(key,value): storage undefined');
20
+ return await this.storage.put(key, value);
21
+ }
22
+ }
23
+
24
+ export default Contract;
package/src/feature.js ADDED
@@ -0,0 +1,32 @@
1
+ class Feature {
2
+
3
+ constructor(peer, options = {}) {
4
+ this.peer = peer;
5
+ this.key = '';
6
+ this.options = options;
7
+ }
8
+
9
+ async append(key, value){
10
+ await this.peer.base.append({ type: 'feature', key: this.key + '_' + key, value : {
11
+ dispatch : {
12
+ type : this.key + '_feature',
13
+ key : key,
14
+ value : value
15
+ }
16
+ }});
17
+ }
18
+
19
+ async start(options = {}) {
20
+ throw Error('Not implemented: start(options = {})');
21
+ }
22
+
23
+ async stop(options = {}) {
24
+ throw Error('Not implemented: stop(options = {})');
25
+ }
26
+
27
+ async sleep(ms) {
28
+ return new Promise(resolve => setTimeout(resolve, ms));
29
+ }
30
+ }
31
+
32
+ export default Feature;
@@ -0,0 +1,129 @@
1
+ import BlindPairing from "blind-pairing";
2
+
3
+ export function resolveNumberString(number, decimals){
4
+
5
+ let splitted = number.split(".");
6
+ if(splitted.length == 1 && decimals > 0){
7
+ splitted[1] = '';
8
+ }
9
+ if(splitted.length > 1) {
10
+ let size = decimals - splitted[1].length;
11
+ for (let i = 0; i < size; i++) {
12
+ splitted[1] += "0";
13
+ }
14
+ let new_splitted = '';
15
+ for(let i = 0; i < splitted[1].length; i++)
16
+ {
17
+ if(i >= decimals)
18
+ {
19
+ break;
20
+ }
21
+ new_splitted += splitted[1][i];
22
+ }
23
+ number = "" + (splitted[0] == '0' ? '' : splitted[0]) + new_splitted;
24
+ if(BigInt(number) == 0n || number === ''){
25
+ number = "0";
26
+ }
27
+ }
28
+
29
+ try {
30
+
31
+ while (number.charAt(0) === '0') {
32
+ number = number.substring(1);
33
+ }
34
+
35
+ }catch(e){
36
+
37
+ number = '0';
38
+ }
39
+
40
+ return number === '' ? '0' : number;
41
+ }
42
+
43
+ export function formatNumberString(string, decimals) {
44
+
45
+ let pos = string.length - decimals;
46
+
47
+ if(decimals == 0) {
48
+ // nothing
49
+ }else
50
+ if(pos > 0){
51
+ string = string.substring(0, pos) + "." + string.substring(pos, string.length);
52
+ }else{
53
+ string = '0.' + ( "0".repeat( decimals - string.length ) ) + string;
54
+ }
55
+
56
+ return removeTrailingZeros(string);
57
+ }
58
+
59
+ export function removeTrailingZeros(value) {
60
+ value = value.toString();
61
+ if (value.indexOf('.') === -1) {
62
+ return value;
63
+ }
64
+
65
+ while((value.slice(-1) === '0' || value.slice(-1) === '.') && value.indexOf('.') !== -1) {
66
+ value = value.substr(0, value.length - 1);
67
+ }
68
+ return value;
69
+ }
70
+
71
+
72
+ export function restoreManifest(parsedManifest) {
73
+
74
+ if (Array.isArray(parsedManifest.signers)) {
75
+ parsedManifest.signers = parsedManifest.signers.map(signer => {
76
+ if(signer.namespace && signer.namespace.data &&signer.publicKey && signer.publicKey.data){
77
+ return {
78
+ ...signer,
79
+ namespace: Buffer.from(signer.namespace.data),
80
+ publicKey: Buffer.from(signer.publicKey.data),
81
+ }
82
+ } else {
83
+ return signer;
84
+ }
85
+ });
86
+ }
87
+
88
+ return parsedManifest;
89
+ }
90
+
91
+ export async function setAutoAddWriters(input, peer){
92
+ const splitted = input.split(' ');
93
+ const value = splitted[1];
94
+ if(value !== 'on' && value !== 'off') throw new Error('setAutoAddWriters: use on and off values.');
95
+ const msg = { type: 'setAutoAddWriters', key: value }
96
+ const signature = {
97
+ msg: msg
98
+ };
99
+ const hash = peer.wallet.sign(JSON.stringify(msg));
100
+ signature['hash'] = hash;
101
+ await peer.base.append({type: 'setAutoAddWriters', key: value, value: signature });
102
+ }
103
+
104
+ export async function addAdmin(input, peer){
105
+ const splitted = input.split(' ');
106
+ const publicKey = splitted[1];
107
+ await peer.base.append({ type: 'addAdmin', key: publicKey });
108
+ }
109
+
110
+ export async function addWriter(input, peer){
111
+ const splitted = input.split(' ');
112
+ if(splitted[0] === '/add_indexer'){
113
+ const msg = { type: 'addIndexer', key: splitted[splitted.length - 1] }
114
+ const signature = {
115
+ msg: msg
116
+ };
117
+ const hash = peer.wallet.sign(JSON.stringify(msg));
118
+ signature['hash'] = hash;
119
+ peer.emit('announce', { op : 'append_writer', type: 'addIndexer', key: splitted[splitted.length - 1], value: signature });
120
+ } else if(splitted[0] === '/add_writer') {
121
+ const msg = { type: 'addWriter', key: splitted[splitted.length - 1] }
122
+ const signature = {
123
+ msg: msg
124
+ };
125
+ const hash = peer.wallet.sign(JSON.stringify(msg));
126
+ signature['hash'] = hash;
127
+ peer.emit('announce', { op : 'append_writer', type: 'addWriter', key: splitted[splitted.length - 1], value: signature });
128
+ }
129
+ }
package/src/index.js ADDED
@@ -0,0 +1,372 @@
1
+ /** @typedef {import('pear-interface')} */ /* global Pear */
2
+ import Autobase from 'autobase';
3
+ import Hyperswarm from 'hyperswarm';
4
+ import ReadyResource from 'ready-resource';
5
+ import b4a from 'b4a';
6
+ import Hyperbee from 'hyperbee';
7
+ import readline from 'readline';
8
+ import Corestore from 'corestore';
9
+ import {createHash} from "node:crypto";
10
+ import w from 'protomux-wakeup';
11
+ const wakeup = new w();
12
+ import {addWriter, addAdmin, setAutoAddWriters} from "./functions.js";
13
+ export {default as Protocol} from "./protocol.js";
14
+ export {default as Contract} from "./contract.js";
15
+ export {default as Feature} from "./feature.js";
16
+ export {default as Wallet} from "./wallet.js";
17
+
18
+ export class Peer extends ReadyResource {
19
+
20
+ constructor(options = {}) {
21
+ super();
22
+ this.STORES_DIRECTORY = options.stores_directory;
23
+ this.KEY_PAIR_PATH = `${this.STORES_DIRECTORY}${options.store_name}/keypair.json`;
24
+ this.keyPair = null;
25
+ this.store = new Corestore(this.STORES_DIRECTORY + options.store_name);
26
+ this.msb = options.msb || null;
27
+ this.swarm = null;
28
+ this.base = null;
29
+ this.key = null;
30
+ this.tx_pool = {};
31
+ this.writerLocalKey = null;
32
+ this.tx_pool_max_size = options.tx_pool_max_size || 1_000;
33
+ this.max_tx_delay = options.max_tx_delay || 60;
34
+ this.bootstrap = options.bootstrap || null;
35
+ this.protocol = options.protocol || null;
36
+ this.contract = options.contract || null;
37
+ this.wallet = options.wallet || null;
38
+ this.features = options.features || [];
39
+ this.protocol_instance = null;
40
+ this.contract_instance = null;
41
+ this.channel = Buffer.alloc(32).fill(options.channel) || null;
42
+ this.tx_channel = Buffer.alloc(32).fill(options.tx_channel) || null;
43
+ this.bee = null;
44
+ this.replicate = options.replicate !== false;
45
+ this.connectedNodes = 1;
46
+ this.isStreaming = false;
47
+ this.connectedPeers = new Set();
48
+ this.options = options;
49
+
50
+ this.tx_observer();
51
+ this.nodeListener();
52
+ this._boot();
53
+ this.ready().catch(noop);
54
+ }
55
+
56
+ async _boot() {
57
+ const _this = this;
58
+ this.base = new Autobase(this.store, this.bootstrap, {
59
+ valueEncoding: 'json',
60
+
61
+ open(store) {
62
+ _this.bee = new Hyperbee(store.get('view'), {
63
+ extension: false,
64
+ keyEncoding: 'utf-8',
65
+ valueEncoding: 'json'
66
+ })
67
+ return _this.bee;
68
+ },
69
+
70
+ apply: async (nodes, view, base) => {
71
+ if(this.contract_instance === null) await this.initContract();
72
+
73
+ for (const node of nodes) {
74
+ const op = node.value;
75
+ if (op.type === 'tx') {
76
+ const msb_view_session = _this.msb.base.view.checkout(op.value.msbsl);
77
+ const post_tx = await msb_view_session.get(op.key);
78
+ await msb_view_session.close();
79
+
80
+ if (null !== post_tx &&
81
+ null === await view.get(op.key) &&
82
+ op.key === post_tx.value.tx &&
83
+ post_tx.value.ch === createHash('sha256').update(JSON.stringify(op.value.dispatch)).digest('hex')) {
84
+ await view.put(op.key, op.value);
85
+ await _this.contract_instance.dispatch(op, node, view);
86
+ console.log(`${op.key} appended`);
87
+ }
88
+ } else if (op.type === 'feature') {
89
+ await _this.contract_instance.dispatch(op, node, view);
90
+ console.log(`Feature ${op.key} appended`);
91
+ } else if (op.type === 'addIndexer') {
92
+ const admin = await view.get('admin');
93
+ if(null !== admin && op.value.msg.key === op.key && op.value.msg.type === 'addIndexer') {
94
+ const verified = _this.wallet.verify(JSON.stringify(op.value.msg), op.value.hash, admin.value);
95
+ if(verified){
96
+ const writerKey = b4a.from(op.key, 'hex');
97
+ await base.addWriter(writerKey);
98
+ console.log(`Indexer added: ${op.key}`);
99
+ }
100
+ }
101
+ } else if (op.type === 'addWriter') {
102
+ const admin = await view.get('admin');
103
+ if(null !== admin && op.value.msg.key === op.key && op.value.msg.type === 'addWriter') {
104
+ const verified = _this.wallet.verify(JSON.stringify(op.value.msg), op.value.hash, admin.value);
105
+ if(verified){
106
+ const writerKey = b4a.from(op.key, 'hex');
107
+ await base.addWriter(writerKey, { isIndexer : false });
108
+ console.log(`Writer added: ${op.key}`);
109
+ }
110
+ }
111
+ } else if (op.type === 'setAutoAddWriters') {
112
+ const admin = await view.get('admin');
113
+ if(null !== admin && op.value.msg.key === op.key &&
114
+ op.value.msg.type === 'setAutoAddWriters' &&
115
+ (op.key === 'on' || op.key === 'off')) {
116
+ const verified = _this.wallet.verify(JSON.stringify(op.value.msg), op.value.hash, admin.value);
117
+ if(verified){
118
+ await view.put('auto_add_writers', op.key);
119
+ console.log(`Set auto_add_writers: ${op.key}`);
120
+ }
121
+ }
122
+ } else if (op.type === 'autoAddWriter') {
123
+ const auto_add_writers = await view.get('auto_add_writers');
124
+ if(null !== auto_add_writers && auto_add_writers.value === 'on'){
125
+ const writerKey = b4a.from(op.key, 'hex');
126
+ await base.addWriter(writerKey, { isIndexer : false });
127
+ }
128
+ console.log(`Writer auto added: ${op.key}`);
129
+ } else if (op.type === 'addAdmin') {
130
+ const bootstrap = Buffer(node.from.key).toString('hex')
131
+ if(null === await view.get('admin') && bootstrap === _this.bootstrap){
132
+ await view.put('admin', op.key);
133
+ console.log(`Admin added: ${op.key}`);
134
+ }
135
+ }
136
+ }
137
+ }
138
+ })
139
+ this.base.on('warning', (e) => console.log(e))
140
+ }
141
+
142
+ async _open() {
143
+ await this.base.ready();
144
+ await this.wallet.initKeyPair(this.KEY_PAIR_PATH);
145
+ this.writerLocalKey = b4a.toString(this.base.local.key, 'hex');
146
+ if(!this.init_contract_starting){
147
+ await this.initContract();
148
+ }
149
+ if (this.replicate) await this._replicate();
150
+ await this.txChannel();
151
+ const auto_add_writers = await this.base.view.get('auto_add_writers');
152
+ if(!this.base.writable && null !== auto_add_writers && auto_add_writers.value === 'on'){
153
+ this.emit('announce', {
154
+ op : 'auto-add-writer',
155
+ type : 'autoAddWriter',
156
+ key : this.writerLocalKey
157
+ });
158
+ }
159
+ }
160
+
161
+ async initContract(){
162
+ this.init_contract_starting = true;
163
+ this.protocol_instance = new this.protocol({
164
+ peer : this,
165
+ base : this.base,
166
+ local_db : this.bee
167
+ });
168
+ this.contract_instance = new this.contract(this.protocol_instance);
169
+ }
170
+
171
+ async close() {
172
+ if (this.swarm) {
173
+ await this.swarm.destroy();
174
+ }
175
+ await this.base.close();
176
+ }
177
+
178
+ async txChannel() {
179
+ const _this = this;
180
+ this.tx_swarm = new Hyperswarm({ maxPeers: 1024, maxParallel: 512, maxServerConnections: 256 });
181
+
182
+ this.tx_swarm.on('connection', async (connection, peerInfo) => {
183
+ const peerName = b4a.toString(connection.remotePublicKey, 'hex');
184
+ this.connectedPeers.add(peerName);
185
+ this.connectedNodes++;
186
+
187
+ connection.on('close', () => {
188
+ this.connectedNodes--;
189
+ this.connectedPeers.delete(peerName);
190
+ });
191
+
192
+ connection.on('error', (error) => { });
193
+
194
+ _this.on('tx', async (msg) => {
195
+ if(Object.keys(_this.tx_pool).length < _this.tx_pool_max_size && !_this.tx_pool[msg.tx]){
196
+ await connection.write(JSON.stringify(msg))
197
+ msg['ts'] = Math.floor(Date.now() / 1000);
198
+ _this.tx_pool[msg.tx] = msg;
199
+ }
200
+ });
201
+ });
202
+
203
+ const channelBuffer = this.tx_channel;
204
+ this.tx_swarm.join(channelBuffer, { server: true, client: true });
205
+ await this.tx_swarm.flush();
206
+ console.log('Joined MSB TX channel');
207
+ }
208
+
209
+ async tx_observer(){
210
+ while(true){
211
+ const ts = Math.floor(Date.now() / 1000);
212
+ for(let tx in this.tx_pool){
213
+ if(ts - this.tx_pool[tx].ts > this.max_tx_delay){
214
+ console.log('Dropping TX', tx);
215
+ delete this.tx_pool[tx];
216
+ delete this.protocol_instance.prepared_transactions_content[tx];
217
+ continue;
218
+ }
219
+ const msbsl = this.msb.base.view.core.signedLength;
220
+ const view_session = this.msb.base.view.checkout(msbsl);
221
+ const msb_tx = await view_session.get(tx);
222
+ await view_session.close();
223
+ if(null !== msb_tx){
224
+ msb_tx['dispatch'] = this.protocol_instance.prepared_transactions_content[tx];
225
+ msb_tx['msbsl'] = msbsl;
226
+ delete this.tx_pool[tx];
227
+ delete this.protocol_instance.prepared_transactions_content[tx];
228
+ await this.base.append({ type: 'tx', key: tx, value: msb_tx });
229
+ }
230
+ await this.sleep(5);
231
+ }
232
+ await this.sleep(10);
233
+ }
234
+ }
235
+
236
+ async sleep(ms) {
237
+ return new Promise(resolve => setTimeout(resolve, ms));
238
+ }
239
+
240
+ async _replicate() {
241
+ if (!this.swarm) {
242
+ const keyPair = await this.store.createKeyPair('hyperswarm');
243
+ this.swarm = new Hyperswarm({ keyPair });
244
+
245
+ console.log(`Writer key: ${this.writerLocalKey}`)
246
+
247
+ this.swarm.on('connection', async (connection, peerInfo) => {
248
+ const peerName = b4a.toString(connection.remotePublicKey, 'hex');
249
+ this.connectedPeers.add(peerName);
250
+ wakeup.addStream(connection);
251
+ this.store.replicate(connection);
252
+ this.connectedNodes++;
253
+
254
+ connection.on('close', () => {
255
+ this.connectedNodes--;
256
+ this.connectedPeers.delete(peerName);
257
+ });
258
+
259
+ connection.on('error', (error) => { });
260
+
261
+ connection.on('data', async (msg) => {
262
+ try{
263
+ msg = JSON.parse(msg);
264
+ if(msg.op && msg.op === 'append_writer' && this.base.localWriter.isActive &&
265
+ this.writerLocalKey !== msg.key) {
266
+ await this.base.append(msg);
267
+ } else if(msg.op && msg.op === 'auto-add-writer' && this.base.localWriter.isActive &&
268
+ this.writerLocalKey !== msg.key) {
269
+ await this.base.append(msg);
270
+ }
271
+ } catch(e){ }
272
+ });
273
+
274
+ this.on('announce', async function(msg){
275
+ await connection.write(JSON.stringify(msg))
276
+ });
277
+
278
+ if (!this.isStreaming) {
279
+ this.emit('readyNode');
280
+ }
281
+ });
282
+
283
+ const channelBuffer = this.channel;
284
+ this.swarm.join(channelBuffer, { server: true, client: true });
285
+ await this.swarm.flush();
286
+ console.log('Joined channel');
287
+ }
288
+ }
289
+
290
+ nodeListener() {
291
+ this.on('readyNode', async () => {
292
+ if (!this.isStreaming) {
293
+ this.isStreaming = true;
294
+ }
295
+ });
296
+ }
297
+
298
+ async verifyDag() {
299
+ try {
300
+ console.log('--- DAG Monitoring ---');
301
+ const dagView = await this.base.view.core.treeHash();
302
+ const lengthdagView = this.base.view.core.length;
303
+ const dagSystem = await this.base.system.core.treeHash();
304
+ const lengthdagSystem = this.base.system.core.length;
305
+ console.log('this.base.view.core.signedLength:', this.base.view.core.signedLength);
306
+ console.log("this.base.signedLength", this.base.signedLength);
307
+ console.log("this.base.linearizer.indexers.length", this.base.linearizer.indexers.length);
308
+ console.log("this.base.indexedLength", this.base.indexedLength);
309
+ //console.log("this.base.system.core", this.base.system.core);
310
+ console.log(`writerLocalKey: ${this.writerLocalKey}`);
311
+ console.log(`base.key: ${this.base.key.toString('hex')}`);
312
+ console.log('discoveryKey:', b4a.toString(this.base.discoveryKey, 'hex'));
313
+
314
+ console.log(`VIEW Dag: ${dagView.toString('hex')} (length: ${lengthdagView})`);
315
+ console.log(`SYSTEM Dag: ${dagSystem.toString('hex')} (length: ${lengthdagSystem})`);
316
+
317
+ } catch (error) {
318
+ console.error('Error during DAG monitoring:', error.message);
319
+ }
320
+ }
321
+
322
+ async interactiveMode() {
323
+ const rl = readline.createInterface({
324
+ input: process.stdin,
325
+ output: process.stdout,
326
+ });
327
+
328
+ console.log('Node started. Available commands:');
329
+ console.log('- /add_admin: only once on bootstrap node: enter a wallet public key to assign admin rights to it. Admin rights are required to set indexers and writers.');
330
+ console.log('- /add_indexer: enter a peer writer key as argument to get included as indexer for this network.');
331
+ console.log('- /add_writer: enter a peer writer key as argument to get included as writer.');
332
+ console.log('- /set_auto_add_writers: use "on" or "off" as 2nd parameter to allow/disallow peers automatically being added as writers.');
333
+ console.log('- /dag: check system properties such as writer key, DAG, etc.');
334
+ console.log('- /get_keys: prints the signing key pair');
335
+ console.log('- /exit: Exit the program');
336
+ this.protocol_instance.printOptions();
337
+
338
+ rl.on('line', async (input) => {
339
+ switch (input) {
340
+ case '/dag':
341
+ await this.verifyDag();
342
+ break;
343
+ case '/exit':
344
+ console.log('Exiting...');
345
+ rl.close();
346
+ await this.close();
347
+ process.exit(0);
348
+ case '/get_keys':
349
+ console.log("Public Key: ", this.wallet.publicKey.toString('hex'));
350
+ console.log("Secret Key: ", this.wallet.secretKey.toString('hex'));
351
+ break;
352
+ default:
353
+ if (input.startsWith('/add_indexer') || input.startsWith('/add_writer')) {
354
+ await addWriter(input, this);
355
+ } else if (input.startsWith('/add_admin')) {
356
+ await addAdmin(input, this);
357
+ } else if (input.startsWith('/set_auto_add_writers')) {
358
+ await setAutoAddWriters(input, this);
359
+ } else {
360
+ this.protocol_instance.execute(input);
361
+ }
362
+ }
363
+ rl.prompt();
364
+ });
365
+
366
+ rl.prompt();
367
+ }
368
+ }
369
+
370
+ function noop() { }
371
+
372
+ export default Peer;
@@ -0,0 +1,78 @@
1
+ import { formatNumberString, resolveNumberString } from "./functions.js";
2
+ import {createHash} from "node:crypto";
3
+
4
+ class Protocol{
5
+ constructor(options = {}) {
6
+ this.base = options.base || null;
7
+ this.peer = options.peer || null;
8
+ this.local_db = options.local_db || null;
9
+ this.options = options;
10
+ this.input = null;
11
+ this.tokenized_input = null;
12
+ this.fromBigIntString = formatNumberString;
13
+ this.toBigIntString = resolveNumberString;
14
+ this.nonce = 0;
15
+ this.prepared_transactions_content = {};
16
+ this.features = {};
17
+ }
18
+
19
+ async addFeature(key, feature){
20
+ const pk1 = this.peer.wallet.publicKey.toString('hex');
21
+ const pk2 = await this.base.view.get('admin');
22
+ if(null === pk2 || pk1 !== pk2.value) throw new Error('addFeature(key, feature): Features only allowed for admin.');
23
+ if(typeof this.features[key] !== "undefined") throw new Error('addFeature(key, feature): Feature key exists already.');
24
+ feature.key = key;
25
+ this.features[key] = feature;
26
+ }
27
+
28
+ async broadcastTransaction(writer, obj){
29
+ if((this.peer.wallet.publicKey !== null &&
30
+ this.peer.wallet.secretKey !== null) &&
31
+ this.base.localWriter !== null && this.tokenized_input !== null)
32
+ {
33
+ this.nonce = Date.now();
34
+ const MSBwriter = writer;
35
+ const content_hash = createHash('sha256').update(JSON.stringify(obj)).digest('hex');
36
+ let tx = createHash('sha256').update(
37
+ MSBwriter + '-' +
38
+ this.peer.writerLocalKey + '-' +
39
+ this.peer.wallet.publicKey + '-' +
40
+ content_hash + '-' +
41
+ this.nonce).digest('hex');
42
+ tx = createHash('sha256').update(tx).digest('hex');
43
+ const signature = this.peer.wallet.sign(tx);
44
+ this.peer.emit('tx', {
45
+ op: 'pre-tx',
46
+ tx: tx,
47
+ is: signature,
48
+ w: MSBwriter,
49
+ i: this.peer.writerLocalKey,
50
+ ipk: this.peer.wallet.publicKey.toString('hex'),
51
+ ch : content_hash,
52
+ in : this.nonce
53
+ });
54
+ this.prepared_transactions_content[tx] = obj;
55
+ } else {
56
+ throw Error('broadcastTransaction(writer, obj): Cannot prepare transaction. Please make sure inputs and local writer are set.');
57
+ }
58
+ }
59
+
60
+ async tokenizeInput(input){
61
+ this.input = input;
62
+ if(typeof input === "string"){
63
+ this.tokenized_input = input.split(' ').map(function(item) {
64
+ return item.trim();
65
+ });
66
+ }
67
+ }
68
+
69
+ async execute(input){
70
+ throw new Error('Not implemented: Protocol.execute(input)');
71
+ }
72
+
73
+ async printOptions(){
74
+ throw new Error('Not implemented: Protocol.printOptions()');
75
+ }
76
+ }
77
+
78
+ export default Protocol;
package/src/wallet.js ADDED
@@ -0,0 +1,9 @@
1
+ import PeerWallet from "ed25519-key-generator"
2
+
3
+ class Wallet extends PeerWallet{
4
+ constructor() {
5
+ super();
6
+ }
7
+ }
8
+
9
+ export default Wallet;