@leofcoin/codec-format-interface 1.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/.gitattributes ADDED
@@ -0,0 +1,2 @@
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 vandeurenglenn
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,2 @@
1
+ # codec-format-interface
2
+
package/dist/index.js ADDED
@@ -0,0 +1,523 @@
1
+ 'use strict';
2
+
3
+ var bs32 = require('@vandeurenglenn/base32');
4
+ var bs58 = require('@vandeurenglenn/base58');
5
+ var isHex = require('@vandeurenglenn/is-hex');
6
+ var varint = require('varint');
7
+ var createKeccakHash = require('keccak');
8
+
9
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
10
+
11
+ var bs32__default = /*#__PURE__*/_interopDefaultLegacy(bs32);
12
+ var bs58__default = /*#__PURE__*/_interopDefaultLegacy(bs58);
13
+ var isHex__default = /*#__PURE__*/_interopDefaultLegacy(isHex);
14
+ var varint__default = /*#__PURE__*/_interopDefaultLegacy(varint);
15
+ var createKeccakHash__default = /*#__PURE__*/_interopDefaultLegacy(createKeccakHash);
16
+
17
+ class BasicInterface {
18
+ #handleDecode() {
19
+ if (!this.decode) throw new Error('bad implementation: needs decode func')
20
+ this.decode();
21
+ }
22
+
23
+ #handleEncode() {
24
+ if (!this.encode) throw new Error('bad implementation: needs encode func')
25
+ this.encode();
26
+ }
27
+ isHex(string) {
28
+ return isHex__default["default"](string)
29
+ }
30
+ isBase32(string) {
31
+ return base32.isBase32(string)
32
+ }
33
+ isBase58(string) {
34
+ return base58.isBase32(string)
35
+ }
36
+ /**
37
+ * @param {String} encoded
38
+ */
39
+ fromBs32(encoded) {
40
+ this.encoded = bs32__default["default"].decode(encoded);
41
+ return this.#handleDecode()
42
+ }
43
+
44
+ /**
45
+ * @param {String} encoded
46
+ */
47
+ fromBs58(encoded) {
48
+ this.encoded = bs58__default["default"].decode(encoded);
49
+ return this.#handleDecode()
50
+ }
51
+
52
+ async toArray() {
53
+ const array = [];
54
+ for await (const value of this.encoded.values()) {
55
+ array.push(value);
56
+ }
57
+ return array
58
+ }
59
+
60
+ fromString(string) {
61
+ this.encoded = new Uint8Array(string.split(','));
62
+ return this.#handleDecode()
63
+ }
64
+
65
+ fromArray(array) {
66
+ this.encoded = new Uint8Array([...array]);
67
+ return this.#handleDecode()
68
+ }
69
+
70
+ /**
71
+ * @param {Buffer} encoded
72
+ */
73
+ fromEncoded(encoded) {
74
+ this.encoded = encoded;
75
+ return this.#handleDecode()
76
+ }
77
+
78
+ /**
79
+ * @param {String} encoded
80
+ */
81
+ fromHex(encoded) {
82
+ this.encoded = Buffer.from(encoded, 'hex');
83
+ return this.#handleDecode()
84
+ }
85
+
86
+ toString(encoding = 'utf8') {
87
+ if (!this.encoded) this.#handleEncode();
88
+ return this.encoded.toString(encoding)
89
+ }
90
+
91
+ /**
92
+ * @return {String} encoded
93
+ */
94
+ toHex() {
95
+ return this.toString('hex')
96
+ }
97
+
98
+ /**
99
+ * @return {String} encoded
100
+ */
101
+ toBs32() {
102
+ if (!this.encoded) this.#handleEncode();
103
+ return bs32__default["default"].encode(this.encoded)
104
+ }
105
+
106
+ /**
107
+ * @return {String} encoded
108
+ */
109
+ toBs58() {
110
+ if (!this.encoded) this.#handleEncode();
111
+ return bs58__default["default"].encode(this.encoded)
112
+ }
113
+
114
+ /**
115
+ * @param {Object} data
116
+ */
117
+ create(data) {
118
+ const decoded = {};
119
+ if (this.keys?.length > 0) {
120
+ for (const key of this.keys) {
121
+ Object.defineProperties(decoded, {
122
+ [key]: {
123
+ enumerable: true,
124
+ configurable: true,
125
+ set: (val) => value = data[key],
126
+ get: () => data[key]
127
+ }
128
+ });
129
+ }
130
+
131
+ this.decoded = decoded;
132
+ this.encode();
133
+ }
134
+ }
135
+ }
136
+
137
+ var codecs = {
138
+ // just a hash
139
+ 'disco-hash': {
140
+ codec: parseInt('30', 16),
141
+ hashAlg: 'dbl-keccak-256', // ,
142
+ // testnet: 'olivia'
143
+ },
144
+ 'peernet-peer-response': {
145
+ codec: parseInt('707072', 16),
146
+ hashAlg: 'keccak-256',
147
+ },
148
+ 'peernet-peer': {
149
+ codec: parseInt('7070', 16),
150
+ hashAlg: 'keccak-256',
151
+ },
152
+ 'peernet-dht': {
153
+ codec: parseInt('706468', 16),
154
+ hashAlg: 'keccak-256',
155
+ },
156
+ 'peernet-dht-response': {
157
+ codec: parseInt('706472', 16),
158
+ hashAlg: 'keccak-256',
159
+ },
160
+ // data
161
+ 'peernet-data': {
162
+ codec: parseInt('706461', 16),
163
+ hashAlg: 'keccak-256',
164
+ },
165
+ 'peernet-data-response': {
166
+ codec: parseInt('70646172', 16),
167
+ hashAlg: 'keccak-256',
168
+ },
169
+ // message
170
+ 'peernet-message': {
171
+ codec: parseInt('706d65', 16),
172
+ hashAlg: 'keccak-256',
173
+ },
174
+ // pubsub
175
+ 'peernet-ps': {
176
+ codec: parseInt('707073', 16),
177
+ hashAlg: 'keccak-256',
178
+ },
179
+ 'peernet-response': {
180
+ codec: parseInt('7072', 16),
181
+ hashAlg: 'keccak-256',
182
+ },
183
+ 'peernet-request': {
184
+ codec: parseInt('707271', 16),
185
+ hashAlg: 'keccak-256',
186
+ },
187
+ // normal block
188
+ 'leofcoin-block': {
189
+ codec: parseInt('6c62', 16),
190
+ hashAlg: 'dbl-keccak-512', // ,
191
+ // testnet: 'olivia'
192
+ },
193
+ 'leofcoin-tx': {
194
+ codec: parseInt('6c74', 16),
195
+ hashAlg: 'dbl-keccak-512', // ,
196
+ // testnet: 'olivia'
197
+ },
198
+ // itx
199
+ 'leofcoin-itx': {
200
+ codec: parseInt('6c69', 16),
201
+ hashAlg: 'keccak-512', // ,
202
+ // testnet: 'olivia'
203
+ },
204
+ // peer reputation
205
+ 'leofcoin-pr': {
206
+ codec: parseInt('6c70', 16),
207
+ hashAlg: 'keccak-256', // ,
208
+ // testnet: 'olivia'
209
+ },
210
+ // chat message
211
+ 'chat-message': {
212
+ codec: parseInt('636d', 16),
213
+ hashAlg: 'dbl-keccak-256',
214
+ },
215
+ };
216
+
217
+ class PeernetCodec extends BasicInterface {
218
+ get codecs() {
219
+ return {...globalThis.peernet.codecs, ...codecs}
220
+ }
221
+ constructor(buffer) {
222
+ super();
223
+ if (buffer) {
224
+ if (buffer instanceof Uint8Array) {
225
+ const codec = varint__default["default"].decode(buffer);
226
+ const name = this.getCodecName(codec);
227
+ if (name) {
228
+ this.name = name;
229
+ this.encoded = buffer;
230
+ this.decode(buffer);
231
+ } else {
232
+ this.encode(buffer);
233
+ }
234
+ } else if (buffer instanceof ArrayBuffer) {
235
+ const encoded = new Uint8Array(buffer.byteLength);
236
+
237
+ for (let i = 0; i < buffer.byteLength; i++) {
238
+ encoded[i] = buffer[i];
239
+ }
240
+ this.encoded = encoded;
241
+ // this.encoded = new Uint8Array(buffer, buffer.byteOffset, buffer.byteLength)
242
+ this.decode(buffer);
243
+ return
244
+ }
245
+ if (typeof buffer === 'string') {
246
+ if (this.codecs[buffer]) this.fromName(buffer);
247
+ else if (this.isHex(buffer)) this.fromHex(buffer);
248
+ else if (this.isBase32(buffer)) this.fromBs32(buffer);
249
+ else if (this.isBase58(buffer)) this.fromBs58(buffer);
250
+ else throw new Error(`unsupported string ${buffer}`)
251
+ }
252
+ if (!isNaN(buffer)) if (this.codecs[this.getCodecName(buffer)]) this.fromCodec(buffer);
253
+ }
254
+ }
255
+
256
+ fromEncoded(encoded) {
257
+ const codec = varint__default["default"].decode(encoded);
258
+ const name = this.getCodecName(codec);
259
+ this.name = name;
260
+ this.encoded = encoded;
261
+ this.decode(encoded);
262
+ }
263
+
264
+ getCodec(name) {
265
+ return this.codecs[name].codec
266
+ }
267
+
268
+ getCodecName(codec) {
269
+ return Object.keys(this.codecs).reduce((p, c) => {
270
+ const item = this.codecs[c];
271
+ if (item.codec === codec) return c;
272
+ else return p;
273
+ }, undefined)
274
+ }
275
+
276
+ getHashAlg(name) {
277
+ return this.codecs[name].hashAlg
278
+ }
279
+
280
+ fromCodec(codec) {
281
+ this.name = this.getCodecName(codec);
282
+ this.hashAlg = this.getHashAlg(this.name);
283
+
284
+ this.codec = this.getCodec(this.name);
285
+ this.codecBuffer = varint__default["default"].encode(codec);
286
+ }
287
+
288
+ fromName(name) {
289
+ const codec = this.getCodec(name);
290
+ this.name = name;
291
+ this.codec = codec;
292
+ this.hashAlg = this.getHashAlg(name);
293
+ this.codecBuffer = varint__default["default"].encode(codec);
294
+ }
295
+
296
+ decode() {
297
+ const codec = varint__default["default"].decode(this.encoded);
298
+ this.fromCodec(codec);
299
+ }
300
+
301
+ encode() {
302
+ const codec = varint__default["default"].encode(this.decoded);
303
+ this.encoded = codec;
304
+ return this.encoded
305
+ }
306
+ }
307
+
308
+ class CodecHash extends BasicInterface {
309
+ constructor(buffer, options = {}) {
310
+ super();
311
+ if (options.name) this.name = options.name;
312
+ else this.name = 'disco-hash';
313
+ if (options.codecs) this.codecs = options.codecs;
314
+ if (buffer) {
315
+ if (buffer instanceof Uint8Array) {
316
+ this.discoCodec = new PeernetCodec(buffer, this.codecs);
317
+ const name = this.discoCodec.name;
318
+
319
+ if (name) {
320
+ this.name = name;
321
+ this.decode(buffer);
322
+ } else {
323
+ this.encode(buffer);
324
+ }
325
+ }
326
+
327
+ if (typeof buffer === 'string') {
328
+ if (this.isHex(buffer)) this.fromHex(buffer);
329
+ if (this.isBase32(buffer)) this.fromBs32(buffer);
330
+ else if (this.isBase58(buffer)) this.fromBs58(buffer);
331
+ else throw new Error(`unsupported string ${buffer}`)
332
+ } else if (typeof buffer === 'object') this.fromJSON(buffer);
333
+ }
334
+ }
335
+
336
+ get prefix() {
337
+ const length = this.length;
338
+ const uint8Array = new Uint8Array(length.length + this.discoCodec.codecBuffer.length);
339
+ uint8Array.set(length);
340
+ uint8Array.set(this.discoCodec.codecBuffer, length.length);
341
+
342
+ return uint8Array
343
+ }
344
+
345
+ get length() {
346
+ return varint__default["default"].encode(this.size)
347
+ }
348
+
349
+ get buffer() {
350
+ return this.encoded
351
+ }
352
+
353
+ get hash() {
354
+ return this.encoded
355
+ }
356
+
357
+ fromJSON(json) {
358
+ return this.encode(Buffer.from(JSON.stringify(json)))
359
+ }
360
+
361
+ encode(buffer, name) {
362
+ if (!this.name && name) this.name = name;
363
+ if (!buffer) buffer = this.buffer;
364
+ this.discoCodec = new PeernetCodec(this.name, this.codecs);
365
+ this.discoCodec.fromName(this.name);
366
+ let hashAlg = this.discoCodec.hashAlg;
367
+ if (hashAlg.includes('dbl')) {
368
+ hashAlg = hashAlg.replace('dbl-', '');
369
+ buffer = createKeccakHash__default["default"](hashAlg.replace('-', '')).update(buffer).digest();
370
+ }
371
+ this.digest = createKeccakHash__default["default"](hashAlg.replace('-', '')).update(buffer).digest();
372
+ this.size = this.digest.length;
373
+
374
+ this.codec = this.discoCodec.encode();
375
+ this.codec = this.discoCodec.codecBuffer;
376
+ const uint8Array = new Uint8Array(this.digest.length + this.prefix.length);
377
+ uint8Array.set(this.prefix);
378
+ uint8Array.set(this.digest, this.prefix.length);
379
+
380
+ this.encoded = uint8Array;
381
+
382
+ return this.encoded
383
+ }
384
+
385
+ validate(buffer) {
386
+ if (Buffer.isBuffer(buffer)) {
387
+ const codec = varint__default["default"].decode(buffer);
388
+ if (this.codecs[codec]) {
389
+ this.decode(buffer);
390
+ } else {
391
+ this.encode(buffer);
392
+ }
393
+ }
394
+ if (typeof buffer === 'string') {
395
+ if (this.isHex(buffer)) this.fromHex(buffer);
396
+ if (this.isBase32(buffer)) this.fromBs32(buffer);
397
+ }
398
+ if (typeof buffer === 'object') this.fromJSON(buffer);
399
+ }
400
+
401
+ decode(buffer) {
402
+ this.encoded = buffer;
403
+ const codec = varint__default["default"].decode(buffer);
404
+
405
+ this.discoCodec = new PeernetCodec(codec, this.codecs);
406
+ // TODO: validate codec
407
+ buffer = buffer.slice(varint__default["default"].decode.bytes);
408
+ this.size = varint__default["default"].decode(buffer);
409
+ this.digest = buffer.slice(varint__default["default"].decode.bytes);
410
+ if (this.digest.length !== this.size) {
411
+ throw new Error(`hash length inconsistent: 0x${this.encoded.toString('hex')}`)
412
+ }
413
+
414
+ // const discoCodec = new Codec(codec, this.codecs)
415
+
416
+ this.name = this.discoCodec.name;
417
+
418
+
419
+ this.size = this.digest.length;
420
+
421
+ return {
422
+ codec: this.codec,
423
+ name: this.name,
424
+ size: this.size,
425
+ length: this.length,
426
+ digest: this.digest,
427
+ }
428
+ }
429
+ }
430
+
431
+ class FormatInterface extends BasicInterface {
432
+ /**
433
+ * @param {Buffer|String|Object} buffer - data - The data needed to create the desired message
434
+ * @param {Object} proto - {encode, decode}
435
+ * @param {Object} options - {hashFormat, name}
436
+ */
437
+ constructor(buffer, proto, options = {}) {
438
+ super();
439
+ this.protoEncode = proto.encode;
440
+ this.protoDecode = proto.decode;
441
+ this.hashFormat = options.hashFormat || 'bs32';
442
+ if (options.name) this.name = options.name;
443
+ if (buffer instanceof Uint8Array) this.fromUint8Array(buffer);
444
+ else if (buffer instanceof ArrayBuffer) this.fromArrayBuffer(buffer);
445
+ else if (buffer.name === options.name) return buffer
446
+ else if (buffer instanceof String) {
447
+ if (this.isHex(buffer)) this.fromHex(buffer);
448
+ else if (this.isBase32(buffer)) this.fromBs32(buffer);
449
+ else if (this.isBase58(buffer)) this.fromBs58(buffer);
450
+ else throw new Error(`unsupported string ${buffer}`)
451
+ } else {
452
+ this.create(buffer);
453
+ }
454
+ }
455
+
456
+ /**
457
+ * @return {PeernetHash}
458
+ */
459
+ get peernetHash() {
460
+ return new CodecHash(this.decoded, {name: this.name})
461
+ }
462
+
463
+ /**
464
+ * @return {peernetHash}
465
+ */
466
+ get hash() {
467
+ const upper = this.hashFormat.charAt(0).toUpperCase();
468
+ const format = `${upper}${this.hashFormat.substring(1, this.hashFormat.length)}`;
469
+ return this.peernetHash[`to${format}`]()
470
+ }
471
+
472
+ /**
473
+ * @return {Object}
474
+ */
475
+ decode() {
476
+ let encoded = this.encoded;
477
+ const discoCodec = new PeernetCodec(this.encoded);
478
+ encoded = encoded.slice(discoCodec.codecBuffer.length);
479
+ this.name = discoCodec.name;
480
+ this.decoded = this.protoDecode(encoded);
481
+ return this.decoded
482
+ }
483
+
484
+ /**
485
+ * @return {Buffer}
486
+ */
487
+ encode(decoded) {
488
+ if (!decoded) decoded = this.decoded;
489
+ const codec = new PeernetCodec(this.name);
490
+ const encoded = this.protoEncode(decoded);
491
+ const uint8Array = new Uint8Array(encoded.length + codec.codecBuffer.length);
492
+ uint8Array.set(codec.codecBuffer);
493
+ uint8Array.set(encoded, codec.codecBuffer.length);
494
+ this.encoded = uint8Array;
495
+ return this.encoded
496
+ }
497
+
498
+ hasCodec() {
499
+ if (!this.encoded) return false
500
+ const codec = new PeernetCodec(this.encoded);
501
+ if (codec.name) return true
502
+ }
503
+
504
+ fromUint8Array(buffer) {
505
+ this.encoded = buffer;
506
+ if (!this.hasCodec()) this.create(
507
+ JSON.parse(new TextDecoder().decode(this.encoded))
508
+ );
509
+ else this.decode();
510
+ }
511
+
512
+ fromArrayBuffer(buffer) {
513
+ this.encoded = new Uint8Array(buffer, buffer.byteOffset, buffer.byteLength);
514
+ if (!this.hasCodec()) this.create(
515
+ JSON.parse(new TextDecoder().decode(this.encoded))
516
+ );
517
+ else this.decode();
518
+ }
519
+ }
520
+
521
+ var index = { codecs, Codec: PeernetCodec, CodecHash, FormatInterface, BasicInterface };
522
+
523
+ module.exports = index;
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@leofcoin/codec-format-interface",
3
+ "version": "1.0.1",
4
+ "description": "",
5
+ "main": "dist/index.js",
6
+ "module": "src/index.js",
7
+ "scripts": {
8
+ "test": "echo \"Error: no test specified\" && exit 1",
9
+ "c": "rollup -c"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/leofcoin/codec-format-interface.git"
14
+ },
15
+ "keywords": [],
16
+ "author": "",
17
+ "license": "MIT",
18
+ "bugs": {
19
+ "url": "https://github.com/leofcoin/codec-format-interface/issues"
20
+ },
21
+ "homepage": "https://github.com/leofcoin/codec-format-interface#readme",
22
+ "dependencies": {
23
+ "@vandeurenglenn/base32": "^1.1.0",
24
+ "@vandeurenglenn/base58": "^1.1.0",
25
+ "@vandeurenglenn/is-hex": "^1.0.0",
26
+ "keccak": "^3.0.2",
27
+ "varint": "^6.0.0"
28
+ },
29
+ "devDependencies": {
30
+ "rollup": "^2.74.1",
31
+ "tape": "^5.5.3"
32
+ }
33
+ }
@@ -0,0 +1,7 @@
1
+ export default [{
2
+ input: ['src/index.js'],
3
+ output: [{
4
+ dir: 'dist',
5
+ format: 'cjs'
6
+ }]
7
+ }]
@@ -0,0 +1,123 @@
1
+ import bs32 from '@vandeurenglenn/base32';
2
+ import bs58 from '@vandeurenglenn/base58';
3
+ import isHex from '@vandeurenglenn/is-hex';
4
+
5
+ export default class BasicInterface {
6
+ #handleDecode() {
7
+ if (!this.decode) throw new Error('bad implementation: needs decode func')
8
+ this.decode()
9
+ }
10
+
11
+ #handleEncode() {
12
+ if (!this.encode) throw new Error('bad implementation: needs encode func')
13
+ this.encode()
14
+ }
15
+ isHex(string) {
16
+ return isHex(string)
17
+ }
18
+ isBase32(string) {
19
+ return base32.isBase32(string)
20
+ }
21
+ isBase58(string) {
22
+ return base58.isBase32(string)
23
+ }
24
+ /**
25
+ * @param {String} encoded
26
+ */
27
+ fromBs32(encoded) {
28
+ this.encoded = bs32.decode(encoded)
29
+ return this.#handleDecode()
30
+ }
31
+
32
+ /**
33
+ * @param {String} encoded
34
+ */
35
+ fromBs58(encoded) {
36
+ this.encoded = bs58.decode(encoded)
37
+ return this.#handleDecode()
38
+ }
39
+
40
+ async toArray() {
41
+ const array = []
42
+ for await (const value of this.encoded.values()) {
43
+ array.push(value)
44
+ }
45
+ return array
46
+ }
47
+
48
+ fromString(string) {
49
+ this.encoded = new Uint8Array(string.split(','))
50
+ return this.#handleDecode()
51
+ }
52
+
53
+ fromArray(array) {
54
+ this.encoded = new Uint8Array([...array])
55
+ return this.#handleDecode()
56
+ }
57
+
58
+ /**
59
+ * @param {Buffer} encoded
60
+ */
61
+ fromEncoded(encoded) {
62
+ this.encoded = encoded
63
+ return this.#handleDecode()
64
+ }
65
+
66
+ /**
67
+ * @param {String} encoded
68
+ */
69
+ fromHex(encoded) {
70
+ this.encoded = Buffer.from(encoded, 'hex')
71
+ return this.#handleDecode()
72
+ }
73
+
74
+ toString(encoding = 'utf8') {
75
+ if (!this.encoded) this.#handleEncode()
76
+ return this.encoded.toString(encoding)
77
+ }
78
+
79
+ /**
80
+ * @return {String} encoded
81
+ */
82
+ toHex() {
83
+ return this.toString('hex')
84
+ }
85
+
86
+ /**
87
+ * @return {String} encoded
88
+ */
89
+ toBs32() {
90
+ if (!this.encoded) this.#handleEncode()
91
+ return bs32.encode(this.encoded)
92
+ }
93
+
94
+ /**
95
+ * @return {String} encoded
96
+ */
97
+ toBs58() {
98
+ if (!this.encoded) this.#handleEncode()
99
+ return bs58.encode(this.encoded)
100
+ }
101
+
102
+ /**
103
+ * @param {Object} data
104
+ */
105
+ create(data) {
106
+ const decoded = {}
107
+ if (this.keys?.length > 0) {
108
+ for (const key of this.keys) {
109
+ Object.defineProperties(decoded, {
110
+ [key]: {
111
+ enumerable: true,
112
+ configurable: true,
113
+ set: (val) => value = data[key],
114
+ get: () => data[key]
115
+ }
116
+ })
117
+ }
118
+
119
+ this.decoded = decoded
120
+ this.encode()
121
+ }
122
+ }
123
+ }
@@ -0,0 +1,93 @@
1
+ import BasicInterface from './basic-interface.js'
2
+ import Codec from './codec.js';
3
+ import Hash from './codec-hash.js'
4
+
5
+ export default class FormatInterface extends BasicInterface {
6
+ /**
7
+ * @param {Buffer|String|Object} buffer - data - The data needed to create the desired message
8
+ * @param {Object} proto - {encode, decode}
9
+ * @param {Object} options - {hashFormat, name}
10
+ */
11
+ constructor(buffer, proto, options = {}) {
12
+ super()
13
+ this.protoEncode = proto.encode
14
+ this.protoDecode = proto.decode
15
+ this.hashFormat = options.hashFormat || 'bs32'
16
+ if (options.name) this.name = options.name
17
+ if (buffer instanceof Uint8Array) this.fromUint8Array(buffer)
18
+ else if (buffer instanceof ArrayBuffer) this.fromArrayBuffer(buffer)
19
+ else if (buffer.name === options.name) return buffer
20
+ else if (buffer instanceof String) {
21
+ if (this.isHex(buffer)) this.fromHex(buffer)
22
+ else if (this.isBase32(buffer)) this.fromBs32(buffer)
23
+ else if (this.isBase58(buffer)) this.fromBs58(buffer)
24
+ else throw new Error(`unsupported string ${buffer}`)
25
+ } else {
26
+ this.create(buffer)
27
+ }
28
+ }
29
+
30
+ /**
31
+ * @return {PeernetHash}
32
+ */
33
+ get peernetHash() {
34
+ return new Hash(this.decoded, {name: this.name})
35
+ }
36
+
37
+ /**
38
+ * @return {peernetHash}
39
+ */
40
+ get hash() {
41
+ const upper = this.hashFormat.charAt(0).toUpperCase()
42
+ const format = `${upper}${this.hashFormat.substring(1, this.hashFormat.length)}`
43
+ return this.peernetHash[`to${format}`]()
44
+ }
45
+
46
+ /**
47
+ * @return {Object}
48
+ */
49
+ decode() {
50
+ let encoded = this.encoded;
51
+ const discoCodec = new Codec(this.encoded)
52
+ encoded = encoded.slice(discoCodec.codecBuffer.length)
53
+ this.name = discoCodec.name
54
+ this.decoded = this.protoDecode(encoded)
55
+ return this.decoded
56
+ }
57
+
58
+ /**
59
+ * @return {Buffer}
60
+ */
61
+ encode(decoded) {
62
+ if (!decoded) decoded = this.decoded;
63
+ const codec = new Codec(this.name)
64
+ const encoded = this.protoEncode(decoded)
65
+ const uint8Array = new Uint8Array(encoded.length + codec.codecBuffer.length)
66
+ uint8Array.set(codec.codecBuffer)
67
+ uint8Array.set(encoded, codec.codecBuffer.length)
68
+ this.encoded = uint8Array
69
+ return this.encoded
70
+ }
71
+
72
+ hasCodec() {
73
+ if (!this.encoded) return false
74
+ const codec = new Codec(this.encoded)
75
+ if (codec.name) return true
76
+ }
77
+
78
+ fromUint8Array(buffer) {
79
+ this.encoded = buffer
80
+ if (!this.hasCodec()) this.create(
81
+ JSON.parse(new TextDecoder().decode(this.encoded))
82
+ )
83
+ else this.decode()
84
+ }
85
+
86
+ fromArrayBuffer(buffer) {
87
+ this.encoded = new Uint8Array(buffer, buffer.byteOffset, buffer.byteLength)
88
+ if (!this.hasCodec()) this.create(
89
+ JSON.parse(new TextDecoder().decode(this.encoded))
90
+ )
91
+ else this.decode()
92
+ }
93
+ }
@@ -0,0 +1,127 @@
1
+ import createKeccakHash from 'keccak';
2
+ import varint from 'varint';
3
+ import BasicInterface from './basic-interface.js'
4
+ import Codec from './codec';
5
+
6
+ export default class CodecHash extends BasicInterface {
7
+ constructor(buffer, options = {}) {
8
+ super()
9
+ if (options.name) this.name = options.name
10
+ else this.name = 'disco-hash'
11
+ if (options.codecs) this.codecs = options.codecs
12
+ if (buffer) {
13
+ if (buffer instanceof Uint8Array) {
14
+ this.discoCodec = new Codec(buffer, this.codecs)
15
+ const name = this.discoCodec.name
16
+
17
+ if (name) {
18
+ this.name = name
19
+ this.decode(buffer)
20
+ } else {
21
+ this.encode(buffer)
22
+ }
23
+ }
24
+
25
+ if (typeof buffer === 'string') {
26
+ if (this.isHex(buffer)) this.fromHex(buffer)
27
+ if (this.isBase32(buffer)) this.fromBs32(buffer)
28
+ else if (this.isBase58(buffer)) this.fromBs58(buffer)
29
+ else throw new Error(`unsupported string ${buffer}`)
30
+ } else if (typeof buffer === 'object') this.fromJSON(buffer)
31
+ }
32
+ }
33
+
34
+ get prefix() {
35
+ const length = this.length
36
+ const uint8Array = new Uint8Array(length.length + this.discoCodec.codecBuffer.length)
37
+ uint8Array.set(length)
38
+ uint8Array.set(this.discoCodec.codecBuffer, length.length)
39
+
40
+ return uint8Array
41
+ }
42
+
43
+ get length() {
44
+ return varint.encode(this.size)
45
+ }
46
+
47
+ get buffer() {
48
+ return this.encoded
49
+ }
50
+
51
+ get hash() {
52
+ return this.encoded
53
+ }
54
+
55
+ fromJSON(json) {
56
+ return this.encode(Buffer.from(JSON.stringify(json)))
57
+ }
58
+
59
+ encode(buffer, name) {
60
+ if (!this.name && name) this.name = name;
61
+ if (!buffer) buffer = this.buffer;
62
+ this.discoCodec = new Codec(this.name, this.codecs)
63
+ this.discoCodec.fromName(this.name)
64
+ let hashAlg = this.discoCodec.hashAlg
65
+ if (hashAlg.includes('dbl')) {
66
+ hashAlg = hashAlg.replace('dbl-', '')
67
+ buffer = createKeccakHash(hashAlg.replace('-', '')).update(buffer).digest()
68
+ }
69
+ this.digest = createKeccakHash(hashAlg.replace('-', '')).update(buffer).digest()
70
+ this.size = this.digest.length
71
+
72
+ this.codec = this.discoCodec.encode();
73
+ this.codec = this.discoCodec.codecBuffer
74
+ const uint8Array = new Uint8Array(this.digest.length + this.prefix.length)
75
+ uint8Array.set(this.prefix)
76
+ uint8Array.set(this.digest, this.prefix.length)
77
+
78
+ this.encoded = uint8Array
79
+
80
+ return this.encoded
81
+ }
82
+
83
+ validate(buffer) {
84
+ if (Buffer.isBuffer(buffer)) {
85
+ const codec = varint.decode(buffer);
86
+ if (this.codecs[codec]) {
87
+ this.decode(buffer)
88
+ } else {
89
+ this.encode(buffer)
90
+ }
91
+ }
92
+ if (typeof buffer === 'string') {
93
+ if (this.isHex(buffer)) this.fromHex(buffer)
94
+ if (this.isBase32(buffer)) this.fromBs32(buffer)
95
+ }
96
+ if (typeof buffer === 'object') this.fromJSON(buffer)
97
+ }
98
+
99
+ decode(buffer) {
100
+ this.encoded = buffer
101
+ const codec = varint.decode(buffer);
102
+
103
+ this.discoCodec = new Codec(codec, this.codecs)
104
+ // TODO: validate codec
105
+ buffer = buffer.slice(varint.decode.bytes);
106
+ this.size = varint.decode(buffer);
107
+ this.digest = buffer.slice(varint.decode.bytes);
108
+ if (this.digest.length !== this.size) {
109
+ throw new Error(`hash length inconsistent: 0x${this.encoded.toString('hex')}`)
110
+ }
111
+
112
+ // const discoCodec = new Codec(codec, this.codecs)
113
+
114
+ this.name = this.discoCodec.name
115
+
116
+
117
+ this.size = this.digest.length
118
+
119
+ return {
120
+ codec: this.codec,
121
+ name: this.name,
122
+ size: this.size,
123
+ length: this.length,
124
+ digest: this.digest,
125
+ }
126
+ }
127
+ }
package/src/codec.js ADDED
@@ -0,0 +1,94 @@
1
+ import varint from 'varint';
2
+ import codecs from './codecs.js'
3
+ import BasicInterface from './basic-interface.js'
4
+
5
+ export default class PeernetCodec extends BasicInterface {
6
+ get codecs() {
7
+ return {...globalThis.peernet.codecs, ...codecs}
8
+ }
9
+ constructor(buffer) {
10
+ super()
11
+ if (buffer) {
12
+ if (buffer instanceof Uint8Array) {
13
+ const codec = varint.decode(buffer);
14
+ const name = this.getCodecName(codec)
15
+ if (name) {
16
+ this.name = name
17
+ this.encoded = buffer
18
+ this.decode(buffer)
19
+ } else {
20
+ this.encode(buffer)
21
+ }
22
+ } else if (buffer instanceof ArrayBuffer) {
23
+ const encoded = new Uint8Array(buffer.byteLength)
24
+
25
+ for (let i = 0; i < buffer.byteLength; i++) {
26
+ encoded[i] = buffer[i]
27
+ }
28
+ this.encoded = encoded
29
+ // this.encoded = new Uint8Array(buffer, buffer.byteOffset, buffer.byteLength)
30
+ this.decode(buffer)
31
+ return
32
+ }
33
+ if (typeof buffer === 'string') {
34
+ if (this.codecs[buffer]) this.fromName(buffer)
35
+ else if (this.isHex(buffer)) this.fromHex(buffer)
36
+ else if (this.isBase32(buffer)) this.fromBs32(buffer)
37
+ else if (this.isBase58(buffer)) this.fromBs58(buffer)
38
+ else throw new Error(`unsupported string ${buffer}`)
39
+ }
40
+ if (!isNaN(buffer)) if (this.codecs[this.getCodecName(buffer)]) this.fromCodec(buffer)
41
+ }
42
+ }
43
+
44
+ fromEncoded(encoded) {
45
+ const codec = varint.decode(encoded);
46
+ const name = this.getCodecName(codec)
47
+ this.name = name
48
+ this.encoded = encoded
49
+ this.decode(encoded)
50
+ }
51
+
52
+ getCodec(name) {
53
+ return this.codecs[name].codec
54
+ }
55
+
56
+ getCodecName(codec) {
57
+ return Object.keys(this.codecs).reduce((p, c) => {
58
+ const item = this.codecs[c]
59
+ if (item.codec === codec) return c;
60
+ else return p;
61
+ }, undefined)
62
+ }
63
+
64
+ getHashAlg(name) {
65
+ return this.codecs[name].hashAlg
66
+ }
67
+
68
+ fromCodec(codec) {
69
+ this.name = this.getCodecName(codec)
70
+ this.hashAlg = this.getHashAlg(this.name)
71
+
72
+ this.codec = this.getCodec(this.name)
73
+ this.codecBuffer = varint.encode(codec)
74
+ }
75
+
76
+ fromName(name) {
77
+ const codec = this.getCodec(name)
78
+ this.name = name
79
+ this.codec = codec
80
+ this.hashAlg = this.getHashAlg(name)
81
+ this.codecBuffer = varint.encode(codec)
82
+ }
83
+
84
+ decode() {
85
+ const codec = varint.decode(this.encoded);
86
+ this.fromCodec(codec)
87
+ }
88
+
89
+ encode() {
90
+ const codec = varint.encode(this.decoded)
91
+ this.encoded = codec
92
+ return this.encoded
93
+ }
94
+ }
package/src/codecs.js ADDED
@@ -0,0 +1,79 @@
1
+ export default {
2
+ // just a hash
3
+ 'disco-hash': {
4
+ codec: parseInt('30', 16),
5
+ hashAlg: 'dbl-keccak-256', // ,
6
+ // testnet: 'olivia'
7
+ },
8
+ 'peernet-peer-response': {
9
+ codec: parseInt('707072', 16),
10
+ hashAlg: 'keccak-256',
11
+ },
12
+ 'peernet-peer': {
13
+ codec: parseInt('7070', 16),
14
+ hashAlg: 'keccak-256',
15
+ },
16
+ 'peernet-dht': {
17
+ codec: parseInt('706468', 16),
18
+ hashAlg: 'keccak-256',
19
+ },
20
+ 'peernet-dht-response': {
21
+ codec: parseInt('706472', 16),
22
+ hashAlg: 'keccak-256',
23
+ },
24
+ // data
25
+ 'peernet-data': {
26
+ codec: parseInt('706461', 16),
27
+ hashAlg: 'keccak-256',
28
+ },
29
+ 'peernet-data-response': {
30
+ codec: parseInt('70646172', 16),
31
+ hashAlg: 'keccak-256',
32
+ },
33
+ // message
34
+ 'peernet-message': {
35
+ codec: parseInt('706d65', 16),
36
+ hashAlg: 'keccak-256',
37
+ },
38
+ // pubsub
39
+ 'peernet-ps': {
40
+ codec: parseInt('707073', 16),
41
+ hashAlg: 'keccak-256',
42
+ },
43
+ 'peernet-response': {
44
+ codec: parseInt('7072', 16),
45
+ hashAlg: 'keccak-256',
46
+ },
47
+ 'peernet-request': {
48
+ codec: parseInt('707271', 16),
49
+ hashAlg: 'keccak-256',
50
+ },
51
+ // normal block
52
+ 'leofcoin-block': {
53
+ codec: parseInt('6c62', 16),
54
+ hashAlg: 'dbl-keccak-512', // ,
55
+ // testnet: 'olivia'
56
+ },
57
+ 'leofcoin-tx': {
58
+ codec: parseInt('6c74', 16),
59
+ hashAlg: 'dbl-keccak-512', // ,
60
+ // testnet: 'olivia'
61
+ },
62
+ // itx
63
+ 'leofcoin-itx': {
64
+ codec: parseInt('6c69', 16),
65
+ hashAlg: 'keccak-512', // ,
66
+ // testnet: 'olivia'
67
+ },
68
+ // peer reputation
69
+ 'leofcoin-pr': {
70
+ codec: parseInt('6c70', 16),
71
+ hashAlg: 'keccak-256', // ,
72
+ // testnet: 'olivia'
73
+ },
74
+ // chat message
75
+ 'chat-message': {
76
+ codec: parseInt('636d', 16),
77
+ hashAlg: 'dbl-keccak-256',
78
+ },
79
+ }
package/src/index.js ADDED
@@ -0,0 +1,7 @@
1
+ import BasicInterface from './basic-interface.js'
2
+ import FormatInterface from './codec-format-interface.js'
3
+ import CodecHash from './codec-hash.js'
4
+ import Codec from './codec.js'
5
+ import codecs from './codecs.js'
6
+
7
+ export default { codecs, Codec, CodecHash, FormatInterface, BasicInterface }
package/test/test.js ADDED
@@ -0,0 +1,9 @@
1
+ const test = require('tape');
2
+ const {FormatInterface} = require('./../dist/index')
3
+
4
+ test('', tape => {
5
+ tape.plan(1)
6
+ const int = new FormatInterface()
7
+ console.log(int);
8
+ tape.plan('ok')
9
+ })