trac-peer 0.1.43 → 0.1.44

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "trac-peer",
3
3
  "main": "src/index.js",
4
- "version": "0.1.43",
4
+ "version": "0.1.44",
5
5
  "type": "module",
6
6
  "dependencies": {
7
7
  "assert": "npm:bare-node-assert",
package/src/api.js CHANGED
@@ -1,6 +1,146 @@
1
+ import b4a from "b4a";
2
+ import {jsonStringify} from "./functions.js";
3
+
1
4
  export class ProtocolApi{
2
- constructor(options = {}) {
3
- this.peer = options.peer || null;
5
+ constructor(peer, options = {}) {
6
+ this.peer = peer;
7
+ this.api_tx_exposed = options.api_tx_exposed === true;
8
+ this.api_msg_exposed = options.api_msg_exposed === true;
9
+ this.options = options;
10
+ }
11
+
12
+ getPeerValidatorAddress(){
13
+ return this.peer.validator;
14
+ }
15
+
16
+ getPeerBootstrap(){
17
+ return this.peer.bootstrap;
18
+ }
19
+
20
+ getPeerMsbBootstrap(){
21
+ return this.peer.msb.bootstrap;
22
+ }
23
+
24
+ getPeerWriterKey(){
25
+ return this.peer.writerLocalKey;
26
+ }
27
+
28
+ generateNonce(){
29
+ return this.peer.protocol_instance.generateNonce();
30
+ }
31
+
32
+ async prepareMessage(msg, address, reply_to = null, attachments = []){
33
+ if(typeof msg !== 'string') throw new Error('Msg must be a string');
34
+ if(b4a.byteLength(jsonStringify(address)) > 64) throw new Error('Address too large.');
35
+ if(b4a.byteLength(jsonStringify(reply_to)) > 256) throw new Error('Reply to too large.');
36
+ if(reply_to !== null && isNaN(parseInt(reply_to))) throw new Error('Reply to not a number.');
37
+ if(false === Array.isArray(attachments)) throw new Error('attachments must be an array.');
38
+ if(attachments.length > 20) throw new Error('Too many attachments');
39
+ for(let i = 0; i < attachments.length; i++){
40
+ if(typeof attachments[i] !== 'string') throw new Error('Attachment at index ' + i + ' is not a string.');
41
+ }
42
+ const prepared = {
43
+ dispatch : {
44
+ type : 'msg',
45
+ msg: msg,
46
+ address : address,
47
+ attachments : attachments,
48
+ deleted_by : null,
49
+ reply_to : reply_to !== null ? parseInt(reply_to) : null,
50
+ pinned : false,
51
+ pin_id : null
52
+ }};
53
+ if(b4a.byteLength(jsonStringify(prepared)) > this.peer.protocol_instance.msgMaxBytes()) throw new Error('Message too large.');
54
+ return prepared;
55
+ }
56
+
57
+ async post(prepared_message, signature, nonce){
58
+ if(false === this.api_msg_exposed) throw new Error('Posting messages not exposed in API.');
59
+ if(b4a.byteLength(jsonStringify(prepared_message)) > this.peer.protocol_instance.msgMaxBytes()) throw new Error('Prepared message too large.');
60
+ if(typeof prepared_message !== 'object') throw new Error('Prepared message must be an object generated with api.prepareMessage().');
61
+ if(prepared_message.dispatch === undefined || prepared_message.dispatch.type === undefined ||
62
+ prepared_message.dispatch.msg === undefined || prepared_message.dispatch.address === undefined ||
63
+ prepared_message.dispatch.attachments === undefined || prepared_message.dispatch.deleted_by === undefined ||
64
+ prepared_message.dispatch.reply_to === undefined || prepared_message.dispatch.pinned === undefined ||
65
+ prepared_message.dispatch.pin_id === undefined) throw new Error('Invalid prepared message.');
66
+ if(prepared_message.dispatch.type !== 'msg') throw new Error('Invalid type.');
67
+ if(typeof prepared_message.dispatch.msg !== 'string') throw new Error('Msg must be a string');
68
+ if(b4a.toString(b4a.from(prepared_message.dispatch.address, 'hex'), 'hex') !== prepared_message.dispatch.address) throw new Error('Invalid address.');
69
+ if(false === Array.isArray(prepared_message.dispatch.attachments)) throw new Error('attachments must be an array.');
70
+ if(prepared_message.dispatch.attachments.length > 20) throw new Error('Too many attachments');
71
+ for(let i = 0; i < prepared_message.dispatch.attachments.length; i++){
72
+ if(typeof prepared_message.dispatch.attachments[i] !== 'string') throw new Error('Attachment at index ' + i + ' is not a string.');
73
+ }
74
+ if(prepared_message.dispatch.deleted_by !== null) throw new Error('deleted_by must be null');
75
+ if(prepared_message.dispatch.reply_to !== null && isNaN(parseInt(prepared_message.dispatch.reply_to))) throw new Error('Reply to not a number.');
76
+ if(prepared_message.dispatch.pinned !== false) throw new Error('pinned must be false');
77
+ if(prepared_message.dispatch.pin_id !== null) throw new Error('pin_id must be null');
78
+ const verified = this.peer.wallet.verify(signature, JSON.stringify(prepared_message) + nonce, prepared_message.dispatch.address);
79
+ if(false === verified) throw new Error('Invalid signature. Please sign your prepared message.');
80
+ await this.peer.base.append({type: 'msg', value: prepared_message, hash : signature, nonce: nonce });
81
+ }
82
+
83
+ async generateTx(address, command_hash, nonce) {
84
+ return await this.peer.protocol_instance.generateTx(this.getPeerBootstrap(),
85
+ this.getPeerMsbBootstrap(), this.getPeerValidatorAddress(), this.getPeerWriterKey(),
86
+ address, command_hash, nonce);
87
+ }
88
+
89
+ async prepareTxCommand(command){
90
+ return await this.peer.protocol_instance.mapTxCommand(command);
91
+ }
92
+
93
+ /**
94
+ * To pass a TX, an ed25519 signature has to be provided over the given tx + nonce.
95
+ *
96
+ * Signing steps:
97
+ * let nonce = api.generateNonce()
98
+ * let tx = api.generateTx(address, sha256(JSON.stringify(api.prepareTxCommand(command))), nonce)
99
+ * let sig = your_sign_lib.sign(tx + nonce, address)
100
+ * api.tx(tx, api.prepareTxCommand(command), address, sig, nonce)
101
+ *
102
+ * @param tx
103
+ * @param prepared_command
104
+ * @param address
105
+ * @param signature
106
+ * @param nonce
107
+ * @param sim
108
+ * @returns {Promise<boolean>}
109
+ */
110
+ async tx(tx, prepared_command, address, signature, nonce, sim = false ){
111
+ if(false === this.api_tx_exposed) throw new Error('Transactions not exposed in API.');
112
+ if(typeof prepared_command !== 'object') throw new Error('prepared_command must be an object.');
113
+ if(typeof prepared_command.type !== 'string') throw new Error('prepared_command.type must exist and be a string.');
114
+ if(prepared_command.value === undefined) throw new Error('prepared_command.value is missing.');
115
+ if(b4a.byteLength(jsonStringify(prepared_command)) > this.peer.protocol_instance.txMaxBytes()) throw new Error('prepared_command too large.');
116
+ if(b4a.byteLength(jsonStringify(address)) > 64) throw new Error('Address too large.');
117
+ if(b4a.byteLength(jsonStringify(signature)) > 128) throw new Error('Signature too large.');
118
+ if(b4a.byteLength(jsonStringify(nonce)) > 256) throw new Error('Nonce too large.');
119
+ if(b4a.toString(b4a.from(signature, 'hex'), 'hex') !== signature) throw new Error('Invalid signature.');
120
+ if(b4a.toString(b4a.from(address, 'hex'), 'hex') !== address) throw new Error('Invalid address.');
121
+ const verified = this.peer.wallet.verify(signature, tx + nonce, address);
122
+ if(false === verified) throw new Error('Invalid signature.');
123
+ const content_hash = await this.peer.createHash('sha256', this.peer.protocol_instance.safeJsonStringify(prepared_command));
124
+ let _tx = await this.generateTx(address, content_hash, nonce);
125
+ if(tx !== _tx) throw new Error('Invalid TX.');
126
+ while(null !== this.peer.protocol_instance.surrogate_tx) await this.peer.sleep(3);
127
+ this.peer.protocol_instance.surrogate_tx = { tx : ''+tx, nonce : ''+nonce, signature : ''+signature, address : ''+address };
128
+ let res = false;
129
+ try{
130
+ if(true === sim){
131
+ while(true === this.peer.protocol_instance.sim) await this.peer.sleep(3);
132
+ this.peer.protocol_instance.sim = true;
133
+ }
134
+ const subject = { command : prepared_command.value, validator : ''+this.getPeerValidatorAddress() };
135
+ res = await this.peer.protocol_instance.tx(subject);
136
+ } catch(e){ console.log(e) }
137
+ const err = this.peer.protocol_instance.getError(res);
138
+ if(null !== err){
139
+ console.log(err.message);
140
+ }
141
+ this.peer.protocol_instance.sim = false;
142
+ this.peer.protocol_instance.surrogate_tx = null;
143
+ return res;
4
144
  }
5
145
 
6
146
  async getAdmin(signed = true){
package/src/functions.js CHANGED
@@ -332,6 +332,7 @@ export async function tx(input, peer){
332
332
  let res = false;
333
333
  try{
334
334
  if(splitted.sim !== undefined && parseInt(splitted.sim) === 1){
335
+ while(true === peer.protocol_instance.sim) await peer.sleep(3);
335
336
  peer.protocol_instance.sim = true;
336
337
  }
337
338
  res = await peer.protocol_instance.tx(splitted);
package/src/index.js CHANGED
@@ -114,7 +114,6 @@ export class Peer extends ReadyResource {
114
114
  post_tx.value.ch === content_hash &&
115
115
  _this.wallet.verify(post_tx.value.ws, post_tx.value.tx + post_tx.value.wn, op.value.wp) &&
116
116
  _this.wallet.verify(post_tx.value.is, post_tx.value.tx + post_tx.value.in, op.value.ipk) &&
117
- _this.wallet.verify(op.value.hash, post_tx.value.tx + post_tx.value.ch + op.value.nonce, post_tx.value.ipk) &&
118
117
  post_tx.value.tx === await _this.protocol_instance.generateTx(
119
118
  _this.bootstrap, _this.msb.bootstrap,
120
119
  post_tx.value.wp, post_tx.value.i, post_tx.value.ipk,
@@ -493,7 +492,7 @@ export class Peer extends ReadyResource {
493
492
  if(this.validator_stream === null) return;
494
493
  let _msg = safeClone(msg);
495
494
  if(_msg['ts'] !== undefined) delete _msg['ts'];
496
- await this.validator_stream.send(b4a.from(jsonStringify(_msg)));
495
+ try{ await this.validator_stream.send(b4a.from(jsonStringify(_msg))); } catch(e){ }
497
496
  }
498
497
 
499
498
  async getValidatorWriterKey(address){
@@ -554,10 +553,7 @@ export class Peer extends ReadyResource {
554
553
 
555
554
  async initContract(){
556
555
  this.init_contract_starting = true;
557
- this.protocol_instance = new this.protocol({
558
- peer : this,
559
- base : this.base
560
- });
556
+ this.protocol_instance = new this.protocol(this, this.base, this.options);
561
557
  await this.protocol_instance.extendApi();
562
558
  this.contract_instance = new this.contract(this.protocol_instance);
563
559
  }
@@ -651,12 +647,10 @@ export class Peer extends ReadyResource {
651
647
  const msb_tx = await view_session.get(tx);
652
648
  await view_session.close();
653
649
  if(null !== msb_tx){
654
- msb_tx['dispatch'] = this.protocol_instance.prepared_transactions_content[tx];
650
+ msb_tx['dispatch'] = this.protocol_instance.prepared_transactions_content[tx].dispatch;
655
651
  msb_tx['msbsl'] = msbsl;
656
- msb_tx['ipk'] = this.wallet.publicKey;
652
+ msb_tx['ipk'] = this.protocol_instance.prepared_transactions_content[tx].ipk;
657
653
  msb_tx['wp'] = this.validator;
658
- msb_tx['nonce'] = this.protocol_instance.generateNonce();
659
- msb_tx['hash'] = this.wallet.sign(tx + await this.createHash('sha256', jsonStringify(msb_tx['dispatch'])) + msb_tx['nonce']);
660
654
  delete this.tx_pool[tx];
661
655
  delete this.protocol_instance.prepared_transactions_content[tx];
662
656
  await this.base.append({ type: 'tx', key: tx, value: msb_tx });
@@ -706,7 +700,7 @@ export class Peer extends ReadyResource {
706
700
  secretKey: b4a.from(this.wallet.secretKey, 'hex')
707
701
  };
708
702
 
709
- this.swarm = new Hyperswarm({ keyPair, bootstrap: this.dhtBootstrap });
703
+ this.swarm = new Hyperswarm({ keyPair, randomPunchInterval: 500, bootstrap: this.dhtBootstrap });
710
704
  this.dhtNode = this.swarm.dht;
711
705
 
712
706
  console.log(`Writer key: ${this.writerLocalKey}`)
package/src/protocol.js CHANGED
@@ -3,10 +3,10 @@ import {ProtocolApi} from './api.js';
3
3
  import Wallet from 'trac-wallet';
4
4
 
5
5
  class Protocol{
6
- constructor(options = {}) {
7
- this.api = new ProtocolApi({ peer : options.peer });
8
- this.base = options.base || null;
9
- this.peer = options.peer || null;
6
+ constructor(peer, base, options = {}) {
7
+ this.api = new ProtocolApi(peer, options);
8
+ this.base = base;
9
+ this.peer = peer;
10
10
  this.options = options;
11
11
  this.input = null;
12
12
  this.tokenized_input = null;
@@ -15,10 +15,10 @@ class Protocol{
15
15
  this.safeJsonStringify = jsonStringify;
16
16
  this.safeJsonParse = jsonParse;
17
17
  this.safeClone = safeClone;
18
- this.nonce = 0;
19
18
  this.prepared_transactions_content = {};
20
19
  this.features = {};
21
20
  this.sim = false;
21
+ this.surrogate_tx = false;
22
22
  }
23
23
 
24
24
  featMaxBytes(){
@@ -91,10 +91,10 @@ class Protocol{
91
91
  this.features[key] = feature;
92
92
  }
93
93
 
94
- async generateTx(bootstrap, msb_bootstrap, validator_writer_key, local_writer_key, local_public_key, content_hash, nonce){
94
+ async generateTx(bootstrap, msb_bootstrap, validator_public_key, local_writer_key, local_public_key, content_hash, nonce){
95
95
  let tx = bootstrap + '-' +
96
96
  msb_bootstrap + '-' +
97
- validator_writer_key + '-' +
97
+ validator_public_key + '-' +
98
98
  local_writer_key + '-' +
99
99
  local_public_key + '-' +
100
100
  content_hash + '-' +
@@ -106,16 +106,16 @@ class Protocol{
106
106
  const storage = new SimStorage(this.peer);
107
107
  const null_hex = '0000000000000000000000000000000000000000000000000000000000000000';
108
108
  let nonce = this.generateNonce();
109
- const content_hash = await this.peer.createHash('sha256', JSON.stringify(obj));
109
+ const content_hash = await this.peer.createHash('sha256', this.safeJsonStringify(obj));
110
110
  let tx = await this.generateTx(this.peer.bootstrap, this.peer.msb.bootstrap, null_hex,
111
- this.peer.writerLocalKey, this.peer.wallet.publicKey, content_hash, nonce);
111
+ this.peer.writerLocalKey, this.surrogate_tx !== null ? this.surrogate_tx.address : this.peer.wallet.publicKey, content_hash, nonce);
112
112
  const op = {
113
113
  type : 'tx',
114
114
  key : tx,
115
115
  value : {
116
116
  dispatch : obj,
117
117
  value : {
118
- ipk : this.peer.wallet.publicKey,
118
+ ipk : this.surrogate_tx !== null ? this.surrogate_tx.address : this.peer.wallet.publicKey,
119
119
  wp : null_hex
120
120
  }
121
121
  }
@@ -134,24 +134,36 @@ class Protocol{
134
134
  obj.type !== undefined &&
135
135
  obj.value !== undefined)
136
136
  {
137
- this.nonce = this.generateNonce();
138
- const content_hash = await this.peer.createHash('sha256', JSON.stringify(obj));
139
- let tx = await this.generateTx(this.peer.bootstrap, this.peer.msb.bootstrap, validator_pub_key,
140
- this.peer.writerLocalKey, this.peer.wallet.publicKey, content_hash, this.nonce);
141
- const signature = this.peer.wallet.sign(tx + this.nonce);
137
+
138
+ let tx, signature, nonce, publicKey;
139
+ const content_hash = await this.peer.createHash('sha256', this.safeJsonStringify(obj));
140
+
141
+ if(this.surrogate_tx !== null) {
142
+ nonce = this.surrogate_tx.nonce;
143
+ tx = this.surrogate_tx.tx;
144
+ signature = this.surrogate_tx.signature;
145
+ publicKey = this.surrogate_tx.address;
146
+ } else {
147
+ nonce = this.generateNonce();
148
+ tx = await this.generateTx(this.peer.bootstrap, this.peer.msb.bootstrap, validator_pub_key,
149
+ this.peer.writerLocalKey, this.peer.wallet.publicKey, content_hash, nonce);
150
+ signature = this.peer.wallet.sign(tx + nonce);
151
+ publicKey = this.peer.wallet.publicKey;
152
+ }
153
+
142
154
  this.peer.emit('tx', {
143
155
  op: 'pre-tx',
144
156
  tx: tx,
145
157
  is: signature,
146
158
  wp : validator_pub_key,
147
159
  i: this.peer.writerLocalKey,
148
- ipk: this.peer.wallet.publicKey,
160
+ ipk: publicKey,
149
161
  ch : content_hash,
150
- in : this.nonce,
162
+ in : nonce,
151
163
  bs : this.peer.bootstrap,
152
164
  mbs : this.peer.msb.bootstrap
153
165
  });
154
- this.prepared_transactions_content[tx] = obj;
166
+ this.prepared_transactions_content[tx] = { dispatch : obj, ipk : publicKey };
155
167
  } else {
156
168
  throw Error('broadcastTransaction(writer, obj): Cannot prepare transaction. Please make sure inputs and local writer are set.');
157
169
  }
@@ -174,7 +186,20 @@ class Protocol{
174
186
  return null;
175
187
  }
176
188
 
177
- async tx(subject){ }
189
+ async mapTxCommand(command){
190
+ return null;
191
+ }
192
+
193
+ async tx(subject){
194
+ const obj = this.mapTxCommand(subject.command);
195
+ if(null !== obj) {
196
+ return await this.broadcastTransaction(subject.validator,{
197
+ type : obj.type,
198
+ value : obj.value
199
+ });
200
+ }
201
+ return new Error('HyperMallProtocol::tx(): command not found.');
202
+ }
178
203
 
179
204
  async customCommand(input){ }
180
205